In /emu_65c02, add 65c02 emulator from multi_emu.git commit 900d9fa3 instead of vrEmu6502
authorNick Downing <nick@ndcode.org>
Sat, 10 Aug 2024 15:17:32 +0000 (01:17 +1000)
committerNick Downing <nick@mary.local>
Sun, 22 Sep 2024 03:47:45 +0000 (13:47 +1000)
.gitmodules
emu_65c02/Makefile
emu_65c02/cpu_65c02.c [new file with mode: 0644]
emu_65c02/cpu_65c02.h [new file with mode: 0644]
emu_65c02/emu_65c02.c

index 6b0cb37..e026680 100644 (file)
@@ -1,9 +1,6 @@
 [submodule "asxv5pxx"]
        path = asxv5pxx
        url = https://github.com/nickd4/asxv5pxx.git
-[submodule "emu_65c02/vrEmu6502"]
-       path = emu_65c02/vrEmu6502
-       url = https://github.com/nickd4/vrEmu6502.git
 [submodule "dos33fsprogs"]
        path = dos33fsprogs
        url = https://github.com/nickd4/dos33fsprogs.git
index e2d6edb..1c2b957 100644 (file)
@@ -1,4 +1,4 @@
-CFLAGS=-g -Wall -O3 -DVR_6502_EMU_STATIC=1
+CFLAGS=-g -Wall -Wno-attributes -Wno-unused-function -O3
 LDFLAGS=-g
 
 .PHONY: all
@@ -25,7 +25,7 @@ cg_default:
        #${MAKE} ${MAKEFLAGS} -C $@
        ${MAKE} -C $@
 
-emu_65c02: emu_65c02.o stty_sane.o vrEmu6502/src/vrEmu6502.o
+emu_65c02: emu_65c02.o cpu_65c02.o stty_sane.o
        ${CC} ${LDFLAGS} -o $@ $^ -lSDL2 -lm
 
 emu_65c02.o: cg_default
