#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;
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"
#else
#define MEM_SIZE 0x14000
#endif
+#endif
uint8_t mem[MEM_SIZE];
#if TRACE
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
#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);
}
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;
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;
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:
{
}
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) {
int main(int argc, char **argv) {
int argn = 1;
+#if APPLE
char *cg_rom_file = NULL;
char *video_rom_file = NULL;
#if APPLE_IIE
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)
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
#endif
}
}
+#endif
int entry_point = -1;
+#if APPLE
#if APPLE_IIE
memset(mem + 0x20000, 0xff, 0x4000);
if (cd_rom_file)
load_bin(f0_rom_file, 0x13000, 0x800);
if (f8_rom_file)
load_bin(f8_rom_file, 0x13800, 0x800);
+#endif
#endif
while (argn < argc) {
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
mem[RESET_VECTOR + 1 + 0x10000 - 0xc000] = (uint8_t)(entry_point >> 8);
#endif
}
+#endif
// open pty and child process if requested
if (argn < argc) {
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);
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));
}
}
break;
+#if APPLE
case SDL_JOYBUTTONUP:
{
SDL_JoyButtonEvent *e = (SDL_JoyButtonEvent *)&event;
((e->value + 0x8000) * JOYSTICK_COUNT) >> 16;
}
break;
+#endif
}
// video update
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;
}
}
}
+#endif
SDL_UpdateTexture(texture, NULL, frame, WINDOW_WIDTH * sizeof(uint32_t));
SDL_RenderCopy(renderer, texture, NULL, NULL);
SDL_RenderPresent(renderer);
-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}