Implement our own 8080 emulator, works but need to check AC flag, DAA instruction...
authorNick Downing <nick@ndcode.org>
Wed, 3 Dec 2025 09:03:38 +0000 (20:03 +1100)
committerNick Downing <nick@ndcode.org>
Thu, 4 Dec 2025 03:35:06 +0000 (14:35 +1100)
.gitignore
.gitmodules
Makefile
cpu_8080.c [new file with mode: 0644]
cpu_8080.h [new file with mode: 0644]
cpu_z80.c
decode_8080.py [new file with mode: 0755]
doc/Optable.pdf [new file with mode: 0644]
emu_8080.c [new file with mode: 0644]
instr_8080.txt [new file with mode: 0644]
jefftranter_8080 [new submodule]

index 3972a8e..fa5bbaa 100644 (file)
@@ -14,6 +14,8 @@
 /emu_68000_alt
 /emu_6809
 /emu_6809_alt
+/emu_8080
+/emu_8080_alt
 /emu_8086
 /emu_8086_alt
 /emu_mips
index c4d83a0..da21646 100644 (file)
@@ -49,3 +49,6 @@
 [submodule "intel-8080"]
        path = intel-8080
        url = https://github.com/nickd4/intel-8080.git
+[submodule "jefftranter_8080"]
+       path = jefftranter_8080
+       url = https://github.com/nickd4/jefftranter_8080.git
index 498b535..d042b92 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -35,8 +35,8 @@ emu_z80_alt \
 zexall.ihx \
 zexdoc.ihx \
 mbasic.ihx \
+emu_8080 \
 emu_8080_alt
-#emu_8080
 #emu_mips
 #emu_pdp11
 #emu_68000
@@ -300,12 +300,12 @@ zexdoc.ihx: ZEXALL/zexdoc.com
        rm __temp__.ihx
 
 # 8080
-#emu_8080: emu_8080.o cpu_8080.o
-#      ${CC} ${CFLAGS} -o $@ $^
+emu_8080: emu_8080.o cpu_8080.o
+       ${CC} ${CFLAGS} -o $@ $^
 
-#emu_8080.o: cpu_8080.h
+emu_8080.o: cpu_8080.h
 
-#cpu_8080.o: cpu_8080.h
+cpu_8080.o: cpu_8080.h
 
 emu_8080_alt: emu_8080_alt.o 8080_cpu.o
        ${CC} ${CFLAGS} -o $@ $^
