Add emu_z80, only alternate backend so far, can run ZEXALL (investigate 1 test)
authorNick Downing <nick@ndcode.org>
Wed, 20 Jul 2022 00:37:30 +0000 (10:37 +1000)
committerNick Downing <nick@ndcode.org>
Thu, 21 Jul 2022 02:43:21 +0000 (12:43 +1000)
.gitignore
.gitmodules
Makefile
ZEXALL [new submodule]
emu_65c02.c
emu_z80.c [new file with mode: 0644]
z80 [new submodule]

index 6e6fa2b..e5ab023 100644 (file)
@@ -2,3 +2,5 @@
 *.o
 /emu_65c02
 /emu_65c02_alt
+/emu_z80
+/emu_z80_alt
index c444d04..686a3ca 100644 (file)
@@ -4,3 +4,9 @@
 [submodule "vrEmu6502"]
        path = vrEmu6502
        url = https://github.com/nickd4/vrEmu6502.git
+[submodule "z80"]
+       path = z80
+       url = https://github.com/nickd4/z80.git
+[submodule "ZEXALL"]
+       path = ZEXALL
+       url = https://github.com/agn453/ZEXALL.git
index 6c03c95..1ab7b74 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -7,7 +7,11 @@ all: \
 emu_65c02 \
 emu_65c02_alt \
 6502_functional_test.ihx \
-65C02_extended_opcodes_test.ihx
+65C02_extended_opcodes_test.ihx \
+emu_z80_alt \
+zexall.ihx \
+zexdoc.ihx
+#emu_z80
 
 emu_65c02: emu_65c02.o cpu_65c02.o
        ${CC} ${CFLAGS} -o $@ $^
@@ -20,7 +24,7 @@ emu_65c02_alt: emu_65c02_alt.o vrEmu6502.o
        ${CC} ${CFLAGS} -o $@ $^
 
 emu_65c02_alt.o: emu_65c02.c vrEmu6502/src/vrEmu6502.h
-       ${CC} ${CFLAGS} -DVREMU6502=1 -DVR_6502_EMU_STATIC=1 -o $@ -c $<
+       ${CC} ${CFLAGS} -DALT_BACKEND=1 -DVR_6502_EMU_STATIC=1 -o $@ -c $<
 
 vrEmu6502.o: vrEmu6502/src/vrEmu6502.c vrEmu6502/src/vrEmu6502.h
        ${CC} ${CFLAGS} -DVR_6502_EMU_STATIC=1 -c $<
@@ -37,6 +41,32 @@ vrEmu6502.o: vrEmu6502/src/vrEmu6502.c vrEmu6502/src/vrEmu6502.h
        ./entry_point.py 0x400 __temp__.ihx $@
        rm __temp__.ihx
 
+emu_z80: emu_z80.o cpu_z80.o
+       ${CC} ${CFLAGS} -o $@ $^
+
+emu_z80.o: cpu_z80.h
+
+cpu_z80.o: cpu_z80.h
+
+emu_z80_alt: emu_z80_alt.o z80.o
+       ${CC} ${CFLAGS} -o $@ $^
+
+emu_z80_alt.o: emu_z80.c z80/z80.h
+       ${CC} ${CFLAGS} -DALT_BACKEND=1 -o $@ -c $<
+
+z80.o: z80/z80.c z80/z80.h
+       ${CC} ${CFLAGS} -c $<
+
+zexall.ihx: ZEXALL/zexall.com
+       ${BIN2HEX} --offset=0x100 $< __temp__.ihx
+       ./entry_point.py 0x100 __temp__.ihx $@
+       rm __temp__.ihx
+
+zexdoc.ihx: ZEXALL/zexdoc.com
+       ${BIN2HEX} --offset=0x100 $< __temp__.ihx
+       ./entry_point.py 0x100 __temp__.ihx $@
+       rm __temp__.ihx
+
 .PHONY: clean
 clean:
-       rm -f *.ihx *.o emu_65c02
+       rm -f *.ihx *.o emu_65c02 emu_65c02_alt emu_z80 emu_z80_alt
diff --git a/ZEXALL b/ZEXALL
new file mode 160000 (submodule)
index 0000000..aca2aa6
--- /dev/null
+++ b/ZEXALL
@@ -0,0 +1 @@
+Subproject commit aca2aa66cb19a4e675a5039c1a1c5aed1f52353e
index 65c9c58..8a91895 100644 (file)
@@ -3,7 +3,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#if VREMU6502
+#if ALT_BACKEND
 #include "vrEmu6502/src/vrEmu6502.h"
 #else
 #include "cpu_65c02.h"
