Implement 65C02 interrupt system
authorNick Downing <nick@ndcode.org>
Sat, 23 Jul 2022 02:09:52 +0000 (12:09 +1000)
committerNick Downing <nick@ndcode.org>
Sat, 23 Jul 2022 02:12:43 +0000 (12:12 +1000)
cpu_65c02.c
cpu_65c02.h
decode_65c02.sed
emu_65c02.c

index 26fc1b3..a6520b1 100644 (file)
@@ -13,7 +13,6 @@ void cpu_65c02_init(
 ) {
   memset(self, 0, sizeof(struct cpu_65c02));
   self->regs.byte.p = 0x30; // unused bits are hard coded to 1
-  self->regs.word.sp = 0x1ff; // unused byte is hard coded to 01
   self->read_byte = read_byte;
   self->read_byte_context = read_byte_context;
   self->write_byte = write_byte;
@@ -22,9 +21,39 @@ void cpu_65c02_init(
 
 // 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:
-    cpu_65c02_brk(self);
+    ++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)));
index 35a188c..0fe8f24 100644 (file)
@@ -3,7 +3,6 @@
 
 #include <stdbool.h>
 #include <stdint.h>
-#include <stdlib.h>
 
 // gcc specific
 #ifndef ALWAYS_INLINE
@@ -19,6 +18,7 @@
 #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
 
@@ -26,8 +26,8 @@
 #define CPU_65C02_EA_A (-8)
 #define CPU_65C02_EA_X (-7)
 #define CPU_65C02_EA_Y (-6)
-#define CPU_65C02_EA_P (-5)
-#define CPU_65C02_EA_S (-4)
+#define CPU_65C02_EA_S (-5)
+#define CPU_65C02_EA_P (-4)
 
 // registers, in same order as special memory locations, but reversed on
 // big endian hardware where special memory address will be complemented
