Add emu_mips_alt, yams and mips-tetris, no console yet
authorNick Downing <nick@ndcode.org>
Sun, 1 Jan 2023 22:03:46 +0000 (09:03 +1100)
committerNick Downing <nick@ndcode.org>
Tue, 3 Jan 2023 03:58:17 +0000 (14:58 +1100)
.gitignore
.gitmodules
Makefile
doc/HP_AppA.pdf [new file with mode: 0644]
doc/MIPS_Vol1.pdf [new file with mode: 0644]
doc/MIPS_Vol2.pdf [new file with mode: 0644]
doc/MIPS_Vol3.pdf [new file with mode: 0644]
emu_mips.c [new file with mode: 0644]
mips-tetris [new submodule]
yams [new submodule]

index ce7a251..3972a8e 100644 (file)
@@ -16,6 +16,8 @@
 /emu_6809_alt
 /emu_8086
 /emu_8086_alt
+/emu_mips
+/emu_mips_alt
 /emu_pdp11
 /emu_pdp11_alt
 /emu_z80
index 8868d42..b493372 100644 (file)
@@ -31,3 +31,9 @@
 [submodule "simh"]
        path = simh
        url = https://github.com/nickd4/simh.git
+[submodule "yams"]
+       path = yams
+       url = https://github.com/nickd4/yams.git
+[submodule "mips-tetris"]
+       path = mips-tetris
+       url = https://github.com/nickd4/mips-tetris.git
index d3c61d3..673a5e3 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -6,6 +6,7 @@ ALT_6800_CFLAGS=-DALT_BACKEND=1 -DUSE_PROTOTYPES -DM6800 -Isim68xx/inc/arch/m680
 ALT_68000_CFLAGS=-DALT_BACKEND=1 -IMusashi -IMusashi/softfloat
 ALT_6809_CFLAGS=-DALT_BACKEND=1 -IVCC
 ALT_8086_CFLAGS=-DALT_BACKEND=1 -Ivirtualxt/lib/vxt -Ivirtualxt/lib/vxt/include
+ALT_MIPS_CFLAGS=-DALT_BACKEND=1 -DHAVE_CONFIG_H -Iyams -Iyams/src -Wno-enum-compare
 ALT_PDP11_CFLAGS=-DALT_BACKEND=1 -Isimh -Isimh/PDP11
 ALT_Z80_CFLAGS=-DALT_BACKEND=1
 
@@ -23,12 +24,14 @@ emu_6809_alt \
 basic_6809.ihx \
 emu_8086_alt \
 msbasic.ihx \
+emu_mips_alt \
 emu_pdp11_alt \
 basic_pdp11.ihx \
 emu_z80 \
 emu_z80_alt \
 zexall.ihx \
 zexdoc.ihx
+#emu_mips
 #emu_pdp11
 #emu_68000
 #emu_8086
@@ -196,6 +199,39 @@ msbasic.ihx: msbasic.com
        ./entry_point.py 0x50:0x100 __temp__.ihx $@
        rm __temp__.ihx
 
+# MIPS
+emu_mips: emu_mips.o cpu_mips.o
+       ${CC} ${CFLAGS} -o $@ $^
+
+emu_mips.o: cpu_mips.h
+
+cpu_mips.o: cpu_mips.h
+
+emu_mips_alt: \
+emu_mips_alt.o \
+cp0.o \
+cpu.o \
+misc.o
+       ${CC} ${CFLAGS} -o $@ $^
+
+emu_mips_alt.o: \
+emu_mips.c \
+yams/src/cpu.h \
+yams/src/cpu_defs.h \
+yams/src/cpuregs.h \
+yams/src/memory.h \
+yams/src/simulator.h
+       ${CC} ${CFLAGS} ${ALT_MIPS_CFLAGS} -o $@ -c $<
+
+cp0.o: yams/src/cp0.c
+       ${CC} ${CFLAGS} ${ALT_MIPS_CFLAGS} -o $@ -c $<
+
+cpu.o: yams/src/cpu.c
+       ${CC} ${CFLAGS} ${ALT_MIPS_CFLAGS} -o $@ -c $<
+
+misc.o: yams/src/misc.c
+       ${CC} ${CFLAGS} ${ALT_MIPS_CFLAGS} -o $@ -c $<
+
 # PDP-11
 emu_pdp11: emu_pdp11.o cpu_pdp11.o
        ${CC} ${CFLAGS} -o $@ $^
