Refactoring 65C02 registers and branches
authorNick Downing <nick@ndcode.org>
Thu, 21 Jul 2022 00:46:01 +0000 (10:46 +1000)
committerNick Downing <nick@ndcode.org>
Thu, 21 Jul 2022 02:43:21 +0000 (12:43 +1000)
cpu_65c02.c
cpu_65c02.h
decode_65c02.py
emu_65c02.c
emu_z80.c

index dabdb9a..31e52ef 100644 (file)
@@ -1,4 +1,5 @@
 #include <stdlib.h>
+#include <string.h>
 #include "cpu_65c02.h"
 
 #define NMI_VECTOR 0xfffa
@@ -8,21 +9,24 @@
 // gcc specific
 #define INLINE __attribute__((always_inline))
 
-// convenient register access
-#define A (self->regs[CPU_65C02_REG_A])
-#define X (self->regs[CPU_65C02_REG_X])
-#define Y (self->regs[CPU_65C02_REG_Y])
-#define S (self->regs[CPU_65C02_REG_S])
-#define P (self->regs[CPU_65C02_REG_P])
-#define PC (self->pc)
+// convenient byte register access
+#define A (self->regs.b[CPU_65C02_REG_A])
+#define X (self->regs.b[CPU_65C02_REG_X])
+#define Y (self->regs.b[CPU_65C02_REG_Y])
+#define P (self->regs.b[CPU_65C02_REG_P])
+#define S (self->regs.b[CPU_65C02_REG_S])
+
+// convenient word register access
+#define SP (self->regs.w[CPU_65C02_REG_SP >> 1])
+#define PC (self->regs.w[CPU_65C02_REG_PC >> 1])
 
 // convenient flags bit access
-#define FLAG_C (1 << CPU_65C02_BIT_C)
-#define FLAG_Z (1 << CPU_65C02_BIT_Z)
-#define FLAG_I (1 << CPU_65C02_BIT_I)
-#define FLAG_D (1 << CPU_65C02_BIT_D)
-#define FLAG_V (1 << CPU_65C02_BIT_V)
-#define FLAG_N (1 << CPU_65C02_BIT_N)
+#define FLAG_C (1 << CPU_65C02_REG_P_BIT_C)
+#define FLAG_Z (1 << CPU_65C02_REG_P_BIT_Z)
+#define FLAG_I (1 << CPU_65C02_REG_P_BIT_I)
+#define FLAG_D (1 << CPU_65C02_REG_P_BIT_D)
+#define FLAG_V (1 << CPU_65C02_REG_P_BIT_V)
+#define FLAG_N (1 << CPU_65C02_REG_P_BIT_N)
 
 // read, write, fetch, push, pop byte/word for instruction decode/execute
 // these routines automatically apply cycle count of 1 (byte) or 2 (word)
@@ -43,8 +47,8 @@
 #define EA_A (CPU_65C02_REG_A - CPU_65C02_N_REGS)
 #define EA_X (CPU_65C02_REG_X - CPU_65C02_N_REGS)
 #define EA_Y (CPU_65C02_REG_Y - CPU_65C02_N_REGS)
-#define EA_S (CPU_65C02_REG_S - CPU_65C02_N_REGS)
 #define EA_P (CPU_65C02_REG_P - CPU_65C02_N_REGS)
+#define EA_S (CPU_65C02_REG_S - CPU_65C02_N_REGS)
 
 // lvalue modes
 // effective address calculations that involve indexing require their
 #define BMI(lvalue) cpu_65c02_bs(self, FLAG_N, (lvalue))
 #define BNE(lvalue) cpu_65c02_bc(self, FLAG_Z, (lvalue))
 #define BPL(lvalue) cpu_65c02_bc(self, FLAG_N, (lvalue))