@@ -36,7 +36,11 @@ union cpu_65c02_regs {
 #if __BYTE_ORDER == __BIG_ENDIAN
   struct {
     uint16_t _fill_pc;
-    uint16_t _fill_sp;
+    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;
@@ -45,23 +49,25 @@ union cpu_65c02_regs {
     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;
   } bit;
   struct {
     uint16_t _fill_pc;
-    uint8_t _fill_sph;
-    uint8_t s;
+    uint8_t iflags;
     uint8_t p;
+    uint8_t s;
     uint8_t y;
     uint8_t x;
     uint8_t a;
   } byte;
   struct {
     uint16_t pc;
-    uint16_t sp;
+    uint8_t _fill_iflags;
     uint8_t _fill_p;
+    uint8_t _fill_s;
     uint8_t _fill_y;
     uint8_t _fill_x;
     uint8_t _fill_a;
@@ -72,6 +78,7 @@ union cpu_65c02_regs {
     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;
@@ -80,24 +87,29 @@ union cpu_65c02_regs {
     uint8_t _fill_5f: 1;
     uint8_t vf : 1;
     uint8_t nf : 1;
-    uint16_t _fill_sp;
+    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;
     uint16_t _fill_pc;
   } bit;
   struct {
     uint8_t a;
     uint8_t x;
     uint8_t y;
-    uint8_t p;
     uint8_t s;
-    uint8_t _fill_sph;
+    uint8_t p;
+    uint8_t iflags;
     uint16_t _fill_pc;
   } byte;
   struct {
     uint8_t _fill_a;
     uint8_t _fill_x;
     uint8_t _fill_y;
+    uint8_t _fill_s;
     uint8_t _fill_p;
-    uint16_t sp;
+    uint8_t _fill_iflags;
     uint16_t pc;
   } word;
   uint8_t mem_le[8];
@@ -163,7 +175,7 @@ static ALWAYS_INLINE int cpu_65c02_fetch_word(struct cpu_65c02 *self) {
 }
 
 static ALWAYS_INLINE void cpu_65c02_push_byte(struct cpu_65c02 *self, int data) {
-  cpu_65c02_write_byte(self, self->regs.word.sp, data);
+  cpu_65c02_write_byte(self, self->regs.byte.s | 0x100, data);
   --self->regs.byte.s;
 }
 
@@ -174,7 +186,7 @@ static ALWAYS_INLINE void cpu_65c02_push_word(struct cpu_65c02 *self, int data)
 
 static ALWAYS_INLINE int cpu_65c02_pop_byte(struct cpu_65c02 *self) {
   ++self->regs.byte.s;
-  return cpu_65c02_read_byte(self, self->regs.word.sp);
+  return cpu_65c02_read_byte(self, self->regs.byte.s | 0x100);
 }
 
 static ALWAYS_INLINE int cpu_65c02_pop_word(struct cpu_65c02 *self) {
@@ -288,15 +300,6 @@ static ALWAYS_INLINE void cpu_65c02_bra(struct cpu_65c02 *self, bool pred, int l
   }
 }
 
-static ALWAYS_INLINE void cpu_65c02_brk(struct cpu_65c02 *self) {
-  ++self->cycles;
-  cpu_65c02_push_word(self, (self->regs.word.pc + 1) & 0xffff);
-  cpu_65c02_push_byte(self, self->regs.byte.p);
-  self->regs.word.pc = cpu_65c02_read_word(self, CPU_65C02_IRQ_VECTOR);
-  self->regs.bit._if = true;
-  self->regs.bit.df = false;
-}
-
 static ALWAYS_INLINE void cpu_65c02_cl(struct cpu_65c02 *self, int n) {
   self->regs.byte.p &= ~(1 << n);
 }
@@ -360,6 +363,19 @@ static ALWAYS_INLINE void cpu_65c02_inc(struct cpu_65c02 *self, int lvalue) {
   self->regs.bit.nf = result >> 7;
 }
 
+static ALWAYS_INLINE void cpu_65c02_irq(struct cpu_65c02 *self, bool brk, int 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, int lvalue) {
   self->regs.word.pc = lvalue;
 }
@@ -482,7 +498,7 @@ static ALWAYS_INLINE void cpu_65c02_st(struct cpu_65c02 *self, int rvalue, int l
 }
 
 static ALWAYS_INLINE void cpu_65c02_stp(struct cpu_65c02 *self) {
-  abort();
+  self->regs.bit.stp_flag = true;
 }
 
 static ALWAYS_INLINE void cpu_65c02_trb(struct cpu_65c02 *self, int lvalue) {
@@ -502,7 +518,7 @@ static ALWAYS_INLINE void cpu_65c02_tsb(struct cpu_65c02 *self, int lvalue) {
 }
 
 static ALWAYS_INLINE void cpu_65c02_wai(struct cpu_65c02 *self) {
-  abort();
+  self->regs.bit.wai_flag = true;
 }
 
 // prototypes
index 9a6e717..d813378 100644 (file)
@@ -21,6 +21,7 @@ s/cpu_65c02_in\([xy]\)(self/cpu_65c02_inc(self, CPU_65C02_EA_\1/
 s/cpu_65c02_de\([xy]\)(self/cpu_65c02_dec(self, CPU_65C02_EA_\1/
 s/cpu_65c02_cmp(self/cpu_65c02_cmp(self, self->regs.byte.a/
 s/cpu_65c02_cp\([xy]\)(self/cpu_65c02_cmp(self, self->regs.byte.\1/
+s/^    cpu_65c02_brk(self);$/    ++self->regs.word.pc;\n    cpu_65c02_irq(self, true, CPU_65C02_IRQ_VECTOR);/
 s/CPU_65C02_EA_a/CPU_65C02_EA_A/g
 s/CPU_65C02_EA_x/CPU_65C02_EA_X/g
 s/CPU_65C02_EA_y/CPU_65C02_EA_Y/g
index 6bd5470..583865d 100644 (file)
@@ -206,6 +206,7 @@ int main(int argc, char **argv) {
 #else
   struct cpu_65c02 cpu;
   cpu_65c02_init(&cpu, read_byte, NULL, write_byte, NULL);
+  cpu.regs.byte.s = 0xff;
   cpu.regs.word.pc = entry_point;
 
   while (true) {