diff --git a/emu_65c02/cpu_65c02.c b/emu_65c02/cpu_65c02.c
new file mode 100644 (file)
index 0000000..8789d63
--- /dev/null
@@ -0,0 +1,879 @@
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include "cpu_65c02.h"
+
+// initialization
+void cpu_65c02_init(
+  struct cpu_65c02 *self,
+  uint8_t (*read_byte)(void *context, uint16_t addr),
+  void *read_byte_context,
+  void (*write_byte)(void *context, uint16_t addr, uint8_t data),
+  void *write_byte_context
+) {
+  memset(self, 0, sizeof(struct cpu_65c02));
+  self->regs.byte.p = 0x30; // unused bits are hard coded to 1
+  self->read_byte = read_byte;
+  self->read_byte_context = read_byte_context;
+  self->write_byte = write_byte;
+  self->write_byte_context = write_byte_context;
+}
+
+void cpu_65c02_reset(struct cpu_65c02 *self) {
+  self->regs.word.pc = cpu_65c02_read_word(self, CPU_65C02_RESET_VECTOR);
+  self->regs.byte.iflags = 0;
+  self->regs.bit._if = true;
+  self->regs.bit.df = false;
+}
+
+// instruction decode
+void cpu_65c02_execute(struct cpu_65c02 *self) {
+  if (self->regs.bit.stp_flag) {
+    ++self->cycles;
+    return;
+  }
+
+  if (self->regs.bit.nmi_pending) {
+    self->regs.bit.nmi_pending = false;
+    self->regs.bit.wai_flag = false;
+    cpu_65c02_irq(self, false, CPU_65C02_NMI_VECTOR);
+    return;
+  }
+
+  if (self->regs.bit.irq_pending) {
+    if (self->regs.bit._if == 0) {
+      self->regs.bit.irq_pending = false;
+      self->regs.bit.wai_flag = false;
+      cpu_65c02_irq(self, false, CPU_65C02_IRQ_VECTOR);
+      return;
+    }
+    if (self->regs.bit.wai_flag) {
+      self->regs.bit.irq_pending = false;
+      self->regs.bit.wai_flag = false;
+    }
+  }
+  else if (self->regs.bit.wai_flag) {
+    ++self->cycles;
+    return;
+  }
+
+  switch (cpu_65c02_fetch_byte(self)) {
+  case 0x00:
+    ++self->regs.word.pc;
+    cpu_65c02_irq(self, true, CPU_65C02_IRQ_VECTOR);
+    break;
+  case 0x01:
+    cpu_65c02_ora(self, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page_indexed_indirect(self, self->regs.byte.x)));
+    break;
+  case 0x02:
+    cpu_65c02_illegal_opcode22(self);
+    break;
+  case 0x03:
+    cpu_65c02_illegal_opcode11(self);
+    break;
+  case 0x04:
+    cpu_65c02_tsb(self, cpu_65c02_ea_zero_page(self));
+    break;
+  case 0x05:
+    cpu_65c02_ora(self, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page(self)));
+    break;
+  case 0x06:
+    cpu_65c02_asl(self, cpu_65c02_ea_zero_page(self));
+    break;
+  case 0x07:
+    cpu_65c02_rmb(self, 0, cpu_65c02_ea_zero_page(self));
+    break;
+  case 0x08:
+    cpu_65c02_ph(self, self->regs.byte.p);
+    break;
+  case 0x09:
+    cpu_65c02_ora(self, cpu_65c02_fetch_byte(self));
+    break;
+  case 0x0a:
+    cpu_65c02_asl(self, CPU_65C02_EA_A);
+    break;
+  case 0x0b:
+    cpu_65c02_illegal_opcode11(self);
+    break;
+  case 0x0c:
+    cpu_65c02_tsb(self, cpu_65c02_ea_absolute(self));
+    break;
+  case 0x0d:
+    cpu_65c02_ora(self, cpu_65c02_read_byte(self, cpu_65c02_ea_absolute(self)));
+    break;
+  case 0x0e:
+    cpu_65c02_asl(self, cpu_65c02_ea_absolute(self));
+    break;
+  case 0x0f:
+    {
+      int rvalue = cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page(self));
+      cpu_65c02_bra(self, !(rvalue & 1), cpu_65c02_ea_relative(self));
+    }
+    break;
+  case 0x10:
+    cpu_65c02_bra(self, !self->regs.bit.nf, cpu_65c02_ea_relative(self));
+    break;
+  case 0x11:
+    cpu_65c02_ora(self, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page_indirect_indexed(self, self->regs.byte.y)));
+    break;
+  case 0x12:
+    cpu_65c02_ora(self, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page_indirect(self)));
+    break;
+  case 0x13:
+    cpu_65c02_illegal_opcode11(self);
+    break;
+  case 0x14:
+    cpu_65c02_trb(self, cpu_65c02_ea_zero_page(self));
+    break;
+  case 0x15:
+    cpu_65c02_ora(self, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page_indexed(self, self->regs.byte.x)));
+    break;
+  case 0x16:
+    cpu_65c02_asl(self, cpu_65c02_ea_zero_page_indexed(self, self->regs.byte.x));
+    break;
+  case 0x17:
+    cpu_65c02_rmb(self, 1, cpu_65c02_ea_zero_page(self));
+    break;
+  case 0x18:
+    cpu_65c02_cl(self, CPU_65C02_REG_P_BIT_C);
+    break;
+  case 0x19:
+    cpu_65c02_ora(self, cpu_65c02_read_byte(self, cpu_65c02_ea_absolute_indexed(self, self->regs.byte.y)));
+    break;
+  case 0x1a:
+    cpu_65c02_inc(self, CPU_65C02_EA_A);
+    break;
+  case 0x1b:
+    cpu_65c02_illegal_opcode11(self);
+    break;
+  case 0x1c:
+    cpu_65c02_trb(self, cpu_65c02_ea_absolute(self));
+    break;
+  case 0x1d:
+    cpu_65c02_ora(self, cpu_65c02_read_byte(self, cpu_65c02_ea_absolute_indexed(self, self->regs.byte.x)));
+    break;
+  case 0x1e:
+    cpu_65c02_asl(self, cpu_65c02_ea_absolute_indexed(self, self->regs.byte.x));
+    break;
+  case 0x1f:
+    {
+      int rvalue = cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page(self));
+      cpu_65c02_bra(self, !((rvalue >> 1) & 1), cpu_65c02_ea_relative(self));
+    }
+    break;
+  case 0x20:
+    cpu_65c02_jsr(self, cpu_65c02_ea_absolute(self));
+    break;
+  case 0x21:
+    cpu_65c02_and(self, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page_indexed_indirect(self, self->regs.byte.x)));
+    break;
+  case 0x22:
+    cpu_65c02_illegal_opcode22(self);
+    break;
+  case 0x23:
+    cpu_65c02_illegal_opcode11(self);
+    break;
+  case 0x24:
+    cpu_65c02_bit(self, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page(self)));
+    break;
+  case 0x25:
+    cpu_65c02_and(self, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page(self)));
+    break;
+  case 0x26:
+    cpu_65c02_rol(self, cpu_65c02_ea_zero_page(self));
+    break;
+  case 0x27:
+    cpu_65c02_rmb(self, 2, cpu_65c02_ea_zero_page(self));
+    break;
+  case 0x28:
+    cpu_65c02_plp(self);
+    break;
+  case 0x29:
+    cpu_65c02_and(self, cpu_65c02_fetch_byte(self));
+    break;
+  case 0x2a:
+    cpu_65c02_rol(self, CPU_65C02_EA_A);
+    break;
+  case 0x2b:
+    cpu_65c02_illegal_opcode11(self);
+    break;
+  case 0x2c:
+    cpu_65c02_bit(self, cpu_65c02_read_byte(self, cpu_65c02_ea_absolute(self)));
+    break;
+  case 0x2d:
+    cpu_65c02_and(self, cpu_65c02_read_byte(self, cpu_65c02_ea_absolute(self)));
+    break;
+  case 0x2e:
+    cpu_65c02_rol(self, cpu_65c02_ea_absolute(self));
+    break;
+  case 0x2f:
+    {
+      int rvalue = cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page(self));
+      cpu_65c02_bra(self, !((rvalue >> 2) & 1), cpu_65c02_ea_relative(self));
+    }
+    break;
+  case 0x30:
+    cpu_65c02_bra(self, self->regs.bit.nf, cpu_65c02_ea_relative(self));
+    break;
+  case 0x31:
+    cpu_65c02_and(self, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page_indirect_indexed(self, self->regs.byte.y)));
+    break;
+  case 0x32:
+    cpu_65c02_and(self, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page_indirect(self)));
+    break;
+  case 0x33:
+    cpu_65c02_illegal_opcode11(self);
+    break;
+  case 0x34:
+    cpu_65c02_bit(self, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page_indexed(self, self->regs.byte.x)));
+    break;
+  case 0x35:
+    cpu_65c02_and(self, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page_indexed(self, self->regs.byte.x)));
+    break;
+  case 0x36:
+    cpu_65c02_rol(self, cpu_65c02_ea_zero_page_indexed(self, self->regs.byte.x));
+    break;
+  case 0x37:
+    cpu_65c02_rmb(self, 3, cpu_65c02_ea_zero_page(self));
+    break;
+  case 0x38:
+    cpu_65c02_se(self, CPU_65C02_REG_P_BIT_C);
+    break;
+  case 0x39:
+    cpu_65c02_and(self, cpu_65c02_read_byte(self, cpu_65c02_ea_absolute_indexed(self, self->regs.byte.y)));
+    break;
+  case 0x3a:
+    cpu_65c02_dec(self, CPU_65C02_EA_A);
+    break;
+  case 0x3b:
+    cpu_65c02_illegal_opcode11(self);
+    break;
+  case 0x3c:
+    cpu_65c02_bit(self, cpu_65c02_read_byte(self, cpu_65c02_ea_absolute_indexed(self, self->regs.byte.x)));
+    break;
+  case 0x3d:
+    cpu_65c02_and(self, cpu_65c02_read_byte(self, cpu_65c02_ea_absolute_indexed(self, self->regs.byte.x)));
+    break;
+  case 0x3e:
+    cpu_65c02_rol(self, cpu_65c02_ea_absolute_indexed(self, self->regs.byte.x));
+    break;
+  case 0x3f:
+    {
+      int rvalue = cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page(self));
+      cpu_65c02_bra(self, !((rvalue >> 3) & 1), cpu_65c02_ea_relative(self));
+    }
+    break;
+  case 0x40:
+    cpu_65c02_rti(self);
+    break;
+  case 0x41:
+    cpu_65c02_eor(self, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page_indexed_indirect(self, self->regs.byte.x)));
+    break;
+  case 0x42:
+    cpu_65c02_illegal_opcode22(self);
+    break;
+  case 0x43:
+    cpu_65c02_illegal_opcode11(self);
+    break;
+  case 0x44:
+    cpu_65c02_illegal_opcode23(self);
+    break;
+  case 0x45:
+    cpu_65c02_eor(self, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page(self)));
+    break;
+  case 0x46:
+    cpu_65c02_lsr(self, cpu_65c02_ea_zero_page(self));
+    break;
+  case 0x47:
+    cpu_65c02_rmb(self, 4, cpu_65c02_ea_zero_page(self));
+    break;
+  case 0x48:
+    cpu_65c02_ph(self, self->regs.byte.a);
+    break;
+  case 0x49:
+    cpu_65c02_eor(self, cpu_65c02_fetch_byte(self));
+    break;
+  case 0x4a:
+    cpu_65c02_lsr(self, CPU_65C02_EA_A);
+    break;
+  case 0x4b:
+    cpu_65c02_illegal_opcode11(self);
+    break;
+  case 0x4c:
+    cpu_65c02_jmp(self, cpu_65c02_ea_absolute(self));
+    break;
+  case 0x4d:
+    cpu_65c02_eor(self, cpu_65c02_read_byte(self, cpu_65c02_ea_absolute(self)));
+    break;
+  case 0x4e:
+    cpu_65c02_lsr(self, cpu_65c02_ea_absolute(self));
+    break;
+  case 0x4f:
+    {
+      int rvalue = cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page(self));
+      cpu_65c02_bra(self, !((rvalue >> 4) & 1), cpu_65c02_ea_relative(self));
+    }
+    break;
+  case 0x50:
+    cpu_65c02_bra(self, !self->regs.bit.vf, cpu_65c02_ea_relative(self));
+    break;
+  case 0x51:
+    cpu_65c02_eor(self, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page_indirect_indexed(self, self->regs.byte.y)));
+    break;
+  case 0x52:
+    cpu_65c02_eor(self, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page_indirect(self)));
+    break;
+  case 0x53:
+    cpu_65c02_illegal_opcode11(self);
+    break;
+  case 0x54:
+    cpu_65c02_illegal_opcode24(self);
+    break;
+  case 0x55:
+    cpu_65c02_eor(self, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page_indexed(self, self->regs.byte.x)));
+    break;
+  case 0x56:
+    cpu_65c02_lsr(self, cpu_65c02_ea_zero_page_indexed(self, self->regs.byte.x));
+    break;
+  case 0x57:
+    cpu_65c02_rmb(self, 5, cpu_65c02_ea_zero_page(self));
+    break;
+  case 0x58:
+    cpu_65c02_cl(self, CPU_65C02_REG_P_BIT_I);
+    break;
+  case 0x59:
+    cpu_65c02_eor(self, cpu_65c02_read_byte(self, cpu_65c02_ea_absolute_indexed(self, self->regs.byte.y)));
+    break;
+  case 0x5a:
+    cpu_65c02_ph(self, self->regs.byte.y);
+    break;
+  case 0x5b:
+    cpu_65c02_illegal_opcode11(self);
+    break;
+  case 0x5c:
+    cpu_65c02_illegal_opcode38(self);
+    break;
+  case 0x5d:
+    cpu_65c02_eor(self, cpu_65c02_read_byte(self, cpu_65c02_ea_absolute_indexed(self, self->regs.byte.x)));
+    break;
+  case 0x5e:
+    cpu_65c02_lsr(self, cpu_65c02_ea_absolute_indexed(self, self->regs.byte.x));
+    break;
+  case 0x5f:
+    {
+      int rvalue = cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page(self));
+      cpu_65c02_bra(self, !((rvalue >> 5) & 1), cpu_65c02_ea_relative(self));
+    }
+    break;
+  case 0x60:
+    cpu_65c02_rts(self);
+    break;
+  case 0x61:
+    cpu_65c02_adc(self, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page_indexed_indirect(self, self->regs.byte.x)));
+    break;
+  case 0x62:
+    cpu_65c02_illegal_opcode22(self);
+    break;
+  case 0x63:
+    cpu_65c02_illegal_opcode11(self);
+    break;
+  case 0x64:
+    cpu_65c02_st(self, 0, cpu_65c02_ea_zero_page(self));
+    break;
+  case 0x65:
+    cpu_65c02_adc(self, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page(self)));
+    break;
+  case 0x66:
+    cpu_65c02_ror(self, cpu_65c02_ea_zero_page(self));
+    break;
+  case 0x67:
+    cpu_65c02_rmb(self, 6, cpu_65c02_ea_zero_page(self));
+    break;
+  case 0x68:
+    cpu_65c02_pl(self, CPU_65C02_EA_A);
+    break;
+  case 0x69:
+    cpu_65c02_adc(self, cpu_65c02_fetch_byte(self));
+    break;
+  case 0x6a:
+    cpu_65c02_ror(self, CPU_65C02_EA_A);
+    break;
+  case 0x6b:
+    cpu_65c02_illegal_opcode11(self);
+    break;
+  case 0x6c:
+    cpu_65c02_jmp(self, cpu_65c02_ea_absolute_indirect(self));
+    break;
+  case 0x6d:
+    cpu_65c02_adc(self, cpu_65c02_read_byte(self, cpu_65c02_ea_absolute(self)));
+    break;
+  case 0x6e:
+    cpu_65c02_ror(self, cpu_65c02_ea_absolute(self));
+    break;
+  case 0x6f:
+    {
+      int rvalue = cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page(self));
+      cpu_65c02_bra(self, !((rvalue >> 6) & 1), cpu_65c02_ea_relative(self));
+    }
+    break;
+  case 0x70:
+    cpu_65c02_bra(self, self->regs.bit.vf, cpu_65c02_ea_relative(self));
+    break;
+  case 0x71:
+    cpu_65c02_adc(self, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page_indirect_indexed(self, self->regs.byte.y)));
+    break;
+  case 0x72:
+    cpu_65c02_adc(self, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page_indirect(self)));
+    break;
+  case 0x73:
+    cpu_65c02_illegal_opcode11(self);
+    break;
+  case 0x74:
+    cpu_65c02_st(self, 0, cpu_65c02_ea_zero_page_indexed(self, self->regs.byte.x));
+    break;
+  case 0x75:
+    cpu_65c02_adc(self, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page_indexed(self, self->regs.byte.x)));
+    break;
+  case 0x76:
+    cpu_65c02_ror(self, cpu_65c02_ea_zero_page_indexed(self, self->regs.byte.x));
+    break;
+  case 0x77:
+    cpu_65c02_rmb(self, 7, cpu_65c02_ea_zero_page(self));
+    break;
+  case 0x78:
+    cpu_65c02_se(self, CPU_65C02_REG_P_BIT_I);
+    break;
+  case 0x79:
+    cpu_65c02_adc(self, cpu_65c02_read_byte(self, cpu_65c02_ea_absolute_indexed(self, self->regs.byte.y)));
+    break;
+  case 0x7a:
+    cpu_65c02_pl(self, CPU_65C02_EA_Y);
+    break;
+  case 0x7b:
+    cpu_65c02_illegal_opcode11(self);
+    break;
+  case 0x7c:
+    cpu_65c02_jmp(self, cpu_65c02_ea_absolute_indexed_indirect(self, self->regs.byte.x));
+    break;
+  case 0x7d:
+    cpu_65c02_adc(self, cpu_65c02_read_byte(self, cpu_65c02_ea_absolute_indexed(self, self->regs.byte.x)));
+    break;
+  case 0x7e:
+    cpu_65c02_ror(self, cpu_65c02_ea_absolute_indexed(self, self->regs.byte.x));
+    break;
+  case 0x7f:
+    {
+      int rvalue = cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page(self));
+      cpu_65c02_bra(self, !((rvalue >> 7) & 1), cpu_65c02_ea_relative(self));
+    }
+    break;
+  case 0x80:
+    cpu_65c02_bra(self, true, cpu_65c02_ea_relative(self));
+    break;
+  case 0x81:
+    cpu_65c02_st(self, self->regs.byte.a, cpu_65c02_ea_zero_page_indexed_indirect(self, self->regs.byte.x));
+    break;
+  case 0x82:
+    cpu_65c02_illegal_opcode22(self);
+    break;
+  case 0x83:
+    cpu_65c02_illegal_opcode11(self);
+    break;
+  case 0x84:
+    cpu_65c02_st(self, self->regs.byte.y, cpu_65c02_ea_zero_page(self));
+    break;
+  case 0x85:
+    cpu_65c02_st(self, self->regs.byte.a, cpu_65c02_ea_zero_page(self));
+    break;
+  case 0x86:
+    cpu_65c02_st(self, self->regs.byte.x, cpu_65c02_ea_zero_page(self));
+    break;
+  case 0x87:
+    cpu_65c02_smb(self, 0, cpu_65c02_ea_zero_page(self));
+    break;
+  case 0x88:
+    cpu_65c02_dec(self, CPU_65C02_EA_Y);
+    break;
+  case 0x89:
+    cpu_65c02_bit_imm(self, cpu_65c02_fetch_byte(self));
+    break;
+  case 0x8a:
+    cpu_65c02_ld(self, CPU_65C02_EA_A, self->regs.byte.x);
+    break;
+  case 0x8b:
+    cpu_65c02_illegal_opcode11(self);
+    break;
+  case 0x8c:
+    cpu_65c02_st(self, self->regs.byte.y, cpu_65c02_ea_absolute(self));
+    break;
+  case 0x8d:
+    cpu_65c02_st(self, self->regs.byte.a, cpu_65c02_ea_absolute(self));
+    break;
+  case 0x8e:
+    cpu_65c02_st(self, self->regs.byte.x, cpu_65c02_ea_absolute(self));
+    break;
+  case 0x8f:
+    {
+      int rvalue = cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page(self));
+      cpu_65c02_bra(self, rvalue & 1, cpu_65c02_ea_relative(self));
+    }
+    break;
+  case 0x90:
+    cpu_65c02_bra(self, !self->regs.bit.cf, cpu_65c02_ea_relative(self));
+    break;
+  case 0x91:
+    cpu_65c02_st(self, self->regs.byte.a, cpu_65c02_ea_zero_page_indirect_indexed(self, self->regs.byte.y));
+    break;
+  case 0x92:
+    cpu_65c02_st(self, self->regs.byte.a, cpu_65c02_ea_zero_page_indirect(self));
+    break;
+  case 0x93:
+    cpu_65c02_illegal_opcode11(self);
+    break;
+  case 0x94:
+    cpu_65c02_st(self, self->regs.byte.y, cpu_65c02_ea_zero_page_indexed(self, self->regs.byte.x));
+    break;
+  case 0x95:
+    cpu_65c02_st(self, self->regs.byte.a, cpu_65c02_ea_zero_page_indexed(self, self->regs.byte.x));
+    break;
+  case 0x96:
+    cpu_65c02_st(self, self->regs.byte.x, cpu_65c02_ea_zero_page_indexed(self, self->regs.byte.y));
+    break;
+  case 0x97:
+    cpu_65c02_smb(self, 1, cpu_65c02_ea_zero_page(self));
+    break;
+  case 0x98:
+    cpu_65c02_ld(self, CPU_65C02_EA_A, self->regs.byte.y);
+    break;
+  case 0x99:
+    cpu_65c02_st(self, self->regs.byte.a, cpu_65c02_ea_absolute_indexed(self, self->regs.byte.y));
+    break;
+  case 0x9a:
+    cpu_65c02_st(self, self->regs.byte.x, CPU_65C02_EA_S);
+    break;
+  case 0x9b:
+    cpu_65c02_illegal_opcode11(self);
+    break;
+  case 0x9c:
+    cpu_65c02_st(self, 0, cpu_65c02_ea_absolute(self));
+    break;
+  case 0x9d:
+    cpu_65c02_st(self, self->regs.byte.a, cpu_65c02_ea_absolute_indexed(self, self->regs.byte.x));
+    break;
+  case 0x9e:
+    cpu_65c02_st(self, 0, cpu_65c02_ea_absolute_indexed(self, self->regs.byte.x));
+    break;
+  case 0x9f:
+    {
+      int rvalue = cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page(self));
+      cpu_65c02_bra(self, (rvalue >> 1) & 1, cpu_65c02_ea_relative(self));
+    }
+    break;
+  case 0xa0:
+    cpu_65c02_ld(self, CPU_65C02_EA_Y, cpu_65c02_fetch_byte(self));
+    break;
+  case 0xa1:
+    cpu_65c02_ld(self, CPU_65C02_EA_A, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page_indexed_indirect(self, self->regs.byte.x)));
+    break;
+  case 0xa2:
+    cpu_65c02_ld(self, CPU_65C02_EA_X, cpu_65c02_fetch_byte(self));
+    break;
+  case 0xa3:
+    cpu_65c02_illegal_opcode11(self);
+    break;
+  case 0xa4:
+    cpu_65c02_ld(self, CPU_65C02_EA_Y, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page(self)));
+    break;
+  case 0xa5:
+    cpu_65c02_ld(self, CPU_65C02_EA_A, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page(self)));
+    break;
+  case 0xa6:
+    cpu_65c02_ld(self, CPU_65C02_EA_X, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page(self)));
+    break;
+  case 0xa7:
+    cpu_65c02_smb(self, 2, cpu_65c02_ea_zero_page(self));
+    break;
+  case 0xa8:
+    cpu_65c02_ld(self, CPU_65C02_EA_Y, self->regs.byte.a);
+    break;
+  case 0xa9:
+    cpu_65c02_ld(self, CPU_65C02_EA_A, cpu_65c02_fetch_byte(self));
+    break;
+  case 0xaa:
+    cpu_65c02_ld(self, CPU_65C02_EA_X, self->regs.byte.a);
+    break;
+  case 0xab:
+    cpu_65c02_illegal_opcode11(self);
+    break;
+  case 0xac:
+    cpu_65c02_ld(self, CPU_65C02_EA_Y, cpu_65c02_read_byte(self, cpu_65c02_ea_absolute(self)));
+    break;
+  case 0xad:
+    cpu_65c02_ld(self, CPU_65C02_EA_A, cpu_65c02_read_byte(self, cpu_65c02_ea_absolute(self)));
+    break;
+  case 0xae:
+    cpu_65c02_ld(self, CPU_65C02_EA_X, cpu_65c02_read_byte(self, cpu_65c02_ea_absolute(self)));
+    break;
+  case 0xaf:
+    {
+      int rvalue = cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page(self));
+      cpu_65c02_bra(self, (rvalue >> 2) & 1, cpu_65c02_ea_relative(self));
+    }
+    break;
+  case 0xb0:
+    cpu_65c02_bra(self, self->regs.bit.cf, cpu_65c02_ea_relative(self));
+    break;
+  case 0xb1:
+    cpu_65c02_ld(self, CPU_65C02_EA_A, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page_indirect_indexed(self, self->regs.byte.y)));
+    break;
+  case 0xb2:
+    cpu_65c02_ld(self, CPU_65C02_EA_A, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page_indirect(self)));
+    break;
+  case 0xb3:
+    cpu_65c02_illegal_opcode11(self);
+    break;
+  case 0xb4:
+    cpu_65c02_ld(self, CPU_65C02_EA_Y, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page_indexed(self, self->regs.byte.x)));
+    break;
+  case 0xb5:
+    cpu_65c02_ld(self, CPU_65C02_EA_A, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page_indexed(self, self->regs.byte.x)));
+    break;
+  case 0xb6:
+    cpu_65c02_ld(self, CPU_65C02_EA_X, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page_indexed(self, self->regs.byte.y)));
+    break;
+  case 0xb7:
+    cpu_65c02_smb(self, 3, cpu_65c02_ea_zero_page(self));
+    break;
+  case 0xb8:
+    cpu_65c02_cl(self, CPU_65C02_REG_P_BIT_V);
+    break;
+  case 0xb9:
+    cpu_65c02_ld(self, CPU_65C02_EA_A, cpu_65c02_read_byte(self, cpu_65c02_ea_absolute_indexed(self, self->regs.byte.y)));
+    break;
+  case 0xba:
+    cpu_65c02_ld(self, CPU_65C02_EA_X, self->regs.byte.s);
+    break;
+  case 0xbb:
+    cpu_65c02_illegal_opcode11(self);
+    break;
+  case 0xbc:
+    cpu_65c02_ld(self, CPU_65C02_EA_Y, cpu_65c02_read_byte(self, cpu_65c02_ea_absolute_indexed(self, self->regs.byte.x)));
+    break;
+  case 0xbd:
+    cpu_65c02_ld(self, CPU_65C02_EA_A, cpu_65c02_read_byte(self, cpu_65c02_ea_absolute_indexed(self, self->regs.byte.x)));
+    break;
+  case 0xbe:
+    cpu_65c02_ld(self, CPU_65C02_EA_X, cpu_65c02_read_byte(self, cpu_65c02_ea_absolute_indexed(self, self->regs.byte.y)));
+    break;
+  case 0xbf:
+    {
+      int rvalue = cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page(self));
+      cpu_65c02_bra(self, (rvalue >> 3) & 1, cpu_65c02_ea_relative(self));
+    }
+    break;
+  case 0xc0:
+    cpu_65c02_cmp(self, self->regs.byte.y, cpu_65c02_fetch_byte(self));
+    break;
+  case 0xc1:
+    cpu_65c02_cmp(self, self->regs.byte.a, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page_indexed_indirect(self, self->regs.byte.x)));
+    break;
+  case 0xc2:
+    cpu_65c02_illegal_opcode22(self);
+    break;
+  case 0xc3:
+    cpu_65c02_illegal_opcode11(self);
+    break;
+  case 0xc4:
+    cpu_65c02_cmp(self, self->regs.byte.y, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page(self)));
+    break;
+  case 0xc5:
+    cpu_65c02_cmp(self, self->regs.byte.a, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page(self)));
+    break;
+  case 0xc6:
+    cpu_65c02_dec(self, cpu_65c02_ea_zero_page(self));
+    break;
+  case 0xc7:
+    cpu_65c02_smb(self, 4, cpu_65c02_ea_zero_page(self));
+    break;
+  case 0xc8:
+    cpu_65c02_inc(self, CPU_65C02_EA_Y);
+    break;
+  case 0xc9:
+    cpu_65c02_cmp(self, self->regs.byte.a, cpu_65c02_fetch_byte(self));
+    break;
+  case 0xca:
+    cpu_65c02_dec(self, CPU_65C02_EA_X);
+    break;
+  case 0xcb:
+    cpu_65c02_wai(self);
+    break;
+  case 0xcc:
+    cpu_65c02_cmp(self, self->regs.byte.y, cpu_65c02_read_byte(self, cpu_65c02_ea_absolute(self)));
+    break;
+  case 0xcd:
+    cpu_65c02_cmp(self, self->regs.byte.a, cpu_65c02_read_byte(self, cpu_65c02_ea_absolute(self)));
+    break;
+  case 0xce:
+    cpu_65c02_dec(self, cpu_65c02_ea_absolute(self));
+    break;
+  case 0xcf:
+    {
+      int rvalue = cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page(self));
+      cpu_65c02_bra(self, (rvalue >> 4) & 1, cpu_65c02_ea_relative(self));
+    }
+    break;
+  case 0xd0:
+    cpu_65c02_bra(self, !self->regs.bit.zf, cpu_65c02_ea_relative(self));
+    break;
+  case 0xd1:
+    cpu_65c02_cmp(self, self->regs.byte.a, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page_indirect_indexed(self, self->regs.byte.y)));
+    break;
+  case 0xd2:
+    cpu_65c02_cmp(self, self->regs.byte.a, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page_indirect(self)));
+    break;
+  case 0xd3:
+    cpu_65c02_illegal_opcode11(self);
+    break;
+  case 0xd4:
+    cpu_65c02_illegal_opcode24(self);
+    break;
+  case 0xd5:
+    cpu_65c02_cmp(self, self->regs.byte.a, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page_indexed(self, self->regs.byte.x)));
+    break;
+  case 0xd6:
+    cpu_65c02_dec(self, cpu_65c02_ea_zero_page_indexed(self, self->regs.byte.x));
+    break;
+  case 0xd7:
+    cpu_65c02_smb(self, 5, cpu_65c02_ea_zero_page(self));
+    break;
+  case 0xd8:
+    cpu_65c02_cl(self, CPU_65C02_REG_P_BIT_D);
+    break;
+  case 0xd9:
+    cpu_65c02_cmp(self, self->regs.byte.a, cpu_65c02_read_byte(self, cpu_65c02_ea_absolute_indexed(self, self->regs.byte.y)));
+    break;
+  case 0xda:
+    cpu_65c02_ph(self, self->regs.byte.x);
+    break;
+  case 0xdb:
+    cpu_65c02_illegal_opcode11(self);
+    break;
+  case 0xdc:
+    cpu_65c02_illegal_opcode34(self);
+    break;
+  case 0xdd:
+    cpu_65c02_cmp(self, self->regs.byte.a, cpu_65c02_read_byte(self, cpu_65c02_ea_absolute_indexed(self, self->regs.byte.x)));
+    break;
+  case 0xde:
+    cpu_65c02_dec(self, cpu_65c02_ea_absolute_indexed(self, self->regs.byte.x));
+    break;
+  case 0xdf:
+    {
+      int rvalue = cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page(self));
+      cpu_65c02_bra(self, (rvalue >> 5) & 1, cpu_65c02_ea_relative(self));
+    }
+    break;
+  case 0xe0:
+    cpu_65c02_cmp(self, self->regs.byte.x, cpu_65c02_fetch_byte(self));
+    break;
+  case 0xe1:
+    cpu_65c02_sbc(self, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page_indexed_indirect(self, self->regs.byte.x)));
+    break;
+  case 0xe2:
+    cpu_65c02_illegal_opcode22(self);
+    break;
+  case 0xe3:
+    cpu_65c02_illegal_opcode11(self);
+    break;
+  case 0xe4:
+    cpu_65c02_cmp(self, self->regs.byte.x, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page(self)));
+    break;
+  case 0xe5:
+    cpu_65c02_sbc(self, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page(self)));
+    break;
+  case 0xe6:
+    cpu_65c02_inc(self, cpu_65c02_ea_zero_page(self));
+    break;
+  case 0xe7:
+    cpu_65c02_smb(self, 6, cpu_65c02_ea_zero_page(self));
+    break;
+  case 0xe8:
+    cpu_65c02_inc(self, CPU_65C02_EA_X);
+    break;
+  case 0xe9:
+    cpu_65c02_sbc(self, cpu_65c02_fetch_byte(self));
+    break;
+  case 0xea:
+    cpu_65c02_nop(self);
+    break;
+  case 0xeb:
+    cpu_65c02_illegal_opcode11(self);
+    break;
+  case 0xec:
+    cpu_65c02_cmp(self, self->regs.byte.x, cpu_65c02_read_byte(self, cpu_65c02_ea_absolute(self)));
+    break;
+  case 0xed:
+    cpu_65c02_sbc(self, cpu_65c02_read_byte(self, cpu_65c02_ea_absolute(self)));
+    break;
+  case 0xee:
+    cpu_65c02_inc(self, cpu_65c02_ea_absolute(self));
+    break;
+  case 0xef:
+    {
+      int rvalue = cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page(self));
+      cpu_65c02_bra(self, (rvalue >> 6) & 1, cpu_65c02_ea_relative(self));
+    }
+    break;
+  case 0xf0:
+    cpu_65c02_bra(self, self->regs.bit.zf, cpu_65c02_ea_relative(self));
+    break;
+  case 0xf1:
+    cpu_65c02_sbc(self, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page_indirect_indexed(self, self->regs.byte.y)));
+    break;
+  case 0xf2:
+    cpu_65c02_sbc(self, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page_indirect(self)));
+    break;
+  case 0xf3:
+    cpu_65c02_illegal_opcode11(self);
+    break;
+  case 0xf4:
+    cpu_65c02_illegal_opcode24(self);
+    break;
+  case 0xf5:
+    cpu_65c02_sbc(self, cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page_indexed(self, self->regs.byte.x)));
+    break;
+  case 0xf6:
+    cpu_65c02_inc(self, cpu_65c02_ea_zero_page_indexed(self, self->regs.byte.x));
+    break;
+  case 0xf7:
+    cpu_65c02_smb(self, 7, cpu_65c02_ea_zero_page(self));
+    break;
+  case 0xf8:
+    cpu_65c02_se(self, CPU_65C02_REG_P_BIT_D);
+    break;
+  case 0xf9:
+    cpu_65c02_sbc(self, cpu_65c02_read_byte(self, cpu_65c02_ea_absolute_indexed(self, self->regs.byte.y)));
+    break;
+  case 0xfa:
+    cpu_65c02_pl(self, CPU_65C02_EA_X);
+    break;
+  case 0xfb:
+    cpu_65c02_illegal_opcode11(self);
+    break;
+  case 0xfc:
+    cpu_65c02_illegal_opcode34(self);
+    break;
+  case 0xfd:
+    cpu_65c02_sbc(self, cpu_65c02_read_byte(self, cpu_65c02_ea_absolute_indexed(self, self->regs.byte.x)));
+    break;
+  case 0xfe:
+    cpu_65c02_inc(self, cpu_65c02_ea_absolute_indexed(self, self->regs.byte.x));
+    break;
+  case 0xff:
+    {
+      int rvalue = cpu_65c02_read_byte(self, cpu_65c02_ea_zero_page(self));
+      cpu_65c02_bra(self, (rvalue >> 7) & 1, cpu_65c02_ea_relative(self));
+    }
+    break;
+  }
+}
diff --git a/emu_65c02/cpu_65c02.h b/emu_65c02/cpu_65c02.h
new file mode 100644 (file)
index 0000000..0a506db
--- /dev/null
@@ -0,0 +1,535 @@
+#ifndef _CPU_65C02_H
+#define _CPU_65C02_H
+
+#include <endian.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+// gcc specific
+#ifndef ALWAYS_INLINE
+#define ALWAYS_INLINE __attribute__((always_inline))
+#endif
+
+#define CPU_65C02_NMI_VECTOR 0xfffa
+#define CPU_65C02_RESET_VECTOR 0xfffc
+#define CPU_65C02_IRQ_VECTOR 0xfffe
+
+// bits within REG_P
+#define CPU_65C02_REG_P_BIT_C 0
+#define CPU_65C02_REG_P_BIT_Z 1
+#define CPU_65C02_REG_P_BIT_I 2
+#define CPU_65C02_REG_P_BIT_D 3
+#define CPU_65C02_REG_P_BIT_B 4
+#define CPU_65C02_REG_P_BIT_V 6
+#define CPU_65C02_REG_P_BIT_N 7
+
+// special memory locations (negative address)
+#define CPU_65C02_EA_PC (-8)
+#define CPU_65C02_EA_A (-6)
+#define CPU_65C02_EA_X (-5)
+#define CPU_65C02_EA_Y (-4)
+#define CPU_65C02_EA_S (-3)
+#define CPU_65C02_EA_P (-2)
+#define CPU_65C02_EA_IFLAGS (-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_65c02_regs {
+#if __BYTE_ORDER == __BIG_ENDIAN
+  struct {
+    uint8_t _fill_iflags : 4;
+    uint8_t stp_flag : 1;
+    uint8_t wai_flag : 1;
+    uint8_t irq_pending : 1;
+    uint8_t nmi_pending : 1;
+    uint8_t nf : 1;
+    uint8_t vf : 1;
+    uint8_t _fill_5f: 1;
+    uint8_t _fill_4f: 1;
+    uint8_t df : 1;
+    uint8_t _if : 1;
+    uint8_t zf : 1;
+    uint8_t cf : 1;
+    uint8_t _fill_s;
+    uint8_t _fill_y;
+    uint8_t _fill_x;
+    uint8_t _fill_a;
+    uint16_t _fill_pc;
+  } bit;
+  struct {
+    uint8_t iflags;
+    uint8_t p;
+    uint8_t s;
+    uint8_t y;
+    uint8_t x;
+    uint8_t a;
+    uint16_t _fill_pc;
+  } byte;
+  struct {
+    uint8_t _fill_iflags;
+    uint8_t _fill_p;
+    uint8_t _fill_s;
+    uint8_t _fill_y;
+    uint8_t _fill_x;
+    uint8_t _fill_a;
+    uint16_t pc;
+  } word;
+  uint8_t mem_be[8];
+#else
+  struct {
+    uint16_t _fill_pc;
+    uint8_t _fill_a;
+    uint8_t _fill_x;
+    uint8_t _fill_y;
+    uint8_t _fill_s;
+    uint8_t cf : 1;
+    uint8_t zf : 1;
+    uint8_t _if : 1;
+    uint8_t df : 1;
+    uint8_t _fill_4f: 1;
+    uint8_t _fill_5f: 1;
+    uint8_t vf : 1;
+    uint8_t nf : 1;
+    uint8_t nmi_pending : 1;
+    uint8_t irq_pending : 1;
+    uint8_t wai_flag : 1;
+    uint8_t stp_flag : 1;
+    uint8_t _fill_iflags : 4;
+  } bit;
+  struct {
+    uint16_t _fill_pc;
+    uint8_t a;
+    uint8_t x;
+    uint8_t y;
+    uint8_t s;
+    uint8_t p;
+    uint8_t iflags;
+  } byte;
+  struct {
+    uint16_t pc;
+    uint8_t _fill_a;
+    uint8_t _fill_x;
+    uint8_t _fill_y;
+    uint8_t _fill_s;
+    uint8_t _fill_p;
+    uint8_t _fill_iflags;
+  } word;
+  uint8_t mem_le[8];
+#endif
+};
+
+struct cpu_65c02 {
+  int cycles;
+  uint8_t (*read_byte)(void *context, uint16_t addr);
+  void *read_byte_context;
+  void (*write_byte)(void *context, uint16_t addr, uint8_t data);
+  void *write_byte_context;
+  union cpu_65c02_regs regs;
+};
+
+// memory or special memory access
+static ALWAYS_INLINE uint8_t cpu_65c02_read_byte(struct cpu_65c02 *self, int32_t addr) {
+  if (addr < 0)
+#if __BYTE_ORDER == __BIG_ENDIAN
+    return self->regs.mem_be[~addr];
+#else
+    return self->regs.mem_le[sizeof(union cpu_65c02_regs) + addr];
+#endif
+  self->cycles += 1;
+  return self->read_byte(self->read_byte_context, addr & 0xffff);
+}
+
+static ALWAYS_INLINE uint16_t cpu_65c02_read_word(struct cpu_65c02 *self, int32_t addr) {
+  int data = cpu_65c02_read_byte(self, addr);
+  return data | (cpu_65c02_read_byte(self, addr + 1) << 8);
+}
+
+static ALWAYS_INLINE uint16_t cpu_65c02_read_word_zero_page(struct cpu_65c02 *self, uint8_t addr) {
+  int data = cpu_65c02_read_byte(self, addr & 0xff);
+  return data | (cpu_65c02_read_byte(self, (addr + 1) & 0xff) << 8);
+}
+
+static ALWAYS_INLINE void cpu_65c02_write_byte(struct cpu_65c02 *self, int32_t addr, uint8_t 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_65c02_regs) + addr] = data;
+#endif
+  else
+    self->write_byte(self->write_byte_context, addr, data);
+}
+
+static ALWAYS_INLINE void cpu_65c02_write_word(struct cpu_65c02 *self, int32_t addr, uint16_t data) {
+  cpu_65c02_write_byte(self, addr, data & 0xff);
+  cpu_65c02_write_byte(self, addr + 1, data >> 8);
+}
+
+static ALWAYS_INLINE uint8_t cpu_65c02_fetch_byte(struct cpu_65c02 *self) {
+  int data = cpu_65c02_read_byte(self, self->regs.word.pc++);
+  return data;
+}
+
+static ALWAYS_INLINE uint16_t cpu_65c02_fetch_word(struct cpu_65c02 *self) {
+  int data = cpu_65c02_fetch_byte(self);
+  return data | (cpu_65c02_fetch_byte(self) << 8);
+}
+
+static ALWAYS_INLINE void cpu_65c02_push_byte(struct cpu_65c02 *self, uint8_t data) {
+  cpu_65c02_write_byte(self, self->regs.byte.s-- | 0x100, data);
+}
+
+static ALWAYS_INLINE void cpu_65c02_push_word(struct cpu_65c02 *self, uint16_t data) {
+  cpu_65c02_push_byte(self, data >> 8);
+  cpu_65c02_push_byte(self, data & 0xff);
+}
+
+static ALWAYS_INLINE uint8_t cpu_65c02_pop_byte(struct cpu_65c02 *self) {
+  return cpu_65c02_read_byte(self, ++self->regs.byte.s | 0x100);
+}
+
+static ALWAYS_INLINE uint16_t cpu_65c02_pop_word(struct cpu_65c02 *self) {
+  int data = cpu_65c02_pop_byte(self);
+  return data | (cpu_65c02_pop_byte(self) << 8);
+}
+
+// effective address calculation
+static ALWAYS_INLINE uint16_t cpu_65c02_ea_absolute(struct cpu_65c02 *self) {
+  return cpu_65c02_fetch_word(self);
+}
+
+static ALWAYS_INLINE uint16_t cpu_65c02_ea_absolute_indexed(struct cpu_65c02 *self, uint8_t rvalue) {
+  int addr = cpu_65c02_ea_absolute(self);
+  self->cycles += ((addr & 0xff) + (rvalue & 0xff)) >> 8;
+  return addr + rvalue;
+}
+
+static ALWAYS_INLINE uint16_t cpu_65c02_ea_absolute_indexed_indirect(struct cpu_65c02 *self, uint8_t rvalue) {
+  return cpu_65c02_read_word(self, cpu_65c02_ea_absolute_indexed(self, rvalue));
+}
+
+static ALWAYS_INLINE uint16_t cpu_65c02_ea_absolute_indirect(struct cpu_65c02 *self) {
+  return cpu_65c02_read_word(self, cpu_65c02_ea_absolute(self));
+}
+
+static ALWAYS_INLINE int8_t cpu_65c02_ea_relative(struct cpu_65c02 *self) {
+  return (int8_t)cpu_65c02_fetch_byte(self);
+}
+
+static ALWAYS_INLINE uint8_t cpu_65c02_ea_zero_page(struct cpu_65c02 *self) {
+  return cpu_65c02_fetch_byte(self);
+}
+
+static ALWAYS_INLINE uint8_t cpu_65c02_ea_zero_page_indexed(struct cpu_65c02 *self, uint8_t rvalue) {
+  return (cpu_65c02_ea_zero_page(self) + rvalue) & 0xff;
+}
+
+static ALWAYS_INLINE uint16_t cpu_65c02_ea_zero_page_indexed_indirect(struct cpu_65c02 *self, uint8_t rvalue) {
+  return cpu_65c02_read_word_zero_page(self, cpu_65c02_ea_zero_page_indexed(self, rvalue));
+}
+
+static ALWAYS_INLINE uint16_t cpu_65c02_ea_zero_page_indirect(struct cpu_65c02 *self) {
+  return cpu_65c02_read_word_zero_page(self, cpu_65c02_ea_zero_page(self));
+}
+
+static ALWAYS_INLINE uint16_t cpu_65c02_ea_zero_page_indirect_indexed(struct cpu_65c02 *self, uint8_t rvalue) {
+  int addr = cpu_65c02_read_word_zero_page(self, cpu_65c02_ea_zero_page(self));
+  self->cycles += ((addr & 0xff) + (rvalue & 0xff)) >> 8;
+  return addr + rvalue;
+}
+
+// instruction execute
+static ALWAYS_INLINE void cpu_65c02_adc(struct cpu_65c02 *self, uint8_t rvalue) {
+  int result0, result1, result2;
+  if (self->regs.bit.df) {
+    result0 = self->regs.bit.cf + (self->regs.byte.a & 0xf) + (rvalue & 0xf);
+    if (result0 >= 0xa)
+      result0 = (result0 + 6) & 0x1f;
+    result0 += (self->regs.byte.a & 0x70) + (rvalue & 0x70);
+    result1 = result0 + (self->regs.byte.a & 0x80) + (rvalue & 0x80);
+    result2 = result1;
+    if (result2 >= 0xa0)
+      result2 = (result2 + 0x60) & 0x1ff;
+  }
+  else {
+    result0 = self->regs.bit.cf + (self->regs.byte.a & 0x7f) + (rvalue & 0x7f);
+    result1 = result0 + (self->regs.byte.a & 0x80) + (rvalue & 0x80);
+    result2 = result1;
+  }
+  self->regs.byte.a = result2 & 0xff;
+  self->regs.bit.cf = result2 >> 8;
+  self->regs.bit.zf = (result2 & 0xff) == 0;
+  self->regs.bit.vf = ((result0 >> 7) ^ (result1 >> 8)) & 1;
+  self->regs.bit.nf = (result2 >> 7) & 1;
+}
+
+static ALWAYS_INLINE void cpu_65c02_and(struct cpu_65c02 *self, uint8_t rvalue) {
+  int result = self->regs.byte.a & rvalue;
+  self->regs.byte.a = result;
+  self->regs.bit.zf = result == 0;
+  self->regs.bit.nf = result >> 7;
+}
+
+static ALWAYS_INLINE void cpu_65c02_asl(struct cpu_65c02 *self, int32_t lvalue) {
+  int result = cpu_65c02_read_byte(self, lvalue) << 1;
+  ++self->cycles;
+  cpu_65c02_write_byte(self, lvalue, result & 0xff);
+  self->regs.bit.cf = result >> 8;
+  self->regs.bit.zf = (result & 0xff) == 0;
+  self->regs.bit.nf = (result >> 7) & 1;
+}
+
+static ALWAYS_INLINE void cpu_65c02_bit(struct cpu_65c02 *self, uint8_t rvalue) {
+  int result = self->regs.byte.a & rvalue;
+  self->regs.bit.zf = result == 0;
+  self->regs.bit.vf = (rvalue >> 6) & 1;
+  self->regs.bit.nf = (rvalue >> 7) & 1;
+}
+
+static ALWAYS_INLINE void cpu_65c02_bit_imm(struct cpu_65c02 *self, uint8_t rvalue) {
+  int result = self->regs.byte.a & rvalue;
+  self->regs.bit.zf = result == 0;
+}
+
+static ALWAYS_INLINE void cpu_65c02_bra(struct cpu_65c02 *self, bool pred, int8_t rvalue) {
+  if (pred) {
+    self->cycles += ((self->regs.word.pc & 0xff) + (rvalue & 0xff)) >> 8;
+    self->regs.word.pc += rvalue;
+  }
+}
+
+static ALWAYS_INLINE void cpu_65c02_cl(struct cpu_65c02 *self, int n) {
+  self->regs.byte.p &= ~(1 << n);
+}
+
+static ALWAYS_INLINE void cpu_65c02_cmp(struct cpu_65c02 *self, uint8_t rvalue0, uint8_t rvalue1) {
+  rvalue1 ^= 0xff;
+  int result = 1 + rvalue0 + rvalue1;
+  self->regs.bit.cf = result >> 8;
+  self->regs.bit.zf = (result & 0xff) == 0;
+  self->regs.bit.nf = (result >> 7) & 1;
+}
+
+static ALWAYS_INLINE void cpu_65c02_dec(struct cpu_65c02 *self, int32_t lvalue) {
+  int result = (cpu_65c02_read_byte(self, lvalue) - 1) & 0xff;
+  ++self->cycles;
+  cpu_65c02_write_byte(self, lvalue, result);
+  self->regs.bit.zf = result == 0;
+  self->regs.bit.nf = result >> 7;
+}
+
+static ALWAYS_INLINE void cpu_65c02_eor(struct cpu_65c02 *self, uint8_t rvalue) {
+  int result = self->regs.byte.a ^ rvalue;
+  self->regs.byte.a = result;
+  self->regs.bit.zf = result == 0;
+  self->regs.bit.nf = result >> 7;
+}
+
+static ALWAYS_INLINE void cpu_65c02_illegal_opcode11(struct cpu_65c02 *self) {
+}
+
+static ALWAYS_INLINE void cpu_65c02_illegal_opcode22(struct cpu_65c02 *self) {
+  ++self->cycles;
+  self->regs.word.pc += 1;
+}
+
+static ALWAYS_INLINE void cpu_65c02_illegal_opcode23(struct cpu_65c02 *self) {
+  self->cycles += 2;
+  self->regs.word.pc += 1;
+}
+
+static ALWAYS_INLINE void cpu_65c02_illegal_opcode24(struct cpu_65c02 *self) {
+  self->cycles += 3;
+  self->regs.word.pc += 1;
+}
+
+static ALWAYS_INLINE void cpu_65c02_illegal_opcode34(struct cpu_65c02 *self) {
+  self->cycles += 3;
+  self->regs.word.pc += 2;
+}
+
+static ALWAYS_INLINE void cpu_65c02_illegal_opcode38(struct cpu_65c02 *self) {
+  self->cycles += 7;
+  self->regs.word.pc += 2;
+}
+
+static ALWAYS_INLINE void cpu_65c02_inc(struct cpu_65c02 *self, int32_t lvalue) {
+  int result = (cpu_65c02_read_byte(self, lvalue) + 1) & 0xff;
+  ++self->cycles;
+  cpu_65c02_write_byte(self, lvalue, result);
+  self->regs.bit.zf = result == 0;
+  self->regs.bit.nf = result >> 7;
+}
+
+static ALWAYS_INLINE void cpu_65c02_irq(struct cpu_65c02 *self, bool brk, int32_t lvalue) {
+  ++self->cycles;
+  cpu_65c02_push_word(self, self->regs.word.pc);
+  cpu_65c02_push_byte(
+    self,
+    ((self->regs.byte.p) & ~(1 << CPU_65C02_REG_P_BIT_B)) |
+    (brk << CPU_65C02_REG_P_BIT_B)
+  );
+  self->regs.word.pc = cpu_65c02_read_word(self, lvalue);
+  self->regs.bit._if = true;
+  self->regs.bit.df = false;
+}
+
+static ALWAYS_INLINE void cpu_65c02_jmp(struct cpu_65c02 *self, int32_t lvalue) {
+  self->regs.word.pc = lvalue;
+}
+
+static ALWAYS_INLINE void cpu_65c02_jsr(struct cpu_65c02 *self, int32_t lvalue) {
+  cpu_65c02_push_word(self, (self->regs.word.pc - 1) & 0xffff);
+  self->regs.word.pc = lvalue;
+}
+
+static ALWAYS_INLINE void cpu_65c02_ld(struct cpu_65c02 *self, int32_t lvalue, uint8_t rvalue) {
+  cpu_65c02_write_byte(self, lvalue, rvalue);
+  self->regs.bit.zf = rvalue == 0;
+  self->regs.bit.nf = (rvalue >> 7) & 1;
+}
+
+static ALWAYS_INLINE void cpu_65c02_lsr(struct cpu_65c02 *self, int32_t lvalue) {
+  int result = cpu_65c02_read_byte(self, lvalue);
+  ++self->cycles;
+  cpu_65c02_write_byte(self, lvalue, result >> 1);
+  self->regs.bit.cf = result & 1;
+  self->regs.bit.zf = (result & 0xfe) == 0;
+  self->regs.bit.nf = false;
+}
+
+static ALWAYS_INLINE void cpu_65c02_nop(struct cpu_65c02 *self) {
+}
+
+static ALWAYS_INLINE void cpu_65c02_ora(struct cpu_65c02 *self, uint8_t rvalue) {
+  int result = self->regs.byte.a | rvalue;
+  self->regs.byte.a = result;
+  self->regs.bit.zf = result == 0;
+  self->regs.bit.nf = result >> 7;
+}
+
+static ALWAYS_INLINE void cpu_65c02_ph(struct cpu_65c02 *self, uint8_t rvalue) {
+  cpu_65c02_push_byte(self, rvalue);
+}
+
+static ALWAYS_INLINE void cpu_65c02_pl(struct cpu_65c02 *self, int32_t lvalue) {
+  int result = cpu_65c02_pop_byte(self);
+  cpu_65c02_write_byte(self, lvalue, result);
+  self->regs.bit.zf = result == 0;
+  self->regs.bit.nf = result >> 7;
+}
+
+static ALWAYS_INLINE void cpu_65c02_plp(struct cpu_65c02 *self) {
+  self->regs.byte.p = cpu_65c02_pop_byte(self) | 0x30;
+}
+
+static ALWAYS_INLINE void cpu_65c02_rmb(struct cpu_65c02 *self, int n, int32_t lvalue) {
+  int result = cpu_65c02_read_byte(self, lvalue) & ~(1 << n);
+  ++self->cycles;
+  cpu_65c02_write_byte(self, lvalue, result);
+}
+
+static ALWAYS_INLINE void cpu_65c02_rol(struct cpu_65c02 *self, int32_t lvalue) {
+  int result = self->regs.bit.cf | (cpu_65c02_read_byte(self, lvalue) << 1);
+  ++self->cycles;
+  cpu_65c02_write_byte(self, lvalue, result & 0xff);
+  self->regs.bit.cf = result >> 8;
+  self->regs.bit.zf = (result & 0xff) == 0;
+  self->regs.bit.nf = (result >> 7) & 1;
+}
+
+static ALWAYS_INLINE void cpu_65c02_ror(struct cpu_65c02 *self, int32_t lvalue) {
+  int result = cpu_65c02_read_byte(self, lvalue) | (self->regs.bit.cf << 8);
+  ++self->cycles;
+  cpu_65c02_write_byte(self, lvalue, result >> 1);
+  self->regs.bit.cf = result & 1;
+  self->regs.bit.zf = (result & 0x1fe) == 0;
+  self->regs.bit.nf = (result >> 8) & 1;
+}
+
+static ALWAYS_INLINE void cpu_65c02_rti(struct cpu_65c02 *self) {
+  self->regs.byte.p = cpu_65c02_pop_byte(self) | 0x30;
+  self->regs.word.pc = cpu_65c02_pop_word(self);
+}
+
+static ALWAYS_INLINE void cpu_65c02_rts(struct cpu_65c02 *self) {
+  self->regs.word.pc = (cpu_65c02_pop_word(self) + 1) & 0xffff;
+}
+
+static ALWAYS_INLINE void cpu_65c02_sbc(struct cpu_65c02 *self, uint8_t rvalue) {
+  rvalue ^= 0xff;
+  int result0, result1, result2;
+  if (self->regs.bit.df) {
+    result0 = self->regs.bit.cf + (self->regs.byte.a & 0xf) + (rvalue & 0xf);
+    if (result0 < 0x10)
+      result0 = (result0 - 6) & 0x1f;
+    result0 += (self->regs.byte.a & 0x70) + (rvalue & 0x70);
+    result1 = result0 + (self->regs.byte.a & 0x80) + (rvalue & 0x80);
+    result2 = result1;
+    if (result2 < 0x100)
+      result2 = (result2 - 0x60) & 0x1ff;
+  }
+  else {
+    result0 = self->regs.bit.cf + (self->regs.byte.a & 0x7f) + (rvalue & 0x7f);
+    result1 = result0 + (self->regs.byte.a & 0x80) + (rvalue & 0x80);
+    result2 = result1;
+  }
+  self->regs.byte.a = result2 & 0xff;
+  self->regs.bit.cf = result2 >> 8;
+  self->regs.bit.zf = (result2 & 0xff) == 0;
+  self->regs.bit.vf = ((result0 >> 7) ^ (result1 >> 8)) & 1;
+  self->regs.bit.nf = (result2 >> 7) & 1;
+}
+
+static ALWAYS_INLINE void cpu_65c02_se(struct cpu_65c02 *self, int n) {
+  self->regs.byte.p |= 1 << n;
+}
+
+static ALWAYS_INLINE void cpu_65c02_smb(struct cpu_65c02 *self, int n, int32_t lvalue) {
+  int result = cpu_65c02_read_byte(self, lvalue) | (1 << n);
+  ++self->cycles;
+  cpu_65c02_write_byte(self, lvalue, result);
+}
+
+static ALWAYS_INLINE void cpu_65c02_st(struct cpu_65c02 *self, uint8_t rvalue, int32_t lvalue) {
+  cpu_65c02_write_byte(self, lvalue, rvalue);
+}
+
+static ALWAYS_INLINE void cpu_65c02_stp(struct cpu_65c02 *self) {
+  self->regs.bit.stp_flag = true;
+}
+
+static ALWAYS_INLINE void cpu_65c02_trb(struct cpu_65c02 *self, int32_t lvalue) {
+  int result = cpu_65c02_read_byte(self, lvalue);
+  ++self->cycles;
+  cpu_65c02_write_byte(self, lvalue, result & ~self->regs.byte.a);
+  result &= self->regs.byte.a;
+  self->regs.bit.zf = result == 0;
+}
+
+static ALWAYS_INLINE void cpu_65c02_tsb(struct cpu_65c02 *self, int32_t lvalue) {
+  int result = cpu_65c02_read_byte(self, lvalue);
+  ++self->cycles;
+  cpu_65c02_write_byte(self, lvalue, result | self->regs.byte.a);
+  result &= self->regs.byte.a;
+  self->regs.bit.zf = result == 0;
+}
+
+static ALWAYS_INLINE void cpu_65c02_wai(struct cpu_65c02 *self) {
+  self->regs.bit.wai_flag = true;
+}
+
+// prototypes
+void cpu_65c02_init(
+  struct cpu_65c02 *self,
+  uint8_t (*read_byte)(void *context, uint16_t addr),
+  void *read_byte_context,
+  void (*write_byte)(void *context, uint16_t addr, uint8_t data),
+  void *write_byte_context
+);
+void cpu_65c02_reset(struct cpu_65c02 *self);
+void cpu_65c02_execute(struct cpu_65c02 *self);
+#endif
index 461936a..abcfddc 100644 (file)
@@ -13,7 +13,7 @@
 #include <unistd.h>
 #include <SDL2/SDL.h>
 #include "stty_sane.h"
