Initial 68000 emulator, from multi_emu.git commit 07320529
authorNick Downing <nick@ndcode.org>
Sun, 21 Aug 2022 08:19:21 +0000 (18:19 +1000)
committerNick Downing <nick@ndcode.org>
Sun, 21 Aug 2022 12:10:26 +0000 (22:10 +1000)
.gitignore [new file with mode: 0644]
.gitmodules [new file with mode: 0644]
Makefile [new file with mode: 0644]
Musashi [new submodule]
emu_68000.c [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..8d4b223
--- /dev/null
@@ -0,0 +1,3 @@
+*.o
+/emu_68000
+/emu_68000_alt
diff --git a/.gitmodules b/.gitmodules
new file mode 100644 (file)
index 0000000..11cdffe
--- /dev/null
@@ -0,0 +1,3 @@
+[submodule "Musashi"]
+       path = Musashi
+       url = https://github.com/nickd4/Musashi.git
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..4985e36
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,42 @@
+BIN2HEX=bin2hex.py
+
+CFLAGS=-g -Og -Wall -Wno-attributes -Wno-unused-function
+ALT_68000_CFLAGS=-DALT_BACKEND=1 -IMusashi -IMusashi/softfloat
+
+.PHONY: all
+all: \
+emu_68000_alt \
+#emu_68000
+
+# 68000
+emu_68000: emu_68000.o cpu_68000.o
+       ${CC} ${CFLAGS} -o $@ $^
+
+emu_68000.o: cpu_68000.h
+
+cpu_68000.o: cpu_68000.h
+
+emu_68000_alt: \
+emu_68000_alt.o \
+m68kcpu.o \
+m68kops.o \
+softfloat.o
+       ${CC} ${CFLAGS} -o $@ $^
+
+emu_68000_alt.o: \
+emu_68000.c \
+Musashi/m68k.h
+       ${CC} ${CFLAGS} ${ALT_68000_CFLAGS} -o $@ -c $<
+
+m68kcpu.o: Musashi/m68kcpu.c Musashi/m68kfpu.c
+       ${CC} ${CFLAGS} ${ALT_68000_CFLAGS} -o $@ -c $<
+
+m68kops.o: Musashi/m68kops.c
+       ${CC} ${CFLAGS} ${ALT_68000_CFLAGS} -o $@ -c $<
+
+softfloat.o: Musashi/softfloat/softfloat.c
+       ${CC} ${CFLAGS} ${ALT_68000_CFLAGS} -o $@ -c $<
+
+.PHONY: clean
+clean:
+       rm -f *.o emu_68000 emu_68000_alt
diff --git a/Musashi b/Musashi
new file mode 160000 (submodule)
index 0000000..5b2b80f
--- /dev/null
+++ b/Musashi
@@ -0,0 +1 @@
+Subproject commit 5b2b80f4b42ea251093583aac1d19033bb37f35c
diff --git a/emu_68000.c b/emu_68000.c
new file mode 100644 (file)
index 0000000..762038d
--- /dev/null
@@ -0,0 +1,369 @@
+#include <endian.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#if ALT_BACKEND
+#include "Musashi/m68k.h"
+#else
+#include "cpu_68000.h"
+#endif
+
+#define EXCEPTION_TRAP_BASE 0x20
+
+#define REG_TRACE 0
+#define MEM_TRACE 0
+
+#define MEM_SIZE 0x8000
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define MEM_SIZE_M1 (MEM_SIZE - 1)
+#define MEM_SIZE_M2 (MEM_SIZE - 2)
+#define MEM_SIZE_M4 (MEM_SIZE - 4)
+#else
+#define MEM_SIZE_M1 0
+#define MEM_SIZE_M2 0
+#define MEM_SIZE_M4 0
+#endif
+union {
+  uint8_t b[MEM_SIZE];
+  uint16_t w[MEM_SIZE >> 1];
+  uint32_t l[MEM_SIZE >> 2];
+} mem;
+
+void load_s19(char *name) {
+  FILE *fp = fopen(name, "r");
+  if (fp == NULL) {
+    perror(name);
+    exit(EXIT_FAILURE);
+  }
+
+  char line[0x100];
+  while (fgets(line, 0x100, fp)) {
+    for (char *p = line; *p; ++p)
+      if (*p == '\n') {
+        *p = 0;
+        break;
+      }
+
+    // allow comments and concatenation
+    if (line[0] != 'S' || line[1] != '1')
+      continue;
+
+    uint8_t buf[0x7f];
+    int len;
+    for (len = 0; len < 0x7f; ++len) {
+      char *p = line + 2 + 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 < 4) {
+      fprintf(stderr, "too short: %s\n", line);
+      exit(EXIT_FAILURE);
+    }
+
+    uint8_t checksum = 0;
+    for (int i = 0; i < len; ++i)
+      checksum += buf[i];
+    if (checksum != 0xff) {
+      checksum -= 0xff + buf[len - 1];
+      fprintf(
+        stderr,
+        "checksum %02x, should be %02x\n",
+        checksum,
+        buf[len - 1]
+      );
+      exit(EXIT_FAILURE);
+    }
+
+    if (len != buf[0] + 1) {
+      fprintf(stderr, "incorrect length: %s\n", line);
+      exit(EXIT_FAILURE);
+    }
+    len -= 4;
+
+    int addr = (buf[1] << 8) | buf[2], end_addr;
+    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);
+    }
+    for (int i = 0; i < len; ++i)
+      mem.b[(addr + i) ^ MEM_SIZE_M1] = buf[3 + i];
+  }
+
+  fclose(fp);
+}
+
+int read_byte(void *context, int addr) {
+  int data;
+  switch (addr) {
+  case 0x10040:
+    return 3; // bit 0 = rdrf, bit 1 = tdre
+  case 0x10042:
+    data = getchar();
+    switch (data) {
+    case '\n':
+      data = '\r';
+      break;
+    case 0x7f:
+      data = '\b';
+      break;
+#if 0 // just keep sending 0xff at EOF
+    case EOF:
+      exit(EXIT_SUCCESS);
+      break;
+#endif
+    }
+    break;
+  default:
+    if (addr < 0 || addr >= MEM_SIZE) {
+      fprintf(stderr, "invalid read byte: %06x\n", addr);
+      exit(EXIT_FAILURE);
+    }
+    data = mem.b[addr ^ MEM_SIZE_M1];
+    break;
+  }
+#if MEM_TRACE
+  fprintf(stderr, "addr=%06x rd=%02x\n", addr, data);
+#endif
+  return data;
+}
+
+int read_word(void *context, int addr) {
+  if (addr < 0 || addr >= MEM_SIZE || (addr & 1)) {
+    fprintf(stderr, "invalid read word: %06x\n", addr);
+    exit(EXIT_FAILURE);
+  }
+  int data = mem.w[(addr ^ MEM_SIZE_M2) >> 1];
+#if MEM_TRACE
+  fprintf(stderr, "addr=%06x rd=%04x\n", addr, data);
+#endif
+  return data;
+}
+
+int read_long(void *context, int addr) {
+  if (addr < 0 || addr >= MEM_SIZE - 2 || (addr & 1)) {
+    fprintf(stderr, "invalid read long: %06x\n", addr);
+    exit(EXIT_FAILURE);
+  }
+  int data =
+    (mem.w[(addr ^ MEM_SIZE_M2) >> 1] << 16) |
+    mem.w[((addr + 2) ^ MEM_SIZE_M2) >> 1];
+#if MEM_TRACE
+  fprintf(stderr, "addr=%06x rd=%08x\n", addr, data);
+#endif
+  return data;
+}
+
+void write_byte(void *context, int addr, int data) {
+#if MEM_TRACE
+  fprintf(stderr, "addr=%06x wr=%02x\n", addr, data);
+#endif
+  switch (addr) {
+  case 0x10040:
+    break;
+  case 0x10042:
+    data &= 0x7f;
+    switch (data) {
+    case '\r':
+      putchar('\n');
+      break;
+    case '\n':
+      break;
+    default:
+      putchar(data);
+      break;
+    }
+    break;
+  default:
+    if (addr < 0 || addr >= MEM_SIZE) {
+      fprintf(stderr, "invalid write byte: %06x\n", addr);
+      exit(EXIT_FAILURE);
+    }
+    mem.b[addr ^ MEM_SIZE_M1] = data;
+  }
+}
+
+void write_word(void *context, int addr, int data) {
+#if MEM_TRACE
+  fprintf(stderr, "addr=%06x wr=%04x\n", addr, data);
+#endif
+  if (addr < 0 || addr >= MEM_SIZE || (addr & 1)) {
+    fprintf(stderr, "invalid write word: %06x\n", addr);
+    exit(EXIT_FAILURE);
+  }
+  mem.w[(addr ^ MEM_SIZE_M2) >> 1] = data;
+}
+
+void write_long(void *context, int addr, int data) {
+#if MEM_TRACE
+  fprintf(stderr, "addr=%06x wr=%08x\n", addr, data);
+#endif
+  if (addr < 0 || addr >= MEM_SIZE - 2 || (addr & 1)) {
+    fprintf(stderr, "invalid write long: %06x\n", addr);
+    exit(EXIT_FAILURE);
+  }
+  mem.w[(addr ^ MEM_SIZE_M2) >> 1] = data >> 16;
+  mem.w[((addr + 2) ^ MEM_SIZE_M2) >> 1] = data;
+}
+
+#if ALT_BACKEND
+unsigned int m68k_read_memory_8(unsigned int address) {
+  return read_byte(NULL, address);
+}
+
+unsigned int m68k_read_memory_16(unsigned int address) {
+  return read_word(NULL, address);
+}
+
+unsigned int m68k_read_memory_32(unsigned int address) {
+  return read_long(NULL, address);
+}
+
+void m68k_write_memory_8(unsigned int address, unsigned int value) {
+  write_byte(NULL, address, value);
+}
+
+void m68k_write_memory_16(unsigned int address, unsigned int value) {
+  write_word(NULL, address, value);
+}
+
+void m68k_write_memory_32(unsigned int address, unsigned int value) {
+  write_long(NULL, address, value);
+}
+
+/* Called when the CPU pulses the RESET line */
+void cpu_pulse_reset(void)
+{
+//     nmi_device_reset();
+//     output_device_reset();
+//     input_device_reset();
+}
+
+/* Called when the CPU changes the function code pins */
+void cpu_set_fc(unsigned int fc)
+{
+//     g_fc = fc;
+}
+
+/* Called when the CPU acknowledges an interrupt */
+int cpu_irq_ack(int level)
+{
+//     switch(level)
+//     {
+//             case IRQ_NMI_DEVICE:
+//                     return nmi_device_ack();
+//             case IRQ_INPUT_DEVICE:
+//                     return input_device_ack();
+//             case IRQ_OUTPUT_DEVICE:
+//                     return output_device_ack();
+//     }
+//     return M68K_INT_ACK_SPURIOUS;
+  return 0;
+}
+
+int m68k_service_trap(unsigned int vector) {
+  //fprintf(stderr, "trap %x\n", vector);
+  if (vector == EXCEPTION_TRAP_BASE + 0xe) // TBI68K bye command
+    exit(EXIT_SUCCESS);
+  return 0;
+}
+#endif
+
+int main(int argc, char **argv) {
+  if (argc < 2) {
+    printf("usage: %s image.s19\n", argv[0]);
+    exit(EXIT_FAILURE);
+  }
+  load_s19(argv[1]);
+
+  // emulate Tutor firmware for TBI68K
+  mem.l[(0 ^ MEM_SIZE_M4) >> 2] = 0x8000; // initial SP
+  mem.l[(4 ^ MEM_SIZE_M4) >> 2] = 0x900; // initial PC
+
+#if ALT_BACKEND
+  m68k_init();
+  m68k_set_cpu_type(M68K_CPU_TYPE_68000);
+  m68k_pulse_reset();
+
+  while (true) {
+#if REG_TRACE
+    int sr = m68k_get_reg(NULL, M68K_REG_SR);
+    fprintf(
+      stderr,
+      "pc=%08x d0=%08x d1=%08x d2=%08x d3=%08x d4=%08x d5=%08x d6=%08x d7=%08x a0=%08x a1=%08x a2=%08x a3=%08x a4=%08x a5=%08x a6=%08x a7=%08x sr=%04x xf=%d nf=%d zf=%d vf=%d cf=%d\n",
+      m68k_get_reg(NULL, M68K_REG_PC),
+      m68k_get_reg(NULL, M68K_REG_D0),
+      m68k_get_reg(NULL, M68K_REG_D1),
+      m68k_get_reg(NULL, M68K_REG_D2),
+      m68k_get_reg(NULL, M68K_REG_D3),
+      m68k_get_reg(NULL, M68K_REG_D4),
+      m68k_get_reg(NULL, M68K_REG_D5),
+      m68k_get_reg(NULL, M68K_REG_D6),
+      m68k_get_reg(NULL, M68K_REG_D7),
+      m68k_get_reg(NULL, M68K_REG_A0),
+      m68k_get_reg(NULL, M68K_REG_A1),
+      m68k_get_reg(NULL, M68K_REG_A2),
+      m68k_get_reg(NULL, M68K_REG_A3),
+      m68k_get_reg(NULL, M68K_REG_A4),
+      m68k_get_reg(NULL, M68K_REG_A5),
+      m68k_get_reg(NULL, M68K_REG_A6),
+      m68k_get_reg(NULL, M68K_REG_A7),
+      sr,
+      (sr >> 4) & 1,
+      (sr >> 3) & 1,
+      (sr >> 2) & 1,
+      (sr >> 1) & 1,
+      sr & 1
+    );
+#endif
+
+    m68k_execute(1);
+  }
+#else
+  struct cpu_68000 cpu;
+  cpu_68000_init(&cpu, read_byte, NULL, write_byte, NULL);
+  cpu_68000_reset(&cpu);
+
+  while (true) {
+#if REG_TRACE
+    fprintf(
+      stderr,
+      "pc=%04x a=%02x b=%02x s=%04x x=%04x p=%02x hf=%d if=%d nf=%d zf=%d vf=%d cf=%d\n",
+      cpu.regs.word.pc,
+      cpu.regs.byte.a,
+      cpu.regs.byte.b,
+      cpu.regs.word.s,
+      cpu.regs.word.x,
+      cpu.regs.byte.p,
+      cpu.regs.bit.hf,
+      cpu.regs.bit._if,
+      cpu.regs.bit.nf,
+      cpu.regs.bit.zf,
+      cpu.regs.bit.vf,
+      cpu.regs.bit.cf
+    );
+#endif
+
+    cpu_68000_execute(&cpu);
+  }
+#endif
+
+  return 0;
+}