diff --git a/doc/HP_AppA.pdf b/doc/HP_AppA.pdf
new file mode 100644 (file)
index 0000000..4a3073d
Binary files /dev/null and b/doc/HP_AppA.pdf differ
diff --git a/doc/MIPS_Vol1.pdf b/doc/MIPS_Vol1.pdf
new file mode 100644 (file)
index 0000000..414361d
Binary files /dev/null and b/doc/MIPS_Vol1.pdf differ
diff --git a/doc/MIPS_Vol2.pdf b/doc/MIPS_Vol2.pdf
new file mode 100644 (file)
index 0000000..ca99401
Binary files /dev/null and b/doc/MIPS_Vol2.pdf differ
diff --git a/doc/MIPS_Vol3.pdf b/doc/MIPS_Vol3.pdf
new file mode 100644 (file)
index 0000000..487dbe9
Binary files /dev/null and b/doc/MIPS_Vol3.pdf differ
diff --git a/emu_mips.c b/emu_mips.c
new file mode 100644 (file)
index 0000000..6141f10
--- /dev/null
@@ -0,0 +1,389 @@
+#include <endian.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#if ALT_BACKEND
+#include "yams/src/cpu.h"
+#include "yams/src/cpu_defs.h"
+#include "yams/src/cpuregs.h"
+#include "yams/src/memory.h"
+#include "yams/src/simulator.h"
+#else
+#include "cpu_mips.h"
+#endif
+
+#define EXCEPTION_TRAP_BASE 0x20
+
+#define REG_TRACE 1
+#define MEM_TRACE 1
+
+#define MEM_SIZE 0x800000 // 4 MBytes code, 4 MBytes data
+#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 hw[MEM_SIZE >> 1];
+  uint32_t w[MEM_SIZE >> 2];
+} mem;
+
+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);
+      }
+      for (int i = 0; i < len; ++i)
+        mem.b[(addr + i) ^ MEM_SIZE_M1] = buf[4 + i];
+      break;
+    case 1:
+      had_eof = true;
+      break;
+    case 3:
+      if (len < 4) {
+        fprintf(stderr, "invalid start address record: %s\n", line);
+        exit(EXIT_FAILURE);
+      }
+      entry_point = (buf[4] << 24) | (buf[5] << 16) | (buf[6] << 8) | buf[7];
+      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;
+#if 0
+    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;
+#endif
+    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 read_byte(void *context, int addr) {
+  if (addr < 0 || addr >= MEM_SIZE) {
+    fprintf(stderr, "invalid read byte: %08x\n", addr);
+    exit(EXIT_FAILURE);
+  }
+  int data = mem.b[addr ^ MEM_SIZE_M1];
+#if MEM_TRACE
+  fprintf(stderr, "addr=%08x rd=%02x\n", addr, data);
+#endif
+  return data;
+}
+
+int read_halfword(void *context, int addr) {
+  if (addr < 0 || addr >= MEM_SIZE || (addr & 1)) {
+    fprintf(stderr, "invalid read halfword: %08x\n", addr);
+    exit(EXIT_FAILURE);
+  }
+  int data = mem.hw[(addr ^ MEM_SIZE_M2) >> 1];
+#if MEM_TRACE
+  fprintf(stderr, "addr=%08x rd=%04x\n", addr, data);
+#endif
+  return data;
+}
+
+int read_word(void *context, int addr) {
+  if (addr < 0 || addr >= MEM_SIZE || (addr & 3)) {
+    fprintf(stderr, "invalid read word: %08x\n", addr);
+    exit(EXIT_FAILURE);
+  }
+  int data = mem.w[(addr ^ MEM_SIZE_M4) >> 2];
+#if MEM_TRACE
+  fprintf(stderr, "addr=%08x rd=%08x\n", addr, data);
+#endif
+  return data;
+}
+
+void write_byte(void *context, int addr, int data) {
+#if MEM_TRACE
+  fprintf(stderr, "addr=%08x wr=%02x\n", addr, data);
+#endif
+  if (addr < 0 || addr >= MEM_SIZE) {
+    fprintf(stderr, "invalid write byte: %08x\n", addr);
+    exit(EXIT_FAILURE);
+  }
+  mem.b[addr ^ MEM_SIZE_M1] = data;
+}
+
+void write_halfword(void *context, int addr, int data) {
+#if MEM_TRACE
+  fprintf(stderr, "addr=%08x wr=%04x\n", addr, data);
+#endif
+  if (addr < 0 || addr >= MEM_SIZE || (addr & 1)) {
+    fprintf(stderr, "invalid write halfword: %08x\n", addr);
+    exit(EXIT_FAILURE);
+  }
+  mem.hw[(addr ^ MEM_SIZE_M2) >> 1] = data;
+}
+
+void write_word(void *context, int addr, int data) {
+#if MEM_TRACE
+  fprintf(stderr, "addr=%08x wr=%08x\n", addr, data);
+#endif
+  if (addr < 0 || addr >= MEM_SIZE || (addr & 3)) {
+    fprintf(stderr, "invalid write word: %08x\n", addr);
+    exit(EXIT_FAILURE);
+  }
+  mem.w[(addr ^ MEM_SIZE_M4) >> 2] = data;
+}
+
+#if ALT_BACKEND
+int simulator_bigendian = 1;
+
+/* Translates a virtual address to a physical address
+ * does not check TLB exceptions so make sure they do not 
+ * occur when calling this ;) (e.g. by doing memory load/store _before_)
+ */
+uint32_t phys_addr(uint32_t vaddr, cpu_t *cpu) {
+#if MEM_TRACE
+  fprintf(stderr, "vaddr=%08x\n", vaddr);
+#endif
+  // emulate MARS memory model
+  //   .text: [0x00400000, 0x00800000) -> [0x00000000, 0x00400000)
+  //   .data: [0x10000000, 0x10400000) -> [0x00400000, 0x00800000)
+  if (vaddr < 0x400000)
+    abort();
+  if (vaddr < 0x800000)
+    return vaddr + (0 - 0x400000);
+  if (vaddr < 0x10000000)
+    abort();
+  if (vaddr < 0x10400000)
+    return vaddr + (0x400000 - 0x10000000);
+  abort();
+}
+
+/* Read a byte, halfword or word from memory. Return exception number. */
+exception_t mem_read(memory_t *mem, uint32_t addr, void *buf, 
+                     int size, cpu_t *cpu) {
+  addr = phys_addr(addr, NULL);
+  switch (size) {
+  case 1:
+    *(uint8_t *)buf = read_byte(NULL, addr);
+    break;
+  case 2:
+    *(uint16_t *)buf = read_halfword(NULL, addr);
+    break;
+  case 4:
+    *(uint32_t *)buf = read_word(NULL, addr);
+    break;
+  default:
+    abort();
+  }
+  return NoException;
+}
+
+/* Write a byte, halfword or word to memory. Return exception number. */
+exception_t mem_write(memory_t *mem, uint32_t addr, void *buf, 
+                      int size, cpu_t *cpu) {
+  addr = phys_addr(addr, NULL);
+  switch (size) {
+  case 1:
+    write_byte(NULL, addr, *(uint8_t *)buf);
+    break;
+  case 2:
+    write_halfword(NULL, addr, *(uint16_t *)buf);
+    break;
+  case 4:
+    write_word(NULL, addr, *(uint32_t *)buf);
+    break;
+  default:
+    abort();
+  }
+  return NoException;
+}
+#endif
+
+int main(int argc, char **argv) {
+  if (argc < 2) {
+    printf("usage: %s image.ihx\n", argv[0]);
+    exit(EXIT_FAILURE);
+  }
+  load_ihx(argv[1]);
+
+#if ALT_BACKEND
+  cpu_t *cpu = cpu_init(0);
+
+  // for mips-tetris assume main is at 0x400000
+  cpu->registers[PC] = 0x400000;
+  cpu->next_pc = cpu->registers[PC] + 4;
+  cpu->registers[R29] = 0x103ffffc;
+
+  while (true) {
+#if REG_TRACE
+    fprintf(
+      stderr,
+      "pc=%08x zero=%08x at=%08x v0=%08x v1=%08x a0=%08x a1=%08x a2=%08x a3=%08x t0=%08x t1=%08x t2=%08x t3=%08x t4=%08x t5=%08x t6=%08x t7=%08x s0=%08x s1=%08x s2=%08x s3=%08x s4=%08x s5=%08x s6=%08x s7=%08x t8=%08x t9=%08x k0=%08x k1=%08x gp=%08x sp=%08x fp=%08x ra=%08x hi=%08x lo=%08x\n",
+      cpu->registers[PC],
+      cpu->registers[R0],
+      cpu->registers[R1],
+      cpu->registers[R2],
+      cpu->registers[R3],
+      cpu->registers[R4],
+      cpu->registers[R5],
+      cpu->registers[R6],
+      cpu->registers[R7],
+      cpu->registers[R8],
+      cpu->registers[R9],
+      cpu->registers[R10],
+      cpu->registers[R11],
+      cpu->registers[R12],
+      cpu->registers[R13],
+      cpu->registers[R14],
+      cpu->registers[R15],
+      cpu->registers[R16],
+      cpu->registers[R17],
+      cpu->registers[R18],
+      cpu->registers[R19],
+      cpu->registers[R20],
+      cpu->registers[R21],
+      cpu->registers[R22],
+      cpu->registers[R23],
+      cpu->registers[R24],
+      cpu->registers[R25],
+      cpu->registers[R26],
+      cpu->registers[R27],
+      cpu->registers[R28],
+      cpu->registers[R29],
+      cpu->registers[R30],
+      cpu->registers[R31],
+      cpu->registers[HI],
+      cpu->registers[LO]
+    );
+#endif
+
+    cpu_update(cpu);
+  }
+#else
+  struct cpu_mips cpu;
+  cpu_mips_init(&cpu, read_byte, NULL, write_byte, NULL);
+  cpu_mips_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_mips_execute(&cpu);
+  }
+#endif
+
+  return 0;
+}
diff --git a/mips-tetris b/mips-tetris
new file mode 160000 (submodule)
index 0000000..54c0a47
--- /dev/null
@@ -0,0 +1 @@
+Subproject commit 54c0a47e9cd2bc82fd81c8cad3d48512c4d98040
diff --git a/yams b/yams
new file mode 160000 (submodule)
index 0000000..1596100
--- /dev/null
+++ b/yams
@@ -0,0 +1 @@
+Subproject commit 1596100ae4e4b1135ac54c5abffb02d92b270218