Start to implement Pac-Man emulator
authorNick Downing <nick@ndcode.org>
Fri, 1 Jul 2022 15:07:03 +0000 (01:07 +1000)
committerNick Downing <nick@ndcode.org>
Fri, 1 Jul 2022 15:19:22 +0000 (01:19 +1000)
.gitignore
emu_z80/emu_z80.c
pacman/Makefile

index c318fe7..5a49f2f 100644 (file)
 /emu_z80/cg_default/*.png
 /emu_z80/cg_default/*.ppm
 /emu_z80/emu_z80
+/pacman/82s123.7f
+/pacman/82s126.1m
+/pacman/82s126.3m
+/pacman/82s126.4a
+/pacman/pacman.5e
+/pacman/pacman.5f
+/pacman/pacman.6e
+/pacman/pacman.6f
+/pacman/pacman.6h
+/pacman/pacman.6j
 /orig/pacman.zip
index c84b9f6..ef11105 100644 (file)
 #include "stty_sane.h"
 #include "z80/z80.h"
 
+#define PACMAN 1
+#define APPLE 0
+
+#if PACMAN
+#define PACMAN_WIDTH 224
+#define PACMAN_HEIGHT 288
+
+#define PADDED_WIDTH PACMAN_WIDTH
+#define PADDED_HEIGHT PACMAN_HEIGHT
+
+#define WINDOW_X_SCALE 2
+#define WINDOW_Y_SCALE 2
+#else // APPLE
 #define APPLE_IIE 1
 #define APPLE_WIDTH 560
 #define APPLE_HEIGHT 192
 
 #define WINDOW_X_SCALE 2
 #define WINDOW_Y_SCALE 4
+#endif
+
 #define WINDOW_WIDTH (PADDED_WIDTH * WINDOW_X_SCALE)
 #define WINDOW_HEIGHT (PADDED_HEIGHT * WINDOW_Y_SCALE)
 
 #define CYCLES_PER_SAMPLE 34 // 44100 Hz @ 1.5 MHz
 #define SAMPLES_PER_UPDATE 500
 
+#if PACMAN
+// define pacman I/O addresses here
+#else // APPLE
 #define IO_PAGE 0xe000
 #define HW_KBD 0xe000 // R last key pressed + 128
 #define HW_CLR80COL 0xe000 // W use $C002-C005 for aux mem (80STOREOFF)
 #define HW_LC_BANK1_ROM_WP 0xe086 // RW
 #define HW_LC_BANK1_RAM_WE 0xe087 // RWx2
 #define HW_CLRROM 0xefff // disable slot C8 ROM
+#endif
 
 #define STDIN_DATA 0xf0
 #define STDOUT_DATA 0xf1
 #define DOS_LO 0xf9
 #define DOS_HI 0xfa
 
+#if 0 // this isn't correct for Z80
 #define RESET_VECTOR 0xfffc
+#endif
 
 #define TRACE 0
-#define MEM_TRACE 0
+#define IO_TRACE 1
+#define MEM_TRACE 1
 
 extern char **environ;
 
@@ -155,6 +177,19 @@ uint32_t mono_palette[2] = {0xff000000, 0xffffffff};
 
 z80 cpu;
 
+#if PACMAN
+#define ROM_PACMAN_6E_ADDR 0
+#define ROM_PACMAN_6F_ADDR 0x1000
+#define ROM_PACMAN_6H_ADDR 0x2000
+#define ROM_PACMAN_6J_ADDR 0x3000
+#define ROM_82S123_7F_ADDR 0x5c00
+#define ROM_82S126_1M_ADDR 0x5d00
+#define ROM_82S126_3M_ADDR 0x5e00
+#define ROM_82S126_4A_ADDR 0x5f00
+#define ROM_PACMAN_5E_ADDR 0x6000
+#define ROM_PACMAN_5F_ADDR 0x7000
+#define MEM_SIZE 0x8000
+#else // APPLE
 #if APPLE_IIE
 #include "cg_default/cg_lowercase_20.inc"
 #include "cg_default/cg_lowercase_40.inc"
@@ -174,6 +209,7 @@ uint8_t *video_rom;
 #else
 #define MEM_SIZE 0x14000
 #endif
+#endif
 uint8_t mem[MEM_SIZE];
 
 #if TRACE
@@ -206,6 +242,20 @@ uint8_t usleep_lo;
 uint8_t dos_lo;
 int usleep_count, exit_flag;
 
+#if PACMAN
+// define pacman I/O state here
+
+// just for now until we have pacman audio
+#define C0X0_SOFT_SWITCH_TAPEOUT 4
+#define C0X0_SOFT_SWITCH_SPKR 8
+uint8_t c0x0_soft_switches = 0;
+
+// every n cycles, sample the tape and speaker outputs to here
+#define C0X0_SOFT_SWITCHES_BUF_SIZE 0x400 // next power of 2 >= 441 * 2
+int c0x0_soft_switches_buf_head;
+int c0x0_soft_switches_buf_count;
+uint8_t c0x0_soft_switches_buf[C0X0_SOFT_SWITCHES_BUF_SIZE];
+#else // APPLE
 #if APPLE_IIE
 #define C00X_SOFT_SWITCH_80COL 1
 #define C00X_SOFT_SWITCH_RDCARDRAM 2
@@ -267,6 +317,7 @@ int joystick_axes[4] = {
 #define LC_SELECT_ROM_WP 2
 #define LC_SELECT_RAM_WE 3
 uint8_t lc_state = LC_SELECT_ROM_WP | LC_BANK2;
+#endif
 
 void rom_load(char *name, uint8_t *data, int size) {
   int fd = open(name, O_RDONLY);
@@ -540,6 +591,22 @@ void dos(char *line) {
 }
 
 uint8_t rb(void *userdata, uint16_t addr0) {
+#if PACMAN
+  int addr = addr0;
+
+  if (addr < 0x5000) {
+#if MEM_TRACE
+    int pc = cpu.pc;
+    fprintf(stderr, "pc=%04x addr=%04x rd=%02x\n", pc, addr, mem[addr]);
+#endif
+    return mem[addr];
+  }
+
+#if MEM_TRACE
+  int pc = cpu.pc;
+  fprintf(stderr, "pc=%04x addr=%04x rd=??\n", pc, addr);
+#endif
+#else
   // for joystick, for now count memory accesses as proxy for cycles
   if (joystick_count < JOYSTICK_COUNT) {
     ++joystick_count;
@@ -890,10 +957,27 @@ uint8_t rb(void *userdata, uint16_t addr0) {
     lc_state = addr & 7; // should check RWx2 for write enable
     break;
   }
+#endif
   return 0xff;
 }
 
 void wb(void *userdata, uint16_t addr0, uint8_t val) {
+#if PACMAN
+  int addr = addr0;
+
+  if (addr >= 0x4000 && addr < 0x5000) {
+#if MEM_TRACE
+    int pc = cpu.pc;
+    fprintf(stderr, "pc=%04x addr=%04x wr=%02x\n", pc, addr, val);
+#endif
+    mem[addr] = val;
+  }
+
+#if MEM_TRACE
+    int pc = cpu.pc;
+    fprintf(stderr, "pc=%04x addr=%04x wr=%02x (nop)\n", pc, addr, val);
+#endif
+#else
   // for joystick, for now count memory accesses as proxy for cycles
   if (joystick_count < JOYSTICK_COUNT) {
     ++joystick_count;
@@ -1088,9 +1172,15 @@ void wb(void *userdata, uint16_t addr0, uint8_t val) {
     lc_state = addr & 7; // should check RWx2 for write enable
     break;
   }
+#endif
 }
 
 uint8_t in(z80 *const z, uint8_t port) {
+#if IO_TRACE
+  int pc = cpu.pc;
+  fprintf(stderr, "pc=%04x port=%02x rd=??\n", pc, port);
+#endif
+
   switch (port) {
   case STDIN_DATA:
     {
@@ -1142,6 +1232,11 @@ uint8_t in(z80 *const z, uint8_t port) {
 }
 
 void out(z80 *const z, uint8_t port, uint8_t val) {
+#if IO_TRACE
+  int pc = cpu.pc;
+  fprintf(stderr, "pc=%04x port=%02x wr=%02x\n", pc, port, val);
+#endif
+
   switch (port) {
   case STDOUT_DATA:
     if (write(fd_out, &val, 1) == -1) {
@@ -1204,6 +1299,7 @@ void termios_atexit(void) {
 
 int main(int argc, char **argv) {
   int argn = 1;
+#if APPLE
   char *cg_rom_file = NULL;
   char *video_rom_file = NULL;
 #if APPLE_IIE
@@ -1216,13 +1312,19 @@ int main(int argc, char **argv) {
   char *e8_rom_file = NULL;
   char *f0_rom_file = NULL;
   char *f8_rom_file = NULL;
+#endif
 #endif
   bool timing = false;
   while (argn < argc) {
     if (strcmp(argv[argn], "--help") == 0) {
+#if PACMAN
+      printf("usage: %s [--timing] [program.obj[,aNNNN] ...] [-- child-executable [child-argument ...]]\n", argv[0]);
+#else // APPLE
       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]);
+#endif
       exit(EXIT_FAILURE);
     }
+#if APPLE
     if (memcmp(argv[argn], "--cg-rom=", 9) == 0)
       cg_rom_file = argv[argn] + 9;
     else if (memcmp(argv[argn], "--video_rom=", 12) == 0)
@@ -1246,13 +1348,16 @@ 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)
+    else
+#endif
+    if (strcmp(argv[argn], "--timing") == 0)
       timing = true;
     else
       break;
     ++argn;
   }
 
+#if APPLE
   // the IIe emulation can run without a video ROM and fall back to
   // II+ style character generation, this is done for the benefit
   // of DHGR or 128K games that require Apple IIe but not text mode
@@ -1290,8 +1395,10 @@ int main(int argc, char **argv) {
 #endif
     }
   }
+#endif
 
   int entry_point = -1;
+#if APPLE
 #if APPLE_IIE
   memset(mem + 0x20000, 0xff, 0x4000);
   if (cd_rom_file)
@@ -1312,6 +1419,7 @@ int main(int argc, char **argv) {
     load_bin(f0_rom_file, 0x13000, 0x800);
   if (f8_rom_file)
     load_bin(f8_rom_file, 0x13800, 0x800);
+#endif
 #endif
 
   while (argn < argc) {
@@ -1321,6 +1429,7 @@ int main(int argc, char **argv) {
     entry_point = load(p);
   }
 
+#if 0 // this isn't correct for Z80
   // do this before creating the CPU
   if (entry_point != -1) {
 #if APPLE_IIE
@@ -1331,6 +1440,7 @@ int main(int argc, char **argv) {
     mem[RESET_VECTOR + 1 + 0x10000 - 0xc000] = (uint8_t)(entry_point >> 8);
 #endif
   }
+#endif
 
   // open pty and child process if requested
   if (argn < argc) {
@@ -1570,10 +1680,12 @@ int main(int argc, char **argv) {
       exit(EXIT_FAILURE);
     }
 
+#if APPLE
     // we should get the current position to use until first event,
     // for now just centre it (no joystick is fully to right and down)
     for (int i = 0; i < 4; ++i)
       joystick_axes[i] = JOYSTICK_COUNT >> 1;
+#endif
   }
 
   z80_init(&cpu);
@@ -1581,6 +1693,7 @@ int main(int argc, char **argv) {
   cpu.write_byte = wb;
   cpu.port_in = in;
   cpu.port_out = out;
+  cpu.pc = entry_point;
 
 #if TRACE
   memset(pc_pairs, 0xff, N_PC_PAIRS * sizeof(struct pc_pair));
@@ -1705,6 +1818,7 @@ int main(int argc, char **argv) {
           }
         }
         break;
+#if APPLE
       case SDL_JOYBUTTONUP:
         {
           SDL_JoyButtonEvent *e = (SDL_JoyButtonEvent *)&event;
@@ -1739,6 +1853,7 @@ int main(int argc, char **argv) {
               ((e->value + 0x8000) * JOYSTICK_COUNT) >> 16;
         }
         break;
+#endif
       }
 
     // video update
@@ -1748,6 +1863,9 @@ int main(int argc, char **argv) {
       SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0xff);
       SDL_RenderClear(renderer);
 
+#if PACMAN
+      // define pacman video update here
+#else // APPLE
       struct timespec timeval;
       clock_gettime(CLOCK_MONOTONIC, &timeval);
       bool flash_state = timeval.tv_nsec >= 500000000;
@@ -1903,6 +2021,7 @@ int main(int argc, char **argv) {
           }
         }
       }
+#endif
       SDL_UpdateTexture(texture, NULL, frame, WINDOW_WIDTH * sizeof(uint32_t));
       SDL_RenderCopy(renderer, texture, NULL, NULL);
       SDL_RenderPresent(renderer);
index 1263948..5ccfbc0 100644 (file)
@@ -1 +1,72 @@
-all:
+# need to install intelhex package in Python first:
+#   pip3 install --user intelhex
+BIN2HEX=bin2hex.py
+HEX2BIN=hex2bin.py
+
+# some ROMs are loaded into emulator addresses not accessible by software,
+# this is for convenience so we can build entire game from one source file
+ROM_PACMAN_6E_ADDR=0
+ROM_PACMAN_6F_ADDR=0x1000
+ROM_PACMAN_6H_ADDR=0x2000
+ROM_PACMAN_6J_ADDR=0x3000
+ROM_82S123_7F_ADDR=0x5c00
+ROM_82S126_1M_ADDR=0x5d00
+ROM_82S126_3M_ADDR=0x5e00
+ROM_82S126_4A_ADDR=0x5f00
+ROM_PACMAN_5E_ADDR=0x6000
+ROM_PACMAN_5F_ADDR=0x7000
+
+ROMS= \
+pacman.6e \
+pacman.6f \
+pacman.6h \
+pacman.6j \
+82s123.7f \
+82s126.1m \
+82s126.3m \
+82s126.4a \
+pacman.5e \
+pacman.5f
+
+all: pacman0.ihx
+
+pacman0.ihx: ${ROMS:%=%.ihx}
+       hexmerge.py -o $@ $^
+
+pacman.6e.ihx: pacman.6e
+       ${BIN2HEX} --offset=${ROM_PACMAN_6E_ADDR} $< $@
+
+pacman.6f.ihx: pacman.6f
+       ${BIN2HEX} --offset=${ROM_PACMAN_6F_ADDR} $< $@
+
+pacman.6h.ihx: pacman.6h
+       ${BIN2HEX} --offset=${ROM_PACMAN_6H_ADDR} $< $@
+
+pacman.6j.ihx: pacman.6j
+       ${BIN2HEX} --offset=${ROM_PACMAN_6J_ADDR} $< $@
+
+82s123.7f.ihx: 82s123.7f
+       ${BIN2HEX} --offset=${ROM_82S123_7F_ADDR} $< $@
+
+82s126.1m.ihx: 82s126.1m
+       ${BIN2HEX} --offset=${ROM_82S123_1M_ADDR} $< $@
+
+82s126.3m.ihx: 82s126.3m
+       ${BIN2HEX} --offset=${ROM_82S123_3M_ADDR} $< $@
+
+82s126.4a.ihx: 82s126.4a
+       ${BIN2HEX} --offset=${ROM_82S123_4A_ADDR} $< $@
+
+pacman.5e.ihx: pacman.5e
+       ${BIN2HEX} --offset=${ROM_PACMAN_5E_ADDR} $< $@
+
+pacman.5f.ihx: pacman.5f
+       ${BIN2HEX} --offset=${ROM_PACMAN_5F_ADDR} $< $@
+
+${ROMS}: ../orig/pacman.zip
+       rm -f ${ROMS}
+       unzip $<
+       touch ${ROMS}
+
+clean:
+       rm -f *.ihx ${ROMS}