Add my Z80 emulator, can compile but not run (all instructions trapped)
authorNick Downing <nick@ndcode.org>
Thu, 21 Jul 2022 10:11:51 +0000 (20:11 +1000)
committerNick Downing <nick@ndcode.org>
Thu, 21 Jul 2022 10:14:35 +0000 (20:14 +1000)
Makefile
cpu_65c02.c
cpu_65c02.h
cpu_z80.c [new file with mode: 0644]
cpu_z80.h [new file with mode: 0644]
decode_65c02.py
decode_65c02.sed [new file with mode: 0644]
decode_z80.py [new file with mode: 0755]
decode_z80.sed [new file with mode: 0644]
emu_65c02.c
emu_z80.c

index b5b1d6a..38482dd 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -8,10 +8,10 @@ emu_65c02 \
 emu_65c02_alt \
 6502_functional_test.ihx \
 65C02_extended_opcodes_test.ihx \
+emu_z80 \
 emu_z80_alt \
 zexall.ihx \
 zexdoc.ihx
-#emu_z80
 
 emu_65c02: emu_65c02.o cpu_65c02.o
        ${CC} ${CFLAGS} -o $@ $^
index c29cb1a..9e7cbb4 100644 (file)
@@ -6,14 +6,12 @@
 // initialization
 void cpu_65c02_init(
   struct cpu_65c02 *self,
-  int pc,
   int (*read_byte)(int addr),
   void (*write_byte)(int addr, int data)
 ) {
   memset(self, 0, sizeof(struct cpu_65c02));
   self->regs.byte.p = 0x30; // unused bits are hard coded to 1
   self->regs.word.sp = 0x1ff; // unused byte is hard coded to 01
-  self->regs.word.pc = pc;
   self->read_byte = read_byte;
   self->write_byte = write_byte;
 }
index de6e295..a6b964c 100644 (file)
@@ -9,9 +9,6 @@
 #ifndef ALWAYS_INLINE
 #define ALWAYS_INLINE __attribute__((always_inline))
 #endif
-#ifndef PACKED
-#define PACKED __attribute__((__packed__))
-#endif
 
 #define CPU_65C02_NMI_VECTOR 0xfffa
 #define CPU_65C02_RESET_VECTOR 0xfffc