@@ -148,7 +148,7 @@ void write_byte(int addr, int data) {
   mem[addr] = data;
 }
 
-#if VREMU6502
+#if ALT_BACKEND
 uint8_t mem_read(uint16_t addr, bool isDbg) {
   return read_byte(addr);
 }
@@ -165,7 +165,7 @@ int main(int argc, char **argv) {
   }
   int entry_point = load_ihx(argv[1]);
 
-#if VREMU6502
+#if ALT_BACKEND
   mem[0xfffc] = (uint8_t)(entry_point & 0xff);
   mem[0xfffd] = (uint8_t)(entry_point >> 8);
 
diff --git a/emu_z80.c b/emu_z80.c
new file mode 100644 (file)
index 0000000..6ab0a55
--- /dev/null
+++ b/emu_z80.c
@@ -0,0 +1,308 @@
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#if ALT_BACKEND
+#include "z80/z80.h"
+#else
+#include "cpu_z80.h"
+#endif
+
+#define REG_TRACE 0
+#define MEM_TRACE 0
+#define IO_TRACE 0
+
+#define MEM_SIZE 0x10000
+uint8_t mem[MEM_SIZE];
+
+// read and write are separated for I/O
+#define IO_SIZE 0x100
+uint8_t io_read[IO_SIZE];
+uint8_t io_write[IO_SIZE];
+
+int load_ihx(char *name) {
+  FILE *fp = fopen(name, "r");
+  if (fp == NULL) {
+    perror(name);
+    exit(EXIT_FAILURE);
+  }
+
+  int base = 0, entry_point = 0;
+  bool had_eof = false;
+  char line[0x100];
+  while (fgets(line, 0x100, fp)) {
+    for (char *p = line; *p; ++p)
+      if (*p == '\n') {
+        *p = 0;
+        break;
+      }
+
+    if (had_eof) {
+      fprintf(stderr, "garbage after EOF record: %s\n", line);
+      exit(EXIT_FAILURE);
+    }
+
+    if (line[0] != ':') {
+      fprintf(stderr, "require colon: %s\n", line);
+      exit(EXIT_FAILURE);
+    }
+
+    uint8_t buf[0x7f];
+    int len;
+    for (len = 0; len < 0x7f; ++len) {
+      char *p = line + 1 + len * 2;
+      if (*p == 0)
+        break;
+      if (*p == '\n') {
+        *p = 0;
+        break;
+      }
+      uint8_t c = p[2];
+      p[2] = 0;
+
+      char *q;
+      buf[len] = (uint8_t)strtol(p, &q, 16);
+      p[2] = c;
+      if (q != p + 2) {
+        fprintf(stderr, "not hex byte: %s\n", p);
+        exit(EXIT_FAILURE);
+      }
+    }
+
+    if (len == 0) {
+      fprintf(stderr, "empty line: %s\n", line);
+      exit(EXIT_FAILURE);
+    }
+
+    uint8_t checksum = 0;
+    for (int i = 0; i < len; ++i)
+      checksum += buf[i];
+    if (checksum) {
+      checksum -= buf[len - 1];
+      fprintf(
+        stderr,
+        "checksum %02x, should be %02x\n",
+        checksum,
+        buf[len - 1]
+      );
+      exit(EXIT_FAILURE);
+    }
+
+    len -= 5;
+    if (len != buf[0]) {
+      fprintf(stderr, "incorrect length: %s\n", line);
+      exit(EXIT_FAILURE);
+    }
+
+    int addr = (buf[1] << 8) | buf[2], end_addr;
+    switch (buf[3]) {
+    case 0:
+      addr += base;
+      end_addr = addr + len;
+      if (end_addr < addr || end_addr > MEM_SIZE) {
+        fprintf(stderr, "invalid load range: [0x%x, 0x%x)\n", addr, end_addr);
+        exit(EXIT_FAILURE);
+      }
+      memcpy(mem + addr, buf + 4, len);
+      break;
+    case 1:
+      had_eof = true;
+      break;
+    case 4:
+      if (len < 2) {
+        fprintf(stderr, "invalid extended linear address record: %s\n", line);
+        exit(EXIT_FAILURE);
+      }
+      base = (buf[4] << 24) | (buf[5] << 16);
+      break;
+    case 5:
+      if (len < 4) {
+        fprintf(stderr, "invalid start linear address record: %s\n", line);
+        exit(EXIT_FAILURE);
+      }
+      entry_point = (buf[4] << 24) | (buf[5] << 16) | (buf[6] << 8) | buf[7];
+      break;
+    default:
+      fprintf(stderr, "unknown record type: 0x%x\n", buf[3]);
+      exit(EXIT_FAILURE);
+    }
+  }
+  if (!had_eof) {
+    fprintf(stderr, "no EOF record\n");
+    exit(EXIT_FAILURE);
+  }
+
+  fclose(fp);
+  return entry_point;
+}
+
+int bdos(int c, int de) {
+  switch (c) {
+  case 2:
+    de &= 0xff;
+    if (de != '\r')
+      putchar(de);
+    break;
+  case 9:
+    while (mem[de] != '$') {
+      if (mem[de] != '\r')
+        putchar(mem[de]);
+      de = (de + 1) & 0xffff;
+    }
+    break;
+  default:
+    fprintf(stderr, "unimplemented BDOS call 0x%02x\n", c);
+    exit(EXIT_FAILURE);
+  }
+  return de;
+}
+
+int mem_read_byte(int addr) {
+#if MEM_TRACE
+  int data = mem[addr];
+  printf("mem_addr=%04x rd=%02x\n", addr, data);
+  return data;
+#else
+  return mem[addr];
+#endif
+}
+
+void mem_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) {
+#if IO_TRACE
+  int data = io_read[addr];
+  printf("io_addr=%04x rd=%02x\n", addr, data);
+  return data;
+#else
+  return io_read[addr];
+#endif
+}
+
+void io_write_byte(int addr, int data) {
+#if IO_TRACE
+  printf("io_addr=%04x wr=%02x\n", addr, data);
+#endif
+  io_write[addr] = data;
+}
+
+#if ALT_BACKEND
+uint8_t rb(void *userdata, uint16_t addr) {
+  return mem_read_byte(addr);
+}
+
+void wb(void *userdata, uint16_t addr, uint8_t val) {
+  mem_write_byte(addr, val);
+}
+
+uint8_t in(z80 *const z, uint8_t port) {
+  return io_read_byte(port);
+}
+
+void out(z80 *const z, uint8_t port, uint8_t val) {
+  io_write_byte(port, val);
+}
+#endif
+
+int main(int argc, char **argv) {
+  if (argc < 2) {
+    fprintf(stderr, "usage: %s image.ihx\n", argv[0]);
+    exit(EXIT_FAILURE);
+  }
+  int entry_point = load_ihx(argv[1]);
+
+  mem[5] = 0xc9; // ret, for bdos emulation
+
+#if ALT_BACKEND
+  z80 cpu;
+  z80_init(&cpu);
+  cpu.read_byte = rb;
+  cpu.write_byte = wb;
+  cpu.port_in = in;
+  cpu.port_out = out;
+  cpu.pc = entry_point;
+
+  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",
+      cpu.pc,
+      cpu.cf |
+      (cpu.nf << 1) |
+      (cpu.pf << 2) |
+      (cpu.hf << 4) |
+      (cpu.zf << 6) |
+      (cpu.sf << 7) |
+      (cpu.a << 8), 
+      cpu.c | (cpu.b << 8),
+      cpu.e | (cpu.d << 8),
+      cpu.l | (cpu.h << 8),
+      cpu.sp,
+      cpu.ix,
+      cpu.iy,
+      cpu.cf,
+      cpu.nf,
+      cpu.pf,
+      cpu.hf,
+      cpu.zf,
+      cpu.sf
+    );
+#endif
+
+    if (cpu.pc == 0) {
+      putchar('\n');
+      break;
+    }
+    if (cpu.pc == 5) {
+      int de = cpu.e | (cpu.d << 8);
+      de = bdos(cpu.c, de);
+      cpu.e = de & 0xff;
+      cpu.d = de >> 8;
+    }
+    z80_step(&cpu, 1);
+  }
+#else
+  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;
+
+  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_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
+    );
+#endif
+
+    int pc = cpu.pc;
+    cpu_z80_execute(&cpu);
+    if (pc == cpu.pc) {
+      printf("hung at %04x\n", pc);
+      break;
+    }
+  }
+#endif
+
+  return 0;
+}
diff --git a/z80 b/z80
new file mode 160000 (submodule)
index 0000000..ae62511
--- /dev/null
+++ b/z80
@@ -0,0 +1 @@
+Subproject commit ae625116f1f9b013fd1a69d0173f8207f8703e21