diff --git a/cpu_8080.c b/cpu_8080.c
new file mode 100644 (file)
index 0000000..4f8b820
--- /dev/null
@@ -0,0 +1,856 @@
+#include <assert.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include "cpu_8080.h"
+
+// initialization
+void cpu_8080_init(
+  struct cpu_8080 *self,
+  int (*read_byte)(void *context, int addr),
+  void *read_byte_context,
+  void (*write_byte)(void *context, int addr, int data),
+  void *write_byte_context,
+  int (*in_byte)(void *context, int addr),
+  void *in_byte_context,
+  void (*out_byte)(void *context, int addr, int data),
+  void *out_byte_context
+) {
+  memset(self, 0, sizeof(struct cpu_8080));
+  self->read_byte = read_byte;
+  self->read_byte_context = read_byte_context;
+  self->write_byte = write_byte;
+  self->write_byte_context = write_byte_context;
+  self->in_byte = in_byte;
+  self->in_byte_context = in_byte_context;
+  self->out_byte = out_byte;
+  self->out_byte_context = out_byte_context;
+}
+
+void cpu_8080_reset(struct cpu_8080 *self) {
+  self->regs.word.pc = CPU_8080_RESET_ADDR;
+  self->regs.word.psw = 0; //0xffff;
+  self->regs.word.sp = 0xffff;
+  self->regs.word.iflags = 0;
+}
+
+// instruction decode
+void cpu_8080_execute(struct cpu_8080 *self) {
+  if (self->regs.bit.nmi_pending) {
+    self->regs.bit.nmi_pending = false;
+
+    self->regs.word.pc += self->regs.bit.halt_flag;
+    self->regs.bit.halt_flag = false;
+
+    self->regs.bit.iff1 = false;
+    cpu_8080_call(self, CPU_8080_NMI_ADDR);
+    return;
+  }
+
+  if (
+    self->regs.bit.irq_pending &&
+    self->regs.bit.iff1 &&
+    !self->regs.bit.ei_flag
+  ) {
+    self->regs.bit.irq_pending = false;
+
+    self->regs.word.pc += self->regs.bit.halt_flag;
+    self->regs.bit.halt_flag = false;
+
+    self->regs.bit.iff1 = false;
+    self->regs.bit.iff2 = false;
+    switch (self->regs.bit.im) {
+    case 0:
+      // self->regs.byte.v contains the opcode placed on bus by peripheral
+      // only RST instructions are supported at the moment (systems with
+      // 8259 / 8228 could also use CALL, that would be extra complexity)
+      assert((self->regs.byte.v & 0xc7) == 0xc7);
+      cpu_8080_call(self, self->regs.byte.v & 0x38);
+      break;
+    case 1:
+      cpu_8080_call(self, CPU_8080_IRQ_ADDR);
+      break;
+    case 2:
+      // self->regs.byte.v contains the vector placed on bus by peripheral
+      // it is combined with self->regs.byte.i to create vector word "iv"
+      cpu_8080_call(self, cpu_8080_read_word(self, self->regs.word.iv & 0xfffe));
+      break;
+    default:
+      abort();
+    } 
+    return;
+  }
+
+  self->regs.bit.halt_flag = false;
+  self->regs.bit.ei_flag = false;
+  switch (cpu_8080_fetch_byte(self)) {
+  case 0x00:
+    cpu_8080_nop(self);
+    break;
+  case 0x01:
+    cpu_8080_lxi(self, CPU_8080_EA_BC, cpu_8080_fetch_word(self));
+    break;
+  case 0x02:
+    cpu_8080_stax(self, self->regs.word.bc);
+    break;
+  case 0x03:
+    cpu_8080_inx(self, CPU_8080_EA_BC);
+    break;
+  case 0x04:
+    cpu_8080_inr(self, CPU_8080_EA_B);
+    break;
+  case 0x05:
+    cpu_8080_dcr(self, CPU_8080_EA_B);
+    break;
+  case 0x06:
+    cpu_8080_mvi(self, CPU_8080_EA_B, cpu_8080_fetch_byte(self));
+    break;
+  case 0x07:
+    cpu_8080_rlc(self);
+    break;
+  case 0x08:
+    cpu_8080_nop(self);
+    break;
+  case 0x09:
+    cpu_8080_dad(self, self->regs.word.bc);
+    break;
+  case 0x0a:
+    cpu_8080_ldax(self, self->regs.word.bc);
+    break;
+  case 0x0b:
+    cpu_8080_dcx(self, CPU_8080_EA_BC);
+    break;
+  case 0x0c:
+    cpu_8080_inr(self, CPU_8080_EA_C);
+    break;
+  case 0x0d:
+    cpu_8080_dcr(self, CPU_8080_EA_C);
+    break;
+  case 0x0e:
+    cpu_8080_mvi(self, CPU_8080_EA_C, cpu_8080_fetch_byte(self));
+    break;
+  case 0x0f:
+    cpu_8080_rrc(self);
+    break;
+  case 0x10:
+    cpu_8080_nop(self);
+    break;
+  case 0x11:
+    cpu_8080_lxi(self, CPU_8080_EA_DE, cpu_8080_fetch_word(self));
+    break;
+  case 0x12:
+    cpu_8080_stax(self, self->regs.word.de);
+    break;
+  case 0x13:
+    cpu_8080_inx(self, CPU_8080_EA_DE);
+    break;
+  case 0x14:
+    cpu_8080_inr(self, CPU_8080_EA_D);
+    break;
+  case 0x15:
+    cpu_8080_dcr(self, CPU_8080_EA_D);
+    break;
+  case 0x16:
+    cpu_8080_mvi(self, CPU_8080_EA_D, cpu_8080_fetch_byte(self));
+    break;
+  case 0x17:
+    cpu_8080_ral(self);
+    break;
+  case 0x18:
+    cpu_8080_nop(self);
+    break;
+  case 0x19:
+    cpu_8080_dad(self, self->regs.word.de);
+    break;
+  case 0x1a:
+    cpu_8080_ldax(self, self->regs.word.de);
+    break;
+  case 0x1b:
+    cpu_8080_dcx(self, CPU_8080_EA_DE);
+    break;
+  case 0x1c:
+    cpu_8080_inr(self, CPU_8080_EA_E);
+    break;
+  case 0x1d:
+    cpu_8080_dcr(self, CPU_8080_EA_E);
+    break;
+  case 0x1e:
+    cpu_8080_mvi(self, CPU_8080_EA_E, cpu_8080_fetch_byte(self));
+    break;
+  case 0x1f:
+    cpu_8080_rar(self);
+    break;
+  case 0x20:
+    cpu_8080_nop(self);
+    break;
+  case 0x21:
+    cpu_8080_lxi(self, CPU_8080_EA_HL, cpu_8080_fetch_word(self));
+    break;
+  case 0x22:
+    cpu_8080_shld(self, cpu_8080_fetch_word(self));
+    break;
+  case 0x23:
+    cpu_8080_inx(self, CPU_8080_EA_HL);
+    break;
+  case 0x24:
+    cpu_8080_inr(self, CPU_8080_EA_H);
+    break;
+  case 0x25:
+    cpu_8080_dcr(self, CPU_8080_EA_H);
+    break;
+  case 0x26:
+    cpu_8080_mvi(self, CPU_8080_EA_H, cpu_8080_fetch_byte(self));
+    break;
+  case 0x27:
+    cpu_8080_daa(self);
+    break;
+  case 0x28:
+    cpu_8080_nop(self);
+    break;
+  case 0x29:
+    cpu_8080_dad(self, self->regs.word.hl);
+    break;
+  case 0x2a:
+    cpu_8080_lhld(self, cpu_8080_fetch_word(self));
+    break;
+  case 0x2b:
+    cpu_8080_dcx(self, CPU_8080_EA_HL);
+    break;
+  case 0x2c:
+    cpu_8080_inr(self, CPU_8080_EA_L);
+    break;
+  case 0x2d:
+    cpu_8080_dcr(self, CPU_8080_EA_L);
+    break;
+  case 0x2e:
+    cpu_8080_mvi(self, CPU_8080_EA_L, cpu_8080_fetch_byte(self));
+    break;
+  case 0x2f:
+    cpu_8080_cma(self);
+    break;
+  case 0x30:
+    cpu_8080_nop(self);
+    break;
+  case 0x31:
+    cpu_8080_lxi(self, CPU_8080_EA_SP, cpu_8080_fetch_word(self));
+    break;
+  case 0x32:
+    cpu_8080_sta(self, cpu_8080_fetch_word(self));
+    break;
+  case 0x33:
+    cpu_8080_inx(self, CPU_8080_EA_SP);
+    break;
+  case 0x34:
+    cpu_8080_inr(self, self->regs.word.hl);
+    break;
+  case 0x35:
+    cpu_8080_dcr(self, self->regs.word.hl);
+    break;
+  case 0x36:
+    cpu_8080_mvi(self, self->regs.word.hl, cpu_8080_fetch_byte(self));
+    break;
+  case 0x37:
+    cpu_8080_stc(self);
+    break;
+  case 0x38:
+    cpu_8080_nop(self);
+    break;
+  case 0x39:
+    cpu_8080_dad(self, self->regs.word.sp);
+    break;
+  case 0x3a:
+    cpu_8080_lda(self, cpu_8080_fetch_word(self));
+    break;
+  case 0x3b:
+    cpu_8080_dcx(self, CPU_8080_EA_SP);
+    break;
+  case 0x3c:
+    cpu_8080_inr(self, CPU_8080_EA_A);
+    break;
+  case 0x3d:
+    cpu_8080_dcr(self, CPU_8080_EA_A);
+    break;
+  case 0x3e:
+    cpu_8080_mvi(self, CPU_8080_EA_A, cpu_8080_fetch_byte(self));
+    break;
+  case 0x3f:
+    cpu_8080_cmc(self);
+    break;
+  case 0x40:
+    cpu_8080_mov(self, CPU_8080_EA_B, self->regs.byte.b);
+    break;
+  case 0x41:
+    cpu_8080_mov(self, CPU_8080_EA_B, self->regs.byte.c);
+    break;
+  case 0x42:
+    cpu_8080_mov(self, CPU_8080_EA_B, self->regs.byte.d);
+    break;
+  case 0x43:
+    cpu_8080_mov(self, CPU_8080_EA_B, self->regs.byte.e);
+    break;
+  case 0x44:
+    cpu_8080_mov(self, CPU_8080_EA_B, self->regs.byte.h);
+    break;
+  case 0x45:
+    cpu_8080_mov(self, CPU_8080_EA_B, self->regs.byte.l);
+    break;
+  case 0x46:
+    cpu_8080_mov(self, CPU_8080_EA_B, cpu_8080_read_byte(self, self->regs.word.hl));
+    break;
+  case 0x47:
+    cpu_8080_mov(self, CPU_8080_EA_B, self->regs.byte.a);
+    break;
+  case 0x48:
+    cpu_8080_mov(self, CPU_8080_EA_C, self->regs.byte.b);
+    break;
+  case 0x49:
+    cpu_8080_mov(self, CPU_8080_EA_C, self->regs.byte.c);
+    break;
+  case 0x4a:
+    cpu_8080_mov(self, CPU_8080_EA_C, self->regs.byte.d);
+    break;
+  case 0x4b:
+    cpu_8080_mov(self, CPU_8080_EA_C, self->regs.byte.e);
+    break;
+  case 0x4c:
+    cpu_8080_mov(self, CPU_8080_EA_C, self->regs.byte.h);
+    break;
+  case 0x4d:
+    cpu_8080_mov(self, CPU_8080_EA_C, self->regs.byte.l);
+    break;
+  case 0x4e:
+    cpu_8080_mov(self, CPU_8080_EA_C, cpu_8080_read_byte(self, self->regs.word.hl));
+    break;
+  case 0x4f:
+    cpu_8080_mov(self, CPU_8080_EA_C, self->regs.byte.a);
+    break;
+  case 0x50:
+    cpu_8080_mov(self, CPU_8080_EA_D, self->regs.byte.b);
+    break;
+  case 0x51:
+    cpu_8080_mov(self, CPU_8080_EA_D, self->regs.byte.c);
+    break;
+  case 0x52:
+    cpu_8080_mov(self, CPU_8080_EA_D, self->regs.byte.d);
+    break;
+  case 0x53:
+    cpu_8080_mov(self, CPU_8080_EA_D, self->regs.byte.e);
+    break;
+  case 0x54:
+    cpu_8080_mov(self, CPU_8080_EA_D, self->regs.byte.h);
+    break;
+  case 0x55:
+    cpu_8080_mov(self, CPU_8080_EA_D, self->regs.byte.l);
+    break;
+  case 0x56:
+    cpu_8080_mov(self, CPU_8080_EA_D, cpu_8080_read_byte(self, self->regs.word.hl));
+    break;
+  case 0x57:
+    cpu_8080_mov(self, CPU_8080_EA_D, self->regs.byte.a);
+    break;
+  case 0x58:
+    cpu_8080_mov(self, CPU_8080_EA_E, self->regs.byte.b);
+    break;
+  case 0x59:
+    cpu_8080_mov(self, CPU_8080_EA_E, self->regs.byte.c);
+    break;
+  case 0x5a:
+    cpu_8080_mov(self, CPU_8080_EA_E, self->regs.byte.d);
+    break;
+  case 0x5b:
+    cpu_8080_mov(self, CPU_8080_EA_E, self->regs.byte.e);
+    break;
+  case 0x5c:
+    cpu_8080_mov(self, CPU_8080_EA_E, self->regs.byte.h);
+    break;
+  case 0x5d:
+    cpu_8080_mov(self, CPU_8080_EA_E, self->regs.byte.l);
+    break;
+  case 0x5e:
+    cpu_8080_mov(self, CPU_8080_EA_E, cpu_8080_read_byte(self, self->regs.word.hl));
+    break;
+  case 0x5f:
+    cpu_8080_mov(self, CPU_8080_EA_E, self->regs.byte.a);
+    break;
+  case 0x60:
+    cpu_8080_mov(self, CPU_8080_EA_H, self->regs.byte.b);
+    break;
+  case 0x61:
+    cpu_8080_mov(self, CPU_8080_EA_H, self->regs.byte.c);
+    break;
+  case 0x62:
+    cpu_8080_mov(self, CPU_8080_EA_H, self->regs.byte.d);
+    break;
+  case 0x63:
+    cpu_8080_mov(self, CPU_8080_EA_H, self->regs.byte.e);
+    break;
+  case 0x64:
+    cpu_8080_mov(self, CPU_8080_EA_H, self->regs.byte.h);
+    break;
+  case 0x65:
+    cpu_8080_mov(self, CPU_8080_EA_H, self->regs.byte.l);
+    break;
+  case 0x66:
+    cpu_8080_mov(self, CPU_8080_EA_H, cpu_8080_read_byte(self, self->regs.word.hl));
+    break;
+  case 0x67:
+    cpu_8080_mov(self, CPU_8080_EA_H, self->regs.byte.a);
+    break;
+  case 0x68:
+    cpu_8080_mov(self, CPU_8080_EA_L, self->regs.byte.b);
+    break;
+  case 0x69:
+    cpu_8080_mov(self, CPU_8080_EA_L, self->regs.byte.c);
+    break;
+  case 0x6a:
+    cpu_8080_mov(self, CPU_8080_EA_L, self->regs.byte.d);
+    break;
+  case 0x6b:
+    cpu_8080_mov(self, CPU_8080_EA_L, self->regs.byte.e);
+    break;
+  case 0x6c:
+    cpu_8080_mov(self, CPU_8080_EA_L, self->regs.byte.h);
+    break;
+  case 0x6d:
+    cpu_8080_mov(self, CPU_8080_EA_L, self->regs.byte.l);
+    break;
+  case 0x6e:
+    cpu_8080_mov(self, CPU_8080_EA_L, cpu_8080_read_byte(self, self->regs.word.hl));
+    break;
+  case 0x6f:
+    cpu_8080_mov(self, CPU_8080_EA_L, self->regs.byte.a);
+    break;
+  case 0x70:
+    cpu_8080_mov(self, self->regs.word.hl, self->regs.byte.b);
+    break;
+  case 0x71:
+    cpu_8080_mov(self, self->regs.word.hl, self->regs.byte.c);
+    break;
+  case 0x72:
+    cpu_8080_mov(self, self->regs.word.hl, self->regs.byte.d);
+    break;
+  case 0x73:
+    cpu_8080_mov(self, self->regs.word.hl, self->regs.byte.e);
+    break;
+  case 0x74:
+    cpu_8080_mov(self, self->regs.word.hl, self->regs.byte.h);
+    break;
+  case 0x75:
+    cpu_8080_mov(self, self->regs.word.hl, self->regs.byte.l);
+    break;
+  case 0x76:
+    cpu_8080_hlt(self);
+    break;
+  case 0x77:
+    cpu_8080_mov(self, self->regs.word.hl, self->regs.byte.a);
+    break;
+  case 0x78:
+    cpu_8080_mov(self, CPU_8080_EA_A, self->regs.byte.b);
+    break;
+  case 0x79:
+    cpu_8080_mov(self, CPU_8080_EA_A, self->regs.byte.c);
+    break;
+  case 0x7a:
+    cpu_8080_mov(self, CPU_8080_EA_A, self->regs.byte.d);
+    break;
+  case 0x7b:
+    cpu_8080_mov(self, CPU_8080_EA_A, self->regs.byte.e);
+    break;
+  case 0x7c:
+    cpu_8080_mov(self, CPU_8080_EA_A, self->regs.byte.h);
+    break;
+  case 0x7d:
+    cpu_8080_mov(self, CPU_8080_EA_A, self->regs.byte.l);
+    break;
+  case 0x7e:
+    cpu_8080_mov(self, CPU_8080_EA_A, cpu_8080_read_byte(self, self->regs.word.hl));
+    break;
+  case 0x7f:
+    cpu_8080_mov(self, CPU_8080_EA_A, self->regs.byte.a);
+    break;
+  case 0x80:
+    cpu_8080_add(self, self->regs.byte.b);
+    break;
+  case 0x81:
+    cpu_8080_add(self, self->regs.byte.c);
+    break;
+  case 0x82:
+    cpu_8080_add(self, self->regs.byte.d);
+    break;
+  case 0x83:
+    cpu_8080_add(self, self->regs.byte.e);
+    break;
+  case 0x84:
+    cpu_8080_add(self, self->regs.byte.h);
+    break;
+  case 0x85:
+    cpu_8080_add(self, self->regs.byte.l);
+    break;
+  case 0x86:
+    cpu_8080_add(self, cpu_8080_read_byte(self, self->regs.word.hl));
+    break;
+  case 0x87:
+    cpu_8080_add(self, self->regs.byte.a);
+    break;
+  case 0x88:
+    cpu_8080_adc(self, self->regs.byte.b);
+    break;
+  case 0x89:
+    cpu_8080_adc(self, self->regs.byte.c);
+    break;
+  case 0x8a:
+    cpu_8080_adc(self, self->regs.byte.d);
+    break;
+  case 0x8b:
+    cpu_8080_adc(self, self->regs.byte.e);
+    break;
+  case 0x8c:
+    cpu_8080_adc(self, self->regs.byte.h);
+    break;
+  case 0x8d:
+    cpu_8080_adc(self, self->regs.byte.l);
+    break;
+  case 0x8e:
+    cpu_8080_adc(self, cpu_8080_read_byte(self, self->regs.word.hl));
+    break;
+  case 0x8f:
+    cpu_8080_adc(self, self->regs.byte.a);
+    break;
+  case 0x90:
+    cpu_8080_sub(self, self->regs.byte.b);
+    break;
+  case 0x91:
+    cpu_8080_sub(self, self->regs.byte.c);
+    break;
+  case 0x92:
+    cpu_8080_sub(self, self->regs.byte.d);
+    break;
+  case 0x93:
+    cpu_8080_sub(self, self->regs.byte.e);
+    break;
+  case 0x94:
+    cpu_8080_sub(self, self->regs.byte.h);
+    break;
+  case 0x95:
+    cpu_8080_sub(self, self->regs.byte.l);
+    break;
+  case 0x96:
+    cpu_8080_sub(self, cpu_8080_read_byte(self, self->regs.word.hl));
+    break;
+  case 0x97:
+    cpu_8080_sub(self, self->regs.byte.a);
+    break;
+  case 0x98:
+    cpu_8080_sbb(self, self->regs.byte.b);
+    break;
+  case 0x99:
+    cpu_8080_sbb(self, self->regs.byte.c);
+    break;
+  case 0x9a:
+    cpu_8080_sbb(self, self->regs.byte.d);
+    break;
+  case 0x9b:
+    cpu_8080_sbb(self, self->regs.byte.e);
+    break;
+  case 0x9c:
+    cpu_8080_sbb(self, self->regs.byte.h);
+    break;
+  case 0x9d:
+    cpu_8080_sbb(self, self->regs.byte.l);
+    break;
+  case 0x9e:
+    cpu_8080_sbb(self, cpu_8080_read_byte(self, self->regs.word.hl));
+    break;
+  case 0x9f:
+    cpu_8080_sbb(self, self->regs.byte.a);
+    break;
+  case 0xa0:
+    cpu_8080_ana(self, self->regs.byte.b);
+    break;
+  case 0xa1:
+    cpu_8080_ana(self, self->regs.byte.c);
+    break;
+  case 0xa2:
+    cpu_8080_ana(self, self->regs.byte.d);
+    break;
+  case 0xa3:
+    cpu_8080_ana(self, self->regs.byte.e);
+    break;
+  case 0xa4:
+    cpu_8080_ana(self, self->regs.byte.h);
+    break;
+  case 0xa5:
+    cpu_8080_ana(self, self->regs.byte.l);
+    break;
+  case 0xa6:
+    cpu_8080_ana(self, cpu_8080_read_byte(self, self->regs.word.hl));
+    break;
+  case 0xa7:
+    cpu_8080_ana(self, self->regs.byte.a);
+    break;
+  case 0xa8:
+    cpu_8080_xra(self, self->regs.byte.b);
+    break;
+  case 0xa9:
+    cpu_8080_xra(self, self->regs.byte.c);
+    break;
+  case 0xaa:
+    cpu_8080_xra(self, self->regs.byte.d);
+    break;
+  case 0xab:
+    cpu_8080_xra(self, self->regs.byte.e);
+    break;
+  case 0xac:
+    cpu_8080_xra(self, self->regs.byte.h);
+    break;
+  case 0xad:
+    cpu_8080_xra(self, self->regs.byte.l);
+    break;
+  case 0xae:
+    cpu_8080_xra(self, cpu_8080_read_byte(self, self->regs.word.hl));
+    break;
+  case 0xaf:
+    cpu_8080_xra(self, self->regs.byte.a);
+    break;
+  case 0xb0:
+    cpu_8080_ora(self, self->regs.byte.b);
+    break;
+  case 0xb1:
+    cpu_8080_ora(self, self->regs.byte.c);
+    break;
+  case 0xb2:
+    cpu_8080_ora(self, self->regs.byte.d);
+    break;
+  case 0xb3:
+    cpu_8080_ora(self, self->regs.byte.e);
+    break;
+  case 0xb4:
+    cpu_8080_ora(self, self->regs.byte.h);
+    break;
+  case 0xb5:
+    cpu_8080_ora(self, self->regs.byte.l);
+    break;
+  case 0xb6:
+    cpu_8080_ora(self, cpu_8080_read_byte(self, self->regs.word.hl));
+    break;
+  case 0xb7:
+    cpu_8080_ora(self, self->regs.byte.a);
+    break;
+  case 0xb8:
+    cpu_8080_cmp(self, self->regs.byte.b);
+    break;
+  case 0xb9:
+    cpu_8080_cmp(self, self->regs.byte.c);
+    break;
+  case 0xba:
+    cpu_8080_cmp(self, self->regs.byte.d);
+    break;
+  case 0xbb:
+    cpu_8080_cmp(self, self->regs.byte.e);
+    break;
+  case 0xbc:
+    cpu_8080_cmp(self, self->regs.byte.h);
+    break;
+  case 0xbd:
+    cpu_8080_cmp(self, self->regs.byte.l);
+    break;
+  case 0xbe:
+    cpu_8080_cmp(self, cpu_8080_read_byte(self, self->regs.word.hl));
+    break;
+  case 0xbf:
+    cpu_8080_cmp(self, self->regs.byte.a);
+    break;
+  case 0xc0:
+    cpu_8080_rnz(self);
+    break;
+  case 0xc1:
+    cpu_8080_pop(self, CPU_8080_EA_BC);
+    break;
+  case 0xc2:
+    cpu_8080_jnz(self, cpu_8080_fetch_word(self));
+    break;
+  case 0xc3:
+    cpu_8080_jmp(self, cpu_8080_fetch_word(self));
+    break;
+  case 0xc4:
+    cpu_8080_cnz(self, cpu_8080_fetch_word(self));
+    break;
+  case 0xc5:
+    cpu_8080_push(self, self->regs.word.bc);
+    break;
+  case 0xc6:
+    cpu_8080_adi(self, cpu_8080_fetch_byte(self));
+    break;
+  case 0xc7:
+    cpu_8080_rst(self, 0);
+    break;
+  case 0xc8:
+    cpu_8080_rz(self);
+    break;
+  case 0xc9:
+    cpu_8080_ret(self);
+    break;
+  case 0xca:
+    cpu_8080_jz(self, cpu_8080_fetch_word(self));
+    break;
+  case 0xcb:
+    cpu_8080_jmp(self, cpu_8080_fetch_word(self));
+    break;
+  case 0xcc:
+    cpu_8080_cz(self, cpu_8080_fetch_word(self));
+    break;
+  case 0xcd:
+    cpu_8080_call(self, cpu_8080_fetch_word(self));
+    break;
+  case 0xce:
+    cpu_8080_aci(self, cpu_8080_fetch_byte(self));
+    break;
+  case 0xcf:
+    cpu_8080_rst(self, 1);
+    break;
+  case 0xd0:
+    cpu_8080_rnc(self);
+    break;
+  case 0xd1:
+    cpu_8080_pop(self, CPU_8080_EA_DE);
+    break;
+  case 0xd2:
+    cpu_8080_jnc(self, cpu_8080_fetch_word(self));
+    break;
+  case 0xd3:
+    cpu_8080_out(self, cpu_8080_fetch_byte(self));
+    break;
+  case 0xd4:
+    cpu_8080_cnc(self, cpu_8080_fetch_word(self));
+    break;
+  case 0xd5:
+    cpu_8080_push(self, self->regs.word.de);
+    break;
+  case 0xd6:
+    cpu_8080_sui(self, cpu_8080_fetch_byte(self));
+    break;
+  case 0xd7:
+    cpu_8080_rst(self, 2);
+    break;
+  case 0xd8:
+    cpu_8080_rc(self);
+    break;
+  case 0xd9:
+    cpu_8080_ret(self);
+    break;
+  case 0xda:
+    cpu_8080_jc(self, cpu_8080_fetch_word(self));
+    break;
+  case 0xdb:
+    cpu_8080_in(self, cpu_8080_fetch_byte(self));
+    break;
+  case 0xdc:
+    cpu_8080_cc(self, cpu_8080_fetch_word(self));
+    break;
+  case 0xdd:
+    cpu_8080_call(self, cpu_8080_fetch_word(self));
+    break;
+  case 0xde:
+    cpu_8080_sbi(self, cpu_8080_fetch_byte(self));
+    break;
+  case 0xdf:
+    cpu_8080_rst(self, 3);
+    break;
+  case 0xe0:
+    cpu_8080_rpo(self);
+    break;
+  case 0xe1:
+    cpu_8080_pop(self, CPU_8080_EA_HL);
+    break;
+  case 0xe2:
+    cpu_8080_jpo(self, cpu_8080_fetch_word(self));
+    break;
+  case 0xe3:
+    cpu_8080_xthl(self);
+    break;
+  case 0xe4:
+    cpu_8080_cpo(self, cpu_8080_fetch_word(self));
+    break;
+  case 0xe5:
+    cpu_8080_push(self, self->regs.word.hl);
+    break;
+  case 0xe6:
+    cpu_8080_ani(self, cpu_8080_fetch_byte(self));
+    break;
+  case 0xe7:
+    cpu_8080_rst(self, 4);
+    break;
+  case 0xe8:
+    cpu_8080_rpe(self);
+    break;
+  case 0xe9:
+    cpu_8080_pchl(self);
+    break;
+  case 0xea:
+    cpu_8080_jpe(self, cpu_8080_fetch_word(self));
+    break;
+  case 0xeb:
+    cpu_8080_xchg(self);
+    break;
+  case 0xec:
+    cpu_8080_cpe(self, cpu_8080_fetch_word(self));
+    break;
+  case 0xed:
+    cpu_8080_call(self, cpu_8080_fetch_word(self));
+    break;
+  case 0xee:
+    cpu_8080_xri(self, cpu_8080_fetch_byte(self));
+    break;
+  case 0xef:
+    cpu_8080_rst(self, 5);
+    break;
+  case 0xf0:
+    cpu_8080_rp(self);
+    break;
+  case 0xf1:
+    cpu_8080_pop(self, CPU_8080_EA_PSW);
+    break;
+  case 0xf2:
+    cpu_8080_jp(self, cpu_8080_fetch_word(self));
+    break;
+  case 0xf3:
+    cpu_8080_di(self);
+    break;
+  case 0xf4:
+    cpu_8080_cp(self, cpu_8080_fetch_word(self));
+    break;
+  case 0xf5:
+    cpu_8080_push(self, self->regs.word.psw);
+    break;
+  case 0xf6:
+    cpu_8080_ori(self, cpu_8080_fetch_byte(self));
+    break;
+  case 0xf7:
+    cpu_8080_rst(self, 6);
+    break;
+  case 0xf8:
+    cpu_8080_rm(self);
+    break;
+  case 0xf9:
+    cpu_8080_sphl(self);
+    break;
+  case 0xfa:
+    cpu_8080_jm(self, cpu_8080_fetch_word(self));
+    break;
+  case 0xfb:
+    cpu_8080_ei(self);
+    break;
+  case 0xfc:
+    cpu_8080_cm(self, cpu_8080_fetch_word(self));
+    break;
+  case 0xfd:
+    cpu_8080_call(self, cpu_8080_fetch_word(self));
+    break;
+  case 0xfe:
+    cpu_8080_cpi(self, cpu_8080_fetch_byte(self));
+    break;
+  case 0xff:
+    cpu_8080_rst(self, 7);
+    break;
+  }
+}
diff --git a/cpu_8080.h b/cpu_8080.h
new file mode 100644 (file)
index 0000000..01c4d8f
--- /dev/null
@@ -0,0 +1,991 @@
+#ifndef _CPU_8080_H
+#define _CPU_8080_H
+
+#include <endian.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+// gcc specific
+#ifndef ALWAYS_INLINE
+#define ALWAYS_INLINE __attribute__((always_inline))
+#endif
+
+#define CPU_8080_NMI_ADDR 0x66
+#define CPU_8080_RESET_ADDR 0
+#define CPU_8080_IRQ_ADDR 0x38
+
+// bits within REG_F
+#define CPU_8080_REG_F_BIT_C 0
+#define CPU_8080_REG_F_BIT_P 2
+#define CPU_8080_REG_F_BIT_AC 4
+#define CPU_8080_REG_F_BIT_Z 6
+#define CPU_8080_REG_F_BIT_S 7
+
+// special memory locations (negative address)
+// note: CPU_8080_EA_SINK is a byte-wide sink for implementing the "in f,(c)"
+// and DDCB/FDCB undocumented instructions, to store into and never read back
+#define CPU_8080_EA_PC (-0x1e)
+#define CPU_8080_EA_PSW (-0x1c)
+#define CPU_8080_EA_BC (-0x1a)
+#define CPU_8080_EA_DE (-0x18)
+#define CPU_8080_EA_HL (-0x16)
+#define CPU_8080_EA_SP (-0x14)
+#define CPU_8080_EA_IX (-0x12)
+#define CPU_8080_EA_IY (-0x10)
+#define CPU_8080_EA_AF_PRIME (-0xe)
+#define CPU_8080_EA_BC_PRIME (-0xc)
+#define CPU_8080_EA_DE_PRIME (-0xa)
+#define CPU_8080_EA_HL_PRIME (-8)
+#define CPU_8080_EA_IFLAGS (-6)
+#define CPU_8080_EA_IV (-4)
+
+// pc (-0x1e)
+#define CPU_8080_EA_F (-0x1c)
+#define CPU_8080_EA_A (-0x1b)
+#define CPU_8080_EA_C (-0x1a)
+#define CPU_8080_EA_B (-0x19)
+#define CPU_8080_EA_E (-0x18)
+#define CPU_8080_EA_D (-0x17)
+#define CPU_8080_EA_L (-0x16)
+#define CPU_8080_EA_H (-0x15)
+// sp (-0x14)
+#define CPU_8080_EA_IXL (-0x12)
+#define CPU_8080_EA_IXH (-0x11)
+#define CPU_8080_EA_IYL (-0x10)
+#define CPU_8080_EA_IYH (-0xf)
+// af_prime (-0xe)
+// bc_prime (-0xc)
+// de_prime (-0xa)
+// hl_prime (-8)
+// iflags (-6)
+#define CPU_8080_EA_V (-4)
+#define CPU_8080_EA_I (-3)
+#define CPU_8080_EA_R (-2)
+#define CPU_8080_EA_SINK (-1)
+
+// 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_8080_regs {
+#if __BYTE_ORDER == __BIG_ENDIAN
+  struct {
+    uint8_t _fill_sink;
+    uint8_t _fill_r;
+    uint16_t _fill_iv;
+    uint16_t _fill_iflags : 7;
+    uint16_t reti_flag : 1;
+    uint16_t ei_flag : 1;
+    uint16_t halt_flag : 1;
+    uint16_t nmi_pending : 1;
+    uint16_t irq_pending : 1;
+    uint16_t iff2 : 1;
+    uint16_t iff1 : 1;
+    uint16_t im : 2;
+    uint16_t _fill_hl_prime;
+    uint16_t _fill_de_prime;
+    uint16_t _fill_bc_prime;
+    uint16_t _fill_af_prime;
+    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 acf : 1;
+    uint8_t _fill_3f: 1;
+    uint8_t pf: 1;
+    uint8_t _fill_1f : 1;
+    uint8_t cf : 1;
+    uint16_t _fill_pc;
+  } bit;
+  struct {
+    uint8_t sink;
+    uint8_t r;
+    uint8_t i;
+    uint8_t v;
+    uint16_t _fill_iflags;
+    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;
+    uint16_t _fill_pc;
+  } byte;
+  struct {
+    uint8_t _fill_sink;
+    uint8_t _fill_r;
+    uint16_t iv;
+    uint16_t iflags;
+    uint16_t hl_prime;
+    uint16_t de_prime;
+    uint16_t bc_prime;
+    uint16_t af_prime;
+    uint16_t iy;
+    uint16_t ix;
+    uint16_t sp;
+    uint16_t hl;
+    uint16_t de;
+    uint16_t bc;
+    uint16_t psw;
+    uint16_t pc;
+  } word;
+  uint8_t mem_be[0x1e];
+#else
+  struct {
+    uint16_t _fill_pc;
+    uint8_t cf : 1;
+    uint8_t _fill_1f : 1;
+    uint8_t pf: 1;
+    uint8_t _fill_3f: 1;
+    uint8_t acf : 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;
+    uint16_t _fill_af_prime;
+    uint16_t _fill_bc_prime;
+    uint16_t _fill_de_prime;
+    uint16_t _fill_hl_prime;
+    uint16_t im : 2;
+    uint16_t iff1 : 1;
+    uint16_t iff2 : 1;
+    uint16_t irq_pending : 1;
+    uint16_t nmi_pending : 1;
+    uint16_t halt_flag : 1;
+    uint16_t ei_flag : 1;
+    uint16_t reti_flag : 1;
+    uint16_t _fill_iflags : 7;
+    uint16_t _fill_iv;
+    uint8_t _fill_r;
+    uint8_t _fill_sink;
+  } bit;
+  struct {
+    uint16_t _fill_pc;
+    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;
+    uint16_t _fill_af_prime;
+    uint16_t _fill_bc_prime;
+    uint16_t _fill_de_prime;
+    uint16_t _fill_hl_prime;
+    uint16_t _fill_iflags;
+    uint8_t v;
+    uint8_t i;
+    uint8_t r;
+    uint8_t sink;
+  } byte;
+  struct {
+    uint16_t pc;
+    uint16_t psw;
+    uint16_t bc;
+    uint16_t de;
+    uint16_t hl;
+    uint16_t sp;
+    uint16_t ix;
+    uint16_t iy;
+    uint16_t af_prime;
+    uint16_t bc_prime;
+    uint16_t de_prime;
+    uint16_t hl_prime;
+    uint16_t iflags;
+    uint16_t iv;
+    uint8_t _fill_r;
+    uint8_t _fill_sink;
+  } word;
+  uint8_t mem_le[0x1e];
+#endif
+};
+
+struct cpu_8080 {
+  int cycles;
+  int (*read_byte)(void *context, int addr);
+  void *read_byte_context;
+  void (*write_byte)(void *context, int addr, int data);
+  void *write_byte_context;
+  int (*in_byte)(void *context, int addr);
+  void *in_byte_context;
+  void (*out_byte)(void *context, int addr, int data);
+  void *out_byte_context;
+  union cpu_8080_regs regs;
+};
+
+// memory or special memory access
+static ALWAYS_INLINE int cpu_8080_read_byte(struct cpu_8080 *self, int addr) {
+  if (addr < 0)
+#if __BYTE_ORDER == __BIG_ENDIAN
+    return self->regs.mem_be[~addr];
+#else
+    return self->regs.mem_le[sizeof(union cpu_8080_regs) + addr];
+#endif
+  self->cycles += 1;
+  return self->read_byte(self->read_byte_context, addr & 0xffff);
+}
+
+static ALWAYS_INLINE int cpu_8080_read_word(struct cpu_8080 *self, int addr) {
+  int data = cpu_8080_read_byte(self, addr);
+  return data | (cpu_8080_read_byte(self, addr + 1) << 8);
+}
+
+static ALWAYS_INLINE void cpu_8080_write_byte(struct cpu_8080 *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[sizeof(union cpu_8080_regs) + addr] = data;
+#endif
+  else
+    self->write_byte(self->write_byte_context, addr, data);
+}
+
+static ALWAYS_INLINE void cpu_8080_write_word(struct cpu_8080 *self, int addr, int data) {
+  cpu_8080_write_byte(self, addr, data & 0xff);
+  cpu_8080_write_byte(self, addr + 1, data >> 8);
+}
+
+static ALWAYS_INLINE int cpu_8080_fetch_byte(struct cpu_8080 *self) {
+  int data = cpu_8080_read_byte(self, self->regs.word.pc++);
+  return data;
+}
+
+static ALWAYS_INLINE int cpu_8080_fetch_word(struct cpu_8080 *self) {
+  int data = cpu_8080_fetch_byte(self);
+  return data | (cpu_8080_fetch_byte(self) << 8);
+}
+
+static ALWAYS_INLINE void cpu_8080_push_word(struct cpu_8080 *self, int data) {
+  self->regs.word.sp -= 2;
+  cpu_8080_write_word(self, self->regs.word.sp, data);
+}
+
+static ALWAYS_INLINE int cpu_8080_pop_word(struct cpu_8080 *self) {
+  int data = cpu_8080_read_word(self, self->regs.word.sp);
+  self->regs.word.sp += 2;
+  return data;
+}
+
+static ALWAYS_INLINE int cpu_8080_in_byte(struct cpu_8080 *self, int addr) {
+  self->cycles += 1;
+  return self->in_byte(self->in_byte_context, addr);
+}
+
+static ALWAYS_INLINE void cpu_8080_out_byte(struct cpu_8080 *self, int addr, int data) {
+  self->cycles += 1;
+  self->out_byte(self->out_byte_context, addr, data);
+}
+
+// effective address calculation
+static ALWAYS_INLINE int cpu_8080_relative(struct cpu_8080 *self) {
+  return (int8_t)cpu_8080_fetch_byte(self);
+}
+
+static ALWAYS_INLINE int cpu_8080_displacement(struct cpu_8080 *self, int base) {
+  return (base + (int8_t)cpu_8080_fetch_byte(self)) & 0xffff;
+}
+
+// byte-addressed ports are extended to word using a as high 8 bits
+static ALWAYS_INLINE int cpu_8080_port_word(struct cpu_8080 *self) {
+  return cpu_8080_fetch_byte(self) | (self->regs.byte.a << 8);
+}
+
+// instruction execute
+static ALWAYS_INLINE void cpu_8080_aci(struct cpu_8080 *self, int rvalue) {
+  int result0 = (self->regs.byte.a & 0xf) + (rvalue & 0xf) + self->regs.bit.cf;
+  int result1 = result0 + (self->regs.byte.a & 0x70) + (rvalue & 0x70);
+  int result2 = result1 + (self->regs.byte.a & 0x80) + (rvalue & 0x80);
+
+  int parity = result2;
+  parity ^= parity >> 4;
+  parity ^= parity >> 2;
+  parity ^= parity >> 1;
+
+  self->regs.byte.a = result2 & 0xff;
+  self->regs.bit.cf = result2 >> 8;
+  self->regs.bit.pf = (parity & 1) == 0;
+  self->regs.bit.acf = result0 >> 4;
+  self->regs.bit.zf = (result2 & 0xff) == 0;
+  self->regs.bit.sf = (result2 >> 7) & 1;
+}
+
+static ALWAYS_INLINE void cpu_8080_adc(struct cpu_8080 *self, int rvalue) {
+  int result0 = (self->regs.byte.a & 0xf) + (rvalue & 0xf) + self->regs.bit.cf;
+  int result1 = result0 + (self->regs.byte.a & 0x70) + (rvalue & 0x70);
+  int result2 = result1 + (self->regs.byte.a & 0x80) + (rvalue & 0x80);
+
+  int parity = result2;
+  parity ^= parity >> 4;
+  parity ^= parity >> 2;
+  parity ^= parity >> 1;
+
+  self->regs.byte.a = result2 & 0xff;
+  self->regs.bit.cf = result2 >> 8;
+  self->regs.bit.pf = (parity & 1) == 0;
+  self->regs.bit.acf = result0 >> 4;
+  self->regs.bit.zf = (result2 & 0xff) == 0;
+  self->regs.bit.sf = (result2 >> 7) & 1;
+}
+
+static ALWAYS_INLINE void cpu_8080_add(struct cpu_8080 *self, int rvalue) {
+  int result0 = (self->regs.byte.a & 0xf) + (rvalue & 0xf);
+  int result1 = result0 + (self->regs.byte.a & 0x70) + (rvalue & 0x70);
+  int result2 = result1 + (self->regs.byte.a & 0x80) + (rvalue & 0x80);
+
+  int parity = result2;
+  parity ^= parity >> 4;
+  parity ^= parity >> 2;
+  parity ^= parity >> 1;
+
+  self->regs.byte.a = result2 & 0xff;
+  self->regs.bit.cf = result2 >> 8;
+  self->regs.bit.pf = (parity & 1) == 0;
+  self->regs.bit.acf = result0 >> 4;
+  self->regs.bit.zf = (result2 & 0xff) == 0;
+  self->regs.bit.sf = (result2 >> 7) & 1;
+}
+
+static ALWAYS_INLINE void cpu_8080_adi(struct cpu_8080 *self, int rvalue) {
+  int result0 = (self->regs.byte.a & 0xf) + (rvalue & 0xf);
+  int result1 = result0 + (self->regs.byte.a & 0x70) + (rvalue & 0x70);
+  int result2 = result1 + (self->regs.byte.a & 0x80) + (rvalue & 0x80);
+
+  int parity = result2;
+  parity ^= parity >> 4;
+  parity ^= parity >> 2;
+  parity ^= parity >> 1;
+
+  self->regs.byte.a = result2 & 0xff;
+  self->regs.bit.cf = result2 >> 8;
+  self->regs.bit.pf = (parity & 1) == 0;
+  self->regs.bit.acf = result0 >> 4;
+  self->regs.bit.zf = (result2 & 0xff) == 0;
+  self->regs.bit.sf = (result2 >> 7) & 1;
+}
+
+static ALWAYS_INLINE void cpu_8080_ana(struct cpu_8080 *self, int rvalue) {
+  int result = self->regs.byte.a & rvalue;
+
+  int parity = result;
+  parity ^= parity >> 4;
+  parity ^= parity >> 2;
+  parity ^= parity >> 1;
+
+  self->regs.byte.a = result;
+  self->regs.bit.cf = false;
+  self->regs.bit.pf = (parity & 1) == 0;
+  self->regs.bit.acf = true;
+  self->regs.bit.zf = result == 0;
+  self->regs.bit.sf = result >> 7;
+}
+
+static ALWAYS_INLINE void cpu_8080_ani(struct cpu_8080 *self, int rvalue) {
+  int result = self->regs.byte.a & rvalue;
+
+  int parity = result;
+  parity ^= parity >> 4;
+  parity ^= parity >> 2;
+  parity ^= parity >> 1;
+
+  self->regs.byte.a = result;
+  self->regs.bit.cf = false;
+  self->regs.bit.pf = (parity & 1) == 0;
+  self->regs.bit.acf = true;
+  self->regs.bit.zf = result == 0;
+  self->regs.bit.sf = result >> 7;
+}
+
+static ALWAYS_INLINE void cpu_8080_call(struct cpu_8080 *self, int rvalue) {
+  cpu_8080_push_word(self, self->regs.word.pc);
+  self->regs.word.pc = rvalue;
+}
+
+static ALWAYS_INLINE void cpu_8080_cc(struct cpu_8080 *self, int rvalue) {
+  if (self->regs.bit.cf) {
+    cpu_8080_push_word(self, self->regs.word.pc);
+    self->regs.word.pc = rvalue;
+  }
+}
+
+static ALWAYS_INLINE void cpu_8080_cm(struct cpu_8080 *self, int rvalue) {
+  if (self->regs.bit.sf) {
+    cpu_8080_push_word(self, self->regs.word.pc);
+    self->regs.word.pc = rvalue;
+  }
+}
+
+static ALWAYS_INLINE void cpu_8080_cma(struct cpu_8080 *self) {
+  self->regs.byte.a = ~self->regs.byte.a;
+  self->regs.bit.acf = true;
+}
+
+static ALWAYS_INLINE void cpu_8080_cmc(struct cpu_8080 *self) {
+  self->regs.bit.acf = self->regs.bit.cf;
+  self->regs.bit.cf = !self->regs.bit.cf;
+}
+
+static ALWAYS_INLINE void cpu_8080_cmp(struct cpu_8080 *self, int rvalue) {
+  int result0 = (self->regs.byte.a & 0xf) - (rvalue & 0xf);
+  int result1 = result0 + (self->regs.byte.a & 0x70) - (rvalue & 0x70);
+  int result2 = result1 + (self->regs.byte.a & 0x80) - (rvalue & 0x80);
+
+  int parity = result2;
+  parity ^= parity >> 4;
+  parity ^= parity >> 2;
+  parity ^= parity >> 1;
+
+  self->regs.bit.cf = (result2 >> 8) & 1;
+  self->regs.bit.pf = (parity & 1) == 0;
+  self->regs.bit.acf = (result0 >> 4) & 1;
+  self->regs.bit.zf = (result2 & 0xff) == 0;
+  self->regs.bit.sf = (result2 >> 7) & 1;
+}
+
+static ALWAYS_INLINE void cpu_8080_cnc(struct cpu_8080 *self, int rvalue) {
+  if (!self->regs.bit.cf) {
+    cpu_8080_push_word(self, self->regs.word.pc);
+    self->regs.word.pc = rvalue;
+  }
+}
+
+static ALWAYS_INLINE void cpu_8080_cnz(struct cpu_8080 *self, int rvalue) {
+  if (!self->regs.bit.zf) {
+    cpu_8080_push_word(self, self->regs.word.pc);
+    self->regs.word.pc = rvalue;
+  }
+}
+
+static ALWAYS_INLINE void cpu_8080_cp(struct cpu_8080 *self, int rvalue) {
+  if (!self->regs.bit.sf) {
+    cpu_8080_push_word(self, self->regs.word.pc);
+    self->regs.word.pc = rvalue;
+  }
+}
+
+static ALWAYS_INLINE void cpu_8080_cpe(struct cpu_8080 *self, int rvalue) {
+  if (self->regs.bit.pf) {
+    cpu_8080_push_word(self, self->regs.word.pc);
+    self->regs.word.pc = rvalue;
+  }
+}
+
+static ALWAYS_INLINE void cpu_8080_cpi(struct cpu_8080 *self, int rvalue) {
+  int result0 = (self->regs.byte.a & 0xf) - (rvalue & 0xf);
+  int result1 = result0 + (self->regs.byte.a & 0x70) - (rvalue & 0x70);
+  int result2 = result1 + (self->regs.byte.a & 0x80) - (rvalue & 0x80);
+
+  int parity = result2;
+  parity ^= parity >> 4;
+  parity ^= parity >> 2;
+  parity ^= parity >> 1;
+
+  self->regs.bit.cf = (result2 >> 8) & 1;
+  self->regs.bit.pf = (parity & 1) == 0;
+  self->regs.bit.acf = (result0 >> 4) & 1;
+  self->regs.bit.zf = (result2 & 0xff) == 0;
+  self->regs.bit.sf = (result2 >> 7) & 1;
+}
+
+static ALWAYS_INLINE void cpu_8080_cpo(struct cpu_8080 *self, int rvalue) {
+  if (!self->regs.bit.pf) {
+    cpu_8080_push_word(self, self->regs.word.pc);
+    self->regs.word.pc = rvalue;
+  }
+}
+
+static ALWAYS_INLINE void cpu_8080_cz(struct cpu_8080 *self, int rvalue) {
+  if (self->regs.bit.zf) {
+    cpu_8080_push_word(self, self->regs.word.pc);
+    self->regs.word.pc = rvalue;
+  }
+}
+
+static ALWAYS_INLINE void cpu_8080_daa(struct cpu_8080 *self) {
+  int correction = 0;
+  if (self->regs.bit.acf || (self->regs.byte.a & 0xf) >= 0xa)
+    correction = 6;
+  if (self->regs.bit.cf || self->regs.byte.a >= 0x9a) {
+    correction |= 0x60;
+    self->regs.bit.cf = true;
+  }
+
+  int result0 = (self->regs.byte.a & 0xf) + (correction & 0xf);
+  int result1 = result0 + (self->regs.byte.a & 0xf0) + (correction & 0xf0);
+
+  int parity = result1;
+  parity ^= parity >> 4;
+  parity ^= parity >> 2;
+  parity ^= parity >> 1;
+
+  self->regs.byte.a = result1 & 0xff;
+  //self->regs.bit.cf |= (result1 >> 8) & 1;
+  self->regs.bit.pf = (parity & 1) == 0;
+  self->regs.bit.acf = (result0 >> 4) & 1;
+  self->regs.bit.zf = (result1 & 0xff) == 0;
+  self->regs.bit.sf = (result1 >> 7) & 1;
+}
+
+static ALWAYS_INLINE void cpu_8080_dad(struct cpu_8080 *self, int rvalue) {
+  int result0 = (self->regs.word.hl & 0xfff) + (rvalue & 0xfff);
+  int result1 = result0 + (self->regs.word.hl & 0xf000) + (rvalue & 0xf000);
+
+  self->regs.word.hl = result1 & 0xffff;
+  self->regs.bit.cf = result1 >> 16;
+  self->regs.bit.acf = result0 >> 12;
+}
+
+static ALWAYS_INLINE void cpu_8080_dcr(struct cpu_8080 *self, int lvalue) {
+  int data = cpu_8080_read_byte(self, lvalue);
+
+  int result0 = (data & 0xf) - 1;
+  int result1 = result0 + (data & 0x70);
+  int result2 = result1 + (data & 0x80);
+
+  int parity = result2;
+  parity ^= parity >> 4;
+  parity ^= parity >> 2;
+  parity ^= parity >> 1;
+
+  cpu_8080_write_byte(self, lvalue, result2 & 0xff);
+  self->regs.bit.pf = (parity & 1) == 0;
+  self->regs.bit.acf = (result0 >> 4) & 1;
+  self->regs.bit.zf = (result2 & 0xff) == 0;
+  self->regs.bit.sf = (result2 >> 7) & 1;
+}
+
+static ALWAYS_INLINE void cpu_8080_dcx(struct cpu_8080 *self, int lvalue) {
+  int result = (cpu_8080_read_word(self, lvalue) - 1) & 0xffff;
+  cpu_8080_write_word(self, lvalue, result);
+}
+
+static ALWAYS_INLINE void cpu_8080_di(struct cpu_8080 *self) {
+  self->regs.bit.iff1 = false;
+  self->regs.bit.iff2 = false;
+}
+
+static ALWAYS_INLINE void cpu_8080_ei(struct cpu_8080 *self) {
+  self->regs.bit.iff1 = true;
+  self->regs.bit.iff2 = true;
+  self->regs.bit.ei_flag = true; // causes one-cycle interrupt delay
+}
+
+static ALWAYS_INLINE void cpu_8080_hlt(struct cpu_8080 *self) {
+  self->regs.bit.halt_flag = true;
+  --self->regs.word.pc;
+}
+
+static ALWAYS_INLINE void cpu_8080_in(struct cpu_8080 *self, int rvalue) {
+  self->regs.byte.a = cpu_8080_in_byte(self, rvalue);
+}
+
+static ALWAYS_INLINE void cpu_8080_inr(struct cpu_8080 *self, int lvalue) {
+  int data = cpu_8080_read_byte(self, lvalue);
+
+  int result0 = (data & 0xf) + 1;
+  int result1 = result0 + (data & 0x70);
+  int result2 = result1 + (data & 0x80);
+
+  int parity = result2;
+  parity ^= parity >> 4;
+  parity ^= parity >> 2;
+  parity ^= parity >> 1;
+
+  cpu_8080_write_byte(self, lvalue, result2 & 0xff);
+  self->regs.bit.pf = (parity & 1) == 0;
+  self->regs.bit.acf = result0 >> 4;
+  self->regs.bit.zf = (result2 & 0xff) == 0;
+  self->regs.bit.sf = (result2 >> 7) & 1;
+}
+
+static ALWAYS_INLINE void cpu_8080_inx(struct cpu_8080 *self, int lvalue) {
+  int result = (cpu_8080_read_word(self, lvalue) + 1) & 0xffff;
+  cpu_8080_write_word(self, lvalue, result);
+}
+
+static ALWAYS_INLINE void cpu_8080_jc(struct cpu_8080 *self, int rvalue) {
+  if (self->regs.bit.cf)
+    self->regs.word.pc = rvalue;
+}
+
+static ALWAYS_INLINE void cpu_8080_jm(struct cpu_8080 *self, int rvalue) {
+  if (self->regs.bit.sf)
+    self->regs.word.pc = rvalue;
+}
+
+
+static ALWAYS_INLINE void cpu_8080_jmp(struct cpu_8080 *self, int rvalue) {
+  self->regs.word.pc = rvalue;
+}
+
+static ALWAYS_INLINE void cpu_8080_jnc(struct cpu_8080 *self, int rvalue) {
+  if (!self->regs.bit.cf)
+    self->regs.word.pc = rvalue;
+}
+
+static ALWAYS_INLINE void cpu_8080_jnz(struct cpu_8080 *self, int rvalue) {
+  if (!self->regs.bit.zf)
+    self->regs.word.pc = rvalue;
+}
+
+static ALWAYS_INLINE void cpu_8080_jp(struct cpu_8080 *self, int rvalue) {
+  if (!self->regs.bit.sf)
+    self->regs.word.pc = rvalue;
+}
+
+static ALWAYS_INLINE void cpu_8080_jpe(struct cpu_8080 *self, int rvalue) {
+  if (self->regs.bit.pf)
+    self->regs.word.pc = rvalue;
+}
+
+static ALWAYS_INLINE void cpu_8080_jpo(struct cpu_8080 *self, int rvalue) {
+  if (!self->regs.bit.pf)
+    self->regs.word.pc = rvalue;
+}
+
+static ALWAYS_INLINE void cpu_8080_jz(struct cpu_8080 *self, int rvalue) {
+  if (self->regs.bit.zf)
+    self->regs.word.pc = rvalue;
+}
+
+static ALWAYS_INLINE void cpu_8080_lda(struct cpu_8080 *self, int rvalue) {
+  self->regs.byte.a = cpu_8080_read_byte(self, rvalue);
+}
+
+static ALWAYS_INLINE void cpu_8080_ldax(struct cpu_8080 *self, int rvalue) {
+  self->regs.byte.a = cpu_8080_read_byte(self, rvalue);
+}
+
+static ALWAYS_INLINE void cpu_8080_lhld(struct cpu_8080 *self, int rvalue) {
+  self->regs.word.hl = cpu_8080_read_word(self, rvalue);
+}
+
+static ALWAYS_INLINE void cpu_8080_lxi(struct cpu_8080 *self, int lvalue, int rvalue) {
+  cpu_8080_write_word(self, lvalue, rvalue);
+}
+
+static ALWAYS_INLINE void cpu_8080_mov(struct cpu_8080 *self, int lvalue, int rvalue) {
+  cpu_8080_write_byte(self, lvalue, rvalue);
+}
+
+static ALWAYS_INLINE void cpu_8080_mvi(struct cpu_8080 *self, int lvalue, int rvalue) {
+  cpu_8080_write_byte(self, lvalue, rvalue);
+}
+
+static ALWAYS_INLINE void cpu_8080_nop(struct cpu_8080 *self) {
+}
+
+static ALWAYS_INLINE void cpu_8080_ora(struct cpu_8080 *self, int rvalue) {
+  int result = self->regs.byte.a | rvalue;
+
+  int parity = result;
+  parity ^= parity >> 4;
+  parity ^= parity >> 2;
+  parity ^= parity >> 1;
+
+  self->regs.byte.a = result;
+  self->regs.bit.cf = false;
+  self->regs.bit.pf = (parity & 1) == 0;
+  self->regs.bit.acf = false;
+  self->regs.bit.zf = result == 0;
+  self->regs.bit.sf = result >> 7;
+}
+
+static ALWAYS_INLINE void cpu_8080_ori(struct cpu_8080 *self, int rvalue) {
+  int result = self->regs.byte.a | rvalue;
+
+  int parity = result;
+  parity ^= parity >> 4;
+  parity ^= parity >> 2;
+  parity ^= parity >> 1;
+
+  self->regs.byte.a = result;
+  self->regs.bit.cf = false;
+  self->regs.bit.pf = (parity & 1) == 0;
+  self->regs.bit.acf = false;
+  self->regs.bit.zf = result == 0;
+  self->regs.bit.sf = result >> 7;
+}
+
+static ALWAYS_INLINE void cpu_8080_out(struct cpu_8080 *self, int rvalue) {
+  cpu_8080_out_byte(self, rvalue, self->regs.byte.a);
+}
+
+static ALWAYS_INLINE void cpu_8080_pchl(struct cpu_8080 *self) {
+  self->regs.word.pc = self->regs.word.hl;
+}
+
+static ALWAYS_INLINE void cpu_8080_pop(struct cpu_8080 *self, int lvalue) {
+  cpu_8080_write_word(self, lvalue, cpu_8080_pop_word(self));
+}
+
+static ALWAYS_INLINE void cpu_8080_push(struct cpu_8080 *self, int rvalue) {
+  cpu_8080_push_word(self, rvalue);
+}
+
+static ALWAYS_INLINE void cpu_8080_ral(struct cpu_8080 *self) {
+  int result = self->regs.bit.cf | (self->regs.byte.a << 1);
+
+  self->regs.byte.a = result & 0xff;
+  self->regs.bit.cf = result >> 8;
+  self->regs.bit.acf = false;
+}
+
+static ALWAYS_INLINE void cpu_8080_rar(struct cpu_8080 *self) {
+  int result = self->regs.byte.a | (self->regs.bit.cf << 8);
+
+  self->regs.byte.a = result >> 1;
+  self->regs.bit.cf = result & 1;
+  self->regs.bit.acf = false;
+}
+
+static ALWAYS_INLINE void cpu_8080_rc(struct cpu_8080 *self) {
+  if (self->regs.bit.cf)
+    self->regs.word.pc = cpu_8080_pop_word(self);
+}
+
+static ALWAYS_INLINE void cpu_8080_ret(struct cpu_8080 *self) {
+  self->regs.word.pc = cpu_8080_pop_word(self);
+}
+
+static ALWAYS_INLINE void cpu_8080_rlc(struct cpu_8080 *self) {
+  int result = (self->regs.byte.a >> 7) | (self->regs.byte.a << 1);
+
+  self->regs.byte.a = result & 0xff;
+  self->regs.bit.cf = result >> 8;
+  self->regs.bit.acf = false;
+}
+
+static ALWAYS_INLINE void cpu_8080_rm(struct cpu_8080 *self) {
+  if (self->regs.bit.sf)
+    self->regs.word.pc = cpu_8080_pop_word(self);
+}
+
+static ALWAYS_INLINE void cpu_8080_rnc(struct cpu_8080 *self) {
+  if (!self->regs.bit.cf)
+    self->regs.word.pc = cpu_8080_pop_word(self);
+}
+
+static ALWAYS_INLINE void cpu_8080_rnz(struct cpu_8080 *self) {
+  if (!self->regs.bit.zf)
+    self->regs.word.pc = cpu_8080_pop_word(self);
+}
+
+static ALWAYS_INLINE void cpu_8080_rp(struct cpu_8080 *self) {
+  if (!self->regs.bit.sf)
+    self->regs.word.pc = cpu_8080_pop_word(self);
+}
+
+static ALWAYS_INLINE void cpu_8080_rpe(struct cpu_8080 *self) {
+  if (self->regs.bit.pf)
+    self->regs.word.pc = cpu_8080_pop_word(self);
+}
+
+static ALWAYS_INLINE void cpu_8080_rpo(struct cpu_8080 *self) {
+  if (!self->regs.bit.pf)
+    self->regs.word.pc = cpu_8080_pop_word(self);
+}
+
+static ALWAYS_INLINE void cpu_8080_rrc(struct cpu_8080 *self) {
+  int result = self->regs.byte.a | ((self->regs.byte.a & 1) << 8);
+
+  self->regs.byte.a = result >> 1;
+  self->regs.bit.cf = result & 1;
+  self->regs.bit.acf = false;
+}
+
+static ALWAYS_INLINE void cpu_8080_rst(struct cpu_8080 *self, int rvalue) {
+  cpu_8080_push_word(self, self->regs.word.pc);
+  self->regs.word.pc = rvalue << 3;
+}
+
+static ALWAYS_INLINE void cpu_8080_rz(struct cpu_8080 *self) {
+  if (self->regs.bit.zf)
+    self->regs.word.pc = cpu_8080_pop_word(self);
+}
+
+static ALWAYS_INLINE void cpu_8080_sbb(struct cpu_8080 *self, int rvalue) {
+  int result0 = (self->regs.byte.a & 0xf) - (rvalue & 0xf) - self->regs.bit.cf;
+  int result1 = result0 + (self->regs.byte.a & 0x70) - (rvalue & 0x70);
+  int result2 = result1 + (self->regs.byte.a & 0x80) - (rvalue & 0x80);
+
+  int parity = result2;
+  parity ^= parity >> 4;
+  parity ^= parity >> 2;
+  parity ^= parity >> 1;
+
+  self->regs.byte.a = result2 & 0xff;
+  self->regs.bit.cf = (result2 >> 8) & 1;
+  self->regs.bit.pf = (parity & 1) == 0;
+  self->regs.bit.acf = (result0 >> 4) & 1;
+  self->regs.bit.zf = (result2 & 0xff) == 0;
+  self->regs.bit.sf = (result2 >> 7) & 1;
+}
+
+static ALWAYS_INLINE void cpu_8080_sbi(struct cpu_8080 *self, int rvalue) {
+  int result0 = (self->regs.byte.a & 0xf) - (rvalue & 0xf) - self->regs.bit.cf;
+  int result1 = result0 + (self->regs.byte.a & 0x70) - (rvalue & 0x70);
+  int result2 = result1 + (self->regs.byte.a & 0x80) - (rvalue & 0x80);
+
+  int parity = result2;
+  parity ^= parity >> 4;
+  parity ^= parity >> 2;
+  parity ^= parity >> 1;
+
+  self->regs.byte.a = result2 & 0xff;
+  self->regs.bit.cf = (result2 >> 8) & 1;
+  self->regs.bit.pf = (parity & 1) == 0;
+  self->regs.bit.acf = (result0 >> 4) & 1;
+  self->regs.bit.zf = (result2 & 0xff) == 0;
+  self->regs.bit.sf = (result2 >> 7) & 1;
+}
+
+static ALWAYS_INLINE void cpu_8080_shld(struct cpu_8080 *self, int rvalue) {
+  cpu_8080_write_word(self, rvalue, self->regs.word.hl);
+}
+
+static ALWAYS_INLINE void cpu_8080_sphl(struct cpu_8080 *self) {
+  self->regs.word.sp = self->regs.word.hl;
+}
+
+static ALWAYS_INLINE void cpu_8080_sta(struct cpu_8080 *self, int rvalue) {
+  cpu_8080_write_byte(self, rvalue, self->regs.byte.a);
+}
+
+static ALWAYS_INLINE void cpu_8080_stax(struct cpu_8080 *self, int rvalue) {
+  cpu_8080_write_byte(self, rvalue, self->regs.byte.a);
+}
+
+static ALWAYS_INLINE void cpu_8080_stc(struct cpu_8080 *self) {
+  self->regs.bit.cf = true;
+  self->regs.bit.acf = false;
+}
+
+static ALWAYS_INLINE void cpu_8080_sub(struct cpu_8080 *self, int rvalue) {
+  int result0 = (self->regs.byte.a & 0xf) - (rvalue & 0xf);
+  int result1 = result0 + (self->regs.byte.a & 0x70) - (rvalue & 0x70);
+  int result2 = result1 + (self->regs.byte.a & 0x80) - (rvalue & 0x80);
+
+  int parity = result2;
+  parity ^= parity >> 4;
+  parity ^= parity >> 2;
+  parity ^= parity >> 1;
+
+  self->regs.byte.a = result2 & 0xff;
+  self->regs.bit.cf = (result2 >> 8) & 1;
+  self->regs.bit.pf = (parity & 1) == 0;
+  self->regs.bit.acf = (result0 >> 4) & 1;
+  self->regs.bit.zf = (result2 & 0xff) == 0;
+  self->regs.bit.sf = (result2 >> 7) & 1;
+}
+
+static ALWAYS_INLINE void cpu_8080_sui(struct cpu_8080 *self, int rvalue) {
+  int result0 = (self->regs.byte.a & 0xf) - (rvalue & 0xf);
+  int result1 = result0 + (self->regs.byte.a & 0x70) - (rvalue & 0x70);
+  int result2 = result1 + (self->regs.byte.a & 0x80) - (rvalue & 0x80);
+
+  int parity = result2;
+  parity ^= parity >> 4;
+  parity ^= parity >> 2;
+  parity ^= parity >> 1;
+
+  self->regs.byte.a = result2 & 0xff;
+  self->regs.bit.cf = (result2 >> 8) & 1;
+  self->regs.bit.pf = (parity & 1) == 0;
+  self->regs.bit.acf = (result0 >> 4) & 1;
+  self->regs.bit.zf = (result2 & 0xff) == 0;
+  self->regs.bit.sf = (result2 >> 7) & 1;
+}
+
+static ALWAYS_INLINE void cpu_8080_xchg(struct cpu_8080 *self) {
+  int data = self->regs.word.hl;
+  self->regs.word.hl = self->regs.word.de;
+  self->regs.word.de = data;
+}
+
+static ALWAYS_INLINE void cpu_8080_xra(struct cpu_8080 *self, int rvalue) {
+  int result = self->regs.byte.a ^ rvalue;
+
+  int parity = result;
+  parity ^= parity >> 4;
+  parity ^= parity >> 2;
+  parity ^= parity >> 1;
+
+  self->regs.byte.a = result;
+  self->regs.bit.cf = false;
+  self->regs.bit.pf = (parity & 1) == 0;
+  self->regs.bit.acf = false;
+  self->regs.bit.zf = result == 0;
+  self->regs.bit.sf = result >> 7;
+}
+
+static ALWAYS_INLINE void cpu_8080_xri(struct cpu_8080 *self, int rvalue) {
+  int result = self->regs.byte.a ^ rvalue;
+
+  int parity = result;
+  parity ^= parity >> 4;
+  parity ^= parity >> 2;
+  parity ^= parity >> 1;
+
+  self->regs.byte.a = result;
+  self->regs.bit.cf = false;
+  self->regs.bit.pf = (parity & 1) == 0;
+  self->regs.bit.acf = false;
+  self->regs.bit.zf = result == 0;
+  self->regs.bit.sf = result >> 7;
+}
+
+static ALWAYS_INLINE void cpu_8080_xthl(struct cpu_8080 *self) {
+  int data = cpu_8080_read_word(self, self->regs.word.sp);
+  cpu_8080_write_word(self, self->regs.word.sp, self->regs.word.hl);
+  self->regs.word.hl = data;
+}
+
+// prototypes
+void cpu_8080_init(
+  struct cpu_8080 *self,
+  int (*read_byte)(void *context, int addr),
+  void *read_byte_context,
+  void (*write_byte)(void *context, int addr, int data),
+  void *write_byte_context,
+  int (*in_byte)(void *context, int addr),
+  void *in_byte_context,
+  void (*out_byte)(void *context, int addr, int data),
+  void *out_byte_context
+);
+void cpu_8080_reset(struct cpu_8080 *self);
+void cpu_8080_execute(struct cpu_8080 *self);
+
+#endif
index e312c74..67f1763 100644 (file)
--- a/cpu_z80.c
+++ b/cpu_z80.c
@@ -5506,4 +5506,3 @@ void cpu_z80_execute_fd_cb(struct cpu_z80 *self) {
     break;
   }
 }