@@ -37,7 +34,7 @@
 // (this allows special memory to always look like it is little endian)
 union cpu_65c02_regs {
 #if __BYTE_ORDER == __BIG_ENDIAN
-  PACKED struct {
+  struct {
     uint16_t _fill_pc;
     uint16_t _fill_sp;
     uint8_t nf : 1;
@@ -52,7 +49,7 @@ union cpu_65c02_regs {
     uint8_t _fill_x;
     uint8_t _fill_a;
   } bit;
-  PACKED struct {
+  struct {
     uint16_t _fill_pc;
     uint8_t _fill_sph;
     uint8_t s;
@@ -61,7 +58,7 @@ union cpu_65c02_regs {
     uint8_t x;
     uint8_t a;
   } byte;
-  PACKED struct {
+  struct {
     uint16_t pc;
     uint16_t sp;
     uint8_t _fill_p;
@@ -71,7 +68,7 @@ union cpu_65c02_regs {
   } word;
   uint8_t mem_be[8];
 #else
-  PACKED struct {
+  struct {
     uint8_t _fill_a;
     uint8_t _fill_x;
     uint8_t _fill_y;
@@ -86,7 +83,7 @@ union cpu_65c02_regs {
     uint16_t _fill_sp;
     uint16_t _fill_pc;
   } bit;
-  PACKED struct {
+  struct {
     uint8_t a;
     uint8_t x;
     uint8_t y;
@@ -95,7 +92,7 @@ union cpu_65c02_regs {
     uint8_t _fill_sph;
     uint16_t _fill_pc;
   } byte;
-  PACKED struct {
+  struct {
     uint8_t _fill_a;
     uint8_t _fill_x;
     uint8_t _fill_y;
@@ -114,7 +111,7 @@ struct cpu_65c02 {
   union cpu_65c02_regs regs;
 };
 
-// memory (or internal register memory) access
+// memory or special memory access
 static ALWAYS_INLINE int cpu_65c02_read_byte(struct cpu_65c02 *self, int addr) {
   if (addr < 0)
 #if __BYTE_ORDER == __BIG_ENDIAN
@@ -509,7 +506,6 @@ static ALWAYS_INLINE void cpu_65c02_wai(struct cpu_65c02 *self) {
 // prototypes
 void cpu_65c02_init(
   struct cpu_65c02 *self,
-  int pc,
   int (*read_byte)(int addr),
   void (*write_byte)(int addr, int data)
 );
diff --git a/cpu_z80.c b/cpu_z80.c
new file mode 100644 (file)
index 0000000..e17f2e4
--- /dev/null
+++ b/cpu_z80.c
@@ -0,0 +1,3897 @@
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include "cpu_z80.h"
+
+// initialization
+void cpu_z80_init(
+  struct cpu_z80 *self,
+  int (*read_byte)(int addr),
+  void (*write_byte)(int addr, int data),
+  int (*in_byte)(int addr),
+  void (*out_byte)(int addr, int data)
+) {
+  memset(self, 0, sizeof(struct cpu_z80));
+  self->read_byte = read_byte;
+  self->write_byte = write_byte;
+  self->in_byte = in_byte;
+  self->out_byte = out_byte;
+}
+
+// instruction decode
+void cpu_z80_execute(struct cpu_z80 *self) {
+  switch (cpu_z80_fetch_byte(self)) {
+  case 0x00:
+    cpu_z80_nop(self);
+    break;
+  case 0x01:
+    cpu_z80_ld_word(self, CPU_Z80_EA_BC, cpu_z80_fetch_word(self));
+    break;
+  case 0x02:
+    cpu_z80_ld_byte(self, self->regs.word.bc, self->regs.byte.a);
+    break;
+  case 0x03:
+    cpu_z80_inc_word(self, CPU_Z80_EA_BC);
+    break;
+  case 0x04:
+    cpu_z80_inc_byte(self, CPU_Z80_EA_B);
+    break;
+  case 0x05:
+    cpu_z80_dec_byte(self, CPU_Z80_EA_B);
+    break;
+  case 0x06:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_B, cpu_z80_fetch_byte(self));
+    break;
+  case 0x07:
+    cpu_z80_rlca(self);
+    break;
+  case 0x08:
+    cpu_z80_ex(self, CPU_Z80_EA_AF, CPU_Z80_EA_AF_PRIME);
+    break;
+  case 0x09:
+    cpu_z80_add_word(self, CPU_Z80_EA_HL, self->regs.word.bc);
+    break;
+  case 0x0a:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_A, cpu_z80_read_byte(self, self->regs.word.bc));
+    break;
+  case 0x0b:
+    cpu_z80_dec_word(self, CPU_Z80_EA_BC);
+    break;
+  case 0x0c:
+    cpu_z80_inc_byte(self, CPU_Z80_EA_C);
+    break;
+  case 0x0d:
+    cpu_z80_dec_byte(self, CPU_Z80_EA_C);
+    break;
+  case 0x0e:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_C, cpu_z80_fetch_byte(self));
+    break;
+  case 0x0f:
+    cpu_z80_rrca(self);
+    break;
+  case 0x10:
+    cpu_z80_djnz(self, cpu_z80_relative(self));
+    break;
+  case 0x11:
+    cpu_z80_ld_word(self, CPU_Z80_EA_DE, cpu_z80_fetch_word(self));
+    break;
+  case 0x12:
+    cpu_z80_ld_byte(self, self->regs.word.de, self->regs.byte.a);
+    break;
+  case 0x13:
+    cpu_z80_inc_word(self, CPU_Z80_EA_DE);
+    break;
+  case 0x14:
+    cpu_z80_inc_byte(self, CPU_Z80_EA_D);
+    break;
+  case 0x15:
+    cpu_z80_dec_byte(self, CPU_Z80_EA_D);
+    break;
+  case 0x16:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_D, cpu_z80_fetch_byte(self));
+    break;
+  case 0x17:
+    cpu_z80_rla(self);
+    break;
+  case 0x18:
+    cpu_z80_jr(self, true, cpu_z80_relative(self));
+    break;
+  case 0x19:
+    cpu_z80_add_word(self, CPU_Z80_EA_HL, self->regs.word.de);
+    break;
+  case 0x1a:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_A, cpu_z80_read_byte(self, self->regs.word.de));
+    break;
+  case 0x1b:
+    cpu_z80_dec_word(self, CPU_Z80_EA_DE);
+    break;
+  case 0x1c:
+    cpu_z80_inc_byte(self, CPU_Z80_EA_E);
+    break;
+  case 0x1d:
+    cpu_z80_dec_byte(self, CPU_Z80_EA_E);
+    break;
+  case 0x1e:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_E, cpu_z80_fetch_byte(self));
+    break;
+  case 0x1f:
+    cpu_z80_rra(self);
+    break;
+  case 0x20:
+    cpu_z80_jr(self, !self->regs.bit.zf, cpu_z80_relative(self));
+    break;
+  case 0x21:
+    cpu_z80_ld_word(self, CPU_Z80_EA_HL, cpu_z80_fetch_word(self));
+    break;
+  case 0x22:
+    cpu_z80_ld_word(self, cpu_z80_fetch_word(self), self->regs.word.hl);
+    break;
+  case 0x23:
+    cpu_z80_inc_word(self, CPU_Z80_EA_HL);
+    break;
+  case 0x24:
+    cpu_z80_inc_byte(self, CPU_Z80_EA_H);
+    break;
+  case 0x25:
+    cpu_z80_dec_byte(self, CPU_Z80_EA_H);
+    break;
+  case 0x26:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_H, cpu_z80_fetch_byte(self));
+    break;
+  case 0x27:
+    cpu_z80_daa(self);
+    break;
+  case 0x28:
+    cpu_z80_jr(self, self->regs.bit.zf, cpu_z80_relative(self));
+    break;
+  case 0x29:
+    cpu_z80_add_word(self, CPU_Z80_EA_HL, self->regs.word.hl);
+    break;
+  case 0x2a:
+    cpu_z80_ld_word(self, CPU_Z80_EA_HL, cpu_z80_read_word(self, cpu_z80_fetch_word(self)));
+    break;
+  case 0x2b:
+    cpu_z80_dec_word(self, CPU_Z80_EA_HL);
+    break;
+  case 0x2c:
+    cpu_z80_inc_byte(self, CPU_Z80_EA_L);
+    break;
+  case 0x2d:
+    cpu_z80_dec_byte(self, CPU_Z80_EA_L);
+    break;
+  case 0x2e:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_L, cpu_z80_fetch_byte(self));
+    break;
+  case 0x2f:
+    cpu_z80_cpl(self);
+    break;
+  case 0x30:
+    cpu_z80_jr(self, !self->regs.bit.cf, cpu_z80_relative(self));
+    break;
+  case 0x31:
+    cpu_z80_ld_word(self, CPU_Z80_EA_SP, cpu_z80_fetch_word(self));
+    break;
+  case 0x32:
+    cpu_z80_ld_byte(self, cpu_z80_fetch_word(self), self->regs.byte.a);
+    break;
+  case 0x33:
+    cpu_z80_inc_word(self, CPU_Z80_EA_SP);
+    break;
+  case 0x34:
+    cpu_z80_inc_byte(self, self->regs.word.hl);
+    break;
+  case 0x35:
+    cpu_z80_dec_byte(self, self->regs.word.hl);
+    break;
+  case 0x36:
+    cpu_z80_ld_byte(self, self->regs.word.hl, cpu_z80_fetch_byte(self));
+    break;
+  case 0x37:
+    cpu_z80_scf(self);
+    break;
+  case 0x38:
+    cpu_z80_jr(self, self->regs.bit.cf, cpu_z80_relative(self));
+    break;
+  case 0x39:
+    cpu_z80_add_word(self, CPU_Z80_EA_HL, self->regs.word.sp);
+    break;
+  case 0x3a:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_A, cpu_z80_read_byte(self, cpu_z80_fetch_word(self)));
+    break;
+  case 0x3b:
+    cpu_z80_dec_word(self, CPU_Z80_EA_SP);
+    break;
+  case 0x3c:
+    cpu_z80_inc_byte(self, CPU_Z80_EA_A);
+    break;
+  case 0x3d:
+    cpu_z80_dec_byte(self, CPU_Z80_EA_A);
+    break;
+  case 0x3e:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_A, cpu_z80_fetch_byte(self));
+    break;
+  case 0x3f:
+    cpu_z80_ccf(self);
+    break;
+  case 0x40:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_B, self->regs.byte.b);
+    break;
+  case 0x41:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_B, self->regs.byte.c);
+    break;
+  case 0x42:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_B, self->regs.byte.d);
+    break;
+  case 0x43:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_B, self->regs.byte.e);
+    break;
+  case 0x44:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_B, self->regs.byte.h);
+    break;
+  case 0x45:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_B, self->regs.byte.l);
+    break;
+  case 0x46:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_B, cpu_z80_read_byte(self, self->regs.word.hl));
+    break;
+  case 0x47:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_B, self->regs.byte.a);
+    break;
+  case 0x48:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_C, self->regs.byte.b);
+    break;
+  case 0x49:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_C, self->regs.byte.c);
+    break;
+  case 0x4a:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_C, self->regs.byte.d);
+    break;
+  case 0x4b:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_C, self->regs.byte.e);
+    break;
+  case 0x4c:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_C, self->regs.byte.h);
+    break;
+  case 0x4d:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_C, self->regs.byte.l);
+    break;
+  case 0x4e:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_C, cpu_z80_read_byte(self, self->regs.word.hl));
+    break;
+  case 0x4f:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_C, self->regs.byte.a);
+    break;
+  case 0x50:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_D, self->regs.byte.b);
+    break;
+  case 0x51:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_D, self->regs.byte.c);
+    break;
+  case 0x52:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_D, self->regs.byte.d);
+    break;
+  case 0x53:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_D, self->regs.byte.e);
+    break;
+  case 0x54:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_D, self->regs.byte.h);
+    break;
+  case 0x55:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_D, self->regs.byte.l);
+    break;
+  case 0x56:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_D, cpu_z80_read_byte(self, self->regs.word.hl));
+    break;
+  case 0x57:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_D, self->regs.byte.a);
+    break;
+  case 0x58:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_E, self->regs.byte.b);
+    break;
+  case 0x59:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_E, self->regs.byte.c);
+    break;
+  case 0x5a:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_E, self->regs.byte.d);
+    break;
+  case 0x5b:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_E, self->regs.byte.e);
+    break;
+  case 0x5c:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_E, self->regs.byte.h);
+    break;
+  case 0x5d:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_E, self->regs.byte.l);
+    break;
+  case 0x5e:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_E, cpu_z80_read_byte(self, self->regs.word.hl));
+    break;
+  case 0x5f:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_E, self->regs.byte.a);
+    break;
+  case 0x60:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_H, self->regs.byte.b);
+    break;
+  case 0x61:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_H, self->regs.byte.c);
+    break;
+  case 0x62:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_H, self->regs.byte.d);
+    break;
+  case 0x63:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_H, self->regs.byte.e);
+    break;
+  case 0x64:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_H, self->regs.byte.h);
+    break;
+  case 0x65:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_H, self->regs.byte.l);
+    break;
+  case 0x66:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_H, cpu_z80_read_byte(self, self->regs.word.hl));
+    break;
+  case 0x67:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_H, self->regs.byte.a);
+    break;
+  case 0x68:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_L, self->regs.byte.b);
+    break;
+  case 0x69:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_L, self->regs.byte.c);
+    break;
+  case 0x6a:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_L, self->regs.byte.d);
+    break;
+  case 0x6b:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_L, self->regs.byte.e);
+    break;
+  case 0x6c:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_L, self->regs.byte.h);
+    break;
+  case 0x6d:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_L, self->regs.byte.l);
+    break;
+  case 0x6e:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_L, cpu_z80_read_byte(self, self->regs.word.hl));
+    break;
+  case 0x6f:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_L, self->regs.byte.a);
+    break;
+  case 0x70:
+    cpu_z80_ld_byte(self, self->regs.word.hl, self->regs.byte.b);
+    break;
+  case 0x71:
+    cpu_z80_ld_byte(self, self->regs.word.hl, self->regs.byte.c);
+    break;
+  case 0x72:
+    cpu_z80_ld_byte(self, self->regs.word.hl, self->regs.byte.d);
+    break;
+  case 0x73:
+    cpu_z80_ld_byte(self, self->regs.word.hl, self->regs.byte.e);
+    break;
+  case 0x74:
+    cpu_z80_ld_byte(self, self->regs.word.hl, self->regs.byte.h);
+    break;
+  case 0x75:
+    cpu_z80_ld_byte(self, self->regs.word.hl, self->regs.byte.l);
+    break;
+  case 0x76:
+    cpu_z80_halt(self);
+    break;
+  case 0x77:
+    cpu_z80_ld_byte(self, self->regs.word.hl, self->regs.byte.a);
+    break;
+  case 0x78:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_A, self->regs.byte.b);
+    break;
+  case 0x79:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_A, self->regs.byte.c);
+    break;
+  case 0x7a:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_A, self->regs.byte.d);
+    break;
+  case 0x7b:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_A, self->regs.byte.e);
+    break;
+  case 0x7c:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_A, self->regs.byte.h);
+    break;
+  case 0x7d:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_A, self->regs.byte.l);
+    break;
+  case 0x7e:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_A, cpu_z80_read_byte(self, self->regs.word.hl));
+    break;
+  case 0x7f:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_A, self->regs.byte.a);
+    break;
+  case 0x80:
+    cpu_z80_add_byte(self, CPU_Z80_EA_A, self->regs.byte.b);
+    break;
+  case 0x81:
+    cpu_z80_add_byte(self, CPU_Z80_EA_A, self->regs.byte.c);
+    break;
+  case 0x82:
+    cpu_z80_add_byte(self, CPU_Z80_EA_A, self->regs.byte.d);
+    break;
+  case 0x83:
+    cpu_z80_add_byte(self, CPU_Z80_EA_A, self->regs.byte.e);
+    break;
+  case 0x84:
+    cpu_z80_add_byte(self, CPU_Z80_EA_A, self->regs.byte.h);
+    break;
+  case 0x85:
+    cpu_z80_add_byte(self, CPU_Z80_EA_A, self->regs.byte.l);
+    break;
+  case 0x86:
+    cpu_z80_add_byte(self, CPU_Z80_EA_A, cpu_z80_read_byte(self, self->regs.word.hl));
+    break;
+  case 0x87:
+    cpu_z80_add_byte(self, CPU_Z80_EA_A, self->regs.byte.a);
+    break;
+  case 0x88:
+    cpu_z80_adc_byte(self, CPU_Z80_EA_A, self->regs.byte.b);
+    break;
+  case 0x89:
+    cpu_z80_adc_byte(self, CPU_Z80_EA_A, self->regs.byte.c);
+    break;
+  case 0x8a:
+    cpu_z80_adc_byte(self, CPU_Z80_EA_A, self->regs.byte.d);
+    break;
+  case 0x8b:
+    cpu_z80_adc_byte(self, CPU_Z80_EA_A, self->regs.byte.e);
+    break;
+  case 0x8c:
+    cpu_z80_adc_byte(self, CPU_Z80_EA_A, self->regs.byte.h);
+    break;
+  case 0x8d:
+    cpu_z80_adc_byte(self, CPU_Z80_EA_A, self->regs.byte.l);
+    break;
+  case 0x8e:
+    cpu_z80_adc_byte(self, CPU_Z80_EA_A, cpu_z80_read_byte(self, self->regs.word.hl));
+    break;
+  case 0x8f:
+    cpu_z80_adc_byte(self, CPU_Z80_EA_A, self->regs.byte.a);
+    break;
+  case 0x90:
+    cpu_z80_sub(self, self->regs.byte.b);
+    break;
+  case 0x91:
+    cpu_z80_sub(self, self->regs.byte.c);
+    break;
+  case 0x92:
+    cpu_z80_sub(self, self->regs.byte.d);
+    break;
+  case 0x93:
+    cpu_z80_sub(self, self->regs.byte.e);
+    break;
+  case 0x94:
+    cpu_z80_sub(self, self->regs.byte.h);
+    break;
+  case 0x95:
+    cpu_z80_sub(self, self->regs.byte.l);
+    break;
+  case 0x96:
+    cpu_z80_sub(self, cpu_z80_read_byte(self, self->regs.word.hl));
+    break;
+  case 0x97:
+    cpu_z80_sub(self, self->regs.byte.a);
+    break;
+  case 0x98:
+    cpu_z80_sbc_byte(self, CPU_Z80_EA_A, self->regs.byte.b);
+    break;
+  case 0x99:
+    cpu_z80_sbc_byte(self, CPU_Z80_EA_A, self->regs.byte.c);
+    break;
+  case 0x9a:
+    cpu_z80_sbc_byte(self, CPU_Z80_EA_A, self->regs.byte.d);
+    break;
+  case 0x9b:
+    cpu_z80_sbc_byte(self, CPU_Z80_EA_A, self->regs.byte.e);
+    break;
+  case 0x9c:
+    cpu_z80_sbc_byte(self, CPU_Z80_EA_A, self->regs.byte.h);
+    break;
+  case 0x9d:
+    cpu_z80_sbc_byte(self, CPU_Z80_EA_A, self->regs.byte.l);
+    break;
+  case 0x9e:
+    cpu_z80_sbc_byte(self, CPU_Z80_EA_A, cpu_z80_read_byte(self, self->regs.word.hl));
+    break;
+  case 0x9f:
+    cpu_z80_sbc_byte(self, CPU_Z80_EA_A, self->regs.byte.a);
+    break;
+  case 0xa0:
+    cpu_z80_and(self, self->regs.byte.b);
+    break;
+  case 0xa1:
+    cpu_z80_and(self, self->regs.byte.c);
+    break;
+  case 0xa2:
+    cpu_z80_and(self, self->regs.byte.d);
+    break;
+  case 0xa3:
+    cpu_z80_and(self, self->regs.byte.e);
+    break;
+  case 0xa4:
+    cpu_z80_and(self, self->regs.byte.h);
+    break;
+  case 0xa5:
+    cpu_z80_and(self, self->regs.byte.l);
+    break;
+  case 0xa6:
+    cpu_z80_and(self, cpu_z80_read_byte(self, self->regs.word.hl));
+    break;
+  case 0xa7:
+    cpu_z80_and(self, self->regs.byte.a);
+    break;
+  case 0xa8:
+    cpu_z80_xor(self, self->regs.byte.b);
+    break;
+  case 0xa9:
+    cpu_z80_xor(self, self->regs.byte.c);
+    break;
+  case 0xaa:
+    cpu_z80_xor(self, self->regs.byte.d);
+    break;
+  case 0xab:
+    cpu_z80_xor(self, self->regs.byte.e);
+    break;
+  case 0xac:
+    cpu_z80_xor(self, self->regs.byte.h);
+    break;
+  case 0xad:
+    cpu_z80_xor(self, self->regs.byte.l);
+    break;
+  case 0xae:
+    cpu_z80_xor(self, cpu_z80_read_byte(self, self->regs.word.hl));
+    break;
+  case 0xaf:
+    cpu_z80_xor(self, self->regs.byte.a);
+    break;
+  case 0xb0:
+    cpu_z80_or(self, self->regs.byte.b);
+    break;
+  case 0xb1:
+    cpu_z80_or(self, self->regs.byte.c);
+    break;
+  case 0xb2:
+    cpu_z80_or(self, self->regs.byte.d);
+    break;
+  case 0xb3:
+    cpu_z80_or(self, self->regs.byte.e);
+    break;
+  case 0xb4:
+    cpu_z80_or(self, self->regs.byte.h);
+    break;
+  case 0xb5:
+    cpu_z80_or(self, self->regs.byte.l);
+    break;
+  case 0xb6:
+    cpu_z80_or(self, cpu_z80_read_byte(self, self->regs.word.hl));
+    break;
+  case 0xb7:
+    cpu_z80_or(self, self->regs.byte.a);
+    break;
+  case 0xb8:
+    cpu_z80_cp(self, self->regs.byte.b);
+    break;
+  case 0xb9:
+    cpu_z80_cp(self, self->regs.byte.c);
+    break;
+  case 0xba:
+    cpu_z80_cp(self, self->regs.byte.d);
+    break;
+  case 0xbb:
+    cpu_z80_cp(self, self->regs.byte.e);
+    break;
+  case 0xbc:
+    cpu_z80_cp(self, self->regs.byte.h);
+    break;
+  case 0xbd:
+    cpu_z80_cp(self, self->regs.byte.l);
+    break;
+  case 0xbe:
+    cpu_z80_cp(self, cpu_z80_read_byte(self, self->regs.word.hl));
+    break;
+  case 0xbf:
+    cpu_z80_cp(self, self->regs.byte.a);
+    break;
+  case 0xc0:
+    cpu_z80_ret(self, !self->regs.bit.zf);
+    break;
+  case 0xc1:
+    cpu_z80_pop(self, CPU_Z80_EA_BC);
+    break;
+  case 0xc2:
+    cpu_z80_jp(self, !self->regs.bit.zf, cpu_z80_fetch_word(self));
+    break;
+  case 0xc3:
+    cpu_z80_jp(self, true, cpu_z80_fetch_word(self));
+    break;
+  case 0xc4:
+    cpu_z80_call(self, !self->regs.bit.zf, cpu_z80_fetch_word(self));
+    break;
+  case 0xc5:
+    cpu_z80_push(self, self->regs.word.bc);
+    break;
+  case 0xc6:
+    cpu_z80_add_byte(self, CPU_Z80_EA_A, cpu_z80_fetch_byte(self));
+    break;
+  case 0xc7:
+    cpu_z80_call(self, true, 0);
+    break;
+  case 0xc8:
+    cpu_z80_ret(self, self->regs.bit.zf);
+    break;
+  case 0xc9:
+    cpu_z80_ret(self, true);
+    break;
+  case 0xca:
+    cpu_z80_jp(self, self->regs.bit.zf, cpu_z80_fetch_word(self));
+    break;
+  case 0xcb:
+    cpu_z80_execute_cb(self);
+    break;
+  case 0xcc:
+    cpu_z80_call(self, self->regs.bit.zf, cpu_z80_fetch_word(self));
+    break;
+  case 0xcd:
+    cpu_z80_call(self, true, cpu_z80_fetch_word(self));
+    break;
+  case 0xce:
+    cpu_z80_adc_byte(self, CPU_Z80_EA_A, cpu_z80_fetch_byte(self));
+    break;
+  case 0xcf:
+    cpu_z80_call(self, true, 8);
+    break;
+  case 0xd0:
+    cpu_z80_ret(self, !self->regs.bit.cf);
+    break;
+  case 0xd1:
+    cpu_z80_pop(self, CPU_Z80_EA_DE);
+    break;
+  case 0xd2:
+    cpu_z80_jp(self, !self->regs.bit.cf, cpu_z80_fetch_word(self));
+    break;
+  case 0xd3:
+    cpu_z80_out(self, cpu_z80_fetch_byte(self), self->regs.byte.a);
+    break;
+  case 0xd4:
+    cpu_z80_call(self, !self->regs.bit.cf, cpu_z80_fetch_word(self));
+    break;
+  case 0xd5:
+    cpu_z80_push(self, self->regs.word.de);
+    break;
+  case 0xd6:
+    cpu_z80_sub(self, cpu_z80_fetch_byte(self));
+    break;
+  case 0xd7:
+    cpu_z80_call(self, true, 0x10);
+    break;
+  case 0xd8:
+    cpu_z80_ret(self, self->regs.bit.cf);
+    break;
+  case 0xd9:
+    cpu_z80_ex(self, CPU_Z80_EA_BC, CPU_Z80_EA_BC_PRIME);
+    cpu_z80_ex(self, CPU_Z80_EA_DE, CPU_Z80_EA_DE_PRIME);
+    cpu_z80_ex(self, CPU_Z80_EA_HL, CPU_Z80_EA_HL_PRIME);
+    break;
+  case 0xda:
+    cpu_z80_jp(self, self->regs.bit.cf, cpu_z80_fetch_word(self));
+    break;
+  case 0xdb:
+    cpu_z80_in(self, CPU_Z80_EA_A, cpu_z80_in_byte(self, cpu_z80_fetch_byte(self)));
+    break;
+  case 0xdc:
+    cpu_z80_call(self, self->regs.bit.cf, cpu_z80_fetch_word(self));
+    break;
+  case 0xdd:
+    cpu_z80_execute_dd(self);
+    break;
+  case 0xde:
+    cpu_z80_sbc_byte(self, CPU_Z80_EA_A, cpu_z80_fetch_byte(self));
+    break;
+  case 0xdf:
+    cpu_z80_call(self, true, 0x18);
+    break;
+  case 0xe0:
+    cpu_z80_ret(self, self->regs.bit.pvf);
+    break;
+  case 0xe1:
+    cpu_z80_pop(self, CPU_Z80_EA_HL);
+    break;
+  case 0xe2:
+    cpu_z80_jp(self, self->regs.bit.pvf, cpu_z80_fetch_word(self));
+    break;
+  case 0xe3:
+    cpu_z80_ex(self, self->regs.word.sp, CPU_Z80_EA_HL);
+    break;
+  case 0xe4:
+    cpu_z80_call(self, self->regs.bit.pvf, cpu_z80_fetch_word(self));
+    break;
+  case 0xe5:
+    cpu_z80_push(self, self->regs.word.hl);
+    break;
+  case 0xe6:
+    cpu_z80_and(self, cpu_z80_fetch_byte(self));
+    break;
+  case 0xe7:
+    cpu_z80_call(self, true, 0x20);
+    break;
+  case 0xe8:
+    cpu_z80_ret(self, !self->regs.bit.pvf);
+    break;
+  case 0xe9:
+    cpu_z80_jp(self, true, cpu_z80_read_word(self, self->regs.word.hl));
+    break;
+  case 0xea:
+    cpu_z80_jp(self, !self->regs.bit.pvf, cpu_z80_fetch_word(self));
+    break;
+  case 0xeb:
+    cpu_z80_ex(self, CPU_Z80_EA_DE, CPU_Z80_EA_HL);
+    break;
+  case 0xec:
+    cpu_z80_call(self, !self->regs.bit.pvf, cpu_z80_fetch_word(self));
+    break;
+  case 0xed:
+    cpu_z80_execute_ed(self);
+    break;
+  case 0xee:
+    cpu_z80_xor(self, cpu_z80_fetch_byte(self));
+    break;
+  case 0xef:
+    cpu_z80_call(self, true, 0x28);
+    break;
+  case 0xf0:
+    cpu_z80_ret(self, !self->regs.bit.sf);
+    break;
+  case 0xf1:
+    cpu_z80_pop(self, CPU_Z80_EA_AF);
+    break;
+  case 0xf2:
+    cpu_z80_jp(self, !self->regs.bit.sf, cpu_z80_fetch_word(self));
+    break;
+  case 0xf3:
+    cpu_z80_di(self);
+    break;
+  case 0xf4:
+    cpu_z80_call(self, !self->regs.bit.sf, cpu_z80_fetch_word(self));
+    break;
+  case 0xf5:
+    cpu_z80_push(self, self->regs.word.af);
+    break;
+  case 0xf6:
+    cpu_z80_or(self, cpu_z80_fetch_byte(self));
+    break;
+  case 0xf7:
+    cpu_z80_call(self, true, 0x30);
+    break;
+  case 0xf8:
+    cpu_z80_ret(self, self->regs.bit.sf);
+    break;
+  case 0xf9:
+    cpu_z80_ld_word(self, CPU_Z80_EA_SP, self->regs.word.hl);
+    break;
+  case 0xfa:
+    cpu_z80_jp(self, self->regs.bit.sf, cpu_z80_fetch_word(self));
+    break;
+  case 0xfb:
+    cpu_z80_ei(self);
+    break;
+  case 0xfc:
+    cpu_z80_call(self, self->regs.bit.sf, cpu_z80_fetch_word(self));
+    break;
+  case 0xfd:
+    cpu_z80_execute_fd(self);
+    break;
+  case 0xfe:
+    cpu_z80_cp(self, cpu_z80_fetch_byte(self));
+    break;
+  case 0xff:
+    cpu_z80_call(self, true, 0x38);
+    break;
+  }
+}
+
+void cpu_z80_execute_cb(struct cpu_z80 *self) {
+  switch (cpu_z80_fetch_byte(self)) {
+  case 0x00:
+    cpu_z80_rlc(self, CPU_Z80_EA_B);
+    break;
+  case 0x01:
+    cpu_z80_rlc(self, CPU_Z80_EA_C);
+    break;
+  case 0x02:
+    cpu_z80_rlc(self, CPU_Z80_EA_D);
+    break;
+  case 0x03:
+    cpu_z80_rlc(self, CPU_Z80_EA_E);
+    break;
+  case 0x04:
+    cpu_z80_rlc(self, CPU_Z80_EA_H);
+    break;
+  case 0x05:
+    cpu_z80_rlc(self, CPU_Z80_EA_L);
+    break;
+  case 0x06:
+    cpu_z80_rlc(self, self->regs.word.hl);
+    break;
+  case 0x07:
+    cpu_z80_rlc(self, CPU_Z80_EA_A);
+    break;
+  case 0x08:
+    cpu_z80_rrc(self, CPU_Z80_EA_B);
+    break;
+  case 0x09:
+    cpu_z80_rrc(self, CPU_Z80_EA_C);
+    break;
+  case 0x0a:
+    cpu_z80_rrc(self, CPU_Z80_EA_D);
+    break;
+  case 0x0b:
+    cpu_z80_rrc(self, CPU_Z80_EA_E);
+    break;
+  case 0x0c:
+    cpu_z80_rrc(self, CPU_Z80_EA_H);
+    break;
+  case 0x0d:
+    cpu_z80_rrc(self, CPU_Z80_EA_L);
+    break;
+  case 0x0e:
+    cpu_z80_rrc(self, self->regs.word.hl);
+    break;
+  case 0x0f:
+    cpu_z80_rrc(self, CPU_Z80_EA_A);
+    break;
+  case 0x10:
+    cpu_z80_rl(self, CPU_Z80_EA_B);
+    break;
+  case 0x11:
+    cpu_z80_rl(self, CPU_Z80_EA_C);
+    break;
+  case 0x12:
+    cpu_z80_rl(self, CPU_Z80_EA_D);
+    break;
+  case 0x13:
+    cpu_z80_rl(self, CPU_Z80_EA_E);
+    break;
+  case 0x14:
+    cpu_z80_rl(self, CPU_Z80_EA_H);
+    break;
+  case 0x15:
+    cpu_z80_rl(self, CPU_Z80_EA_L);
+    break;
+  case 0x16:
+    cpu_z80_rl(self, self->regs.word.hl);
+    break;
+  case 0x17:
+    cpu_z80_rl(self, CPU_Z80_EA_A);
+    break;
+  case 0x18:
+    cpu_z80_rr(self, CPU_Z80_EA_B);
+    break;
+  case 0x19:
+    cpu_z80_rr(self, CPU_Z80_EA_C);
+    break;
+  case 0x1a:
+    cpu_z80_rr(self, CPU_Z80_EA_D);
+    break;
+  case 0x1b:
+    cpu_z80_rr(self, CPU_Z80_EA_E);
+    break;
+  case 0x1c:
+    cpu_z80_rr(self, CPU_Z80_EA_H);
+    break;
+  case 0x1d:
+    cpu_z80_rr(self, CPU_Z80_EA_L);
+    break;
+  case 0x1e:
+    cpu_z80_rr(self, self->regs.word.hl);
+    break;
+  case 0x1f:
+    cpu_z80_rr(self, CPU_Z80_EA_A);
+    break;
+  case 0x20:
+    cpu_z80_sla(self, CPU_Z80_EA_B);
+    break;
+  case 0x21:
+    cpu_z80_sla(self, CPU_Z80_EA_C);
+    break;
+  case 0x22:
+    cpu_z80_sla(self, CPU_Z80_EA_D);
+    break;
+  case 0x23:
+    cpu_z80_sla(self, CPU_Z80_EA_E);
+    break;
+  case 0x24:
+    cpu_z80_sla(self, CPU_Z80_EA_H);
+    break;
+  case 0x25:
+    cpu_z80_sla(self, CPU_Z80_EA_L);
+    break;
+  case 0x26:
+    cpu_z80_sla(self, self->regs.word.hl);
+    break;
+  case 0x27:
+    cpu_z80_sla(self, CPU_Z80_EA_A);
+    break;
+  case 0x28:
+    cpu_z80_sra(self, CPU_Z80_EA_B);
+    break;
+  case 0x29:
+    cpu_z80_sra(self, CPU_Z80_EA_C);
+    break;
+  case 0x2a:
+    cpu_z80_sra(self, CPU_Z80_EA_D);
+    break;
+  case 0x2b:
+    cpu_z80_sra(self, CPU_Z80_EA_E);
+    break;
+  case 0x2c:
+    cpu_z80_sra(self, CPU_Z80_EA_H);
+    break;
+  case 0x2d:
+    cpu_z80_sra(self, CPU_Z80_EA_L);
+    break;
+  case 0x2e:
+    cpu_z80_sra(self, self->regs.word.hl);
+    break;
+  case 0x2f:
+    cpu_z80_sra(self, CPU_Z80_EA_A);
+    break;
+  case 0x30:
+    cpu_z80_sll(self, CPU_Z80_EA_B);
+    break;
+  case 0x31:
+    cpu_z80_sll(self, CPU_Z80_EA_C);
+    break;
+  case 0x32:
+    cpu_z80_sll(self, CPU_Z80_EA_D);
+    break;
+  case 0x33:
+    cpu_z80_sll(self, CPU_Z80_EA_E);
+    break;
+  case 0x34:
+    cpu_z80_sll(self, CPU_Z80_EA_H);
+    break;
+  case 0x35:
+    cpu_z80_sll(self, CPU_Z80_EA_L);
+    break;
+  case 0x36:
+    cpu_z80_sll(self, self->regs.word.hl);
+    break;
+  case 0x37:
+    cpu_z80_sll(self, CPU_Z80_EA_A);
+    break;
+  case 0x38:
+    cpu_z80_srl(self, CPU_Z80_EA_B);
+    break;
+  case 0x39:
+    cpu_z80_srl(self, CPU_Z80_EA_C);
+    break;
+  case 0x3a:
+    cpu_z80_srl(self, CPU_Z80_EA_D);
+    break;
+  case 0x3b:
+    cpu_z80_srl(self, CPU_Z80_EA_E);
+    break;
+  case 0x3c:
+    cpu_z80_srl(self, CPU_Z80_EA_H);
+    break;
+  case 0x3d:
+    cpu_z80_srl(self, CPU_Z80_EA_L);
+    break;
+  case 0x3e:
+    cpu_z80_srl(self, self->regs.word.hl);
+    break;
+  case 0x3f:
+    cpu_z80_srl(self, CPU_Z80_EA_A);
+    break;
+  case 0x40:
+    cpu_z80_bit(self, 0, self->regs.byte.b);
+    break;
+  case 0x41:
+    cpu_z80_bit(self, 0, self->regs.byte.c);
+    break;
+  case 0x42:
+    cpu_z80_bit(self, 0, self->regs.byte.d);
+    break;
+  case 0x43:
+    cpu_z80_bit(self, 0, self->regs.byte.e);
+    break;
+  case 0x44:
+    cpu_z80_bit(self, 0, self->regs.byte.h);
+    break;
+  case 0x45:
+    cpu_z80_bit(self, 0, self->regs.byte.l);
+    break;
+  case 0x46:
+    cpu_z80_bit(self, 0, cpu_z80_read_byte(self, self->regs.word.hl));
+    break;
+  case 0x47:
+    cpu_z80_bit(self, 0, self->regs.byte.a);
+    break;
+  case 0x48:
+    cpu_z80_bit(self, 1, self->regs.byte.b);
+    break;
+  case 0x49:
+    cpu_z80_bit(self, 1, self->regs.byte.c);
+    break;
+  case 0x4a:
+    cpu_z80_bit(self, 1, self->regs.byte.d);
+    break;
+  case 0x4b:
+    cpu_z80_bit(self, 1, self->regs.byte.e);
+    break;
+  case 0x4c:
+    cpu_z80_bit(self, 1, self->regs.byte.h);
+    break;
+  case 0x4d:
+    cpu_z80_bit(self, 1, self->regs.byte.l);
+    break;
+  case 0x4e:
+    cpu_z80_bit(self, 1, cpu_z80_read_byte(self, self->regs.word.hl));
+    break;
+  case 0x4f:
+    cpu_z80_bit(self, 1, self->regs.byte.a);
+    break;
+  case 0x50:
+    cpu_z80_bit(self, 2, self->regs.byte.b);
+    break;
+  case 0x51:
+    cpu_z80_bit(self, 2, self->regs.byte.c);
+    break;
+  case 0x52:
+    cpu_z80_bit(self, 2, self->regs.byte.d);
+    break;
+  case 0x53:
+    cpu_z80_bit(self, 2, self->regs.byte.e);
+    break;
+  case 0x54:
+    cpu_z80_bit(self, 2, self->regs.byte.h);
+    break;
+  case 0x55:
+    cpu_z80_bit(self, 2, self->regs.byte.l);
+    break;
+  case 0x56:
+    cpu_z80_bit(self, 2, cpu_z80_read_byte(self, self->regs.word.hl));
+    break;
+  case 0x57:
+    cpu_z80_bit(self, 2, self->regs.byte.a);
+    break;
+  case 0x58:
+    cpu_z80_bit(self, 3, self->regs.byte.b);
+    break;
+  case 0x59:
+    cpu_z80_bit(self, 3, self->regs.byte.c);
+    break;
+  case 0x5a:
+    cpu_z80_bit(self, 3, self->regs.byte.d);
+    break;
+  case 0x5b:
+    cpu_z80_bit(self, 3, self->regs.byte.e);
+    break;
+  case 0x5c:
+    cpu_z80_bit(self, 3, self->regs.byte.h);
+    break;
+  case 0x5d:
+    cpu_z80_bit(self, 3, self->regs.byte.l);
+    break;
+  case 0x5e:
+    cpu_z80_bit(self, 3, cpu_z80_read_byte(self, self->regs.word.hl));
+    break;
+  case 0x5f:
+    cpu_z80_bit(self, 3, self->regs.byte.a);
+    break;
+  case 0x60:
+    cpu_z80_bit(self, 4, self->regs.byte.b);
+    break;
+  case 0x61:
+    cpu_z80_bit(self, 4, self->regs.byte.c);
+    break;
+  case 0x62:
+    cpu_z80_bit(self, 4, self->regs.byte.d);
+    break;
+  case 0x63:
+    cpu_z80_bit(self, 4, self->regs.byte.e);
+    break;
+  case 0x64:
+    cpu_z80_bit(self, 4, self->regs.byte.h);
+    break;
+  case 0x65:
+    cpu_z80_bit(self, 4, self->regs.byte.l);
+    break;
+  case 0x66:
+    cpu_z80_bit(self, 4, cpu_z80_read_byte(self, self->regs.word.hl));
+    break;
+  case 0x67:
+    cpu_z80_bit(self, 4, self->regs.byte.a);
+    break;
+  case 0x68:
+    cpu_z80_bit(self, 5, self->regs.byte.b);
+    break;
+  case 0x69:
+    cpu_z80_bit(self, 5, self->regs.byte.c);
+    break;
+  case 0x6a:
+    cpu_z80_bit(self, 5, self->regs.byte.d);
+    break;
+  case 0x6b:
+    cpu_z80_bit(self, 5, self->regs.byte.e);
+    break;
+  case 0x6c:
+    cpu_z80_bit(self, 5, self->regs.byte.h);
+    break;
+  case 0x6d:
+    cpu_z80_bit(self, 5, self->regs.byte.l);
+    break;
+  case 0x6e:
+    cpu_z80_bit(self, 5, cpu_z80_read_byte(self, self->regs.word.hl));
+    break;
+  case 0x6f:
+    cpu_z80_bit(self, 5, self->regs.byte.a);
+    break;
+  case 0x70:
+    cpu_z80_bit(self, 6, self->regs.byte.b);
+    break;
+  case 0x71:
+    cpu_z80_bit(self, 6, self->regs.byte.c);
+    break;
+  case 0x72:
+    cpu_z80_bit(self, 6, self->regs.byte.d);
+    break;
+  case 0x73:
+    cpu_z80_bit(self, 6, self->regs.byte.e);
+    break;
+  case 0x74:
+    cpu_z80_bit(self, 6, self->regs.byte.h);
+    break;
+  case 0x75:
+    cpu_z80_bit(self, 6, self->regs.byte.l);
+    break;
+  case 0x76:
+    cpu_z80_bit(self, 6, cpu_z80_read_byte(self, self->regs.word.hl));
+    break;
+  case 0x77:
+    cpu_z80_bit(self, 6, self->regs.byte.a);
+    break;
+  case 0x78:
+    cpu_z80_bit(self, 7, self->regs.byte.b);
+    break;
+  case 0x79:
+    cpu_z80_bit(self, 7, self->regs.byte.c);
+    break;
+  case 0x7a:
+    cpu_z80_bit(self, 7, self->regs.byte.d);
+    break;
+  case 0x7b:
+    cpu_z80_bit(self, 7, self->regs.byte.e);
+    break;
+  case 0x7c:
+    cpu_z80_bit(self, 7, self->regs.byte.h);
+    break;
+  case 0x7d:
+    cpu_z80_bit(self, 7, self->regs.byte.l);
+    break;
+  case 0x7e:
+    cpu_z80_bit(self, 7, cpu_z80_read_byte(self, self->regs.word.hl));
+    break;
+  case 0x7f:
+    cpu_z80_bit(self, 7, self->regs.byte.a);
+    break;
+  case 0x80:
+    cpu_z80_res(self, 0, CPU_Z80_EA_B);
+    break;
+  case 0x81:
+    cpu_z80_res(self, 0, CPU_Z80_EA_C);
+    break;
+  case 0x82:
+    cpu_z80_res(self, 0, CPU_Z80_EA_D);
+    break;
+  case 0x83:
+    cpu_z80_res(self, 0, CPU_Z80_EA_E);
+    break;
+  case 0x84:
+    cpu_z80_res(self, 0, CPU_Z80_EA_H);
+    break;
+  case 0x85:
+    cpu_z80_res(self, 0, CPU_Z80_EA_L);
+    break;
+  case 0x86:
+    cpu_z80_res(self, 0, self->regs.word.hl);
+    break;
+  case 0x87:
+    cpu_z80_res(self, 0, CPU_Z80_EA_A);
+    break;
+  case 0x88:
+    cpu_z80_res(self, 1, CPU_Z80_EA_B);
+    break;
+  case 0x89:
+    cpu_z80_res(self, 1, CPU_Z80_EA_C);
+    break;
+  case 0x8a:
+    cpu_z80_res(self, 1, CPU_Z80_EA_D);
+    break;
+  case 0x8b:
+    cpu_z80_res(self, 1, CPU_Z80_EA_E);
+    break;
+  case 0x8c:
+    cpu_z80_res(self, 1, CPU_Z80_EA_H);
+    break;
+  case 0x8d:
+    cpu_z80_res(self, 1, CPU_Z80_EA_L);
+    break;
+  case 0x8e:
+    cpu_z80_res(self, 1, self->regs.word.hl);
+    break;
+  case 0x8f:
+    cpu_z80_res(self, 1, CPU_Z80_EA_A);
+    break;
+  case 0x90:
+    cpu_z80_res(self, 2, CPU_Z80_EA_B);
+    break;
+  case 0x91:
+    cpu_z80_res(self, 2, CPU_Z80_EA_C);
+    break;
+  case 0x92:
+    cpu_z80_res(self, 2, CPU_Z80_EA_D);
+    break;
+  case 0x93:
+    cpu_z80_res(self, 2, CPU_Z80_EA_E);
+    break;
+  case 0x94:
+    cpu_z80_res(self, 2, CPU_Z80_EA_H);
+    break;
+  case 0x95:
+    cpu_z80_res(self, 2, CPU_Z80_EA_L);
+    break;
+  case 0x96:
+    cpu_z80_res(self, 2, self->regs.word.hl);
+    break;
+  case 0x97:
+    cpu_z80_res(self, 2, CPU_Z80_EA_A);
+    break;
+  case 0x98:
+    cpu_z80_res(self, 3, CPU_Z80_EA_B);
+    break;
+  case 0x99:
+    cpu_z80_res(self, 3, CPU_Z80_EA_C);
+    break;
+  case 0x9a:
+    cpu_z80_res(self, 3, CPU_Z80_EA_D);
+    break;
+  case 0x9b:
+    cpu_z80_res(self, 3, CPU_Z80_EA_E);
+    break;
+  case 0x9c:
+    cpu_z80_res(self, 3, CPU_Z80_EA_H);
+    break;
+  case 0x9d:
+    cpu_z80_res(self, 3, CPU_Z80_EA_L);
+    break;
+  case 0x9e:
+    cpu_z80_res(self, 3, self->regs.word.hl);
+    break;
+  case 0x9f:
+    cpu_z80_res(self, 3, CPU_Z80_EA_A);
+    break;
+  case 0xa0:
+    cpu_z80_res(self, 4, CPU_Z80_EA_B);
+    break;
+  case 0xa1:
+    cpu_z80_res(self, 4, CPU_Z80_EA_C);
+    break;
+  case 0xa2:
+    cpu_z80_res(self, 4, CPU_Z80_EA_D);
+    break;
+  case 0xa3:
+    cpu_z80_res(self, 4, CPU_Z80_EA_E);
+    break;
+  case 0xa4:
+    cpu_z80_res(self, 4, CPU_Z80_EA_H);
+    break;
+  case 0xa5:
+    cpu_z80_res(self, 4, CPU_Z80_EA_L);
+    break;
+  case 0xa6:
+    cpu_z80_res(self, 4, self->regs.word.hl);
+    break;
+  case 0xa7:
+    cpu_z80_res(self, 4, CPU_Z80_EA_A);
+    break;
+  case 0xa8:
+    cpu_z80_res(self, 5, CPU_Z80_EA_B);
+    break;
+  case 0xa9:
+    cpu_z80_res(self, 5, CPU_Z80_EA_C);
+    break;
+  case 0xaa:
+    cpu_z80_res(self, 5, CPU_Z80_EA_D);
+    break;
+  case 0xab:
+    cpu_z80_res(self, 5, CPU_Z80_EA_E);
+    break;
+  case 0xac:
+    cpu_z80_res(self, 5, CPU_Z80_EA_H);
+    break;
+  case 0xad:
+    cpu_z80_res(self, 5, CPU_Z80_EA_L);
+    break;
+  case 0xae:
+    cpu_z80_res(self, 5, self->regs.word.hl);
+    break;
+  case 0xaf:
+    cpu_z80_res(self, 5, CPU_Z80_EA_A);
+    break;
+  case 0xb0:
+    cpu_z80_res(self, 6, CPU_Z80_EA_B);
+    break;
+  case 0xb1:
+    cpu_z80_res(self, 6, CPU_Z80_EA_C);
+    break;
+  case 0xb2:
+    cpu_z80_res(self, 6, CPU_Z80_EA_D);
+    break;
+  case 0xb3:
+    cpu_z80_res(self, 6, CPU_Z80_EA_E);
+    break;
+  case 0xb4:
+    cpu_z80_res(self, 6, CPU_Z80_EA_H);
+    break;
+  case 0xb5:
+    cpu_z80_res(self, 6, CPU_Z80_EA_L);
+    break;
+  case 0xb6:
+    cpu_z80_res(self, 6, self->regs.word.hl);
+    break;
+  case 0xb7:
+    cpu_z80_res(self, 6, CPU_Z80_EA_A);
+    break;
+  case 0xb8:
+    cpu_z80_res(self, 7, CPU_Z80_EA_B);
+    break;
+  case 0xb9:
+    cpu_z80_res(self, 7, CPU_Z80_EA_C);
+    break;
+  case 0xba:
+    cpu_z80_res(self, 7, CPU_Z80_EA_D);
+    break;
+  case 0xbb:
+    cpu_z80_res(self, 7, CPU_Z80_EA_E);
+    break;
+  case 0xbc:
+    cpu_z80_res(self, 7, CPU_Z80_EA_H);
+    break;
+  case 0xbd:
+    cpu_z80_res(self, 7, CPU_Z80_EA_L);
+    break;
+  case 0xbe:
+    cpu_z80_res(self, 7, self->regs.word.hl);
+    break;
+  case 0xbf:
+    cpu_z80_res(self, 7, CPU_Z80_EA_A);
+    break;
+  case 0xc0:
+    cpu_z80_set(self, 0, CPU_Z80_EA_B);
+    break;
+  case 0xc1:
+    cpu_z80_set(self, 0, CPU_Z80_EA_C);
+    break;
+  case 0xc2:
+    cpu_z80_set(self, 0, CPU_Z80_EA_D);
+    break;
+  case 0xc3:
+    cpu_z80_set(self, 0, CPU_Z80_EA_E);
+    break;
+  case 0xc4:
+    cpu_z80_set(self, 0, CPU_Z80_EA_H);
+    break;
+  case 0xc5:
+    cpu_z80_set(self, 0, CPU_Z80_EA_L);
+    break;
+  case 0xc6:
+    cpu_z80_set(self, 0, self->regs.word.hl);
+    break;
+  case 0xc7:
+    cpu_z80_set(self, 0, CPU_Z80_EA_A);
+    break;
+  case 0xc8:
+    cpu_z80_set(self, 1, CPU_Z80_EA_B);
+    break;
+  case 0xc9:
+    cpu_z80_set(self, 1, CPU_Z80_EA_C);
+    break;
+  case 0xca:
+    cpu_z80_set(self, 1, CPU_Z80_EA_D);
+    break;
+  case 0xcb:
+    cpu_z80_set(self, 1, CPU_Z80_EA_E);
+    break;
+  case 0xcc:
+    cpu_z80_set(self, 1, CPU_Z80_EA_H);
+    break;
+  case 0xcd:
+    cpu_z80_set(self, 1, CPU_Z80_EA_L);
+    break;
+  case 0xce:
+    cpu_z80_set(self, 1, self->regs.word.hl);
+    break;
+  case 0xcf:
+    cpu_z80_set(self, 1, CPU_Z80_EA_A);
+    break;
+  case 0xd0:
+    cpu_z80_set(self, 2, CPU_Z80_EA_B);
+    break;
+  case 0xd1:
+    cpu_z80_set(self, 2, CPU_Z80_EA_C);
+    break;
+  case 0xd2:
+    cpu_z80_set(self, 2, CPU_Z80_EA_D);
+    break;
+  case 0xd3:
+    cpu_z80_set(self, 2, CPU_Z80_EA_E);
+    break;
+  case 0xd4:
+    cpu_z80_set(self, 2, CPU_Z80_EA_H);
+    break;
+  case 0xd5:
+    cpu_z80_set(self, 2, CPU_Z80_EA_L);
+    break;
+  case 0xd6:
+    cpu_z80_set(self, 2, self->regs.word.hl);
+    break;
+  case 0xd7:
+    cpu_z80_set(self, 2, CPU_Z80_EA_A);
+    break;
+  case 0xd8:
+    cpu_z80_set(self, 3, CPU_Z80_EA_B);
+    break;
+  case 0xd9:
+    cpu_z80_set(self, 3, CPU_Z80_EA_C);
+    break;
+  case 0xda:
+    cpu_z80_set(self, 3, CPU_Z80_EA_D);
+    break;
+  case 0xdb:
+    cpu_z80_set(self, 3, CPU_Z80_EA_E);
+    break;
+  case 0xdc:
+    cpu_z80_set(self, 3, CPU_Z80_EA_H);
+    break;
+  case 0xdd:
+    cpu_z80_set(self, 3, CPU_Z80_EA_L);
+    break;
+  case 0xde:
+    cpu_z80_set(self, 3, self->regs.word.hl);
+    break;
+  case 0xdf:
+    cpu_z80_set(self, 3, CPU_Z80_EA_A);
+    break;
+  case 0xe0:
+    cpu_z80_set(self, 4, CPU_Z80_EA_B);
+    break;
+  case 0xe1:
+    cpu_z80_set(self, 4, CPU_Z80_EA_C);
+    break;
+  case 0xe2:
+    cpu_z80_set(self, 4, CPU_Z80_EA_D);
+    break;
+  case 0xe3:
+    cpu_z80_set(self, 4, CPU_Z80_EA_E);
+    break;
+  case 0xe4:
+    cpu_z80_set(self, 4, CPU_Z80_EA_H);
+    break;
+  case 0xe5:
+    cpu_z80_set(self, 4, CPU_Z80_EA_L);
+    break;
+  case 0xe6:
+    cpu_z80_set(self, 4, self->regs.word.hl);
+    break;
+  case 0xe7:
+    cpu_z80_set(self, 4, CPU_Z80_EA_A);
+    break;
+  case 0xe8:
+    cpu_z80_set(self, 5, CPU_Z80_EA_B);
+    break;
+  case 0xe9:
+    cpu_z80_set(self, 5, CPU_Z80_EA_C);
+    break;
+  case 0xea:
+    cpu_z80_set(self, 5, CPU_Z80_EA_D);
+    break;
+  case 0xeb:
+    cpu_z80_set(self, 5, CPU_Z80_EA_E);
+    break;
+  case 0xec:
+    cpu_z80_set(self, 5, CPU_Z80_EA_H);
+    break;
+  case 0xed:
+    cpu_z80_set(self, 5, CPU_Z80_EA_L);
+    break;
+  case 0xee:
+    cpu_z80_set(self, 5, self->regs.word.hl);
+    break;
+  case 0xef:
+    cpu_z80_set(self, 5, CPU_Z80_EA_A);
+    break;
+  case 0xf0:
+    cpu_z80_set(self, 6, CPU_Z80_EA_B);
+    break;
+  case 0xf1:
+    cpu_z80_set(self, 6, CPU_Z80_EA_C);
+    break;
+  case 0xf2:
+    cpu_z80_set(self, 6, CPU_Z80_EA_D);
+    break;
+  case 0xf3:
+    cpu_z80_set(self, 6, CPU_Z80_EA_E);
+    break;
+  case 0xf4:
+    cpu_z80_set(self, 6, CPU_Z80_EA_H);
+    break;
+  case 0xf5:
+    cpu_z80_set(self, 6, CPU_Z80_EA_L);
+    break;
+  case 0xf6:
+    cpu_z80_set(self, 6, self->regs.word.hl);
+    break;
+  case 0xf7:
+    cpu_z80_set(self, 6, CPU_Z80_EA_A);
+    break;
+  case 0xf8:
+    cpu_z80_set(self, 7, CPU_Z80_EA_B);
+    break;
+  case 0xf9:
+    cpu_z80_set(self, 7, CPU_Z80_EA_C);
+    break;
+  case 0xfa:
+    cpu_z80_set(self, 7, CPU_Z80_EA_D);
+    break;
+  case 0xfb:
+    cpu_z80_set(self, 7, CPU_Z80_EA_E);
+    break;
+  case 0xfc:
+    cpu_z80_set(self, 7, CPU_Z80_EA_H);
+    break;
+  case 0xfd:
+    cpu_z80_set(self, 7, CPU_Z80_EA_L);
+    break;
+  case 0xfe:
+    cpu_z80_set(self, 7, self->regs.word.hl);
+    break;
+  case 0xff:
+    cpu_z80_set(self, 7, CPU_Z80_EA_A);
+    break;
+  }
+}
+
+void cpu_z80_execute_dd(struct cpu_z80 *self) {
+  switch (cpu_z80_fetch_byte(self)) {
+  case 0x00:
+    cpu_z80_nop(self);
+    break;
+  case 0x01:
+    cpu_z80_ld_word(self, CPU_Z80_EA_BC, cpu_z80_fetch_word(self));
+    break;
+  case 0x02:
+    cpu_z80_ld_byte(self, self->regs.word.bc, self->regs.byte.a);
+    break;
+  case 0x03:
+    cpu_z80_inc_word(self, CPU_Z80_EA_BC);
+    break;
+  case 0x04:
+    cpu_z80_inc_byte(self, CPU_Z80_EA_B);
+    break;
+  case 0x05:
+    cpu_z80_dec_byte(self, CPU_Z80_EA_B);
+    break;
+  case 0x06:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_B, cpu_z80_fetch_byte(self));
+    break;
+  case 0x07:
+    cpu_z80_rlca(self);
+    break;
+  case 0x08:
+    cpu_z80_ex(self, CPU_Z80_EA_AF, CPU_Z80_EA_AF_PRIME);
+    break;
+  case 0x09:
+    cpu_z80_add_word(self, CPU_Z80_EA_IX, self->regs.word.bc);
+    break;
+  case 0x0a:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_A, cpu_z80_read_byte(self, self->regs.word.bc));
+    break;
+  case 0x0b:
+    cpu_z80_dec_word(self, CPU_Z80_EA_BC);
+    break;
+  case 0x0c:
+    cpu_z80_inc_byte(self, CPU_Z80_EA_C);
+    break;
+  case 0x0d:
+    cpu_z80_dec_byte(self, CPU_Z80_EA_C);
+    break;
+  case 0x0e:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_C, cpu_z80_fetch_byte(self));
+    break;
+  case 0x0f:
+    cpu_z80_rrca(self);
+    break;
+  case 0x10:
+    cpu_z80_djnz(self, cpu_z80_relative(self));
+    break;
+  case 0x11:
+    cpu_z80_ld_word(self, CPU_Z80_EA_DE, cpu_z80_fetch_word(self));
+    break;
+  case 0x12:
+    cpu_z80_ld_byte(self, self->regs.word.de, self->regs.byte.a);
+    break;
+  case 0x13:
+    cpu_z80_inc_word(self, CPU_Z80_EA_DE);
+    break;
+  case 0x14:
+    cpu_z80_inc_byte(self, CPU_Z80_EA_D);
+    break;
+  case 0x15:
+    cpu_z80_dec_byte(self, CPU_Z80_EA_D);
+    break;
+  case 0x16:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_D, cpu_z80_fetch_byte(self));
+    break;
+  case 0x17:
+    cpu_z80_rla(self);
+    break;
+  case 0x18:
+    cpu_z80_jr(self, true, cpu_z80_relative(self));
+    break;
+  case 0x19:
+    cpu_z80_add_word(self, CPU_Z80_EA_IX, self->regs.word.de);
+    break;
+  case 0x1a:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_A, cpu_z80_read_byte(self, self->regs.word.de));
+    break;
+  case 0x1b:
+    cpu_z80_dec_word(self, CPU_Z80_EA_DE);
+    break;
+  case 0x1c:
+    cpu_z80_inc_byte(self, CPU_Z80_EA_E);
+    break;
+  case 0x1d:
+    cpu_z80_dec_byte(self, CPU_Z80_EA_E);
+    break;
+  case 0x1e:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_E, cpu_z80_fetch_byte(self));
+    break;
+  case 0x1f:
+    cpu_z80_rra(self);
+    break;
+  case 0x20:
+    cpu_z80_jr(self, !self->regs.bit.zf, cpu_z80_relative(self));
+    break;
+  case 0x21:
+    cpu_z80_ld_word(self, CPU_Z80_EA_IX, cpu_z80_fetch_word(self));
+    break;
+  case 0x22:
+    cpu_z80_ld_word(self, cpu_z80_fetch_word(self), self->regs.word.ix);
+    break;
+  case 0x23:
+    cpu_z80_inc_word(self, CPU_Z80_EA_IX);
+    break;
+  case 0x24:
+    cpu_z80_inc_byte(self, CPU_Z80_EA_IXH);
+    break;
+  case 0x25:
+    cpu_z80_dec_byte(self, CPU_Z80_EA_IXH);
+    break;
+  case 0x26:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_IXH, cpu_z80_fetch_byte(self));
+    break;
+  case 0x27:
+    cpu_z80_daa(self);
+    break;
+  case 0x28:
+    cpu_z80_jr(self, self->regs.bit.zf, cpu_z80_relative(self));
+    break;
+  case 0x29:
+    cpu_z80_add_word(self, CPU_Z80_EA_IX, self->regs.word.ix);
+    break;
+  case 0x2a:
+    cpu_z80_ld_word(self, CPU_Z80_EA_IX, cpu_z80_read_word(self, cpu_z80_fetch_word(self)));
+    break;
+  case 0x2b:
+    cpu_z80_dec_word(self, CPU_Z80_EA_IX);
+    break;
+  case 0x2c:
+    cpu_z80_inc_byte(self, CPU_Z80_EA_IXL);
+    break;
+  case 0x2d:
+    cpu_z80_dec_byte(self, CPU_Z80_EA_IXL);
+    break;
+  case 0x2e:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_IXL, cpu_z80_fetch_byte(self));
+    break;
+  case 0x2f:
+    cpu_z80_cpl(self);
+    break;
+  case 0x30:
+    cpu_z80_jr(self, !self->regs.bit.cf, cpu_z80_relative(self));
+    break;
+  case 0x31:
+    cpu_z80_ld_word(self, CPU_Z80_EA_SP, cpu_z80_fetch_word(self));
+    break;
+  case 0x32:
+    cpu_z80_ld_byte(self, cpu_z80_fetch_word(self), self->regs.byte.a);
+    break;
+  case 0x33:
+    cpu_z80_inc_word(self, CPU_Z80_EA_SP);
+    break;
+  case 0x34:
+    cpu_z80_inc_byte(self, cpu_z80_displacement(self, self->regs.word.ix));
+    break;
+  case 0x35:
+    cpu_z80_dec_byte(self, cpu_z80_displacement(self, self->regs.word.ix));
+    break;
+  case 0x36:
+    {
+      int ea = cpu_z80_displacement(self, self->regs.word.ix);
+      cpu_z80_ld_byte(self, ea, cpu_z80_fetch_byte(self));
+    }
+    break;
+  case 0x37:
+    cpu_z80_scf(self);
+    break;
+  case 0x38:
+    cpu_z80_jr(self, self->regs.bit.cf, cpu_z80_relative(self));
+    break;
+  case 0x39:
+    cpu_z80_add_word(self, CPU_Z80_EA_IX, self->regs.word.sp);
+    break;
+  case 0x3a:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_A, cpu_z80_read_byte(self, cpu_z80_fetch_word(self)));
+    break;
+  case 0x3b:
+    cpu_z80_dec_word(self, CPU_Z80_EA_SP);
+    break;
+  case 0x3c:
+    cpu_z80_inc_byte(self, CPU_Z80_EA_A);
+    break;
+  case 0x3d:
+    cpu_z80_dec_byte(self, CPU_Z80_EA_A);
+    break;
+  case 0x3e:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_A, cpu_z80_fetch_byte(self));
+    break;
+  case 0x3f:
+    cpu_z80_ccf(self);
+    break;
+  case 0x40:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_B, self->regs.byte.b);
+    break;
+  case 0x41:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_B, self->regs.byte.c);
+    break;
+  case 0x42:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_B, self->regs.byte.d);
+    break;
+  case 0x43:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_B, self->regs.byte.e);
+    break;
+  case 0x44:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_B, self->regs.byte.ixh);
+    break;
+  case 0x45:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_B, self->regs.byte.ixl);
+    break;
+  case 0x46:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_B, cpu_z80_read_byte(self, cpu_z80_displacement(self, self->regs.word.ix)));
+    break;
+  case 0x47:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_B, self->regs.byte.a);
+    break;
+  case 0x48:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_C, self->regs.byte.b);
+    break;
+  case 0x49:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_C, self->regs.byte.c);
+    break;
+  case 0x4a:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_C, self->regs.byte.d);
+    break;
+  case 0x4b:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_C, self->regs.byte.e);
+    break;
+  case 0x4c:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_C, self->regs.byte.ixh);
+    break;
+  case 0x4d:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_C, self->regs.byte.ixl);
+    break;
+  case 0x4e:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_C, cpu_z80_read_byte(self, cpu_z80_displacement(self, self->regs.word.ix)));
+    break;
+  case 0x4f:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_C, self->regs.byte.a);
+    break;
+  case 0x50:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_D, self->regs.byte.b);
+    break;
+  case 0x51:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_D, self->regs.byte.c);
+    break;
+  case 0x52:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_D, self->regs.byte.d);
+    break;
+  case 0x53:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_D, self->regs.byte.e);
+    break;
+  case 0x54:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_D, self->regs.byte.ixh);
+    break;
+  case 0x55:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_D, self->regs.byte.ixl);
+    break;
+  case 0x56:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_D, cpu_z80_read_byte(self, cpu_z80_displacement(self, self->regs.word.ix)));
+    break;
+  case 0x57:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_D, self->regs.byte.a);
+    break;
+  case 0x58:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_E, self->regs.byte.b);
+    break;
+  case 0x59:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_E, self->regs.byte.c);
+    break;
+  case 0x5a:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_E, self->regs.byte.d);
+    break;
+  case 0x5b:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_E, self->regs.byte.e);
+    break;
+  case 0x5c:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_E, self->regs.byte.ixh);
+    break;
+  case 0x5d:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_E, self->regs.byte.ixl);
+    break;
+  case 0x5e:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_E, cpu_z80_read_byte(self, cpu_z80_displacement(self, self->regs.word.ix)));
+    break;
+  case 0x5f:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_E, self->regs.byte.a);
+    break;
+  case 0x60:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_IXH, self->regs.byte.b);
+    break;
+  case 0x61:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_IXH, self->regs.byte.c);
+    break;
+  case 0x62:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_IXH, self->regs.byte.d);
+    break;
+  case 0x63:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_IXH, self->regs.byte.e);
+    break;
+  case 0x64:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_IXH, self->regs.byte.ixh);
+    break;
+  case 0x65:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_IXH, self->regs.byte.ixl);
+    break;
+  case 0x66:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_H, cpu_z80_read_byte(self, cpu_z80_displacement(self, self->regs.word.ix)));
+    break;
+  case 0x67:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_IXH, self->regs.byte.a);
+    break;
+  case 0x68:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_IXL, self->regs.byte.b);
+    break;
+  case 0x69:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_IXL, self->regs.byte.c);
+    break;
+  case 0x6a:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_IXL, self->regs.byte.d);
+    break;
+  case 0x6b:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_IXL, self->regs.byte.e);
+    break;
+  case 0x6c:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_IXL, self->regs.byte.ixh);
+    break;
+  case 0x6d:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_IXL, self->regs.byte.ixl);
+    break;
+  case 0x6e:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_L, cpu_z80_read_byte(self, cpu_z80_displacement(self, self->regs.word.ix)));
+    break;
+  case 0x6f:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_IXL, self->regs.byte.a);
+    break;
+  case 0x70:
+    cpu_z80_ld_byte(self, cpu_z80_displacement(self, self->regs.word.ix), self->regs.byte.b);
+    break;
+  case 0x71:
+    cpu_z80_ld_byte(self, cpu_z80_displacement(self, self->regs.word.ix), self->regs.byte.c);
+    break;
+  case 0x72:
+    cpu_z80_ld_byte(self, cpu_z80_displacement(self, self->regs.word.ix), self->regs.byte.d);
+    break;
+  case 0x73:
+    cpu_z80_ld_byte(self, cpu_z80_displacement(self, self->regs.word.ix), self->regs.byte.e);
+    break;
+  case 0x74:
+    cpu_z80_ld_byte(self, cpu_z80_displacement(self, self->regs.word.ix), self->regs.byte.h);
+    break;
+  case 0x75:
+    cpu_z80_ld_byte(self, cpu_z80_displacement(self, self->regs.word.ix), self->regs.byte.l);
+    break;
+  case 0x76:
+    cpu_z80_halt(self);
+    break;
+  case 0x77:
+    cpu_z80_ld_byte(self, cpu_z80_displacement(self, self->regs.word.ix), self->regs.byte.a);
+    break;
+  case 0x78:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_A, self->regs.byte.b);
+    break;
+  case 0x79:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_A, self->regs.byte.c);
+    break;
+  case 0x7a:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_A, self->regs.byte.d);
+    break;
+  case 0x7b:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_A, self->regs.byte.e);
+    break;
+  case 0x7c:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_A, self->regs.byte.ixh);
+    break;
+  case 0x7d:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_A, self->regs.byte.ixl);
+    break;
+  case 0x7e:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_A, cpu_z80_read_byte(self, cpu_z80_displacement(self, self->regs.word.ix)));
+    break;
+  case 0x7f:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_A, self->regs.byte.a);
+    break;
+  case 0x80:
+    cpu_z80_add_byte(self, CPU_Z80_EA_A, self->regs.byte.b);
+    break;
+  case 0x81:
+    cpu_z80_add_byte(self, CPU_Z80_EA_A, self->regs.byte.c);
+    break;
+  case 0x82:
+    cpu_z80_add_byte(self, CPU_Z80_EA_A, self->regs.byte.d);
+    break;
+  case 0x83:
+    cpu_z80_add_byte(self, CPU_Z80_EA_A, self->regs.byte.e);
+    break;
+  case 0x84:
+    cpu_z80_add_byte(self, CPU_Z80_EA_A, self->regs.byte.ixh);
+    break;
+  case 0x85:
+    cpu_z80_add_byte(self, CPU_Z80_EA_A, self->regs.byte.ixl);
+    break;
+  case 0x86:
+    cpu_z80_add_byte(self, CPU_Z80_EA_A, cpu_z80_read_byte(self, cpu_z80_displacement(self, self->regs.word.ix)));
+    break;
+  case 0x87:
+    cpu_z80_add_byte(self, CPU_Z80_EA_A, self->regs.byte.a);
+    break;
+  case 0x88:
+    cpu_z80_adc_byte(self, CPU_Z80_EA_A, self->regs.byte.b);
+    break;
+  case 0x89:
+    cpu_z80_adc_byte(self, CPU_Z80_EA_A, self->regs.byte.c);
+    break;
+  case 0x8a:
+    cpu_z80_adc_byte(self, CPU_Z80_EA_A, self->regs.byte.d);
+    break;
+  case 0x8b:
+    cpu_z80_adc_byte(self, CPU_Z80_EA_A, self->regs.byte.e);
+    break;
+  case 0x8c:
+    cpu_z80_adc_byte(self, CPU_Z80_EA_A, self->regs.byte.ixh);
+    break;
+  case 0x8d:
+    cpu_z80_adc_byte(self, CPU_Z80_EA_A, self->regs.byte.ixl);
+    break;
+  case 0x8e:
+    cpu_z80_adc_byte(self, CPU_Z80_EA_A, cpu_z80_read_byte(self, cpu_z80_displacement(self, self->regs.word.ix)));
+    break;
+  case 0x8f:
+    cpu_z80_adc_byte(self, CPU_Z80_EA_A, self->regs.byte.a);
+    break;
+  case 0x90:
+    cpu_z80_sub(self, self->regs.byte.b);
+    break;
+  case 0x91:
+    cpu_z80_sub(self, self->regs.byte.c);
+    break;
+  case 0x92:
+    cpu_z80_sub(self, self->regs.byte.d);
+    break;
+  case 0x93:
+    cpu_z80_sub(self, self->regs.byte.e);
+    break;
+  case 0x94:
+    cpu_z80_sub(self, self->regs.byte.ixh);
+    break;
+  case 0x95:
+    cpu_z80_sub(self, self->regs.byte.ixl);
+    break;
+  case 0x96:
+    cpu_z80_sub(self, cpu_z80_read_byte(self, cpu_z80_displacement(self, self->regs.word.ix)));
+    break;
+  case 0x97:
+    cpu_z80_sub(self, self->regs.byte.a);
+    break;
+  case 0x98:
+    cpu_z80_sbc_byte(self, CPU_Z80_EA_A, self->regs.byte.b);
+    break;
+  case 0x99:
+    cpu_z80_sbc_byte(self, CPU_Z80_EA_A, self->regs.byte.c);
+    break;
+  case 0x9a:
+    cpu_z80_sbc_byte(self, CPU_Z80_EA_A, self->regs.byte.d);
+    break;
+  case 0x9b:
+    cpu_z80_sbc_byte(self, CPU_Z80_EA_A, self->regs.byte.e);
+    break;
+  case 0x9c:
+    cpu_z80_sbc_byte(self, CPU_Z80_EA_A, self->regs.byte.ixh);
+    break;
+  case 0x9d:
+    cpu_z80_sbc_byte(self, CPU_Z80_EA_A, self->regs.byte.ixl);
+    break;
+  case 0x9e:
+    cpu_z80_sbc_byte(self, CPU_Z80_EA_A, cpu_z80_read_byte(self, cpu_z80_displacement(self, self->regs.word.ix)));
+    break;
+  case 0x9f:
+    cpu_z80_sbc_byte(self, CPU_Z80_EA_A, self->regs.byte.a);
+    break;
+  case 0xa0:
+    cpu_z80_and(self, self->regs.byte.b);
+    break;
+  case 0xa1:
+    cpu_z80_and(self, self->regs.byte.c);
+    break;
+  case 0xa2:
+    cpu_z80_and(self, self->regs.byte.d);
+    break;
+  case 0xa3:
+    cpu_z80_and(self, self->regs.byte.e);
+    break;
+  case 0xa4:
+    cpu_z80_and(self, self->regs.byte.ixh);
+    break;
+  case 0xa5:
+    cpu_z80_and(self, self->regs.byte.ixl);
+    break;
+  case 0xa6:
+    cpu_z80_and(self, cpu_z80_read_byte(self, cpu_z80_displacement(self, self->regs.word.ix)));
+    break;
+  case 0xa7:
+    cpu_z80_and(self, self->regs.byte.a);
+    break;
+  case 0xa8:
+    cpu_z80_xor(self, self->regs.byte.b);
+    break;
+  case 0xa9:
+    cpu_z80_xor(self, self->regs.byte.c);
+    break;
+  case 0xaa:
+    cpu_z80_xor(self, self->regs.byte.d);
+    break;
+  case 0xab:
+    cpu_z80_xor(self, self->regs.byte.e);
+    break;
+  case 0xac:
+    cpu_z80_xor(self, self->regs.byte.ixh);
+    break;
+  case 0xad:
+    cpu_z80_xor(self, self->regs.byte.ixl);
+    break;
+  case 0xae:
+    cpu_z80_xor(self, cpu_z80_read_byte(self, cpu_z80_displacement(self, self->regs.word.ix)));
+    break;
+  case 0xaf:
+    cpu_z80_xor(self, self->regs.byte.a);
+    break;
+  case 0xb0:
+    cpu_z80_or(self, self->regs.byte.b);
+    break;
+  case 0xb1:
+    cpu_z80_or(self, self->regs.byte.c);
+    break;
+  case 0xb2:
+    cpu_z80_or(self, self->regs.byte.d);
+    break;
+  case 0xb3:
+    cpu_z80_or(self, self->regs.byte.e);
+    break;
+  case 0xb4:
+    cpu_z80_or(self, self->regs.byte.ixh);
+    break;
+  case 0xb5:
+    cpu_z80_or(self, self->regs.byte.ixl);
+    break;
+  case 0xb6:
+    cpu_z80_or(self, cpu_z80_read_byte(self, cpu_z80_displacement(self, self->regs.word.ix)));
+    break;
+  case 0xb7:
+    cpu_z80_or(self, self->regs.byte.a);
+    break;
+  case 0xb8:
+    cpu_z80_cp(self, self->regs.byte.b);
+    break;
+  case 0xb9:
+    cpu_z80_cp(self, self->regs.byte.c);
+    break;
+  case 0xba:
+    cpu_z80_cp(self, self->regs.byte.d);
+    break;
+  case 0xbb:
+    cpu_z80_cp(self, self->regs.byte.e);
+    break;
+  case 0xbc:
+    cpu_z80_cp(self, self->regs.byte.ixh);
+    break;
+  case 0xbd:
+    cpu_z80_cp(self, self->regs.byte.ixl);
+    break;
+  case 0xbe:
+    cpu_z80_cp(self, cpu_z80_read_byte(self, cpu_z80_displacement(self, self->regs.word.ix)));
+    break;
+  case 0xbf:
+    cpu_z80_cp(self, self->regs.byte.a);
+    break;
+  case 0xc0:
+    cpu_z80_ret(self, !self->regs.bit.zf);
+    break;
+  case 0xc1:
+    cpu_z80_pop(self, CPU_Z80_EA_BC);
+    break;
+  case 0xc2:
+    cpu_z80_jp(self, !self->regs.bit.zf, cpu_z80_fetch_word(self));
+    break;
+  case 0xc3:
+    cpu_z80_jp(self, true, cpu_z80_fetch_word(self));
+    break;
+  case 0xc4:
+    cpu_z80_call(self, !self->regs.bit.zf, cpu_z80_fetch_word(self));
+    break;
+  case 0xc5:
+    cpu_z80_push(self, self->regs.word.bc);
+    break;
+  case 0xc6:
+    cpu_z80_add_byte(self, CPU_Z80_EA_A, cpu_z80_fetch_byte(self));
+    break;
+  case 0xc7:
+    cpu_z80_call(self, true, 0);
+    break;
+  case 0xc8:
+    cpu_z80_ret(self, self->regs.bit.zf);
+    break;
+  case 0xc9:
+    cpu_z80_ret(self, true);
+    break;
+  case 0xca:
+    cpu_z80_jp(self, self->regs.bit.zf, cpu_z80_fetch_word(self));
+    break;
+  case 0xcb:
+    cpu_z80_ill(self);
+    break;
+  case 0xcc:
+    cpu_z80_call(self, self->regs.bit.zf, cpu_z80_fetch_word(self));
+    break;
+  case 0xcd:
+    cpu_z80_call(self, true, cpu_z80_fetch_word(self));
+    break;
+  case 0xce:
+    cpu_z80_adc_byte(self, CPU_Z80_EA_A, cpu_z80_fetch_byte(self));
+    break;
+  case 0xcf:
+    cpu_z80_call(self, true, 8);
+    break;
+  case 0xd0:
+    cpu_z80_ret(self, !self->regs.bit.cf);
+    break;
+  case 0xd1:
+    cpu_z80_pop(self, CPU_Z80_EA_DE);
+    break;
+  case 0xd2:
+    cpu_z80_jp(self, !self->regs.bit.cf, cpu_z80_fetch_word(self));
+    break;
+  case 0xd3:
+    cpu_z80_out(self, cpu_z80_fetch_byte(self), self->regs.byte.a);
+    break;
+  case 0xd4:
+    cpu_z80_call(self, !self->regs.bit.cf, cpu_z80_fetch_word(self));
+    break;
+  case 0xd5:
+    cpu_z80_push(self, self->regs.word.de);
+    break;
+  case 0xd6:
+    cpu_z80_sub(self, cpu_z80_fetch_byte(self));
+    break;
+  case 0xd7:
+    cpu_z80_call(self, true, 0x10);
+    break;
+  case 0xd8:
+    cpu_z80_ret(self, self->regs.bit.cf);
+    break;
+  case 0xd9:
+    cpu_z80_ex(self, CPU_Z80_EA_BC, CPU_Z80_EA_BC_PRIME);
+    cpu_z80_ex(self, CPU_Z80_EA_DE, CPU_Z80_EA_DE_PRIME);
+    cpu_z80_ex(self, CPU_Z80_EA_HL, CPU_Z80_EA_HL_PRIME);
+    break;
+  case 0xda:
+    cpu_z80_jp(self, self->regs.bit.cf, cpu_z80_fetch_word(self));
+    break;
+  case 0xdb:
+    cpu_z80_in(self, CPU_Z80_EA_A, cpu_z80_in_byte(self, cpu_z80_fetch_byte(self)));
+    break;
+  case 0xdc:
+    cpu_z80_call(self, self->regs.bit.cf, cpu_z80_fetch_word(self));
+    break;
+  case 0xdd:
+    cpu_z80_ill(self);
+    break;
+  case 0xde:
+    cpu_z80_sbc_byte(self, CPU_Z80_EA_A, cpu_z80_fetch_byte(self));
+    break;
+  case 0xdf:
+    cpu_z80_call(self, true, 0x18);
+    break;
+  case 0xe0:
+    cpu_z80_ret(self, self->regs.bit.pvf);
+    break;
+  case 0xe1:
+    cpu_z80_pop(self, CPU_Z80_EA_IX);
+    break;
+  case 0xe2:
+    cpu_z80_jp(self, self->regs.bit.pvf, cpu_z80_fetch_word(self));
+    break;
+  case 0xe3:
+    cpu_z80_ex(self, self->regs.word.sp, CPU_Z80_EA_IX);
+    break;
+  case 0xe4:
+    cpu_z80_call(self, self->regs.bit.pvf, cpu_z80_fetch_word(self));
+    break;
+  case 0xe5:
+    cpu_z80_push(self, self->regs.word.ix);
+    break;
+  case 0xe6:
+    cpu_z80_and(self, cpu_z80_fetch_byte(self));
+    break;
+  case 0xe7:
+    cpu_z80_call(self, true, 0x20);
+    break;
+  case 0xe8:
+    cpu_z80_ret(self, !self->regs.bit.pvf);
+    break;
+  case 0xe9:
+    cpu_z80_jp(self, true, cpu_z80_read_word(self, self->regs.word.ix));
+    break;
+  case 0xea:
+    cpu_z80_jp(self, !self->regs.bit.pvf, cpu_z80_fetch_word(self));
+    break;
+  case 0xeb:
+    cpu_z80_ex(self, CPU_Z80_EA_DE, CPU_Z80_EA_HL);
+    break;
+  case 0xec:
+    cpu_z80_call(self, !self->regs.bit.pvf, cpu_z80_fetch_word(self));
+    break;
+  case 0xed:
+    cpu_z80_ill(self);
+    break;
+  case 0xee:
+    cpu_z80_xor(self, cpu_z80_fetch_byte(self));
+    break;
+  case 0xef:
+    cpu_z80_call(self, true, 0x28);
+    break;
+  case 0xf0:
+    cpu_z80_ret(self, !self->regs.bit.sf);
+    break;
+  case 0xf1:
+    cpu_z80_pop(self, CPU_Z80_EA_AF);
+    break;
+  case 0xf2:
+    cpu_z80_jp(self, !self->regs.bit.sf, cpu_z80_fetch_word(self));
+    break;
+  case 0xf3:
+    cpu_z80_di(self);
+    break;
+  case 0xf4:
+    cpu_z80_call(self, !self->regs.bit.sf, cpu_z80_fetch_word(self));
+    break;
+  case 0xf5:
+    cpu_z80_push(self, self->regs.word.af);
+    break;
+  case 0xf6:
+    cpu_z80_or(self, cpu_z80_fetch_byte(self));
+    break;
+  case 0xf7:
+    cpu_z80_call(self, true, 0x30);
+    break;
+  case 0xf8:
+    cpu_z80_ret(self, self->regs.bit.sf);
+    break;
+  case 0xf9:
+    cpu_z80_ld_word(self, CPU_Z80_EA_SP, self->regs.word.ix);
+    break;
+  case 0xfa:
+    cpu_z80_jp(self, self->regs.bit.sf, cpu_z80_fetch_word(self));
+    break;
+  case 0xfb:
+    cpu_z80_ei(self);
+    break;
+  case 0xfc:
+    cpu_z80_call(self, self->regs.bit.sf, cpu_z80_fetch_word(self));
+    break;
+  case 0xfd:
+    cpu_z80_ill(self);
+    break;
+  case 0xfe:
+    cpu_z80_cp(self, cpu_z80_fetch_byte(self));
+    break;
+  case 0xff:
+    cpu_z80_call(self, true, 0x38);
+    break;
+  }
+}
+
+void cpu_z80_execute_ed(struct cpu_z80 *self) {
+  switch (cpu_z80_fetch_byte(self)) {
+  case 0x00:
+    cpu_z80_ill(self);
+    break;
+  case 0x01:
+    cpu_z80_ill(self);
+    break;
+  case 0x02:
+    cpu_z80_ill(self);
+    break;
+  case 0x03:
+    cpu_z80_ill(self);
+    break;
+  case 0x04:
+    cpu_z80_ill(self);
+    break;
+  case 0x05:
+    cpu_z80_ill(self);
+    break;
+  case 0x06:
+    cpu_z80_ill(self);
+    break;
+  case 0x07:
+    cpu_z80_ill(self);
+    break;
+  case 0x08:
+    cpu_z80_ill(self);
+    break;
+  case 0x09:
+    cpu_z80_ill(self);
+    break;
+  case 0x0a:
+    cpu_z80_ill(self);
+    break;
+  case 0x0b:
+    cpu_z80_ill(self);
+    break;
+  case 0x0c:
+    cpu_z80_ill(self);
+    break;
+  case 0x0d:
+    cpu_z80_ill(self);
+    break;
+  case 0x0e:
+    cpu_z80_ill(self);
+    break;
+  case 0x0f:
+    cpu_z80_ill(self);
+    break;
+  case 0x10:
+    cpu_z80_ill(self);
+    break;
+  case 0x11:
+    cpu_z80_ill(self);
+    break;
+  case 0x12:
+    cpu_z80_ill(self);
+    break;
+  case 0x13:
+    cpu_z80_ill(self);
+    break;
+  case 0x14:
+    cpu_z80_ill(self);
+    break;
+  case 0x15:
+    cpu_z80_ill(self);
+    break;
+  case 0x16:
+    cpu_z80_ill(self);
+    break;
+  case 0x17:
+    cpu_z80_ill(self);
+    break;
+  case 0x18:
+    cpu_z80_ill(self);
+    break;
+  case 0x19:
+    cpu_z80_ill(self);
+    break;
+  case 0x1a:
+    cpu_z80_ill(self);
+    break;
+  case 0x1b:
+    cpu_z80_ill(self);
+    break;
+  case 0x1c:
+    cpu_z80_ill(self);
+    break;
+  case 0x1d:
+    cpu_z80_ill(self);
+    break;
+  case 0x1e:
+    cpu_z80_ill(self);
+    break;
+  case 0x1f:
+    cpu_z80_ill(self);
+    break;
+  case 0x20:
+    cpu_z80_ill(self);
+    break;
+  case 0x21:
+    cpu_z80_ill(self);
+    break;
+  case 0x22:
+    cpu_z80_ill(self);
+    break;
+  case 0x23:
+    cpu_z80_ill(self);
+    break;
+  case 0x24:
+    cpu_z80_ill(self);
+    break;
+  case 0x25:
+    cpu_z80_ill(self);
+    break;
+  case 0x26:
+    cpu_z80_ill(self);
+    break;
+  case 0x27:
+    cpu_z80_ill(self);
+    break;
+  case 0x28:
+    cpu_z80_ill(self);
+    break;
+  case 0x29:
+    cpu_z80_ill(self);
+    break;
+  case 0x2a:
+    cpu_z80_ill(self);
+    break;
+  case 0x2b:
+    cpu_z80_ill(self);
+    break;
+  case 0x2c:
+    cpu_z80_ill(self);
+    break;
+  case 0x2d:
+    cpu_z80_ill(self);
+    break;
+  case 0x2e:
+    cpu_z80_ill(self);
+    break;
+  case 0x2f:
+    cpu_z80_ill(self);
+    break;
+  case 0x30:
+    cpu_z80_ill(self);
+    break;
+  case 0x31:
+    cpu_z80_ill(self);
+    break;
+  case 0x32:
+    cpu_z80_ill(self);
+    break;
+  case 0x33:
+    cpu_z80_ill(self);
+    break;
+  case 0x34:
+    cpu_z80_ill(self);
+    break;
+  case 0x35:
+    cpu_z80_ill(self);
+    break;
+  case 0x36:
+    cpu_z80_ill(self);
+    break;
+  case 0x37:
+    cpu_z80_ill(self);
+    break;
+  case 0x38:
+    cpu_z80_ill(self);
+    break;
+  case 0x39:
+    cpu_z80_ill(self);
+    break;
+  case 0x3a:
+    cpu_z80_ill(self);
+    break;
+  case 0x3b:
+    cpu_z80_ill(self);
+    break;
+  case 0x3c:
+    cpu_z80_ill(self);
+    break;
+  case 0x3d:
+    cpu_z80_ill(self);
+    break;
+  case 0x3e:
+    cpu_z80_ill(self);
+    break;
+  case 0x3f:
+    cpu_z80_ill(self);
+    break;
+  case 0x40:
+    cpu_z80_in(self, CPU_Z80_EA_B, cpu_z80_in_byte(self, self->regs.word.bc));
+    break;
+  case 0x41:
+    cpu_z80_out(self, self->regs.word.bc, self->regs.byte.b);
+    break;
+  case 0x42:
+    cpu_z80_sbc_word(self, CPU_Z80_EA_HL, self->regs.word.bc);
+    break;
+  case 0x43:
+    cpu_z80_ld_word(self, cpu_z80_fetch_word(self), self->regs.word.bc);
+    break;
+  case 0x44:
+    cpu_z80_neg(self);
+    break;
+  case 0x45:
+    cpu_z80_retn(self);
+    break;
+  case 0x46:
+    cpu_z80_im(self, 0);
+    break;
+  case 0x47:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_I, self->regs.byte.a);
+    break;
+  case 0x48:
+    cpu_z80_in(self, CPU_Z80_EA_C, cpu_z80_in_byte(self, self->regs.word.bc));
+    break;
+  case 0x49:
+    cpu_z80_out(self, self->regs.word.bc, self->regs.byte.c);
+    break;
+  case 0x4a:
+    cpu_z80_adc_word(self, CPU_Z80_EA_HL, self->regs.word.bc);
+    break;
+  case 0x4b:
+    cpu_z80_ld_word(self, CPU_Z80_EA_BC, cpu_z80_read_word(self, cpu_z80_fetch_word(self)));
+    break;
+  case 0x4c:
+    cpu_z80_neg(self);
+    break;
+  case 0x4d:
+    cpu_z80_reti(self);
+    break;
+  case 0x4e:
+    cpu_z80_im(self, 0);
+    break;
+  case 0x4f:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_R, self->regs.byte.a);
+    break;
+  case 0x50:
+    cpu_z80_in(self, CPU_Z80_EA_D, cpu_z80_in_byte(self, self->regs.word.bc));
+    break;
+  case 0x51:
+    cpu_z80_out(self, self->regs.word.bc, self->regs.byte.d);
+    break;
+  case 0x52:
+    cpu_z80_sbc_word(self, CPU_Z80_EA_HL, self->regs.word.de);
+    break;
+  case 0x53:
+    cpu_z80_ld_word(self, cpu_z80_fetch_word(self), self->regs.word.de);
+    break;
+  case 0x54:
+    cpu_z80_neg(self);
+    break;
+  case 0x55:
+    cpu_z80_retn(self);
+    break;
+  case 0x56:
+    cpu_z80_im(self, 1);
+    break;
+  case 0x57:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_A, self->regs.byte.i);
+    break;
+  case 0x58:
+    cpu_z80_in(self, CPU_Z80_EA_E, cpu_z80_in_byte(self, self->regs.word.bc));
+    break;
+  case 0x59:
+    cpu_z80_out(self, self->regs.word.bc, self->regs.byte.e);
+    break;
+  case 0x5a:
+    cpu_z80_adc_word(self, CPU_Z80_EA_HL, self->regs.word.de);
+    break;
+  case 0x5b:
+    cpu_z80_ld_word(self, CPU_Z80_EA_DE, cpu_z80_read_word(self, cpu_z80_fetch_word(self)));
+    break;
+  case 0x5c:
+    cpu_z80_neg(self);
+    break;
+  case 0x5d:
+    cpu_z80_reti(self);
+    break;
+  case 0x5e:
+    cpu_z80_im(self, 2);
+    break;
+  case 0x5f:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_A, self->regs.byte.r);
+    break;
+  case 0x60:
+    cpu_z80_in(self, CPU_Z80_EA_H, cpu_z80_in_byte(self, self->regs.word.bc));
+    break;
+  case 0x61:
+    cpu_z80_out(self, self->regs.word.bc, self->regs.byte.h);
+    break;
+  case 0x62:
+    cpu_z80_sbc_word(self, CPU_Z80_EA_HL, self->regs.word.hl);
+    break;
+  case 0x63:
+    cpu_z80_ld_word(self, cpu_z80_fetch_word(self), self->regs.word.hl);
+    break;
+  case 0x64:
+    cpu_z80_neg(self);
+    break;
+  case 0x65:
+    cpu_z80_retn(self);
+    break;
+  case 0x66:
+    cpu_z80_im(self, 0);
+    break;
+  case 0x67:
+    cpu_z80_rrd(self);
+    break;
+  case 0x68:
+    cpu_z80_in(self, CPU_Z80_EA_L, cpu_z80_in_byte(self, self->regs.word.bc));
+    break;
+  case 0x69:
+    cpu_z80_out(self, self->regs.word.bc, self->regs.byte.l);
+    break;
+  case 0x6a:
+    cpu_z80_adc_word(self, CPU_Z80_EA_HL, self->regs.word.hl);
+    break;
+  case 0x6b:
+    cpu_z80_ld_word(self, CPU_Z80_EA_HL, cpu_z80_read_word(self, cpu_z80_fetch_word(self)));
+    break;
+  case 0x6c:
+    cpu_z80_neg(self);
+    break;
+  case 0x6d:
+    cpu_z80_reti(self);
+    break;
+  case 0x6e:
+    cpu_z80_im(self, 0);
+    break;
+  case 0x6f:
+    cpu_z80_rld(self);
+    break;
+  case 0x70:
+    cpu_z80_in(self, CPU_Z80_EA_NONE, cpu_z80_in_byte(self, self->regs.word.bc));
+    break;
+  case 0x71:
+    cpu_z80_out(self, self->regs.word.bc, 0);
+    break;
+  case 0x72:
+    cpu_z80_sbc_word(self, CPU_Z80_EA_HL, self->regs.word.sp);
+    break;
+  case 0x73:
+    cpu_z80_ld_word(self, cpu_z80_fetch_word(self), self->regs.word.sp);
+    break;
+  case 0x74:
+    cpu_z80_neg(self);
+    break;
+  case 0x75:
+    cpu_z80_retn(self);
+    break;
+  case 0x76:
+    cpu_z80_im(self, 1);
+    break;
+  case 0x77:
+    cpu_z80_nop(self);
+    break;
+  case 0x78:
+    cpu_z80_in(self, CPU_Z80_EA_A, cpu_z80_in_byte(self, self->regs.word.bc));
+    break;
+  case 0x79:
+    cpu_z80_out(self, self->regs.word.bc, self->regs.byte.a);
+    break;
+  case 0x7a:
+    cpu_z80_adc_word(self, CPU_Z80_EA_HL, self->regs.word.sp);
+    break;
+  case 0x7b:
+    cpu_z80_ld_word(self, CPU_Z80_EA_SP, cpu_z80_read_word(self, cpu_z80_fetch_word(self)));
+    break;
+  case 0x7c:
+    cpu_z80_neg(self);
+    break;
+  case 0x7d:
+    cpu_z80_reti(self);
+    break;
+  case 0x7e:
+    cpu_z80_im(self, 2);
+    break;
+  case 0x7f:
+    cpu_z80_nop(self);
+    break;
+  case 0x80:
+    cpu_z80_ill(self);
+    break;
+  case 0x81:
+    cpu_z80_ill(self);
+    break;
+  case 0x82:
+    cpu_z80_ill(self);
+    break;
+  case 0x83:
+    cpu_z80_ill(self);
+    break;
+  case 0x84:
+    cpu_z80_ill(self);
+    break;
+  case 0x85:
+    cpu_z80_ill(self);
+    break;
+  case 0x86:
+    cpu_z80_ill(self);
+    break;
+  case 0x87:
+    cpu_z80_ill(self);
+    break;
+  case 0x88:
+    cpu_z80_ill(self);
+    break;
+  case 0x89:
+    cpu_z80_ill(self);
+    break;
+  case 0x8a:
+    cpu_z80_ill(self);
+    break;
+  case 0x8b:
+    cpu_z80_ill(self);
+    break;
+  case 0x8c:
+    cpu_z80_ill(self);
+    break;
+  case 0x8d:
+    cpu_z80_ill(self);
+    break;
+  case 0x8e:
+    cpu_z80_ill(self);
+    break;
+  case 0x8f:
+    cpu_z80_ill(self);
+    break;
+  case 0x90:
+    cpu_z80_ill(self);
+    break;
+  case 0x91:
+    cpu_z80_ill(self);
+    break;
+  case 0x92:
+    cpu_z80_ill(self);
+    break;
+  case 0x93:
+    cpu_z80_ill(self);
+    break;
+  case 0x94:
+    cpu_z80_ill(self);
+    break;
+  case 0x95:
+    cpu_z80_ill(self);
+    break;
+  case 0x96:
+    cpu_z80_ill(self);
+    break;
+  case 0x97:
+    cpu_z80_ill(self);
+    break;
+  case 0x98:
+    cpu_z80_ill(self);
+    break;
+  case 0x99:
+    cpu_z80_ill(self);
+    break;
+  case 0x9a:
+    cpu_z80_ill(self);
+    break;
+  case 0x9b:
+    cpu_z80_ill(self);
+    break;
+  case 0x9c:
+    cpu_z80_ill(self);
+    break;
+  case 0x9d:
+    cpu_z80_ill(self);
+    break;
+  case 0x9e:
+    cpu_z80_ill(self);
+    break;
+  case 0x9f:
+    cpu_z80_ill(self);
+    break;
+  case 0xa0:
+    cpu_z80_ldi(self);
+    break;
+  case 0xa1:
+    cpu_z80_cpi(self);
+    break;
+  case 0xa2:
+    cpu_z80_ini(self);
+    break;
+  case 0xa3:
+    cpu_z80_outi(self);
+    break;
+  case 0xa4:
+    cpu_z80_ill(self);
+    break;
+  case 0xa5:
+    cpu_z80_ill(self);
+    break;
+  case 0xa6:
+    cpu_z80_ill(self);
+    break;
+  case 0xa7:
+    cpu_z80_ill(self);
+    break;
+  case 0xa8:
+    cpu_z80_ldd(self);
+    break;
+  case 0xa9:
+    cpu_z80_cpd(self);
+    break;
+  case 0xaa:
+    cpu_z80_ind(self);
+    break;
+  case 0xab:
+    cpu_z80_outd(self);
+    break;
+  case 0xac:
+    cpu_z80_ill(self);
+    break;
+  case 0xad:
+    cpu_z80_ill(self);
+    break;
+  case 0xae:
+    cpu_z80_ill(self);
+    break;
+  case 0xaf:
+    cpu_z80_ill(self);
+    break;
+  case 0xb0:
+    cpu_z80_ldir(self);
+    break;
+  case 0xb1:
+    cpu_z80_cpir(self);
+    break;
+  case 0xb2:
+    cpu_z80_inir(self);
+    break;
+  case 0xb3:
+    cpu_z80_otir(self);
+    break;
+  case 0xb4:
+    cpu_z80_ill(self);
+    break;
+  case 0xb5:
+    cpu_z80_ill(self);
+    break;
+  case 0xb6:
+    cpu_z80_ill(self);
+    break;
+  case 0xb7:
+    cpu_z80_ill(self);
+    break;
+  case 0xb8:
+    cpu_z80_lddr(self);
+    break;
+  case 0xb9:
+    cpu_z80_cpdr(self);
+    break;
+  case 0xba:
+    cpu_z80_indr(self);
+    break;
+  case 0xbb:
+    cpu_z80_otdr(self);
+    break;
+  case 0xbc:
+    cpu_z80_ill(self);
+    break;
+  case 0xbd:
+    cpu_z80_ill(self);
+    break;
+  case 0xbe:
+    cpu_z80_ill(self);
+    break;
+  case 0xbf:
+    cpu_z80_ill(self);
+    break;
+  case 0xc0:
+    cpu_z80_ill(self);
+    break;
+  case 0xc1:
+    cpu_z80_ill(self);
+    break;
+  case 0xc2:
+    cpu_z80_ill(self);
+    break;
+  case 0xc3:
+    cpu_z80_ill(self);
+    break;
+  case 0xc4:
+    cpu_z80_ill(self);
+    break;
+  case 0xc5:
+    cpu_z80_ill(self);
+    break;
+  case 0xc6:
+    cpu_z80_ill(self);
+    break;
+  case 0xc7:
+    cpu_z80_ill(self);
+    break;
+  case 0xc8:
+    cpu_z80_ill(self);
+    break;
+  case 0xc9:
+    cpu_z80_ill(self);
+    break;
+  case 0xca:
+    cpu_z80_ill(self);
+    break;
+  case 0xcb:
+    cpu_z80_ill(self);
+    break;
+  case 0xcc:
+    cpu_z80_ill(self);
+    break;
+  case 0xcd:
+    cpu_z80_ill(self);
+    break;
+  case 0xce:
+    cpu_z80_ill(self);
+    break;
+  case 0xcf:
+    cpu_z80_ill(self);
+    break;
+  case 0xd0:
+    cpu_z80_ill(self);
+    break;
+  case 0xd1:
+    cpu_z80_ill(self);
+    break;
+  case 0xd2:
+    cpu_z80_ill(self);
+    break;
+  case 0xd3:
+    cpu_z80_ill(self);
+    break;
+  case 0xd4:
+    cpu_z80_ill(self);
+    break;
+  case 0xd5:
+    cpu_z80_ill(self);
+    break;
+  case 0xd6:
+    cpu_z80_ill(self);
+    break;
+  case 0xd7:
+    cpu_z80_ill(self);
+    break;
+  case 0xd8:
+    cpu_z80_ill(self);
+    break;
+  case 0xd9:
+    cpu_z80_ill(self);
+    break;
+  case 0xda:
+    cpu_z80_ill(self);
+    break;
+  case 0xdb:
+    cpu_z80_ill(self);
+    break;
+  case 0xdc:
+    cpu_z80_ill(self);
+    break;
+  case 0xdd:
+    cpu_z80_ill(self);
+    break;
+  case 0xde:
+    cpu_z80_ill(self);
+    break;
+  case 0xdf:
+    cpu_z80_ill(self);
+    break;
+  case 0xe0:
+    cpu_z80_ill(self);
+    break;
+  case 0xe1:
+    cpu_z80_ill(self);
+    break;
+  case 0xe2:
+    cpu_z80_ill(self);
+    break;
+  case 0xe3:
+    cpu_z80_ill(self);
+    break;
+  case 0xe4:
+    cpu_z80_ill(self);
+    break;
+  case 0xe5:
+    cpu_z80_ill(self);
+    break;
+  case 0xe6:
+    cpu_z80_ill(self);
+    break;
+  case 0xe7:
+    cpu_z80_ill(self);
+    break;
+  case 0xe8:
+    cpu_z80_ill(self);
+    break;
+  case 0xe9:
+    cpu_z80_ill(self);
+    break;
+  case 0xea:
+    cpu_z80_ill(self);
+    break;
+  case 0xeb:
+    cpu_z80_ill(self);
+    break;
+  case 0xec:
+    cpu_z80_ill(self);
+    break;
+  case 0xed:
+    cpu_z80_ill(self);
+    break;
+  case 0xee:
+    cpu_z80_ill(self);
+    break;
+  case 0xef:
+    cpu_z80_ill(self);
+    break;
+  case 0xf0:
+    cpu_z80_ill(self);
+    break;
+  case 0xf1:
+    cpu_z80_ill(self);
+    break;
+  case 0xf2:
+    cpu_z80_ill(self);
+    break;
+  case 0xf3:
+    cpu_z80_ill(self);
+    break;
+  case 0xf4:
+    cpu_z80_ill(self);
+    break;
+  case 0xf5:
+    cpu_z80_ill(self);
+    break;
+  case 0xf6:
+    cpu_z80_ill(self);
+    break;
+  case 0xf7:
+    cpu_z80_ill(self);
+    break;
+  case 0xf8:
+    cpu_z80_ill(self);
+    break;
+  case 0xf9:
+    cpu_z80_ill(self);
+    break;
+  case 0xfa:
+    cpu_z80_ill(self);
+    break;
+  case 0xfb:
+    cpu_z80_ill(self);
+    break;
+  case 0xfc:
+    cpu_z80_ill(self);
+    break;
+  case 0xfd:
+    cpu_z80_ill(self);
+    break;
+  case 0xfe:
+    cpu_z80_ill(self);
+    break;
+  case 0xff:
+    cpu_z80_ill(self);
+    break;
+  }
+}
+
+void cpu_z80_execute_fd(struct cpu_z80 *self) {
+  switch (cpu_z80_fetch_byte(self)) {
+  case 0x00:
+    cpu_z80_nop(self);
+    break;
+  case 0x01:
+    cpu_z80_ld_word(self, CPU_Z80_EA_BC, cpu_z80_fetch_word(self));
+    break;
+  case 0x02:
+    cpu_z80_ld_byte(self, self->regs.word.bc, self->regs.byte.a);
+    break;
+  case 0x03:
+    cpu_z80_inc_word(self, CPU_Z80_EA_BC);
+    break;
+  case 0x04:
+    cpu_z80_inc_byte(self, CPU_Z80_EA_B);
+    break;
+  case 0x05:
+    cpu_z80_dec_byte(self, CPU_Z80_EA_B);
+    break;
+  case 0x06:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_B, cpu_z80_fetch_byte(self));
+    break;
+  case 0x07:
+    cpu_z80_rlca(self);
+    break;
+  case 0x08:
+    cpu_z80_ex(self, CPU_Z80_EA_AF, CPU_Z80_EA_AF_PRIME);
+    break;
+  case 0x09:
+    cpu_z80_add_word(self, CPU_Z80_EA_IY, self->regs.word.bc);
+    break;
+  case 0x0a:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_A, cpu_z80_read_byte(self, self->regs.word.bc));
+    break;
+  case 0x0b:
+    cpu_z80_dec_word(self, CPU_Z80_EA_BC);
+    break;
+  case 0x0c:
+    cpu_z80_inc_byte(self, CPU_Z80_EA_C);
+    break;
+  case 0x0d:
+    cpu_z80_dec_byte(self, CPU_Z80_EA_C);
+    break;
+  case 0x0e:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_C, cpu_z80_fetch_byte(self));
+    break;
+  case 0x0f:
+    cpu_z80_rrca(self);
+    break;
+  case 0x10:
+    cpu_z80_djnz(self, cpu_z80_relative(self));
+    break;
+  case 0x11:
+    cpu_z80_ld_word(self, CPU_Z80_EA_DE, cpu_z80_fetch_word(self));
+    break;
+  case 0x12:
+    cpu_z80_ld_byte(self, self->regs.word.de, self->regs.byte.a);
+    break;
+  case 0x13:
+    cpu_z80_inc_word(self, CPU_Z80_EA_DE);
+    break;
+  case 0x14:
+    cpu_z80_inc_byte(self, CPU_Z80_EA_D);
+    break;
+  case 0x15:
+    cpu_z80_dec_byte(self, CPU_Z80_EA_D);
+    break;
+  case 0x16:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_D, cpu_z80_fetch_byte(self));
+    break;
+  case 0x17:
+    cpu_z80_rla(self);
+    break;
+  case 0x18:
+    cpu_z80_jr(self, true, cpu_z80_relative(self));
+    break;
+  case 0x19:
+    cpu_z80_add_word(self, CPU_Z80_EA_IY, self->regs.word.de);
+    break;
+  case 0x1a:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_A, cpu_z80_read_byte(self, self->regs.word.de));
+    break;
+  case 0x1b:
+    cpu_z80_dec_word(self, CPU_Z80_EA_DE);
+    break;
+  case 0x1c:
+    cpu_z80_inc_byte(self, CPU_Z80_EA_E);
+    break;
+  case 0x1d:
+    cpu_z80_dec_byte(self, CPU_Z80_EA_E);
+    break;
+  case 0x1e:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_E, cpu_z80_fetch_byte(self));
+    break;
+  case 0x1f:
+    cpu_z80_rra(self);
+    break;
+  case 0x20:
+    cpu_z80_jr(self, !self->regs.bit.zf, cpu_z80_relative(self));
+    break;
+  case 0x21:
+    cpu_z80_ld_word(self, CPU_Z80_EA_IY, cpu_z80_fetch_word(self));
+    break;
+  case 0x22:
+    cpu_z80_ld_word(self, cpu_z80_fetch_word(self), self->regs.word.iy);
+    break;
+  case 0x23:
+    cpu_z80_inc_word(self, CPU_Z80_EA_IY);
+    break;
+  case 0x24:
+    cpu_z80_inc_byte(self, CPU_Z80_EA_IYH);
+    break;
+  case 0x25:
+    cpu_z80_dec_byte(self, CPU_Z80_EA_IYH);
+    break;
+  case 0x26:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_IYH, cpu_z80_fetch_byte(self));
+    break;
+  case 0x27:
+    cpu_z80_daa(self);
+    break;
+  case 0x28:
+    cpu_z80_jr(self, self->regs.bit.zf, cpu_z80_relative(self));
+    break;
+  case 0x29:
+    cpu_z80_add_word(self, CPU_Z80_EA_IY, self->regs.word.iy);
+    break;
+  case 0x2a:
+    cpu_z80_ld_word(self, CPU_Z80_EA_IY, cpu_z80_read_word(self, cpu_z80_fetch_word(self)));
+    break;
+  case 0x2b:
+    cpu_z80_dec_word(self, CPU_Z80_EA_IY);
+    break;
+  case 0x2c:
+    cpu_z80_inc_byte(self, CPU_Z80_EA_IYL);
+    break;
+  case 0x2d:
+    cpu_z80_dec_byte(self, CPU_Z80_EA_IYL);
+    break;
+  case 0x2e:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_IYL, cpu_z80_fetch_byte(self));
+    break;
+  case 0x2f:
+    cpu_z80_cpl(self);
+    break;
+  case 0x30:
+    cpu_z80_jr(self, !self->regs.bit.cf, cpu_z80_relative(self));
+    break;
+  case 0x31:
+    cpu_z80_ld_word(self, CPU_Z80_EA_SP, cpu_z80_fetch_word(self));
+    break;
+  case 0x32:
+    cpu_z80_ld_byte(self, cpu_z80_fetch_word(self), self->regs.byte.a);
+    break;
+  case 0x33:
+    cpu_z80_inc_word(self, CPU_Z80_EA_SP);
+    break;
+  case 0x34:
+    cpu_z80_inc_byte(self, cpu_z80_displacement(self, self->regs.word.iy));
+    break;
+  case 0x35:
+    cpu_z80_dec_byte(self, cpu_z80_displacement(self, self->regs.word.iy));
+    break;
+  case 0x36:
+    {
+      int ea = cpu_z80_displacement(self, self->regs.word.iy);
+      cpu_z80_ld_byte(self, ea, cpu_z80_fetch_byte(self));
+    }
+    break;
+  case 0x37:
+    cpu_z80_scf(self);
+    break;
+  case 0x38:
+    cpu_z80_jr(self, self->regs.bit.cf, cpu_z80_relative(self));
+    break;
+  case 0x39:
+    cpu_z80_add_word(self, CPU_Z80_EA_IY, self->regs.word.sp);
+    break;
+  case 0x3a:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_A, cpu_z80_read_byte(self, cpu_z80_fetch_word(self)));
+    break;
+  case 0x3b:
+    cpu_z80_dec_word(self, CPU_Z80_EA_SP);
+    break;
+  case 0x3c:
+    cpu_z80_inc_byte(self, CPU_Z80_EA_A);
+    break;
+  case 0x3d:
+    cpu_z80_dec_byte(self, CPU_Z80_EA_A);
+    break;
+  case 0x3e:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_A, cpu_z80_fetch_byte(self));
+    break;
+  case 0x3f:
+    cpu_z80_ccf(self);
+    break;
+  case 0x40:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_B, self->regs.byte.b);
+    break;
+  case 0x41:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_B, self->regs.byte.c);
+    break;
+  case 0x42:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_B, self->regs.byte.d);
+    break;
+  case 0x43:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_B, self->regs.byte.e);
+    break;
+  case 0x44:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_B, self->regs.byte.iyh);
+    break;
+  case 0x45:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_B, self->regs.byte.iyl);
+    break;
+  case 0x46:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_B, cpu_z80_read_byte(self, cpu_z80_displacement(self, self->regs.word.iy)));
+    break;
+  case 0x47:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_B, self->regs.byte.a);
+    break;
+  case 0x48:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_C, self->regs.byte.b);
+    break;
+  case 0x49:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_C, self->regs.byte.c);
+    break;
+  case 0x4a:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_C, self->regs.byte.d);
+    break;
+  case 0x4b:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_C, self->regs.byte.e);
+    break;
+  case 0x4c:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_C, self->regs.byte.iyh);
+    break;
+  case 0x4d:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_C, self->regs.byte.iyl);
+    break;
+  case 0x4e:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_C, cpu_z80_read_byte(self, cpu_z80_displacement(self, self->regs.word.iy)));
+    break;
+  case 0x4f:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_C, self->regs.byte.a);
+    break;
+  case 0x50:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_D, self->regs.byte.b);
+    break;
+  case 0x51:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_D, self->regs.byte.c);
+    break;
+  case 0x52:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_D, self->regs.byte.d);
+    break;
+  case 0x53:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_D, self->regs.byte.e);
+    break;
+  case 0x54:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_D, self->regs.byte.iyh);
+    break;
+  case 0x55:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_D, self->regs.byte.iyl);
+    break;
+  case 0x56:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_D, cpu_z80_read_byte(self, cpu_z80_displacement(self, self->regs.word.iy)));
+    break;
+  case 0x57:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_D, self->regs.byte.a);
+    break;
+  case 0x58:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_E, self->regs.byte.b);
+    break;
+  case 0x59:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_E, self->regs.byte.c);
+    break;
+  case 0x5a:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_E, self->regs.byte.d);
+    break;
+  case 0x5b:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_E, self->regs.byte.e);
+    break;
+  case 0x5c:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_E, self->regs.byte.iyh);
+    break;
+  case 0x5d:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_E, self->regs.byte.iyl);
+    break;
+  case 0x5e:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_E, cpu_z80_read_byte(self, cpu_z80_displacement(self, self->regs.word.iy)));
+    break;
+  case 0x5f:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_E, self->regs.byte.a);
+    break;
+  case 0x60:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_IYH, self->regs.byte.b);
+    break;
+  case 0x61:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_IYH, self->regs.byte.c);
+    break;
+  case 0x62:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_IYH, self->regs.byte.d);
+    break;
+  case 0x63:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_IYH, self->regs.byte.e);
+    break;
+  case 0x64:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_IYH, self->regs.byte.iyh);
+    break;
+  case 0x65:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_IYH, self->regs.byte.iyl);
+    break;
+  case 0x66:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_H, cpu_z80_read_byte(self, cpu_z80_displacement(self, self->regs.word.iy)));
+    break;
+  case 0x67:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_IYH, self->regs.byte.a);
+    break;
+  case 0x68:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_IYL, self->regs.byte.b);
+    break;
+  case 0x69:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_IYL, self->regs.byte.c);
+    break;
+  case 0x6a:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_IYL, self->regs.byte.d);
+    break;
+  case 0x6b:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_IYL, self->regs.byte.e);
+    break;
+  case 0x6c:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_IYL, self->regs.byte.iyh);
+    break;
+  case 0x6d:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_IYL, self->regs.byte.iyl);
+    break;
+  case 0x6e:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_L, cpu_z80_read_byte(self, cpu_z80_displacement(self, self->regs.word.iy)));
+    break;
+  case 0x6f:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_IYL, self->regs.byte.a);
+    break;
+  case 0x70:
+    cpu_z80_ld_byte(self, cpu_z80_displacement(self, self->regs.word.iy), self->regs.byte.b);
+    break;
+  case 0x71:
+    cpu_z80_ld_byte(self, cpu_z80_displacement(self, self->regs.word.iy), self->regs.byte.c);
+    break;
+  case 0x72:
+    cpu_z80_ld_byte(self, cpu_z80_displacement(self, self->regs.word.iy), self->regs.byte.d);
+    break;
+  case 0x73:
+    cpu_z80_ld_byte(self, cpu_z80_displacement(self, self->regs.word.iy), self->regs.byte.e);
+    break;
+  case 0x74:
+    cpu_z80_ld_byte(self, cpu_z80_displacement(self, self->regs.word.iy), self->regs.byte.h);
+    break;
+  case 0x75:
+    cpu_z80_ld_byte(self, cpu_z80_displacement(self, self->regs.word.iy), self->regs.byte.l);
+    break;
+  case 0x76:
+    cpu_z80_halt(self);
+    break;
+  case 0x77:
+    cpu_z80_ld_byte(self, cpu_z80_displacement(self, self->regs.word.iy), self->regs.byte.a);
+    break;
+  case 0x78:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_A, self->regs.byte.b);
+    break;
+  case 0x79:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_A, self->regs.byte.c);
+    break;
+  case 0x7a:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_A, self->regs.byte.d);
+    break;
+  case 0x7b:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_A, self->regs.byte.e);
+    break;
+  case 0x7c:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_A, self->regs.byte.iyh);
+    break;
+  case 0x7d:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_A, self->regs.byte.iyl);
+    break;
+  case 0x7e:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_A, cpu_z80_read_byte(self, cpu_z80_displacement(self, self->regs.word.iy)));
+    break;
+  case 0x7f:
+    cpu_z80_ld_byte(self, CPU_Z80_EA_A, self->regs.byte.a);
+    break;
+  case 0x80:
+    cpu_z80_add_byte(self, CPU_Z80_EA_A, self->regs.byte.b);
+    break;
+  case 0x81:
+    cpu_z80_add_byte(self, CPU_Z80_EA_A, self->regs.byte.c);
+    break;
+  case 0x82:
+    cpu_z80_add_byte(self, CPU_Z80_EA_A, self->regs.byte.d);
+    break;
+  case 0x83:
+    cpu_z80_add_byte(self, CPU_Z80_EA_A, self->regs.byte.e);
+    break;
+  case 0x84:
+    cpu_z80_add_byte(self, CPU_Z80_EA_A, self->regs.byte.iyh);
+    break;
+  case 0x85:
+    cpu_z80_add_byte(self, CPU_Z80_EA_A, self->regs.byte.iyl);
+    break;
+  case 0x86:
+    cpu_z80_add_byte(self, CPU_Z80_EA_A, cpu_z80_read_byte(self, cpu_z80_displacement(self, self->regs.word.iy)));
+    break;
+  case 0x87:
+    cpu_z80_add_byte(self, CPU_Z80_EA_A, self->regs.byte.a);
+    break;
+  case 0x88:
+    cpu_z80_adc_byte(self, CPU_Z80_EA_A, self->regs.byte.b);
+    break;
+  case 0x89:
+    cpu_z80_adc_byte(self, CPU_Z80_EA_A, self->regs.byte.c);
+    break;
+  case 0x8a:
+    cpu_z80_adc_byte(self, CPU_Z80_EA_A, self->regs.byte.d);
+    break;
+  case 0x8b:
+    cpu_z80_adc_byte(self, CPU_Z80_EA_A, self->regs.byte.e);
+    break;
+  case 0x8c:
+    cpu_z80_adc_byte(self, CPU_Z80_EA_A, self->regs.byte.iyh);
+    break;
+  case 0x8d:
+    cpu_z80_adc_byte(self, CPU_Z80_EA_A, self->regs.byte.iyl);
+    break;
+  case 0x8e:
+    cpu_z80_adc_byte(self, CPU_Z80_EA_A, cpu_z80_read_byte(self, cpu_z80_displacement(self, self->regs.word.iy)));
+    break;
+  case 0x8f:
+    cpu_z80_adc_byte(self, CPU_Z80_EA_A, self->regs.byte.a);
+    break;
+  case 0x90:
+    cpu_z80_sub(self, self->regs.byte.b);
+    break;
+  case 0x91:
+    cpu_z80_sub(self, self->regs.byte.c);
+    break;
+  case 0x92:
+    cpu_z80_sub(self, self->regs.byte.d);
+    break;
+  case 0x93:
+    cpu_z80_sub(self, self->regs.byte.e);
+    break;
+  case 0x94:
+    cpu_z80_sub(self, self->regs.byte.iyh);
+    break;
+  case 0x95:
+    cpu_z80_sub(self, self->regs.byte.iyl);
+    break;
+  case 0x96:
+    cpu_z80_sub(self, cpu_z80_read_byte(self, cpu_z80_displacement(self, self->regs.word.iy)));
+    break;
+  case 0x97:
+    cpu_z80_sub(self, self->regs.byte.a);
+    break;
+  case 0x98:
+    cpu_z80_sbc_byte(self, CPU_Z80_EA_A, self->regs.byte.b);
+    break;
+  case 0x99:
+    cpu_z80_sbc_byte(self, CPU_Z80_EA_A, self->regs.byte.c);
+    break;
+  case 0x9a:
+    cpu_z80_sbc_byte(self, CPU_Z80_EA_A, self->regs.byte.d);
+    break;
+  case 0x9b:
+    cpu_z80_sbc_byte(self, CPU_Z80_EA_A, self->regs.byte.e);
+    break;
+  case 0x9c:
+    cpu_z80_sbc_byte(self, CPU_Z80_EA_A, self->regs.byte.iyh);
+    break;
+  case 0x9d:
+    cpu_z80_sbc_byte(self, CPU_Z80_EA_A, self->regs.byte.iyl);
+    break;
+  case 0x9e:
+    cpu_z80_sbc_byte(self, CPU_Z80_EA_A, cpu_z80_read_byte(self, cpu_z80_displacement(self, self->regs.word.iy)));
+    break;
+  case 0x9f:
+    cpu_z80_sbc_byte(self, CPU_Z80_EA_A, self->regs.byte.a);
+    break;
+  case 0xa0:
+    cpu_z80_and(self, self->regs.byte.b);
+    break;
+  case 0xa1:
+    cpu_z80_and(self, self->regs.byte.c);
+    break;
+  case 0xa2:
+    cpu_z80_and(self, self->regs.byte.d);
+    break;
+  case 0xa3:
+    cpu_z80_and(self, self->regs.byte.e);
+    break;
+  case 0xa4:
+    cpu_z80_and(self, self->regs.byte.iyh);
+    break;
+  case 0xa5:
+    cpu_z80_and(self, self->regs.byte.iyl);
+    break;
+  case 0xa6:
+    cpu_z80_and(self, cpu_z80_read_byte(self, cpu_z80_displacement(self, self->regs.word.iy)));
+    break;
+  case 0xa7:
+    cpu_z80_and(self, self->regs.byte.a);
+    break;
+  case 0xa8:
+    cpu_z80_xor(self, self->regs.byte.b);
+    break;
+  case 0xa9:
+    cpu_z80_xor(self, self->regs.byte.c);
+    break;
+  case 0xaa:
+    cpu_z80_xor(self, self->regs.byte.d);
+    break;
+  case 0xab:
+    cpu_z80_xor(self, self->regs.byte.e);
+    break;
+  case 0xac:
+    cpu_z80_xor(self, self->regs.byte.iyh);
+    break;
+  case 0xad:
+    cpu_z80_xor(self, self->regs.byte.iyl);
+    break;
+  case 0xae:
+    cpu_z80_xor(self, cpu_z80_read_byte(self, cpu_z80_displacement(self, self->regs.word.iy)));
+    break;
+  case 0xaf:
+    cpu_z80_xor(self, self->regs.byte.a);
+    break;
+  case 0xb0:
+    cpu_z80_or(self, self->regs.byte.b);
+    break;
+  case 0xb1:
+    cpu_z80_or(self, self->regs.byte.c);
+    break;
+  case 0xb2:
+    cpu_z80_or(self, self->regs.byte.d);
+    break;
+  case 0xb3:
+    cpu_z80_or(self, self->regs.byte.e);
+    break;
+  case 0xb4:
+    cpu_z80_or(self, self->regs.byte.iyh);
+    break;
+  case 0xb5:
+    cpu_z80_or(self, self->regs.byte.iyl);
+    break;
+  case 0xb6:
+    cpu_z80_or(self, cpu_z80_read_byte(self, cpu_z80_displacement(self, self->regs.word.iy)));
+    break;
+  case 0xb7:
+    cpu_z80_or(self, self->regs.byte.a);
+    break;
+  case 0xb8:
+    cpu_z80_cp(self, self->regs.byte.b);
+    break;
+  case 0xb9:
+    cpu_z80_cp(self, self->regs.byte.c);
+    break;
+  case 0xba:
+    cpu_z80_cp(self, self->regs.byte.d);
+    break;
+  case 0xbb:
+    cpu_z80_cp(self, self->regs.byte.e);
+    break;
+  case 0xbc:
+    cpu_z80_cp(self, self->regs.byte.iyh);
+    break;
+  case 0xbd:
+    cpu_z80_cp(self, self->regs.byte.iyl);
+    break;
+  case 0xbe:
+    cpu_z80_cp(self, cpu_z80_read_byte(self, cpu_z80_displacement(self, self->regs.word.iy)));
+    break;
+  case 0xbf:
+    cpu_z80_cp(self, self->regs.byte.a);
+    break;
+  case 0xc0:
+    cpu_z80_ret(self, !self->regs.bit.zf);
+    break;
+  case 0xc1:
+    cpu_z80_pop(self, CPU_Z80_EA_BC);
+    break;
+  case 0xc2:
+    cpu_z80_jp(self, !self->regs.bit.zf, cpu_z80_fetch_word(self));
+    break;
+  case 0xc3:
+    cpu_z80_jp(self, true, cpu_z80_fetch_word(self));
+    break;
+  case 0xc4:
+    cpu_z80_call(self, !self->regs.bit.zf, cpu_z80_fetch_word(self));
+    break;
+  case 0xc5:
+    cpu_z80_push(self, self->regs.word.bc);
+    break;
+  case 0xc6:
+    cpu_z80_add_byte(self, CPU_Z80_EA_A, cpu_z80_fetch_byte(self));
+    break;
+  case 0xc7:
+    cpu_z80_call(self, true, 0);
+    break;
+  case 0xc8:
+    cpu_z80_ret(self, self->regs.bit.zf);
+    break;
+  case 0xc9:
+    cpu_z80_ret(self, true);
+    break;
+  case 0xca:
+    cpu_z80_jp(self, self->regs.bit.zf, cpu_z80_fetch_word(self));
+    break;
+  case 0xcb:
+    cpu_z80_ill(self);
+    break;
+  case 0xcc:
+    cpu_z80_call(self, self->regs.bit.zf, cpu_z80_fetch_word(self));
+    break;
+  case 0xcd:
+    cpu_z80_call(self, true, cpu_z80_fetch_word(self));
+    break;
+  case 0xce:
+    cpu_z80_adc_byte(self, CPU_Z80_EA_A, cpu_z80_fetch_byte(self));
+    break;
+  case 0xcf:
+    cpu_z80_call(self, true, 8);
+    break;
+  case 0xd0:
+    cpu_z80_ret(self, !self->regs.bit.cf);
+    break;
+  case 0xd1:
+    cpu_z80_pop(self, CPU_Z80_EA_DE);
+    break;
+  case 0xd2:
+    cpu_z80_jp(self, !self->regs.bit.cf, cpu_z80_fetch_word(self));
+    break;
+  case 0xd3:
+    cpu_z80_out(self, cpu_z80_fetch_byte(self), self->regs.byte.a);
+    break;
+  case 0xd4:
+    cpu_z80_call(self, !self->regs.bit.cf, cpu_z80_fetch_word(self));
+    break;
+  case 0xd5:
+    cpu_z80_push(self, self->regs.word.de);
+    break;
+  case 0xd6:
+    cpu_z80_sub(self, cpu_z80_fetch_byte(self));
+    break;
+  case 0xd7:
+    cpu_z80_call(self, true, 0x10);
+    break;
+  case 0xd8:
+    cpu_z80_ret(self, self->regs.bit.cf);
+    break;
+  case 0xd9:
+    cpu_z80_ex(self, CPU_Z80_EA_BC, CPU_Z80_EA_BC_PRIME);
+    cpu_z80_ex(self, CPU_Z80_EA_DE, CPU_Z80_EA_DE_PRIME);
+    cpu_z80_ex(self, CPU_Z80_EA_HL, CPU_Z80_EA_HL_PRIME);
+    break;
+  case 0xda:
+    cpu_z80_jp(self, self->regs.bit.cf, cpu_z80_fetch_word(self));
+    break;
+  case 0xdb:
+    cpu_z80_in(self, CPU_Z80_EA_A, cpu_z80_in_byte(self, cpu_z80_fetch_byte(self)));
+    break;
+  case 0xdc:
+    cpu_z80_call(self, self->regs.bit.cf, cpu_z80_fetch_word(self));
+    break;
+  case 0xdd:
+    cpu_z80_ill(self);
+    break;
+  case 0xde:
+    cpu_z80_sbc_byte(self, CPU_Z80_EA_A, cpu_z80_fetch_byte(self));
+    break;
+  case 0xdf:
+    cpu_z80_call(self, true, 0x18);
+    break;
+  case 0xe0:
+    cpu_z80_ret(self, self->regs.bit.pvf);
+    break;
+  case 0xe1:
+    cpu_z80_pop(self, CPU_Z80_EA_IY);
+    break;
+  case 0xe2:
+    cpu_z80_jp(self, self->regs.bit.pvf, cpu_z80_fetch_word(self));
+    break;
+  case 0xe3:
+    cpu_z80_ex(self, self->regs.word.sp, CPU_Z80_EA_IY);
+    break;
+  case 0xe4:
+    cpu_z80_call(self, self->regs.bit.pvf, cpu_z80_fetch_word(self));
+    break;
+  case 0xe5:
+    cpu_z80_push(self, self->regs.word.iy);
+    break;
+  case 0xe6:
+    cpu_z80_and(self, cpu_z80_fetch_byte(self));
+    break;
+  case 0xe7:
+    cpu_z80_call(self, true, 0x20);
+    break;
+  case 0xe8:
+    cpu_z80_ret(self, !self->regs.bit.pvf);
+    break;
+  case 0xe9:
+    cpu_z80_jp(self, true, cpu_z80_read_word(self, self->regs.word.iy));
+    break;
+  case 0xea:
+    cpu_z80_jp(self, !self->regs.bit.pvf, cpu_z80_fetch_word(self));
+    break;
+  case 0xeb:
+    cpu_z80_ex(self, CPU_Z80_EA_DE, CPU_Z80_EA_HL);
+    break;
+  case 0xec:
+    cpu_z80_call(self, !self->regs.bit.pvf, cpu_z80_fetch_word(self));
+    break;
+  case 0xed:
+    cpu_z80_ill(self);
+    break;
+  case 0xee:
+    cpu_z80_xor(self, cpu_z80_fetch_byte(self));
+    break;
+  case 0xef:
+    cpu_z80_call(self, true, 0x28);
+    break;
+  case 0xf0:
+    cpu_z80_ret(self, !self->regs.bit.sf);
+    break;
+  case 0xf1:
+    cpu_z80_pop(self, CPU_Z80_EA_AF);
+    break;
+  case 0xf2:
+    cpu_z80_jp(self, !self->regs.bit.sf, cpu_z80_fetch_word(self));
+    break;
+  case 0xf3:
+    cpu_z80_di(self);
+    break;
+  case 0xf4:
+    cpu_z80_call(self, !self->regs.bit.sf, cpu_z80_fetch_word(self));
+    break;
+  case 0xf5:
+    cpu_z80_push(self, self->regs.word.af);
+    break;
+  case 0xf6:
+    cpu_z80_or(self, cpu_z80_fetch_byte(self));
+    break;
+  case 0xf7:
+    cpu_z80_call(self, true, 0x30);
+    break;
+  case 0xf8:
+    cpu_z80_ret(self, self->regs.bit.sf);
+    break;
+  case 0xf9:
+    cpu_z80_ld_word(self, CPU_Z80_EA_SP, self->regs.word.iy);
+    break;
+  case 0xfa:
+    cpu_z80_jp(self, self->regs.bit.sf, cpu_z80_fetch_word(self));
+    break;
+  case 0xfb:
+    cpu_z80_ei(self);
+    break;
+  case 0xfc:
+    cpu_z80_call(self, self->regs.bit.sf, cpu_z80_fetch_word(self));
+    break;
+  case 0xfd:
+    cpu_z80_ill(self);
+    break;
+  case 0xfe:
+    cpu_z80_cp(self, cpu_z80_fetch_byte(self));
+    break;
+  case 0xff:
+    cpu_z80_call(self, true, 0x38);
+    break;
+  }
+}
diff --git a/cpu_z80.h b/cpu_z80.h
new file mode 100644 (file)
index 0000000..27527a0
--- /dev/null
+++ b/cpu_z80.h
@@ -0,0 +1,586 @@
+#ifndef _CPU_Z80_H
+#define _CPU_Z80_H
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+// gcc specific
+#ifndef ALWAYS_INLINE
+#define ALWAYS_INLINE __attribute__((always_inline))
+#endif
+
+// bits within REG_F
+#define CPU_Z80_REG_F_BIT_C 0
+#define CPU_Z80_REG_F_BIT_N 1
+#define CPU_Z80_REG_F_BIT_PV 2
+#define CPU_Z80_REG_F_BIT_H 4
+#define CPU_Z80_REG_F_BIT_Z 6
+#define CPU_Z80_REG_F_BIT_S 7
+
+// special memory locations (negative address)
+// note: CPU_Z80_EA_NONE is a byte-wide sink for the "in f,(c)" instruction,
+// so data can be stored somewhere and never read back, but is rounded up
+// to a word for alignment reasons; note it cannot be accessed via regs.xxx
+#define CPU_Z80_EA_F (-0x1c)
+#define CPU_Z80_EA_A (-0x1b)
+#define CPU_Z80_EA_C (-0x1a)
+#define CPU_Z80_EA_B (-0x19)
+#define CPU_Z80_EA_E (-0x18)
+#define CPU_Z80_EA_D (-0x17)
+#define CPU_Z80_EA_L (-0x16)
+#define CPU_Z80_EA_H (-0x15)
+#define CPU_Z80_EA_IXL (-0x12)
+#define CPU_Z80_EA_IXH (-0x11)
+#define CPU_Z80_EA_IYL (-0x10)
+#define CPU_Z80_EA_IYH (-0xf)
+#define CPU_Z80_EA_I (-0xe)
+#define CPU_Z80_EA_R (-0xd)
+
+#define CPU_Z80_EA_AF (-0x1c)
+#define CPU_Z80_EA_BC (-0x1a)
+#define CPU_Z80_EA_DE (-0x18)
+#define CPU_Z80_EA_HL (-0x16)
+#define CPU_Z80_EA_SP (-0x14)
+#define CPU_Z80_EA_IX (-0x12)
+#define CPU_Z80_EA_IY (-0x10)
+#define CPU_Z80_EA_AF_PRIME (-0xc)
+#define CPU_Z80_EA_BC_PRIME (-0xa)
+#define CPU_Z80_EA_DE_PRIME (-8)
+#define CPU_Z80_EA_HL_PRIME (-6)
+#define CPU_Z80_EA_PC (-4)
+#define CPU_Z80_EA_NONE (-2)
+
+// registers, in same order as special memory locations, but reversed on
+// big endian hardware where special memory address will be complemented
+// (this allows special memory to always look like it is little endian)
+union cpu_z80_regs {
+#if __BYTE_ORDER == __BIG_ENDIAN
+  struct {
+    uint16_t _fill_none;
+    uint16_t _fill_pc;
+    uint16_t _fill_hl_prime;
+    uint16_t _fill_de_prime;
+    uint16_t _fill_bc_prime;
+    uint16_t _fill_af_prime;
+    uint8_t _fill_r;
+    uint8_t _fill_i;
+    uint16_t _fill_iy;
+    uint16_t _fill_ix;
+    uint16_t _fill_sp;
+    uint16_t _fill_hl;
+    uint16_t _fill_de;
+    uint16_t _fill_bc;
+    uint8_t _fill_a;
+    uint8_t sf : 1;
+    uint8_t zf : 1;
+    uint8_t _fill_5f: 1;
+    uint8_t hf : 1;
+    uint8_t _fill_3f: 1;
+    uint8_t pvf: 1;
+    uint8_t nf : 1;
+    uint8_t cf : 1;
+  } bit;
+  struct {
+    uint16_t _fill_none;
+    uint16_t _fill_pc;
+    uint16_t _fill_hl_prime;
+    uint16_t _fill_de_prime;
+    uint16_t _fill_bc_prime;
+    uint16_t _fill_af_prime;
+    uint8_t r;
+    uint8_t i;
+    uint8_t iyh;
+    uint8_t iyl;
+    uint8_t ixh;
+    uint8_t ixl;
+    uint16_t _fill_sp;
+    uint8_t h;
+    uint8_t l;
+    uint8_t d;
+    uint8_t e;
+    uint8_t b;
+    uint8_t c;
+    uint8_t a;
+    uint8_t f;
+  } byte;
+  struct {
+    uint16_t _fill_none;
+    uint16_t pc;
+    uint16_t hl_prime;
+    uint16_t de_prime;
+    uint16_t bc_prime;
+    uint16_t af_prime;
+    uint8_t _fill_r;
+    uint8_t _fill_i;
+    uint16_t iy;
+    uint16_t ix;
+    uint16_t sp;
+    uint16_t hl;
+    uint16_t de;
+    uint16_t bc;
+    uint16_t af;
+  } word;
+  uint8_t mem_be[0x1c];
+#else
+  struct {
+    uint8_t cf : 1;
+    uint8_t nf : 1;
+    uint8_t pvf: 1;
+    uint8_t _fill_3f: 1;
+    uint8_t hf : 1;
+    uint8_t _fill_5f: 1;
+    uint8_t zf : 1;
+    uint8_t sf : 1;
+    uint8_t _fill_a;
+    uint16_t _fill_bc;
+    uint16_t _fill_de;
+    uint16_t _fill_hl;
+    uint16_t _fill_sp;
+    uint16_t _fill_ix;
+    uint16_t _fill_iy;
+    uint8_t _fill_i;
+    uint8_t _fill_r;
+    uint16_t _fill_af_prime;
+    uint16_t _fill_bc_prime;
+    uint16_t _fill_de_prime;
+    uint16_t _fill_hl_prime;
+    uint16_t _fill_pc;
+    uint16_t _fill_none;
+  } bit;
+  struct {
+    uint8_t f;
+    uint8_t a;
+    uint8_t c;
+    uint8_t b;
+    uint8_t e;
+    uint8_t d;
+    uint8_t l;
+    uint8_t h;
+    uint16_t _fill_sp;
+    uint8_t ixl;
+    uint8_t ixh;
+    uint8_t iyl;
+    uint8_t iyh;
+    uint8_t i;
+    uint8_t r;
+    uint16_t _fill_af_prime;
+    uint16_t _fill_bc_prime;
+    uint16_t _fill_de_prime;
+    uint16_t _fill_hl_prime;
+    uint16_t _fill_pc;
+    uint16_t _fill_none;
+  } byte;
+  struct {
+    uint16_t af;
+    uint16_t bc;
+    uint16_t de;
+    uint16_t hl;
+    uint16_t sp;
+    uint16_t ix;
+    uint16_t iy;
+    uint8_t _fill_i;
+    uint8_t _fill_r;
+    uint16_t af_prime;
+    uint16_t bc_prime;
+    uint16_t de_prime;
+    uint16_t hl_prime;
+    uint16_t pc;
+    uint16_t _fill_none;
+  } word;
+  uint8_t mem_le[0x1c];
+#endif
+};
+
+struct cpu_z80 {
+  int cycles;
+  int (*read_byte)(int addr);
+  void (*write_byte)(int addr, int data);
+  int (*in_byte)(int addr);
+  void (*out_byte)(int addr, int data);
+  union cpu_z80_regs regs;
+};
+
+// memory or special memory access
+static ALWAYS_INLINE int cpu_z80_read_byte(struct cpu_z80 *self, int addr) {
+  if (addr < 0)
+#if __BYTE_ORDER == __BIG_ENDIAN
+    return self->regs.mem_be[~addr];
+#else
+    return self->regs.mem_le[addr - CPU_Z80_EA_F];
+#endif
+  self->cycles += 1;
+  return self->read_byte(addr & 0xffff);
+}
+
+static ALWAYS_INLINE int cpu_z80_read_word(struct cpu_z80 *self, int addr) {
+  int data = cpu_z80_read_byte(self, addr);
+  return data | (cpu_z80_read_byte(self, addr + 1) << 8);
+}
+
+static ALWAYS_INLINE void cpu_z80_write_byte(struct cpu_z80 *self, int addr, int data) {
+  self->cycles += 1;
+  if (addr < 0)
+#if __BYTE_ORDER == __BIG_ENDIAN
+    self->regs.mem_be[~addr] = data;
+#else
+    self->regs.mem_le[addr - CPU_Z80_EA_F] = data;
+#endif
+  else
+    self->write_byte(addr, data);
+}
+
+static ALWAYS_INLINE void cpu_z80_write_word(struct cpu_z80 *self, int addr, int data) {
+  cpu_z80_write_byte(self, addr, data & 0xff);
+  cpu_z80_write_byte(self, addr + 1, data >> 8);
+}
+
+static ALWAYS_INLINE int cpu_z80_fetch_byte(struct cpu_z80 *self) {
+  int data = cpu_z80_read_byte(self, self->regs.word.pc++);
+  return data;
+}
+
+static ALWAYS_INLINE int cpu_z80_fetch_word(struct cpu_z80 *self) {
+  int data = cpu_z80_fetch_byte(self);
+  return data | (cpu_z80_fetch_byte(self) << 8);
+}
+
+static ALWAYS_INLINE void cpu_z80_push_word(struct cpu_z80 *self, int data) {
+  self->regs.word.sp -= 2;
+  cpu_z80_write_word(self, self->regs.word.sp, data);
+}
+
+static ALWAYS_INLINE int cpu_z80_pop_word(struct cpu_z80 *self) {
+  int data = cpu_z80_read_word(self, self->regs.word.sp);
+  self->regs.word.sp += 2;
+  return data;
+}
+
+static ALWAYS_INLINE int cpu_z80_in_byte(struct cpu_z80 *self, int addr) {
+  self->cycles += 1;
+  return self->in_byte(addr);
+}
+
+static ALWAYS_INLINE void cpu_z80_out_byte(struct cpu_z80 *self, int addr, int data) {
+  self->cycles += 1;
+  self->out_byte(addr, data);
+}
+
+// effective address calculation
+static ALWAYS_INLINE int cpu_z80_relative(struct cpu_z80 *self) {
+  int offset = cpu_z80_fetch_byte(self);
+  return offset - ((offset << 1) & 0x100);
+}
+
+static ALWAYS_INLINE int cpu_z80_displacement(struct cpu_z80 *self, int base) {
+  return (base + cpu_z80_relative(self)) & 0xffff;
+}
+
+// instruction execute
+static ALWAYS_INLINE void cpu_z80_adc_byte(struct cpu_z80 *self, int lvalue, int rvalue) {
+  abort();
+}
+
+static ALWAYS_INLINE void cpu_z80_adc_word(struct cpu_z80 *self, int lvalue, int rvalue) {
+  abort();
+}
+
+static ALWAYS_INLINE void cpu_z80_add_byte(struct cpu_z80 *self, int lvalue, int rvalue) {
+  abort();
+}
+
+static ALWAYS_INLINE void cpu_z80_add_word(struct cpu_z80 *self, int lvalue, int rvalue) {
+  abort();
+}
+
+static ALWAYS_INLINE void cpu_z80_and(struct cpu_z80 *self, int rvalue) {
+  abort();
+}
+
+static ALWAYS_INLINE void cpu_z80_bit(struct cpu_z80 *self, int n, int rvalue) {
+  abort();
+}
+
+static ALWAYS_INLINE void cpu_z80_call(struct cpu_z80 *self, bool pred, int rvalue) {
+  abort();
+}
+
+static ALWAYS_INLINE void cpu_z80_ccf(struct cpu_z80 *self) {
+  abort();
+}
+
+static ALWAYS_INLINE void cpu_z80_cp(struct cpu_z80 *self, int rvalue) {
+  abort();
+}
+
+static ALWAYS_INLINE void cpu_z80_cpd(struct cpu_z80 *self) {
+  abort();
+}
+
+static ALWAYS_INLINE void cpu_z80_cpdr(struct cpu_z80 *self) {
+  abort();
+}
+
+static ALWAYS_INLINE void cpu_z80_cpi(struct cpu_z80 *self) {
+  abort();
+}
+
+static ALWAYS_INLINE void cpu_z80_cpir(struct cpu_z80 *self) {
+  abort();
+}
+
+static ALWAYS_INLINE void cpu_z80_cpl(struct cpu_z80 *self) {
+  abort();
+}
+
+static ALWAYS_INLINE void cpu_z80_daa(struct cpu_z80 *self) {
+  abort();
+}
+
+static ALWAYS_INLINE void cpu_z80_dec_byte(struct cpu_z80 *self, int lvalue) {
+  abort();
+}
+
+static ALWAYS_INLINE void cpu_z80_dec_word(struct cpu_z80 *self, int lvalue) {
+  abort();
+}
+
+static ALWAYS_INLINE void cpu_z80_di(struct cpu_z80 *self) {
+  abort();
+}
+
+static ALWAYS_INLINE void cpu_z80_djnz(struct cpu_z80 *self, int rvalue) {
+  abort();
+}
+
+static ALWAYS_INLINE void cpu_z80_ei(struct cpu_z80 *self) {
+  abort();
+}
+
+static ALWAYS_INLINE void cpu_z80_ex(struct cpu_z80 *self, int lvalue0, int lvalue1) {
+  abort();
+}
+
+static ALWAYS_INLINE void cpu_z80_halt(struct cpu_z80 *self) {
+  abort();
+}
+
+static ALWAYS_INLINE void cpu_z80_ill(struct cpu_z80 *self) {
+  abort();
+}
+
+static ALWAYS_INLINE void cpu_z80_im(struct cpu_z80 *self, int n) {
+  abort();
+}
+
+static ALWAYS_INLINE void cpu_z80_in(struct cpu_z80 *self, int lvalue, int rvalue) {
+  abort();
+}
+
+static ALWAYS_INLINE void cpu_z80_inc_byte(struct cpu_z80 *self, int lvalue) {
+  abort();
+}
+
+static ALWAYS_INLINE void cpu_z80_inc_word(struct cpu_z80 *self, int lvalue) {
+  abort();
+}
+
+static ALWAYS_INLINE void cpu_z80_ind(struct cpu_z80 *self) {
+  abort();
+}
+
+static ALWAYS_INLINE void cpu_z80_indr(struct cpu_z80 *self) {
+  abort();
+}
+
+static ALWAYS_INLINE void cpu_z80_ini(struct cpu_z80 *self) {
+  abort();
+}
+
+static ALWAYS_INLINE void cpu_z80_inir(struct cpu_z80 *self) {
+  abort();
+}
+
+static ALWAYS_INLINE void cpu_z80_jp(struct cpu_z80 *self, bool pred, int rvalue) {
+  abort();
+}
+
+static ALWAYS_INLINE void cpu_z80_jr(struct cpu_z80 *self, bool pred, int rvalue) {
+  abort();
+}
+
+static ALWAYS_INLINE void cpu_z80_ld_byte(struct cpu_z80 *self, int lvalue, int rvalue) {
+  abort();
+}
+
+static ALWAYS_INLINE void cpu_z80_ld_word(struct cpu_z80 *self, int lvalue, int rvalue) {
+  abort();
+}
+
+static ALWAYS_INLINE void cpu_z80_ldd(struct cpu_z80 *self) {
+  abort();
+}
+
+static ALWAYS_INLINE void cpu_z80_lddr(struct cpu_z80 *self) {
+  abort();
+}
+
+static ALWAYS_INLINE void cpu_z80_ldi(struct cpu_z80 *self) {
+  abort();
+}
+
+static ALWAYS_INLINE void cpu_z80_ldir(struct cpu_z80 *self) {
+  abort();
+}
+
+static ALWAYS_INLINE void cpu_z80_neg(struct cpu_z80 *self) {
+  abort();
+}
+
+static ALWAYS_INLINE void cpu_z80_nop(struct cpu_z80 *self) {
+  abort();
+}
+
+static ALWAYS_INLINE void cpu_z80_or(struct cpu_z80 *self, int rvalue) {
+  abort();
+}
+
+static ALWAYS_INLINE void cpu_z80_otdr(struct cpu_z80 *self) {
+  abort();
+}
+
+static ALWAYS_INLINE void cpu_z80_otir(struct cpu_z80 *self) {
+  abort();
+}
+
+static ALWAYS_INLINE void cpu_z80_out(struct cpu_z80 *self, int lvalue, int rvalue) {
+  abort();
+}
+
+static ALWAYS_INLINE void cpu_z80_outd(struct cpu_z80 *self) {
+  abort();
+}
+
+static ALWAYS_INLINE void cpu_z80_outi(struct cpu_z80 *self) {
+  abort();
+}
+
+static ALWAYS_INLINE void cpu_z80_pop(struct cpu_z80 *self, int lvalue) {
+  abort();
+}
+
+static ALWAYS_INLINE void cpu_z80_push(struct cpu_z80 *self, int rvalue) {
+  abort();
+}
+
+static ALWAYS_INLINE void cpu_z80_res(struct cpu_z80 *self, int n, int lvalue) {
+  abort();
+}
+
+static ALWAYS_INLINE void cpu_z80_ret(struct cpu_z80 *self, bool pred) {
+  abort();
+}
+
+static ALWAYS_INLINE void cpu_z80_reti(struct cpu_z80 *self) {
+  abort();
+}
+
+static ALWAYS_INLINE void cpu_z80_retn(struct cpu_z80 *self) {
+  abort();
+}
+
+static ALWAYS_INLINE void cpu_z80_rl(struct cpu_z80 *self, int lvalue) {
+  abort();
+}
+
+static ALWAYS_INLINE void cpu_z80_rla(struct cpu_z80 *self) {
+  abort();
+}
+
+static ALWAYS_INLINE void cpu_z80_rlc(struct cpu_z80 *self, int lvalue) {
+  abort();
+}
+
+static ALWAYS_INLINE void cpu_z80_rlca(struct cpu_z80 *self) {
+  abort();
+}
+
+static ALWAYS_INLINE void cpu_z80_rld(struct cpu_z80 *self) {
+  abort();
+}
+
+static ALWAYS_INLINE void cpu_z80_rr(struct cpu_z80 *self, int lvalue) {
+  abort();
+}
+
+static ALWAYS_INLINE void cpu_z80_rra(struct cpu_z80 *self) {
+  abort();
+}
+
+static ALWAYS_INLINE void cpu_z80_rrc(struct cpu_z80 *self, int lvalue) {
+  abort();
+}
+
+static ALWAYS_INLINE void cpu_z80_rrca(struct cpu_z80 *self) {
+  abort();
+}
+
+static ALWAYS_INLINE void cpu_z80_rrd(struct cpu_z80 *self) {
+  abort();
+}
+
+static ALWAYS_INLINE void cpu_z80_sbc_byte(struct cpu_z80 *self, int lvalue, int rvalue) {
+  abort();
+}
+
+static ALWAYS_INLINE void cpu_z80_sbc_word(struct cpu_z80 *self, int lvalue, int rvalue) {
+  abort();
+}
+
+static ALWAYS_INLINE void cpu_z80_scf(struct cpu_z80 *self) {
+  abort();
+}
+
+static ALWAYS_INLINE void cpu_z80_set(struct cpu_z80 *self, int n, int lvalue) {
+  abort();
+}
+
+static ALWAYS_INLINE void cpu_z80_sla(struct cpu_z80 *self, int lvalue) {
+  abort();
+}
+
+static ALWAYS_INLINE void cpu_z80_sll(struct cpu_z80 *self, int lvalue) {
+  abort();
+}
+
+static ALWAYS_INLINE void cpu_z80_sra(struct cpu_z80 *self, int lvalue) {
+  abort();
+}
+
+static ALWAYS_INLINE void cpu_z80_srl(struct cpu_z80 *self, int lvalue) {
+  abort();
+}
+
+static ALWAYS_INLINE void cpu_z80_sub(struct cpu_z80 *self, int rvalue) {
+  abort();
+}
+
+static ALWAYS_INLINE void cpu_z80_xor(struct cpu_z80 *self, int rvalue) {
+  abort();
+}
+
+// prototypes
+void cpu_z80_init(
+  struct cpu_z80 *self,
+  int (*read_byte)(int addr),
+  void (*write_byte)(int addr, int data),
+  int (*in_byte)(int addr),
+  void (*out_byte)(int addr, int data)
+);
+void cpu_z80_execute(struct cpu_z80 *self);
+void cpu_z80_execute_cb(struct cpu_z80 *self);
+void cpu_z80_execute_dd(struct cpu_z80 *self);
+void cpu_z80_execute_ed(struct cpu_z80 *self);
+void cpu_z80_execute_fd(struct cpu_z80 *self);
+
+#endif
index 6aa5ee1..58d11ec 100755 (executable)
@@ -23,43 +23,44 @@ rvalue_opcodes = {
   'sbc',
 }
 rvalue_modes = {
-  '#$12': 'IMM()',
-  '$0014': 'REL()',
-  '$12': 'ZPG()',
-  '$12,x': 'ZPG_IDX(X)',
-  '$12,y': 'ZPG_IDX(Y)',
-  '$3412': 'ABS()',
-  '$3412,x': 'ABS_IDX(X)',
-  '$3412,y': 'ABS_IDX(Y)',
-  '($12)': 'ZPG_IND()',
-  '($12),y': 'ZPG_IND_IDX(Y)',
-  '($12,x)': 'ZPG_IDX_IND(X)',
-  '($3412)': 'ABS_IND()',
-  '($3412,x)': 'ABS_IDX_IND(X)',
-  'a': 'A',
+  '#$12': 'cpu_65c02_fetch_byte(self)',
+  '$12': 'cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page(self))',
+  '$12,x': 'cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page_indexed(self, self->regs.byte.x))',
+  '$12,y': 'cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page_indexed(self, self->regs.byte.y))',
+  '$3412': 'cpu_65c02_read_byte(self, cpu_65c02_ea_absolute(self))',
+  '$3412,x': 'cpu_65c02_read_byte(self, cpu_65c02_ea_absolute_indexed(self, self->regs.byte.x))',
+  '$3412,y': 'cpu_65c02_read_byte(self, cpu_65c02_ea_absolute_indexed(self, self->regs.byte.y))',
+  '($12)': 'cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page_indirect(self))',
+  '($12),y': 'cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page_indirect_indexed(self, self->regs.byte.y))',
+  '($12,x)': 'cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page_indexed_indirect(self, self->regs.byte.x))',
+  '($3412)': 'cpu_65c02_read_byte(self, cpu_65c02_ea_absolute_indirect(self))',
+  '($3412,x)': 'cpu_65c02_read_byte(self, cpu_65c02_ea_absolute_indexed_indirect(self, self->regs.byte.x))',
 }
 lvalue_modes = {
-  '$0014': 'EA_REL()',
-  '$12': 'EA_ZPG()',
-  '$12,x': 'EA_ZPG_IDX(X)',
-  '$12,y': 'EA_ZPG_IDX(Y)',
-  '$3412': 'EA_ABS()',
-  '$3412,x': 'EA_ABS_IDX(X)',
-  '$3412,y': 'EA_ABS_IDX(Y)',
-  '($12)': 'EA_ZPG_IND()',
-  '($12),y': 'EA_ZPG_IND_IDX(Y)',
-  '($12,x)': 'EA_ZPG_IDX_IND(X)',
-  '($3412)': 'EA_ABS_IND()',
-  '($3412,x)': 'EA_ABS_IDX_IND(X)',
-  'a': 'EA_A',
+  '$0014': 'cpu_65c02_ea_relative(self)',
+  '$12': 'cpu_65c02_ea_zero_page(self)',
+  '$12,x': 'cpu_65c02_ea_zero_page_indexed(self, self->regs.byte.x)',
+  '$12,y': 'cpu_65c02_ea_zero_page_indexed(self, self->regs.byte.y)',
+  '$3412': 'cpu_65c02_ea_absolute(self)',
+  '$3412,x': 'cpu_65c02_ea_absolute_indexed(self, self->regs.byte.x)',
+  '$3412,y': 'cpu_65c02_ea_absolute_indexed(self, self->regs.byte.y)',
+  '($12)': 'cpu_65c02_ea_zero_page_indirect(self)',
+  '($12),y': 'cpu_65c02_ea_zero_page_indirect_indexed(self, self->regs.byte.y)',
+  '($12,x)': 'cpu_65c02_ea_zero_page_indexed_indirect(self, self->regs.byte.x)',
+  '($3412)': 'cpu_65c02_ea_absolute_indirect(self)',
+  '($3412,x)': 'cpu_65c02_ea_absolute_indexed_indirect(self, self->regs.byte.x)',
+  'a': 'CPU_65C02_EA_A',
 }