-#include "vrEmu6502/src/vrEmu6502.h"
+#include "cpu_65c02.h"
 
 #define APPLE_IIE 1
 #define APPLE_WIDTH 560
@@ -153,7 +153,8 @@ uint32_t palette[0x10] = {
 // can make this green or amber to be more realistic
 uint32_t mono_palette[2] = {0xff000000, 0xffffffff};
 
-VrEmu6502 *cpu;
+struct cpu_65c02 cpu;
+bool jam_flag;
 
 #if APPLE_IIE
 #include "cg_default/cg_lowercase_20.inc"
@@ -539,7 +540,7 @@ void dos(char *line) {
   }
 }
 
-uint8_t mem_read(uint16_t addr0/*, bool isDbg*/) {
+uint8_t read_byte(void *context, uint16_t addr0) {
   // for joystick, for now count memory accesses as proxy for cycles
   if (joystick_count < JOYSTICK_COUNT) {
     ++joystick_count;
@@ -607,10 +608,10 @@ uint8_t mem_read(uint16_t addr0/*, bool isDbg*/) {
     //if (addr >= 0x8c08 && addr < 0x8e00) { // data fetch
     //if (addr >= 0x9580 && addr < 0x9600) { // data fetch
     if (addr >= 0x5c00 && addr < 0x5c1b) { // data fetch
-      int pc = vrEmu6502GetPC(cpu);
+      int pc = cpu.regs.word.pc;
       fprintf(stderr, "pc=%04x addr=%04x\n", pc, addr);
       exit_flag = 0x101;
-      vrEmu6502Jam(cpu);
+      jam_flag = true;
     } 
 #endif
 
@@ -632,8 +633,8 @@ uint8_t mem_read(uint16_t addr0/*, bool isDbg*/) {
 
 #if 0 // draw object
     if (addr == 0x1ab4) { // opcode fetch
-      int pc = vrEmu6502GetPC(cpu);
-      int object = vrEmu6502GetX(cpu);
+      int pc = cpu.regs.word.pc;
+      int object = cpu.regs.byte.x;
       int shape = mem[0xabd0 + object]; // object_shape
 
       const struct {int shape; const char *name;} shapes[138] = {
@@ -800,12 +801,12 @@ uint8_t mem_read(uint16_t addr0/*, bool isDbg*/) {
 
 #if 0 // start at mission
     if (addr == 0x1726) // opcode fetch
-      vrEmu6502SetX(cpu, start_at_mission);
+      cpu.regs.byte.x = start_at_mission;
 #endif
 
 #if MEM_TRACE
     {
-      int pc = cpu ? vrEmu6502GetPC(cpu) : 0;
+      int pc = cpu ? cpu.regs.word.pc : 0;
       fprintf(stderr, "pc=%04x addr=%05x rd=%02x\n", pc, addr, mem[addr]);
     }
 #endif
@@ -899,7 +900,7 @@ uint8_t mem_read(uint16_t addr0/*, bool isDbg*/) {
       }
       if (data == 'X' - 0x40) { // count == 0 or ctrl-x (unassigned by hrcg)
         exit_flag = 0x101;
-        vrEmu6502Jam(cpu);
+        jam_flag = true;
       }
       return data;
     }
@@ -938,7 +939,7 @@ uint8_t mem_read(uint16_t addr0/*, bool isDbg*/) {
   return 0xff;
 }
 
-void mem_write(uint16_t addr0, uint8_t val) {
+void write_byte(void *context, uint16_t addr0, uint8_t data) {
   // for joystick, for now count memory accesses as proxy for cycles
   if (joystick_count < JOYSTICK_COUNT) {
     ++joystick_count;
@@ -953,16 +954,6 @@ void mem_write(uint16_t addr0, uint8_t val) {
   }
 
   int addr = addr0;
-#if 0 // vectors
-  if (addr >= 4 && addr < 0x80) {
-    int pc = vrEmu6502GetPC(cpu);
-    if (pc != 0x3f52) {
-      fprintf(stderr, "pc=%04x w=%04x v=%02x\n", pc, addr, val);
-      exit(EXIT_FAILURE);
-    }
-  }
-#endif
-
   if ((addr & 0xff00) != IO_PAGE) {
 #if APPLE_IIE
     if (addr < 0x200) {
@@ -993,9 +984,9 @@ void mem_write(uint16_t addr0, uint8_t val) {
         (lc_state & LC_SELECT) == LC_SELECT_ROM_WP
     ) {
 #if MEM_TRACE
-      int pc = cpu ? vrEmu6502GetPC(cpu): 0;
+      int pc = cpu ? cpu.regs.word.pc: 0;
       addr += 0x20000 - 0xc000;
-      fprintf(stderr, "pc=%04x addr=%05x wr=%02x (nop)\n", pc, addr, val);
+      fprintf(stderr, "pc=%04x addr=%05x wr=%02x (nop)\n", pc, addr, data);
 #endif
       return;
     }
@@ -1014,9 +1005,9 @@ void mem_write(uint16_t addr0, uint8_t val) {
         (lc_state & LC_SELECT) == LC_SELECT_ROM_WP
     ) {
 #if MEM_TRACE
-      int pc = cpu ? vrEmu6502GetPC(cpu) : 0;
+      int pc = cpu ? cpu.regs.word.pc : 0;
       addr += 0x10000 - 0xc000;
-      fprintf(stderr, "pc=%04x addr=%05x wr=%02x (nop)\n", pc, addr, val);
+      fprintf(stderr, "pc=%04x addr=%05x wr=%02x (nop)\n", pc, addr, data);
 #endif
       return;
     }
@@ -1026,23 +1017,23 @@ void mem_write(uint16_t addr0, uint8_t val) {
 
 #if MEM_TRACE
     {
-      int pc = cpu ? vrEmu6502GetPC(cpu) : 0;
-      fprintf(stderr, "pc=%04x addr=%05x wr=%02x\n", pc, addr, val);
+      int pc = cpu ? cpu.regs.word.pc : 0;
+      fprintf(stderr, "pc=%04x addr=%05x wr=%02x\n", pc, addr, data);
     }
 #endif
-    mem[addr] = val;
+    mem[addr] = data;
 
 #if 0 // sound effects
     if (addr >= 0xab80 && addr < 0xab90) {
-      int pc = vrEmu6502GetPC(cpu);
-      fprintf(stderr, "pc=%04x w=%04x v=%02x\r\n", pc, addr, val);
+      int pc = cpu.regs.word.pc;
+      fprintf(stderr, "pc=%04x w=%04x v=%02x\r\n", pc, addr, data);
     }
 #endif
 
 #if 0 // decimals
     if (addr >= 0xb0 && addr < 0xc0) {
-      int pc = vrEmu6502GetPC(cpu);
-      fprintf(stderr, "pc=%04x w=%04x v=%02x", pc, addr, val);
+      int pc = cpu.regs.word.pc;
+      fprintf(stderr, "pc=%04x w=%04x v=%02x", pc, addr, data);
       for (int i = 0xb0; i < 0xc0; i += 2) {
         int v = mem[i] + (mem[i + 1] << 8);
         fprintf(stderr, " dec_%02x=%04x", i, v);
@@ -1053,13 +1044,13 @@ void mem_write(uint16_t addr0, uint8_t val) {
 
 #if 0 // demo mode
     if (addr >= 0xf0 && addr < 0xf3) {
-      int pc = vrEmu6502GetPC(cpu);
+      int pc = cpu.regs.word.pc;
       fprintf(
         stderr,
         "pc=%04x w=%04x v=%02x demo_mode=%02x button_state=%02x key_state=%02x\n",
         pc,
         addr,
-        val,
+        data,
         mem[0xf0],
         mem[0xf1],
         mem[0xf2]
@@ -1135,33 +1126,33 @@ void mem_write(uint16_t addr0, uint8_t val) {
     lc_state = addr & 7; // should check RWx2 for write enable
     break;
   case STDOUT_DATA:
-    if (write(fd_out, &val, 1) == -1) {
+    if (write(fd_out, &data, 1) == -1) {
       perror("write()");
       exit(EXIT_FAILURE);
     }
     break;
   case STDERR_DATA:
-    if (write(STDERR_FILENO, &val, 1) == -1) {
+    if (write(STDERR_FILENO, &data, 1) == -1) {
       perror("write()");
       exit(EXIT_FAILURE);
     }
     break;
   case USLEEP_LO:
-    usleep_lo = val;
+    usleep_lo = data;
     break;
   case USLEEP_HI:
-    usleep_count = usleep_lo | (val << 8);
-    vrEmu6502Jam(cpu); // force a screen update before going to sleep
+    usleep_count = usleep_lo | (data << 8);
+    jam_flag = true; // force a screen update before going to sleep
     break;
   case SYS_EXIT:
-    exit_flag = val | 0x100;
-    vrEmu6502Jam(cpu);
+    exit_flag = data | 0x100;
+    jam_flag = true;
     break;
   case DOS_LO:
-    dos_lo = val;
+    dos_lo = data;
     break;
   case DOS_HI:
-    dos((char *)(mem + (dos_lo | (val << 8))));
+    dos((char *)(mem + (dos_lo | (data << 8))));
     break;
   }
 }
@@ -1208,10 +1199,9 @@ int main(int argc, char **argv) {
   char *f0_rom_file = NULL;
   char *f8_rom_file = NULL;
 #endif
-  bool timing = false;
   while (argn < argc) {
     if (strcmp(argv[argn], "--help") == 0) {
-      printf("usage: %s [--cg-rom=file.bin] [--video-rom=file.bin] [--NN-rom=file.bin ...] [--timing] [program.obj[,aNNNN] ...] [-- child-executable [child-argument ...]]\n", argv[0]);
+      printf("usage: %s [--cg-rom=file.bin] [--video-rom=file.bin] [--NN-rom=file.bin ...] [program.obj[,aNNNN] ...] [-- child-executable [child-argument ...]]\n", argv[0]);
       exit(EXIT_FAILURE);
     }
     if (memcmp(argv[argn], "--cg-rom=", 9) == 0)
@@ -1237,8 +1227,6 @@ int main(int argc, char **argv) {
     else if (memcmp(argv[argn], "--f8-rom=", 9) == 0)
       f8_rom_file = argv[argn] + 9;
 #endif
-    else if (strcmp(argv[argn], "--timing") == 0)
-      timing = true;
     else
       break;
     ++argn;
@@ -1312,17 +1300,6 @@ int main(int argc, char **argv) {
     entry_point = load(p);
   }
 
-  // do this before creating the CPU
-  if (entry_point != -1) {
-#if APPLE_IIE
-    mem[RESET_VECTOR + 0x20000 - 0xc000] = (uint8_t)(entry_point & 0xff);
-    mem[RESET_VECTOR + 1 + 0x20000 - 0xc000] = (uint8_t)(entry_point >> 8);
-#else
-    mem[RESET_VECTOR + 0x10000 - 0xc000] = (uint8_t)(entry_point & 0xff);
-    mem[RESET_VECTOR + 1 + 0x10000 - 0xc000] = (uint8_t)(entry_point >> 8);
-#endif
-  }
-
   // open pty and child process if requested
   if (argn < argc) {
     int fd_master = posix_openpt(O_RDWR);
@@ -1567,11 +1544,10 @@ int main(int argc, char **argv) {
       joystick_axes[i] = JOYSTICK_COUNT >> 1;
   }
 
-  cpu = vrEmu6502New(CPU_65C02, mem_read, mem_write);
-  if (cpu == NULL) {
-    perror("malloc()");
-    exit(EXIT_FAILURE);
-  }
+  cpu_65c02_init(&cpu, read_byte, NULL, write_byte, NULL);
+  cpu_65c02_reset(&cpu);
+  if (entry_point != -1)
+    cpu.regs.word.pc = entry_point;
 
 #if TRACE
   memset(pc_pairs, 0xff, N_PC_PAIRS * sizeof(struct pc_pair));
@@ -1588,7 +1564,6 @@ int main(int argc, char **argv) {
 #endif
 
   // main loop
-  long nb_instructions = 0, nb_cycles = 0;
   int update_count = 0;
   float spkr_rc = 0.f;
   while (true) {
@@ -1954,17 +1929,18 @@ int main(int argc, char **argv) {
 #endif
     }
     else {
-      int i, j;
 #if TRACE
-      struct pc_pair pc_pair = {vrEmu6502GetPC(cpu), 0};
-      for (int k = 0; k < CYCLES_PER_SAMPLE; k += j) {
-        i = vrEmu6502RunInstrs(cpu, 1, &j);
-        nb_instructions += i;
-        nb_cycles += j;
-        if (i == 0)
-          goto jam; //break;
-
-        pc_pair.pc1 = vrEmu6502GetPC(cpu);
+      struct pc_pair pc_pair = {cpu.pc, 0};
+#endif
+      for (cpu.cycles = 0; cpu.cycles < CYCLES_PER_SAMPLE; ) {
+        cpu_65c02_execute(&cpu);
+        if (jam_flag) {
+          jam_flag = false;
+          break;
+        }
+
+#if TRACE
+        pc_pair.pc1 = cpu.regs.word.pc;
         bool show_trace = false;
 
         int hash = (19 * pc_pair.pc0 + 37 * pc_pair.pc1) % N_PC_PAIRS;
@@ -1988,11 +1964,11 @@ int main(int argc, char **argv) {
         }
 
         uint8_t regs[N_TRACE_REGS] = {
-          vrEmu6502GetAcc(cpu),
-          vrEmu6502GetX(cpu),
-          vrEmu6502GetY(cpu),
-          vrEmu6502GetStackPointer(cpu),
-          vrEmu6502GetStatus(cpu)
+          cpu.regs.byte.a,
+          cpu.regs.byte.x,
+          cpu.regs.byte.y,
+          cpu.regs.byte.s,
+          cpu.regs.byte.p
         };
         for (int i = 0; i < N_TRACE_REGS; ++i) {
           if (regs[i] < trace[pc_pair.pc1][i].min_unsigned) {
@@ -2059,18 +2035,10 @@ int main(int argc, char **argv) {
             trace[pc_pair.pc1][TRACE_REG_P].max_bitwise
           );
         pc_pair.pc0 = pc_pair.pc1;
-      }
-jam:
-#else
-      i = vrEmu6502RunCycles(cpu, CYCLES_PER_SAMPLE, &j);
-      nb_instructions += i;
-      nb_cycles += j;
-      //if (j < CYCLES_PER_UPDATE)
-      //  break;
 #endif
+      }
       if (exit_flag)
         break;
-      vrEmu6502Unjam(cpu);
 
       // sample tape and speaker outputs at CYCLES_PER_SAMPLE rate
 #if 0
@@ -2090,8 +2058,6 @@ jam:
   }
 
 quit:
-  vrEmu6502Destroy(cpu);
-
   if (joystick)
     SDL_JoystickClose(joystick);
   SDL_CloseAudioDevice(audio_device_id);
@@ -2099,12 +2065,5 @@ quit:
   SDL_DestroyWindow(window);
   SDL_Quit();
 
-  if (timing)
-    fprintf(
-      stderr,
-      "%lu instructions executed on %lu cycles\n",
-      nb_instructions,
-      nb_cycles
-    );
   exit(exit_flag & 0xff);
 }