-#define BRA(lvalue) cpu_65c02_jmp(self, (lvalue))
+#define BRA(lvalue) cpu_65c02_bc(self, 0, (lvalue))
 #define BRK() cpu_65c02_brk(self)
 #define BVC(lvalue) cpu_65c02_bc(self, FLAG_V, (lvalue))
 #define BVS(lvalue) cpu_65c02_bs(self, FLAG_V, (lvalue))
 #define TYA() cpu_65c02_ld(self, EA_A, Y)
 #define WAI() cpu_65c02_wai(self)
 
+// initialization
+void cpu_65c02_init(
+  struct cpu_65c02 *self,
+  int pc,
+  int (*read_byte)(int addr),
+  void (*write_byte)(int addr, int data)
+) {
+  memset(self, 0, sizeof(struct cpu_65c02));
+  self->regs.w[CPU_65C02_REG_PC >> 1] = pc;
+  self->regs.b[CPU_65C02_REG_P] = 0x30;
+  self->regs.w[CPU_65C02_REG_SP >> 1] = 0x1ff;
+  self->read_byte = read_byte;
+  self->write_byte = write_byte;
+}
+
 // memory (or internal register memory) access
 INLINE int cpu_65c02_rb(struct cpu_65c02 *self, int addr) {
   if (addr < 0)
-    return self->regs[CPU_65C02_N_REGS + addr];
+    return self->regs.b[CPU_65C02_N_REGS + addr];
   self->cycles += 1;
-  return self->read_byte(addr);
+  return self->read_byte(addr & 0xffff);
 }
 
 INLINE int cpu_65c02_rw(struct cpu_65c02 *self, int addr) {
   int data = RB(addr);
-  return data | (RB((addr + 1) & 0xffff) << 8);
+  return data | (RB(addr + 1) << 8);
 }
 
 INLINE int cpu_65c02_rw_zpg(struct cpu_65c02 *self, int addr) {
-  int data = RB(addr);
+  int data = RB(addr & 0xff);
   return data | (RB((addr + 1) & 0xff) << 8);
 }
 
 INLINE void cpu_65c02_wb(struct cpu_65c02 *self, int addr, int data) {
   self->cycles += 1;
   if (addr < 0)
-    self->regs[CPU_65C02_N_REGS + addr] = data;
+    self->regs.b[CPU_65C02_N_REGS + addr] = data;
   else
     self->write_byte(addr, data);
 }
 
 INLINE void cpu_65c02_ww(struct cpu_65c02 *self, int addr, int data) {
   WB(addr, data & 0xff);
-  WB((addr + 1) & 0xffff, data >> 8);
+  WB(addr + 1, data >> 8);
 }
 
 INLINE int cpu_65c02_fb(struct cpu_65c02 *self) {
-  int data = RB(PC);
-  PC = (PC + 1) & 0xffff;
+  int data = RB(PC++);
   return data;
 }
 