+
+print('void cpu_65c02_execute(struct cpu_65c02 *self) {')
+print('  switch (cpu_z80_fetch_byte(self)) {')
 for i in range(0x100):
   print(f'  case 0x{i:02x}:')
   if i == 0x64: # cope with py65 disassembler bug
     n, instr = 2, 'stz $12'
   else:
     mem[0] = i
-    n, instr = disassembler.instruction_at(0)
+    _, instr = disassembler.instruction_at(0)
   instr = instr.lower().split()
   if instr[0] == '???':
     instr[0] = 'ill' # illegal opcode
@@ -68,5 +69,12 @@ for i in range(0x100):
       instr[1] = rvalue_modes[instr[1]]
     else:
       instr[1] = lvalue_modes[instr[1]]
-  print('    {0:s}({1:s});'.format(instr[0].upper(), ', '.join(instr[1:])))
+  print(
+    '    cpu_65c02_{0:s}(self{1:s});'.format(
+      instr[0],
+      ''.join([', ' + j for j in instr[1:]])
+    )
+  )
   print('    break;')
+print('  }')
+print('}')
diff --git a/decode_65c02.sed b/decode_65c02.sed
new file mode 100644 (file)
index 0000000..094ace2
--- /dev/null
@@ -0,0 +1,32 @@
+s/cpu_65c02_ill(self/cpu_65c02_ill11(self/g
+s/cpu_65c02_rmb\([0-7]\)(self/cpu_65c02_rmb(self, \1/g
+s/cpu_65c02_smb\([0-7]\)(self/cpu_65c02_smb(self, \1/g
+s/cpu_65c02_ph\([axyp]\)(self/cpu_65c02_ph(self, self->regs.byte.\1/g
+s/cpu_65c02_pl\([axy]\)(self/cpu_65c02_pl(self, CPU_65C02_EA_\1/g
+s/cpu_65c02_bpl(self/cpu_65c02_bnc(self/g
+s/cpu_65c02_bmi(self/cpu_65c02_bns(self/g
+s/cpu_65c02_bne(self/cpu_65c02_bzc(self/g
+s/cpu_65c02_beq(self/cpu_65c02_bzs(self/g
+s/cpu_65c02_bra(self/cpu_65c02_bra(self, true/g
+s/cpu_65c02_b\([czvn]\)c(self/cpu_65c02_bra(self, !self->regs.bit.\1f/g
+s/cpu_65c02_b\([czvn]\)s(self/cpu_65c02_bra(self, self->regs.bit.\1f/g
+s/cpu_65c02_stz(self/cpu_65c02_st(self, 0/g
+s/cpu_65c02_st\([axy]\)(self/cpu_65c02_st(self, self->regs.byte.\1/g
+s/cpu_65c02_ld\([axy]\)(self/cpu_65c02_ld(self, CPU_65C02_EA_\1/g
+s/cpu_65c02_txs(self/cpu_65c02_st(self, self->regs.byte.x, CPU_65C02_EA_S)/g
+s/cpu_65c02_t\([axys]\)\([axys]\)(self/cpu_65c02_ld(self, CPU_65C02_EA_\2, self->regs.byte.\1/g
+s/cpu_65c02_cl\([cidv]\)(self/cpu_65c02_cl(self, CPU_65C02_REG_P_BIT_\1/g
+s/cpu_65c02_se\([cidv]\)(self/cpu_65c02_se(self, CPU_65C02_REG_P_BIT_\1/g
+s/cpu_65c02_in\([xy]\)(self/cpu_65c02_inc(self, CPU_65C02_EA_\1/g
+s/cpu_65c02_de\([xy]\)(self/cpu_65c02_dec(self, CPU_65C02_EA_\1/g
+s/cpu_65c02_cmp(self/cpu_65c02_cmp(self, self->regs.byte.a/g
+s/cpu_65c02_cp\([xy]\)(self/cpu_65c02_cmp(self, self->regs.byte.\1/g
+s/CPU_65C02_EA_a/CPU_65C02_EA_A/g
+s/CPU_65C02_EA_x/CPU_65C02_EA_X/g
+s/CPU_65C02_EA_y/CPU_65C02_EA_Y/g
+s/CPU_65C02_EA_p/CPU_65C02_EA_P/g
+s/CPU_65C02_EA_s/CPU_65C02_EA_S/g
+s/CPU_65C02_REG_P_BIT_c/CPU_65C02_REG_P_BIT_C/g
+s/CPU_65C02_REG_P_BIT_i/CPU_65C02_REG_P_BIT_I/g
+s/CPU_65C02_REG_P_BIT_d/CPU_65C02_REG_P_BIT_D/g
+s/CPU_65C02_REG_P_BIT_v/CPU_65C02_REG_P_BIT_V/g
diff --git a/decode_z80.py b/decode_z80.py
new file mode 100755 (executable)
index 0000000..986e28f
--- /dev/null
@@ -0,0 +1,252 @@
+#!/usr/bin/env python3
+
+import z80dis.z80
+import sys
+
+# for these opcodes, the last operand is rvalue (any other is lvalue)
+rvalue_opcodes = {
+  'ld',
+  'add',
+  'adc',
+  'sub',
+  'sbc',
+  'or',
+  'and',
+  'xor',
+  'cp',
+  'jr',
+  'jp',
+  'call',
+  'djnz',
+  'in',
+  'in_f',
+  'out',
+  'push',
+  'bit',
+}
+
+# if it is in byte_opcodes it is treated as byte and has no suffix
+byte_opcodes = {
+  'sub',
+  'or',
+  'and',
+  'xor',
+  'cp',
+  'in',
+  'out',
+  'bit',
+  'res',
+  'set',
+  'rl',
+  'rlc',
+  'rr',
+  'rrc',
+  'sla',
+  'sll',
+  'sra',
+  'srl',
+}
+# if any operand is in byte_operands it is treated as byte and has suffix
+# in this case it is mandatory that no operand also be in word_operands
+byte_operands = {
+  '0x12',
+  '0x34',
+  'a',
+  'b',
+  'c',
+  'd',
+  'e',
+  'l',
+  'h',
+  'ixl',
+  'ixh',
+  'iyl',
+  'iyh',
+  '(hl)',
+  '(ix+0x12)',
+  '(iy+0x12)',
+}
+byte_rvalue_modes = {
+  '0': '0', # used for the undocumented "out (c),0" instruction
+  '0x12': 'cpu_z80_fetch_byte(self)',
+  '0x34': 'cpu_z80_fetch_byte(self)',
+  'a': 'self->regs.byte.a',
+  'b': 'self->regs.byte.b',
+  'c': 'self->regs.byte.c',
+  'd': 'self->regs.byte.d',
+  'e': 'self->regs.byte.e',
+  'l': 'self->regs.byte.l',
+  'h': 'self->regs.byte.h',
+  'ixl': 'self->regs.byte.ixl',
+  'ixh': 'self->regs.byte.ixh',
+  'iyl': 'self->regs.byte.iyl',
+  'iyh': 'self->regs.byte.iyh',
+  'i': 'self->regs.byte.i',
+  'r': 'self->regs.byte.r',
+  '(0x3412)': 'cpu_z80_read_byte(self, cpu_z80_fetch_word(self))',
+  '(bc)': 'cpu_z80_read_byte(self, self->regs.word.bc)',
+  '(de)': 'cpu_z80_read_byte(self, self->regs.word.de)',
+  '(hl)': 'cpu_z80_read_byte(self, self->regs.word.hl)',
+  #'(ix)': 'cpu_z80_read_byte(self->regs.word.ix)',
+  #'(iy)': 'cpu_z80_read_byte(self->regs.word.iy)',
+  '(ix+0x12)': 'cpu_z80_read_byte(self, cpu_z80_displacement(self, self->regs.word.ix))',
+  '(iy+0x12)': 'cpu_z80_read_byte(self, cpu_z80_displacement(self, self->regs.word.iy))',
+  '(0x0012)': 'cpu_z80_in_byte(self, cpu_z80_fetch_byte(self))',
+  '(c)': 'cpu_z80_in_byte(self, self->regs.word.bc)',
+}
+byte_lvalue_modes = {
+  '0': '0',
+  '1': '1',
+  '2': '2',
+  '3': '3',
+  '4': '4',
+  '5': '5',
+  '6': '6',
+  '7': '7',
+  'f': 'CPU_Z80_EA_F',
+  'a': 'CPU_Z80_EA_A',
+  'b': 'CPU_Z80_EA_B',
+  'c': 'CPU_Z80_EA_C',
+  'd': 'CPU_Z80_EA_D',
+  'e': 'CPU_Z80_EA_E',
+  'l': 'CPU_Z80_EA_L',
+  'h': 'CPU_Z80_EA_H',
+  'ixl': 'CPU_Z80_EA_IXL',
+  'ixh': 'CPU_Z80_EA_IXH',
+  'iyl': 'CPU_Z80_EA_IYL',
+  'iyh': 'CPU_Z80_EA_IYH',
+  'i': 'CPU_Z80_EA_I',
+  'r': 'CPU_Z80_EA_R',
+  '(0x3412)': 'cpu_z80_fetch_word(self)',
+  '(bc)': 'self->regs.word.bc',
+  '(de)': 'self->regs.word.de',
+  '(hl)': 'self->regs.word.hl',
+  #'(ix)': 'self->regs.word.ix',
+  #'(iy)': 'self->regs.word.iy',
+  '(ix+0x12)': 'cpu_z80_displacement(self, self->regs.word.ix)',
+  '(iy+0x12)': 'cpu_z80_displacement(self, self->regs.word.iy)',
+  '(0x0012)': 'cpu_z80_fetch_byte(self)',
+  '(c)': 'self->regs.word.bc',
+}
+
+# if it is in word_opcodes it is treated as word and has no suffix
+# in this case the operands "c" and "(hl)" do not imply a byte opcode
+word_opcodes = {
+  'jr',
+  'jp',
+  'call',
+  'ret',
+  'djnz',
+  'push',
+  'pop',
+  'ex',
+}
+# if any operand is in word_operands it is treated as word and has suffix
+# in this case it is mandatory that no operand also be in byte_operands
+word_operands = {
+  '0x3412',
+  'af',
+  'bc',
+  'de',
+  'hl',
+  'sp',
+  'ix',
+  'iy',
+}
+word_rvalue_modes = {
+  '0x0014': 'cpu_z80_relative(self)',
+  '0x0015': 'cpu_z80_relative(self)',
+  '0x3412': 'cpu_z80_fetch_word(self)',
+  'af': 'self->regs.word.af',
+  'bc': 'self->regs.word.bc',
+  'de': 'self->regs.word.de',
+  'hl': 'self->regs.word.hl',
+  'ix': 'self->regs.word.ix',
+  'iy': 'self->regs.word.iy',
+  'sp': 'self->regs.word.sp',
+  '(0x3412)': 'cpu_z80_read_word(self, cpu_z80_fetch_word(self))',
+  '(bc)': 'cpu_z80_read_word(self, self->regs.word.bc)',
+  '(de)': 'cpu_z80_read_word(self, self->regs.word.de)',
+  '(hl)': 'cpu_z80_read_word(self, self->regs.word.hl)',
+  '(ix)': 'cpu_z80_read_word(self, self->regs.word.ix)',
+  '(iy)': 'cpu_z80_read_word(self, self->regs.word.iy)',
+  '(sp)': 'cpu_z80_read_word(self, self->regs.word.sp)',
+}
+word_lvalue_modes = {
+  'af': 'CPU_Z80_EA_AF',
+  'af\'': 'CPU_Z80_EA_AF_PRIME',
+  'bc': 'CPU_Z80_EA_BC',
+  'de': 'CPU_Z80_EA_DE',
+  'hl': 'CPU_Z80_EA_HL',
+  'ix': 'CPU_Z80_EA_IX',
+  'iy': 'CPU_Z80_EA_IY',
+  'sp': 'CPU_Z80_EA_SP',
+  '(0x3412)': 'cpu_z80_fetch_word(self)',
+  '(bc)': 'self->regs.word.bc',
+  '(de)': 'self->regs.word.de',
+  '(hl)': 'self->regs.word.hl',
+  '(sp)': 'self->regs.word.sp',
+  'z': 'self->regs.bit.zf',
+  'nz': '!self->regs.bit.zf',
+  'c': 'self->regs.bit.cf',
+  'nc': '!self->regs.bit.cf',
+  'm': 'self->regs.bit.sf',
+  'p': '!self->regs.bit.sf',
+  'po': 'self->regs.bit.pvf',
+  'pe': '!self->regs.bit.pvf',
+}
+
+prefixes = [[], [0xcb], [0xdd], [0xed], [0xfd]]
+prefix_strings = ['', '_cb', '_dd', '_ed', '_fd']
+for i in range(0x500):
+  if (i & 0xff) == 0:
+    print(
+      f'void cpu_z80_execute{prefix_strings[i >> 8]:s}(struct cpu_z80 *self) {{'
+    )
+    print('  switch (cpu_z80_fetch_byte(self)) {')
+  print(f'  case 0x{i & 0xff:02x}:')
+  if [i] in prefixes:
+    print(f'    cpu_z80_execute_{i:02x}(self);')
+  else:
+    instr = z80dis.z80.disasm(
+      bytes(prefixes[i >> 8] + [i & 0xff, 0x12, 0x34, 0x56, 0x78])
+    ).lower().split()
+    if len(instr) == 0:
+      instr = ['ill']
+    elif len(instr) >= 2:
+      instr[1:] = instr[1].split(',')
+      if instr == ['ld', 'h', 'sll']:
+        instr = ['ill'] # deal with this later
+    #print('xxx', instr)
+    suffix = ''
+    if instr[0] not in byte_opcodes and instr[0] not in word_opcodes:
+      for j in instr[1:]:
+        if j in byte_operands:
+          assert suffix != '_word'
+          suffix = '_byte'
+        elif j in word_operands:
+          assert suffix != '_byte'
+          suffix = '_word'
+    j = len(instr) - int(instr[0] in rvalue_opcodes)
+    if suffix == '_byte' or instr[0] in byte_opcodes:
+      for k in range(1, j):
+        instr[k] = byte_lvalue_modes[instr[k]]
+      for k in range(j, len(instr)):
+        instr[k] = byte_rvalue_modes[instr[k]]
+    elif suffix == '_word' or instr[0] in word_opcodes:
+      for k in range(1, j):
+        instr[k] = word_lvalue_modes[instr[k]]
+      for k in range(j, len(instr)):
+        instr[k] = word_rvalue_modes[instr[k]]
+    print(
+      '    cpu_z80_{0:s}{1:s}(self{2:s});'.format(
+        instr[0],
+        suffix,
+        ''.join([', ' + j for j in instr[1:]])
+      )
+    )
+  print('    break;')
+  if (i & 0xff) == 0xff:
+    print('  }')
+    print('}')
+    print()
diff --git a/decode_z80.sed b/decode_z80.sed
new file mode 100644 (file)
index 0000000..a96670f
--- /dev/null
@@ -0,0 +1,9 @@
+s/cpu_z80_jr(self/cpu_z80_jr(self, true/g
+s/cpu_z80_jp(self/cpu_z80_jp(self, true/g
+s/cpu_z80_call(self/cpu_z80_call(self, true/g
+s/cpu_z80_ret(self/cpu_z80_ret(self, true/g
+s/cpu_z80_rst(self/cpu_z80_call(self, true/g
+s/(self, true, \(!\?self->regs\.bit\.\)/(self, \1/g
+s/cpu_z80_in(self, CPU_Z80_EA_F/cpu_z80_in(self, CPU_Z80_EA_NONE/g
+s/^    \(cpu_z80_ld_byte(self, \)\(cpu_z80_displacement(self, self->regs\.word\.i[xy])\)\(, cpu_z80_fetch_byte(self));\)$/    {\n      int ea = \2;\n      \1ea\3\n    }/
+s/^    cpu_z80_exx(self);$/    cpu_z80_ex(self, CPU_Z80_EA_BC, CPU_Z80_EA_BC_PRIME);\n    cpu_z80_ex(self, CPU_Z80_EA_DE, CPU_Z80_EA_DE_PRIME);\n    cpu_z80_ex(self, CPU_Z80_EA_HL, CPU_Z80_EA_HL_PRIME);/
index f556d75..32defe1 100644 (file)
@@ -204,7 +204,8 @@ int main(int argc, char **argv) {
   }
 #else
   struct cpu_65c02 cpu;
-  cpu_65c02_init(&cpu, entry_point, read_byte, write_byte);
+  cpu_65c02_init(&cpu, read_byte, write_byte);
+  cpu.regs.word.pc = entry_point;
 
   while (true) {
 #if REG_TRACE
index dbd4ac0..f408691 100644 (file)
--- a/emu_z80.c
+++ b/emu_z80.c
@@ -161,7 +161,7 @@ int bdos(int c, int de) {
 int read_byte(int addr) {
 #if MEM_TRACE
   int data = mem[addr];
-  printf("mem_addr=%04x rd=%02x\n", addr, data);
+  printf("addr=%04x rd=%02x\n", addr, data);
   return data;
 #else
   return mem[addr];
@@ -170,15 +170,16 @@ int read_byte(int addr) {
 
 void write_byte(int addr, int data) {
 #if MEM_TRACE
-  printf("mem_addr=%04x wr=%02x\n", addr, data);
+  printf("addr=%04x wr=%02x\n", addr, data);
 #endif
   mem[addr] = data;
 }
 
 int in_byte(int addr) {
+  addr &= 0xff;
 #if IO_TRACE
   int data = io_read[addr];
-  printf("io_addr=%04x rd=%02x\n", addr, data);
+  printf("io=%02x rd=%02x\n", addr, data);
   return data;
 #else
   return io_read[addr];
@@ -186,8 +187,9 @@ int in_byte(int addr) {
 }
 
 void out_byte(int addr, int data) {
+  addr &= 0xff;
 #if IO_TRACE
-  printf("io_addr=%04x wr=%02x\n", addr, data);
+  printf("io=%02x wr=%02x\n", addr, data);
 #endif
   io_write[addr] = data;
 }
@@ -271,12 +273,8 @@ int main(int argc, char **argv) {
   }
 #else
   struct cpu_z80 cpu;
-  memset(&cpu, 0, sizeof(struct cpu_z80));
-  cpu.pc = entry_point;
-  cpu.read_byte = read_byte;
-  cpu.write_byte = write_byte;
-  cpu.in_byte = in_byte;
-  cpu.out_byte = write_byte;
+  cpu_z80_init(&cpu, read_byte, write_byte, in_byte, out_byte);
+  cpu.regs.word.pc = entry_point;
 
   while (true) {
 #if REG_TRACE
@@ -299,14 +297,14 @@ int main(int argc, char **argv) {
     );
 #endif
 
-    if (cpu.pc == 0) {
+    if (cpu.regs.word.pc == 0) {
       putchar('\n');
       break;
     }
-    if (cpu.pc == 5) {
-      cpu.regs.w[CPU_Z80_REG_DE >> 1] = bdos(
-        cpu.regs.b[CPU_Z80_REG_C],
-        cpu.regs.w[CPU_Z80_REG_DE >> 1]
+    if (cpu.regs.word.pc == 5) {
+      cpu.regs.word.de = bdos(
+        cpu.regs.byte.c,
+        cpu.regs.word.de
       );
     }
     cpu_z80_execute(&cpu);