-
diff --git a/decode_8080.py b/decode_8080.py
new file mode 100755 (executable)
index 0000000..4475cdc
--- /dev/null
@@ -0,0 +1,179 @@
+#!/usr/bin/env python3
+
+import sys
+
+# end-relative range of operands that are rvalues
+rvalue_opcodes = {
+  'aci': -1,
+  'adc': -1,
+  'add': -1,
+  'adi': -1,
+  'ana': -1,
+  'ani': -1,
+  'call': -1,
+  'cc': -1,
+  'cm': -1,
+  'cmp': -1,
+  'cnc': -1,
+  'cnz': -1,
+  'cp': -1,
+  'cpe': -1,
+  'cpi': -1,
+  'cpo': -1,
+  'cz': -1,
+  'dad': -1,
+  'in': -1,
+  'jc': -1,
+  'jm': -1,
+  'jmp': -1,
+  'jnc': -1,
+  'jnz': -1,
+  'jp': -1,
+  'jpe': -1,
+  'jpo': -1,
+  'jz': -1,
+  'lda': -1,
+  'ldax': -1,
+  'lhld': -1,
+  'lxi': -1,
+  'mov': -1,
+  'mvi': -1,
+  'ora': -1,
+  'ori': -1,
+  'out': -1,
+  'push': -1,
+  'rst': -1,
+  'sbb': -1,
+  'sbi': -1,
+  'shld': -1,
+  'sta': -1,
+  'stax': -1,
+  'sub': -1,
+  'sui': -1,
+  'xra': -1,
+  'xri': -1,
+}
+
+byte_opcodes = {
+  'aci',
+  'adc',
+  'add',
+  'adi',
+  'ana',
+  'ani',
+  'cmp',
+  'cpi',
+  'dcr',
+  'in',
+  'inr',
+  'mov',
+  'mvi',
+  'ora',
+  'ori',
+  'out',
+  'rst',
+  'sbb',
+  'sbi',
+  'sub',
+  'sui',
+  'xra',
+  'xri',
+}
+byte_rvalue_modes = {
+  '0x12': 'cpu_8080_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',
+}
+byte_lvalue_modes = {
+  'a': 'CPU_8080_EA_A',
+  'b': 'CPU_8080_EA_B',
+  'c': 'CPU_8080_EA_C',
+  'd': 'CPU_8080_EA_D',
+  'e': 'CPU_8080_EA_E',
+  'l': 'CPU_8080_EA_L',
+  'h': 'CPU_8080_EA_H',
+  'm': 'self->regs.word.hl',
+}
+
+word_rvalue_modes = {
+  '0x3412': 'cpu_8080_fetch_word(self)',
+  'psw': 'self->regs.word.psw',
+  'b': 'self->regs.word.bc',
+  'd': 'self->regs.word.de',
+  'h': 'self->regs.word.hl',
+  'sp': 'self->regs.word.sp',
+}
+word_lvalue_modes = {
+  'psw': 'CPU_8080_EA_PSW',
+  'b': 'CPU_8080_EA_BC',
+  'd': 'CPU_8080_EA_DE',
+  'h': 'CPU_8080_EA_HL',
+  'sp': 'CPU_8080_EA_SP',
+}
+
+print('void cpu_8080_execute(struct cpu_8080 *self) {')
+print('  switch (cpu_8080_fetch_byte(self)) {')
+
+for i in range(0x100):
+  line = sys.stdin.readline().strip()
+
+  print(f'  case 0x{i:02x}:')
+  instr = line.split()
+  if len(instr) == 0:
+    instr = ['ill']
+  elif len(instr) >= 2:
+    instr[1:] = [l for j in instr[1:] for l in j.split(',')]
+  #print('xxx', instr)
+
+  # work out which operands are rvalue
+  j = len(instr) + rvalue_opcodes.get(instr[0], 0)
+
+  # translate operands
+  if instr[0] in byte_opcodes:
+    # operands [1, j) are lvalue
+    for l in range(1, j):
+      if instr[l] in byte_lvalue_modes:
+        instr[l] = byte_lvalue_modes[instr[l]]
+
+    # operands [j, n) are rvalue
+    for l in range(j, len(instr)):
+      if instr[l] in byte_rvalue_modes:
+        instr[l] = byte_rvalue_modes[instr[l]]
+      elif instr[l] in byte_lvalue_modes:
+        instr[l] = 'cpu_8080_read_byte(self, {0:s})'.format(
+          byte_lvalue_modes[instr[l]]
+        )
+  else:
+    # operands [1, j] are lvalue
+    for l in range(1, j):
+      if instr[l] in word_lvalue_modes:
+        instr[l] = word_lvalue_modes[instr[l]]
+
+    # operands [j, n) are rvalue
+    for l in range(j, len(instr)):
+      if instr[l] in word_rvalue_modes:
+        instr[l] = word_rvalue_modes[instr[l]]
+      elif instr[l] in word_lvalue_modes:
+        instr[l] = 'cpu_8080_read_word(self, {0:s})'.format(
+          word_lvalue_modes[instr[l]]
+        )
+
+  print(
+    '    cpu_8080_{0:s}(self{1:s});'.format(
+      instr[0],
+      ''.join([', ' + j for j in instr[1:]])
+    )
+  )
+  print('    break;')
+
+line = sys.stdin.readline().strip()
+assert len(line) == 0
+
+print('  }')
+print('}')
+print()
diff --git a/doc/Optable.pdf b/doc/Optable.pdf
new file mode 100644 (file)
index 0000000..04695ed
Binary files /dev/null and b/doc/Optable.pdf differ
diff --git a/emu_8080.c b/emu_8080.c
new file mode 100644 (file)
index 0000000..b718006
--- /dev/null
@@ -0,0 +1,541 @@
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#if ALT_BACKEND
+#include "intel-8080/cpu.h"
+#else
+#include "cpu_8080.h"
+#endif
+
+#define REG_TRACE 0
+#define MEM_TRACE 0
+#define IO_TRACE 0
+
+#define BDOS 0xffbb            // ret
+#define JMP_BOOT 0xffbc                // -3: Cold start routine
+#define JMP_WBOOT 0xffbf       //  0: Warm boot - reload command processor
+#define JMP_CONST 0xffc2       //  3: Console status
+#define JMP_CONIN 0xffc5       //  6: Console input
+#define JMP_CONOUT 0xffc8      //  9: Console output
+#define JMP_LIST 0xffcb                // 12: Printer output
+#define JMP_PUNCH 0xffce       // 15: Paper tape punch output
+#define JMP_READER 0xffd1      // 18: Paper tape reader input
+#define JMP_HOME 0xffd4                // 21: Move disc head to track 0
+#define JMP_SELDSK 0xffd7      // 24: Select disc drive
+#define JMP_SETTRK 0xffda      // 27: Set track number
+#define JMP_SETSEC 0xffdd      // 30: Set sector number
+#define JMP_SETDMA 0xffe0      // 33: Set DMA address
+#define JMP_READ 0xffe3                // 36: Read a sector
+#define JMP_WRITE 0xffe6       // 39: Write a sector
+#define JMP_LISTST 0xffe9      // 42: Status of list device
+#define JMP_SECTRAN 0xffec     // 45: Sector translation for skewing
+#define BIOS_BOOT 0xffef       // ret
+#define BIOS_WBOOT 0xfff0      // ret
+#define BIOS_CONST 0xfff1      // ret
+#define BIOS_CONIN 0xfff2      // ret
+#define BIOS_CONOUT 0xfff3     // ret
+#define BIOS_LIST 0xfff4       // ret
+#define BIOS_PUNCH 0xfff5      // ret
+#define BIOS_READER 0xfff6     // ret
+#define BIOS_HOME 0xfff7       // ret
+#define BIOS_SELDSK 0xfff8     // ret
+#define BIOS_SETTRK 0xfff9     // ret
+#define BIOS_SETSEC 0xfffa     // ret
+#define BIOS_SETDMA 0xfffb     // ret
+#define BIOS_READ 0xfffc       // ret
+#define BIOS_WRITE 0xfffd      // ret
+#define BIOS_LISTST 0xfffe     // ret
+#define BIOS_SECTRAN 0xffff    // ret
+
+#define MEM_SIZE 0x10000
+uint8_t mem[MEM_SIZE];
+
+// read and write are separated for I/O
+#define IO_SIZE 0x100
+uint8_t io_read[IO_SIZE];
+uint8_t io_write[IO_SIZE];
+
+int load_ihx(char *name) {
+  FILE *fp = fopen(name, "r");
+  if (fp == NULL) {
+    perror(name);
+    exit(EXIT_FAILURE);
+  }
+
+  int base = 0, entry_point = 0;
+  bool had_eof = false;
+  char line[0x100];
+  while (fgets(line, 0x100, fp)) {
+    for (char *p = line; *p; ++p)
+      if (*p == '\n') {
+        *p = 0;
+        break;
+      }
+
+    if (had_eof) {
+      fprintf(stderr, "garbage after EOF record: %s\n", line);
+      exit(EXIT_FAILURE);
+    }
+
+    if (line[0] != ':') {
+      fprintf(stderr, "require colon: %s\n", line);
+      exit(EXIT_FAILURE);
+    }
+
+    uint8_t buf[0x7f];
+    int len;
+    for (len = 0; len < 0x7f; ++len) {
+      char *p = line + 1 + len * 2;
+      if (*p == 0)
+        break;
+      if (*p == '\n') {
+        *p = 0;
+        break;
+      }
+      uint8_t c = p[2];
+      p[2] = 0;
+
+      char *q;
+      buf[len] = (uint8_t)strtol(p, &q, 16);
+      p[2] = c;
+      if (q != p + 2) {
+        fprintf(stderr, "not hex byte: %s\n", p);
+        exit(EXIT_FAILURE);
+      }
+    }
+
+    if (len == 0) {
+      fprintf(stderr, "empty line: %s\n", line);
+      exit(EXIT_FAILURE);
+    }
+
+    uint8_t checksum = 0;
+    for (int i = 0; i < len; ++i)
+      checksum += buf[i];
+    if (checksum) {
+      checksum -= buf[len - 1];
+      fprintf(
+        stderr,
+        "checksum %02x, should be %02x\n",
+        checksum,
+        buf[len - 1]
+      );
+      exit(EXIT_FAILURE);
+    }
+
+    len -= 5;
+    if (len != buf[0]) {
+      fprintf(stderr, "incorrect length: %s\n", line);
+      exit(EXIT_FAILURE);
+    }
+
+    int addr = (buf[1] << 8) | buf[2], end_addr;
+    switch (buf[3]) {
+    case 0:
+      addr += base;
+      end_addr = addr + len;
+      if (end_addr < addr || end_addr > MEM_SIZE) {
+        fprintf(stderr, "invalid load range: [0x%x, 0x%x)\n", addr, end_addr);
+        exit(EXIT_FAILURE);
+      }
+      memcpy(mem + addr, buf + 4, len);
+      break;
+    case 1:
+      had_eof = true;
+      break;
+    case 4:
+      if (len < 2) {
+        fprintf(stderr, "invalid extended linear address record: %s\n", line);
+        exit(EXIT_FAILURE);
+      }
+      base = (buf[4] << 24) | (buf[5] << 16);
+      break;
+    case 5:
+      if (len < 4) {
+        fprintf(stderr, "invalid start linear address record: %s\n", line);
+        exit(EXIT_FAILURE);
+      }
+      entry_point = (buf[4] << 24) | (buf[5] << 16) | (buf[6] << 8) | buf[7];
+      break;
+    default:
+      fprintf(stderr, "unknown record type: 0x%x\n", buf[3]);
+      exit(EXIT_FAILURE);
+    }
+  }
+  if (!had_eof) {
+    fprintf(stderr, "no EOF record\n");
+    exit(EXIT_FAILURE);
+  }
+
+  fclose(fp);
+  return entry_point;
+}
+
+int bdos(int a, int c, int de, uint16_t *hl) {
+  switch (c) {
+  case 2:
+    // Character output
+    de &= 0xff;
+    switch (de) {
+    case '\n':
+      break;
+    case '\r':
+      de = '\n';
+    default:
+      putchar(de);
+      break;
+    }
+    break;
+#if 0 // unlike MS-DOS version, the CP/M BASIC-80 uses direct BIOS calls
+  case 6:
+    // Direct console I/O
+    de &= 0xff;
+    if (de == 0xff) {
+      int data = getchar();
+      switch (data) {
+      case '\n':
+        data = '\r';
+        goto avail;
+      case 0x7f:
+        data = '\b';
+        //goto avail;
+      default:
+      avail:
+        a = data;
+        break;
+      case EOF:
+        a = 0;
+        break;
+      }
+      break;
+    }
+    switch (de) {
+    case '\n':
+      break;
+    case '\r':
+      de = '\n';
+    default:
+      putchar(de);
+      break;
+    }
+    break;
+#endif
+  case 9:
+    // Display string
+    for (int data; (data = mem[de]) != '$'; de = (de + 1) & 0xffff)
+      switch (data) {
+      case '\n':
+        break;
+      case '\r':
+        data = '\n';
+      default:
+        putchar(data);
+        break;
+      }
+    break;
+  case 0xc:
+    // Get CP/M version
+    *hl = 0x22;
+    break;
+  case 0x29: // DRV_ROVEC - Return bitmap of read-only drives
+    *hl = 0;
+    break;
+  default:
+    fprintf(stderr, "unimplemented BDOS call 0x%02x\n", c);
+    exit(EXIT_FAILURE);
+  }
+  return a;
+}
+
+int bios(int pc, int a, int c) {
+  switch (pc) {
+  case BIOS_WBOOT:
+    exit(EXIT_SUCCESS);
+  case BIOS_CONST:
+    a = 0; // defeat BASIC Ctrl-C checking
+    break;
+  case BIOS_CONIN:
+    {
+      int data = getchar();
+      switch (data) {
+      case '\n':
+        data = '\r';
+        goto avail;
+      case 0x7f:
+        data = '\b';
+        //goto avail;
+      default:
+      avail:
+        a = data;
+        break;
+      case EOF:
+        a = 0;
+        break;
+      }
+    }
+    break;
+  case BIOS_CONOUT:
+    switch (c) {
+    case '\n':
+      break;
+    case '\r':
+      c = '\n';
+    default:
+      putchar(c);
+      break;
+    }
+    break;
+  default:
+    fprintf(stderr, "unimplemented BIOS call 0x%04x\n", pc);
+    exit(EXIT_FAILURE);
+  }
+  return a;
+}
+
+#if ALT_BACKEND
+uint8_t read_byte(uint16_t addr)
+#else
+int read_byte(void *context, int addr)
+#endif
+{
+#if MEM_TRACE
+  int data = mem[addr];
+  fprintf(stderr, "addr=%04x rd=%02x\n", addr, data);
+  return data;
+#else
+  return mem[addr];
+#endif
+}
+
+#if ALT_BACKEND
+void write_byte(uint16_t addr, uint8_t data)
+#else
+void write_byte(void *context, int addr, int data)
+#endif
+{
+#if MEM_TRACE
+  fprintf(stderr, "addr=%04x wr=%02x\n", addr, data);
+#endif
+  mem[addr] = data;
+}
+
+int in_byte(void *context, int addr) {
+  addr &= 0xff;
+#if IO_TRACE
+  int data = io_read[addr];
+  printf("io=%02x rd=%02x\n", addr, data);
+  return data;
+#else
+  return io_read[addr];
+#endif
+}
+
+void out_byte(void *context, int addr, int data) {
+  addr &= 0xff;
+#if IO_TRACE
+  printf("io=%02x wr=%02x\n", addr, data);
+#endif
+  io_write[addr] = data;
+}
+
+int main(int argc, char **argv) {
+  if (argc < 2) {
+    printf("usage: %s image.ihx\n", argv[0]);
+    exit(EXIT_FAILURE);
+  }
+  int entry_point = load_ihx(argv[1]);
+
+  mem[0] = 0xc3; // jmp
+  mem[1] = (uint8_t)JMP_WBOOT;
+  mem[2] = (uint8_t)(JMP_WBOOT >> 8);
+
+  mem[5] = 0xc3; // jmp
+  mem[6] = (uint8_t)BDOS;
+  mem[7] = (uint8_t)(BDOS >> 8);
+
+  mem[BDOS] = 0xc9; // ret
+
+  mem[JMP_BOOT] = 0xc3; // jmp
+  mem[JMP_BOOT + 1] = (uint8_t)BIOS_BOOT;
+  mem[JMP_BOOT + 2] = (uint8_t)(BIOS_BOOT >> 8);
+
+  mem[JMP_WBOOT] = 0xc3; // jmp
+  mem[JMP_WBOOT + 1] = (uint8_t)BIOS_WBOOT;
+  mem[JMP_WBOOT + 2] = (uint8_t)(BIOS_WBOOT >> 8);
+
+  mem[JMP_CONST] = 0xc3; // jmp
+  mem[JMP_CONST + 1] = (uint8_t)BIOS_CONST;
+  mem[JMP_CONST + 2] = (uint8_t)(BIOS_CONST >> 8);
+
+  mem[JMP_CONIN] = 0xc3; // jmp
+  mem[JMP_CONIN + 1] = (uint8_t)BIOS_CONIN;
+  mem[JMP_CONIN + 2] = (uint8_t)(BIOS_CONIN >> 8);
+
+  mem[JMP_CONOUT] = 0xc3; // jmp
+  mem[JMP_CONOUT + 1] = (uint8_t)BIOS_CONOUT;
+  mem[JMP_CONOUT + 2] = (uint8_t)(BIOS_CONOUT >> 8);
+
+  mem[JMP_LIST] = 0xc3; // jmp
+  mem[JMP_LIST + 1] = (uint8_t)BIOS_LIST;
+  mem[JMP_LIST + 2] = (uint8_t)(BIOS_LIST >> 8);
+
+  mem[JMP_PUNCH] = 0xc3; // jmp
+  mem[JMP_PUNCH + 1] = (uint8_t)BIOS_PUNCH;
+  mem[JMP_PUNCH + 2] = (uint8_t)(BIOS_PUNCH >> 8);
+
+  mem[JMP_READER] = 0xc3; // jmp
+  mem[JMP_READER + 1] = (uint8_t)BIOS_READER;
+  mem[JMP_READER + 2] = (uint8_t)(BIOS_READER >> 8);
+
+  mem[JMP_HOME] = 0xc3; // jmp
+  mem[JMP_HOME + 1] = (uint8_t)BIOS_HOME;
+  mem[JMP_HOME + 2] = (uint8_t)(BIOS_HOME >> 8);
+
+  mem[JMP_SELDSK] = 0xc3; // jmp
+  mem[JMP_SELDSK + 1] = (uint8_t)BIOS_SELDSK;
+  mem[JMP_SELDSK + 2] = (uint8_t)(BIOS_SELDSK >> 8);
+
+  mem[JMP_SETTRK] = 0xc3; // jmp
+  mem[JMP_SETTRK + 1] = (uint8_t)BIOS_SETTRK;
+  mem[JMP_SETTRK + 2] = (uint8_t)(BIOS_SETTRK >> 8);
+
+  mem[JMP_SETSEC] = 0xc3; // jmp
+  mem[JMP_SETSEC + 1] = (uint8_t)BIOS_SETSEC;
+  mem[JMP_SETSEC + 2] = (uint8_t)(BIOS_SETSEC >> 8);
+
+  mem[JMP_SETDMA] = 0xc3; // jmp
+  mem[JMP_SETDMA + 1] = (uint8_t)BIOS_SETDMA;
+  mem[JMP_SETDMA + 2] = (uint8_t)(BIOS_SETDMA >> 8);
+
+  mem[JMP_READ] = 0xc3; // jmp
+  mem[JMP_READ + 1] = (uint8_t)BIOS_READ;
+  mem[JMP_READ + 2] = (uint8_t)(BIOS_READ >> 8);
+
+  mem[JMP_WRITE] = 0xc3; // jmp
+  mem[JMP_WRITE + 1] = (uint8_t)BIOS_WRITE;
+  mem[JMP_WRITE + 2] = (uint8_t)(BIOS_WRITE >> 8);
+
+  mem[JMP_LISTST] = 0xc3; // jmp
+  mem[JMP_LISTST + 1] = (uint8_t)BIOS_LISTST;
+  mem[JMP_LISTST + 2] = (uint8_t)(BIOS_LISTST >> 8);
+
+  mem[JMP_SECTRAN] = 0xc3; // jmp
+  mem[JMP_SECTRAN + 1] = (uint8_t)BIOS_SECTRAN;
+  mem[JMP_SECTRAN + 2] = (uint8_t)(BIOS_SECTRAN >> 8);
+
+  mem[BIOS_BOOT] = 0xc9; // ret
+  mem[BIOS_WBOOT] = 0xc9; // ret
+  mem[BIOS_CONST] = 0xc9; // ret
+  mem[BIOS_CONIN] = 0xc9; // ret
+  mem[BIOS_CONOUT] = 0xc9; // ret
+  mem[BIOS_LIST] = 0xc9; // ret
+  mem[BIOS_PUNCH] = 0xc9; // ret
+  mem[BIOS_READER] = 0xc9; // ret
+  mem[BIOS_HOME] = 0xc9; // ret
+  mem[BIOS_SELDSK] = 0xc9; // ret
+  mem[BIOS_SETTRK] = 0xc9; // ret
+  mem[BIOS_SETSEC] = 0xc9; // ret
+  mem[BIOS_SETDMA] = 0xc9; // ret
+  mem[BIOS_READ] = 0xc9; // ret
+  mem[BIOS_WRITE] = 0xc9; // ret
+  mem[BIOS_LISTST] = 0xc9; // ret
+  mem[BIOS_SECTRAN] = 0xc9; // ret
+
+#if ALT_BACKEND
+  regs.pc = entry_point;
+  regs.sp = BDOS - 2; // assume word at BDOS - 2 is initialized to 0
+
+  while (true) {
+#if REG_TRACE
+    fprintf(
+      stderr,
+      "pc=%04x psw=%04x bc=%04x de=%04x hl=%04x sp=%04x cf=%d pf=%d acf=%d zf=%d sf=%d\n",
+      regs.pc,
+      regs.cf |
+      (regs.pf << 2) |
+      (regs.acf << 4) |
+      (regs.zf << 6) |
+      (regs.sf << 7) |
+      (regs.a << 8),
+      regs.c | (regs.b << 8),
+      regs.e | (regs.d << 8),
+      regs.l | (regs.h << 8),
+      regs.sp,
+      regs.cf,
+      regs.pf,
+      regs.acf,
+      regs.zf,
+      regs.sf
+    );
+#endif
+
+    if (regs.pc == BDOS) {
+      int de = regs.e | (regs.d << 8);
+      uint16_t hl = regs.l | (regs.h << 8);
+      regs.a = bdos(regs.a, regs.c, de, &hl);
+      regs.l = (uint8_t)hl;
+      regs.h = (uint8_t)(hl >> 8);
+    }
+    else if (regs.pc >= BIOS_BOOT)
+      regs.a = bios(regs.pc, regs.a, regs.c);
+
+    instruction(read_next_byte()); // should check return code (HLT, RST)
+  }
+#else
+  struct cpu_8080 cpu;
+  cpu_8080_init(
+    &cpu,
+    read_byte,
+    NULL,
+    write_byte,
+    NULL,
+    in_byte,
+    NULL,
+    out_byte,
+    NULL
+  );
+  cpu_8080_reset(&cpu);
+  cpu.regs.word.pc = entry_point;
+  cpu.regs.word.sp = BDOS - 2; // assume word at BDOS - 2 is initialized to 0
+
+  while (true) {
+#if REG_TRACE
+    fprintf(
+      stderr,
+      "pc=%04x psw=%04x bc=%04x de=%04x hl=%04x sp=%04x cf=%d pf=%d acf=%d zf=%d sf=%d\n",
+      cpu.regs.word.pc,
+      cpu.regs.word.psw,
+      cpu.regs.word.bc,
+      cpu.regs.word.de,
+      cpu.regs.word.hl,
+      cpu.regs.word.sp,
+      cpu.regs.bit.cf,
+      cpu.regs.bit.pf,
+      cpu.regs.bit.acf,
+      cpu.regs.bit.zf,
+      cpu.regs.bit.sf
+    );
+#endif
+
+    if (cpu.regs.word.pc == BDOS)
+      cpu.regs.byte.a = bdos(
+        cpu.regs.byte.a,
+        cpu.regs.byte.c,
+        cpu.regs.word.de,
+        &cpu.regs.word.hl
+      );
+    else if (cpu.regs.word.pc >= BIOS_BOOT)
+      cpu.regs.byte.a = bios(
+        cpu.regs.word.pc,
+        cpu.regs.byte.a,
+        cpu.regs.byte.c
+      );
+
+    cpu_8080_execute(&cpu);
+  }
+#endif
+
+  return 0;
+}
diff --git a/instr_8080.txt b/instr_8080.txt
new file mode 100644 (file)
index 0000000..53509ff
--- /dev/null
@@ -0,0 +1,256 @@
+nop
+lxi     b,0x3412
+stax    b
+inx     b
+inr     b
+dcr     b
+mvi     b,0x12
+rlc
+nop
+dad     b
+ldax    b
+dcx     b
+inr     c
+dcr     c
+mvi     c,0x12
+rrc
+nop
+lxi     d,0x3412
+stax    d
+inx     d
+inr     d
+dcr     d
+mvi     d,0x12
+ral
+nop
+dad     d
+ldax    d
+dcx     d
+inr     e
+dcr     e
+mvi     e,0x12
+rar
+nop
+lxi     h,0x3412
+shld    0x3412
+inx     h
+inr     h
+dcr     h
+mvi     h,0x12
+daa
+nop
+dad     h
+lhld    0x3412
+dcx     h
+inr     l
+dcr     l
+mvi     l,0x12
+cma
+nop
+lxi     sp,0x3412
+sta     0x3412
+inx     sp
+inr     m
+dcr     m
+mvi     m,0x12
+stc
+nop
+dad     sp
+lda     0x3412
+dcx     sp
+inr     a
+dcr     a
+mvi     a,0x12
+cmc
+mov     b,b
+mov     b,c
+mov     b,d
+mov     b,e
+mov     b,h
+mov     b,l
+mov     b,m
+mov     b,a
+mov     c,b
+mov     c,c
+mov     c,d
+mov     c,e
+mov     c,h
+mov     c,l
+mov     c,m
+mov     c,a
+mov     d,b
+mov     d,c
+mov     d,d
+mov     d,e
+mov     d,h
+mov     d,l
+mov     d,m
+mov     d,a
+mov     e,b
+mov     e,c
+mov     e,d
+mov     e,e
+mov     e,h
+mov     e,l
+mov     e,m
+mov     e,a
+mov     h,b
+mov     h,c
+mov     h,d
+mov     h,e
+mov     h,h
+mov     h,l
+mov     h,m
+mov     h,a
+mov     l,b
+mov     l,c
+mov     l,d
+mov     l,e
+mov     l,h
+mov     l,l
+mov     l,m
+mov     l,a
+mov     m,b
+mov     m,c
+mov     m,d
+mov     m,e
+mov     m,h
+mov     m,l
+hlt
+mov     m,a
+mov     a,b
+mov     a,c
+mov     a,d
+mov     a,e
+mov     a,h
+mov     a,l
+mov     a,m
+mov     a,a
+add     b
+add     c
+add     d
+add     e
+add     h
+add     l
+add     m
+add     a
+adc     b
+adc     c
+adc     d
+adc     e
+adc     h
+adc     l
+adc     m
+adc     a
+sub     b
+sub     c
+sub     d
+sub     e
+sub     h
+sub     l
+sub     m
+sub     a
+sbb     b
+sbb     c
+sbb     d
+sbb     e
+sbb     h
+sbb     l
+sbb     m
+sbb     a
+ana     b
+ana     c
+ana     d
+ana     e
+ana     h
+ana     l
+ana     m
+ana     a
+xra     b
+xra     c
+xra     d
+xra     e
+xra     h
+xra     l
+xra     m
+xra     a
+ora     b
+ora     c
+ora     d
+ora     e
+ora     h
+ora     l
+ora     m
+ora     a
+cmp     b
+cmp     c
+cmp     d
+cmp     e
+cmp     h
+cmp     l
+cmp     m
+cmp     a
+rnz
+pop     b
+jnz     0x3412
+jmp     0x3412
+cnz     0x3412
+push    b
+adi     0x12
+rst     0
+rz
+ret
+jz      0x3412
+jmp     0x3412
+cz      0x3412
+call    0x3412
+aci     0x12
+rst     1
+rnc
+pop     d
+jnc     0x3412
+out     0x12
+cnc     0x3412
+push    d
+sui     0x12
+rst     2
+rc
+ret
+jc      0x3412
+in      0x12
+cc      0x3412
+call    0x3412
+sbi     0x12
+rst     3
+rpo
+pop     h
+jpo     0x3412
+xthl
+cpo     0x3412
+push    h
+ani     0x12
+rst     4
+rpe
+pchl
+jpe     0x3412
+xchg
+cpe     0x3412
+call    0x3412
+xri     0x12
+rst     5
+rp
+pop     psw
+jp      0x3412
+di
+cp      0x3412
+push    psw
+ori     0x12
+rst     6
+rm
+sphl
+jm      0x3412
+ei
+cm      0x3412
+call    0x3412
+cpi     0x12
+rst     7
diff --git a/jefftranter_8080 b/jefftranter_8080
new file mode 160000 (submodule)
index 0000000..12a4ffb
--- /dev/null
@@ -0,0 +1 @@
+Subproject commit 12a4ffb689b4499b4a835206182ab1a49bbcb09c