@@ -232,8 +250,8 @@ INLINE int cpu_65c02_fw(struct cpu_65c02 *self) {
 }
 
 INLINE void cpu_65c02_phb(struct cpu_65c02 *self, int data) {
-  WB(S | 0x100, data);
-  S = (S - 1) & 0xff;
+  WB(SP, data);
+  --S;
 }
 
 INLINE void cpu_65c02_phw(struct cpu_65c02 *self, int data) {
@@ -242,8 +260,8 @@ INLINE void cpu_65c02_phw(struct cpu_65c02 *self, int data) {
 }
 
 INLINE int cpu_65c02_plb(struct cpu_65c02 *self) {
-  S = (S + 1) & 0xff;
-  return RB(S | 0x100);
+  ++S;
+  return RB(SP);
 }
 
 INLINE int cpu_65c02_plw(struct cpu_65c02 *self) {
@@ -272,10 +290,7 @@ INLINE int cpu_65c02_ea_abs_ind(struct cpu_65c02 *self) {
 
 INLINE int cpu_65c02_ea_rel(struct cpu_65c02 *self) {
   int offset = FB();
-  offset -= (offset << 1) & 0x100; // sign extend
-  int addr = PC;
-  self->cycles += ((addr & 0xff) + (offset & 0xff)) >> 8;
-  return addr + offset;
+  return offset - ((offset << 1) & 0x100); // sign extend
 }
 
 INLINE int cpu_65c02_ea_zpg(struct cpu_65c02 *self) {
@@ -348,23 +363,31 @@ INLINE void cpu_65c02_asl(struct cpu_65c02 *self, int lvalue) {
 }
 
 INLINE void cpu_65c02_bbr(struct cpu_65c02 *self, int rvalue, int lvalue) {
-  if (rvalue == 0)
-    PC = lvalue;
+  if (rvalue == 0) {
+    self->cycles += ((PC & 0xff) + (lvalue & 0xff)) >> 8;
+    PC = (PC + lvalue) & 0xffff;
+  }
 }
 
 INLINE void cpu_65c02_bbs(struct cpu_65c02 *self, int rvalue, int lvalue) {
-  if (rvalue)
-    PC = lvalue;
+  if (rvalue) {
+    self->cycles += ((PC & 0xff) + (lvalue & 0xff)) >> 8;
+    PC = (PC + lvalue) & 0xffff;
+  }
 }
 
 INLINE void cpu_65c02_bc(struct cpu_65c02 *self, int flag, int lvalue) {
-  if ((P & flag) == 0)
-    PC = lvalue;
+  if ((P & flag) == 0) {
+    self->cycles += ((PC & 0xff) + (lvalue & 0xff)) >> 8;
+    PC = (PC + lvalue) & 0xffff;
+  }
 }
 
 INLINE void cpu_65c02_bs(struct cpu_65c02 *self, int flag, int lvalue) {
-  if (P & flag)
-    PC = lvalue;
+  if (P & flag) {
+    self->cycles += ((PC & 0xff) + (lvalue & 0xff)) >> 8;
+    PC = (PC + lvalue) & 0xffff;
+  }
 }
 
 INLINE void cpu_65c02_bit(struct cpu_65c02 *self, int rvalue) {
index 4aec757..0638f23 100644 (file)
@@ -7,26 +7,36 @@
 #define CPU_65C02_REG_A 0
 #define CPU_65C02_REG_X 1
 #define CPU_65C02_REG_Y 2
-#define CPU_65C02_REG_S 3
-#define CPU_65C02_REG_P 4
-#define CPU_65C02_N_REGS 5
+#define CPU_65C02_REG_P 3
+#define CPU_65C02_REG_SP 4
+#define CPU_65C02_REG_S 4
+#define CPU_65C02_REG_PC 6
+#define CPU_65C02_N_REGS 8
 
 // bits within REG_P
-#define CPU_65C02_BIT_C 0
-#define CPU_65C02_BIT_Z 1
-#define CPU_65C02_BIT_I 2
-#define CPU_65C02_BIT_D 3
-#define CPU_65C02_BIT_V 6
-#define CPU_65C02_BIT_N 7
+#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_V 6
+#define CPU_65C02_REG_P_BIT_N 7
 
 struct cpu_65c02 {
-  uint8_t regs[CPU_65C02_N_REGS];
-  uint16_t pc;
   int cycles;
   int (*read_byte)(int addr);
   void (*write_byte)(int addr, int data);
+  union {
+    uint8_t b[CPU_65C02_N_REGS];
+    uint16_t w[CPU_65C02_N_REGS >> 1];
+  } regs;
 };
 
+void cpu_65c02_init(
+  struct cpu_65c02 *self,
+  int pc,
+  int (*read_byte)(int addr),
+  void (*write_byte)(int addr, int data)
+);
 void cpu_65c02_execute(struct cpu_65c02 *self);
  
 #endif
index 06e1718..6aa5ee1 100755 (executable)
@@ -8,7 +8,7 @@ mem = [0, 0x12, 0x34]
 mpu = py65.devices.mpu65c02.MPU(mem, 0)
 disassembler = py65.disassembler.Disassembler(mpu)
 
-rvalue_ops = {
+rvalue_opcodes = {
   'adc',
   'and',
   'bit',
@@ -64,7 +64,7 @@ for i in range(0x100):
   if instr[0] == '???':
     instr[0] = 'ill' # illegal opcode
   if len(instr) >= 2:
-    if instr[0] in rvalue_ops:
+    if instr[0] in rvalue_opcodes:
       instr[1] = rvalue_modes[instr[1]]
     else:
       instr[1] = lvalue_modes[instr[1]]
index 8a91895..1a001f3 100644 (file)
@@ -178,13 +178,13 @@ int main(int argc, char **argv) {
   while (true) {
 #if REG_TRACE
     printf(
-      "pc=%04x a=%02x x=%02x y=%02x s=%02x p=%02x c=%d z=%d i=%d d=%d v=%d n=%d\n",
+      "pc=%04x a=%02x x=%02x y=%02x p=%02x s=%02x c=%d z=%d i=%d d=%d v=%d n=%d\n",
       vrEmu6502GetPC(cpu),
       vrEmu6502GetAcc(cpu),
       vrEmu6502GetX(cpu),
       vrEmu6502GetY(cpu),
-      vrEmu6502GetStackPointer(cpu),
       vrEmu6502GetStatus(cpu) | 0x30,
+      vrEmu6502GetStackPointer(cpu),
       vrEmu6502GetStatus(cpu) & 1,
       (vrEmu6502GetStatus(cpu) >> 1) & 1,
       (vrEmu6502GetStatus(cpu) >> 2) & 1,
@@ -204,35 +204,30 @@ int main(int argc, char **argv) {
   }
 #else
   struct cpu_65c02 cpu;
-  memset(&cpu, 0, sizeof(struct cpu_65c02));
-  cpu.pc = entry_point;
-  cpu.regs[CPU_65C02_REG_S] = 0xff;
-  cpu.regs[CPU_65C02_REG_P] = 0x30;
-  cpu.read_byte = read_byte;
-  cpu.write_byte = write_byte;
+  cpu_65c02_init(&cpu, entry_point, read_byte, write_byte);
 
   while (true) {
 #if REG_TRACE
     printf(
-      "pc=%04x a=%02x x=%02x y=%02x s=%02x p=%02x c=%d z=%d i=%d d=%d v=%d n=%d\n",
-      cpu.pc,
-      cpu.regs[CPU_65C02_REG_A],
-      cpu.regs[CPU_65C02_REG_X],
-      cpu.regs[CPU_65C02_REG_Y],
-      cpu.regs[CPU_65C02_REG_S],
-      cpu.regs[CPU_65C02_REG_P],
-      (cpu.regs[CPU_65C02_REG_P] >> CPU_65C02_BIT_C) & 1,
-      (cpu.regs[CPU_65C02_REG_P] >> CPU_65C02_BIT_Z) & 1,
-      (cpu.regs[CPU_65C02_REG_P] >> CPU_65C02_BIT_I) & 1,
-      (cpu.regs[CPU_65C02_REG_P] >> CPU_65C02_BIT_D) & 1,
-      (cpu.regs[CPU_65C02_REG_P] >> CPU_65C02_BIT_V) & 1,
-      (cpu.regs[CPU_65C02_REG_P] >> CPU_65C02_BIT_N) & 1
+      "pc=%04x a=%02x x=%02x y=%02x p=%02x s=%02x c=%d z=%d i=%d d=%d v=%d n=%d\n",
+      cpu.regs.w[CPU_65C02_REG_PC >> 1],
+      cpu.regs.b[CPU_65C02_REG_A],
+      cpu.regs.b[CPU_65C02_REG_X],
+      cpu.regs.b[CPU_65C02_REG_Y],
+      cpu.regs.b[CPU_65C02_REG_P],
+      cpu.regs.b[CPU_65C02_REG_S],
+      (cpu.regs.b[CPU_65C02_REG_P] >> CPU_65C02_REG_P_BIT_C) & 1,
+      (cpu.regs.b[CPU_65C02_REG_P] >> CPU_65C02_REG_P_BIT_Z) & 1,
+      (cpu.regs.b[CPU_65C02_REG_P] >> CPU_65C02_REG_P_BIT_I) & 1,
+      (cpu.regs.b[CPU_65C02_REG_P] >> CPU_65C02_REG_P_BIT_D) & 1,
+      (cpu.regs.b[CPU_65C02_REG_P] >> CPU_65C02_REG_P_BIT_V) & 1,
+      (cpu.regs.b[CPU_65C02_REG_P] >> CPU_65C02_REG_P_BIT_N) & 1
     );
 #endif
 
-    int pc = cpu.pc;
+    int pc = cpu.regs.w[CPU_65C02_REG_PC >> 1];
     cpu_65c02_execute(&cpu);
-    if (pc == cpu.pc) {
+    if (pc == cpu.regs.w[CPU_65C02_REG_PC >> 1]) {
       printf("hung at %04x\n", pc);
       break;
     }
index 6ab0a55..dbd4ac0 100644 (file)
--- a/emu_z80.c
+++ b/emu_z80.c
@@ -158,7 +158,7 @@ int bdos(int c, int de) {
   return de;
 }
 
-int mem_read_byte(int addr) {
+int read_byte(int addr) {
 #if MEM_TRACE
   int data = mem[addr];
   printf("mem_addr=%04x rd=%02x\n", addr, data);
@@ -168,14 +168,14 @@ int mem_read_byte(int addr) {
 #endif
 }
 
-void mem_write_byte(int addr, int data) {
+void write_byte(int addr, int data) {
 #if MEM_TRACE
   printf("mem_addr=%04x wr=%02x\n", addr, data);
 #endif
   mem[addr] = data;
 }
 
-int io_read_byte(int addr) {
+int in_byte(int addr) {
 #if IO_TRACE
   int data = io_read[addr];
   printf("io_addr=%04x rd=%02x\n", addr, data);
@@ -185,7 +185,7 @@ int io_read_byte(int addr) {
 #endif
 }
 
-void io_write_byte(int addr, int data) {
+void out_byte(int addr, int data) {
 #if IO_TRACE
   printf("io_addr=%04x wr=%02x\n", addr, data);
 #endif
@@ -194,19 +194,19 @@ void io_write_byte(int addr, int data) {
 
 #if ALT_BACKEND
 uint8_t rb(void *userdata, uint16_t addr) {
-  return mem_read_byte(addr);
+  return read_byte(addr);
 }
 
 void wb(void *userdata, uint16_t addr, uint8_t val) {
-  mem_write_byte(addr, val);
+  write_byte(addr, val);
 }
 
 uint8_t in(z80 *const z, uint8_t port) {
-  return io_read_byte(port);
+  return in_byte(port);
 }
 
 void out(z80 *const z, uint8_t port, uint8_t val) {
-  io_write_byte(port, val);
+  out_byte(port, val);
 }
 #endif
 
@@ -218,6 +218,8 @@ int main(int argc, char **argv) {
   int entry_point = load_ihx(argv[1]);
 
   mem[5] = 0xc9; // ret, for bdos emulation
+  mem[6] = 0xff; // memory top, lo byte
+  mem[7] = 0xff; // memory top, hi byte
 
 #if ALT_BACKEND
   z80 cpu;
@@ -231,7 +233,7 @@ int main(int argc, char **argv) {
   while (true) {
 #if REG_TRACE
     printf(
-      "pc=%04x af=%04x bc=%04x de=%04x hl=%04x sp=%04x ix=%04x iy=%04x cf=%d nf=%d pf=%d hf=%d zf=%d sf=%d\n",
+      "pc=%04x af=%04x bc=%04x de=%04x hl=%04x sp=%04x ix=%04x iy=%04x cf=%d nf=%d pv=%d hf=%d zf=%d sf=%d\n",
       cpu.pc,
       cpu.cf |
       (cpu.nf << 1) |
@@ -271,36 +273,43 @@ int main(int argc, char **argv) {
   struct cpu_z80 cpu;
   memset(&cpu, 0, sizeof(struct cpu_z80));
   cpu.pc = entry_point;
-  cpu.regs[CPU_Z80_REG_S] = 0xff;
-  cpu.regs[CPU_Z80_REG_P] = 0x30;
-  cpu.mem_read_byte = mem_read_byte;
-  cpu.mem_write_byte = mem_write_byte;
+  cpu.read_byte = read_byte;
+  cpu.write_byte = write_byte;
+  cpu.in_byte = in_byte;
+  cpu.out_byte = write_byte;
 
   while (true) {
 #if REG_TRACE
     printf(
-      "pc=%04x a=%02x x=%02x y=%02x s=%02x p=%02x c=%d z=%d i=%d d=%d v=%d n=%d\n",
+      "pc=%04x af=%04x bc=%04x de=%04x hl=%04x sp=%04x ix=%04x iy=%04x cf=%d nf=%d pv=%d hf=%d zf=%d sf=%d\n",
       cpu.pc,
-      cpu.regs[CPU_Z80_REG_A],
-      cpu.regs[CPU_Z80_REG_X],
-      cpu.regs[CPU_Z80_REG_Y],
-      cpu.regs[CPU_Z80_REG_S],
-      cpu.regs[CPU_Z80_REG_P],
-      (cpu.regs[CPU_Z80_REG_P] >> CPU_Z80_BIT_C) & 1,
-      (cpu.regs[CPU_Z80_REG_P] >> CPU_Z80_BIT_Z) & 1,
-      (cpu.regs[CPU_Z80_REG_P] >> CPU_Z80_BIT_I) & 1,
-      (cpu.regs[CPU_Z80_REG_P] >> CPU_Z80_BIT_D) & 1,
-      (cpu.regs[CPU_Z80_REG_P] >> CPU_Z80_BIT_V) & 1,
-      (cpu.regs[CPU_Z80_REG_P] >> CPU_Z80_BIT_N) & 1
+      cpu.regs[CPU_Z80_REG_AF] | (cpu.regs[CPU_REG_AF + 1] << 8),
+      cpu.regs[CPU_Z80_REG_BC] | (cpu.regs[CPU_REG_BC + 1] << 8),
+      cpu.regs[CPU_Z80_REG_DE] | (cpu.regs[CPU_REG_DE + 1] << 8),
+      cpu.regs[CPU_Z80_REG_HL] | (cpu.regs[CPU_REG_HL + 1] << 8),
+      cpu.regs[CPU_Z80_REG_SP] | (cpu.regs[CPU_REG_SP + 1] << 8),
+      cpu.regs[CPU_Z80_REG_IX] | (cpu.regs[CPU_REG_IX + 1] << 8),
+      cpu.regs[CPU_Z80_REG_IY] | (cpu.regs[CPU_REG_IY + 1] << 8),
+      (cpu.regs[CPU_Z80_REG_F] >> CPU_Z80_BIT_CF) & 1,
+      (cpu.regs[CPU_Z80_REG_F] >> CPU_Z80_BIT_NF) & 1,
+      (cpu.regs[CPU_Z80_REG_F] >> CPU_Z80_BIT_PV) & 1,
+      (cpu.regs[CPU_Z80_REG_F] >> CPU_Z80_BIT_HF) & 1,
+      (cpu.regs[CPU_Z80_REG_F] >> CPU_Z80_BIT_ZF) & 1,
+      (cpu.regs[CPU_Z80_REG_F] >> CPU_Z80_BIT_SF) & 1
     );
 #endif
 
-    int pc = cpu.pc;
-    cpu_z80_execute(&cpu);
-    if (pc == cpu.pc) {
-      printf("hung at %04x\n", pc);
+    if (cpu.pc == 0) {
+      putchar('\n');
       break;
     }
+    if (cpu.pc == 5) {
+      cpu.regs.w[CPU_Z80_REG_DE >> 1] = bdos(
+        cpu.regs.b[CPU_Z80_REG_C],
+        cpu.regs.w[CPU_Z80_REG_DE >> 1]
+      );
+    }
+    cpu_z80_execute(&cpu);
   }
 #endif