Add PDP-11 emulator (alternate backend only; based on simh) and BASIC-PTS test
authorNick Downing <nick@ndcode.org>
Sun, 11 Sep 2022 16:23:27 +0000 (02:23 +1000)
committerNick Downing <nick@ndcode.org>
Sun, 11 Sep 2022 16:23:27 +0000 (02:23 +1000)
.gitignore
DEC-11-AJPB-PB.ptap [new file with mode: 0644]
Makefile
absolute_loader.py [new file with mode: 0755]
doc/BASIC_PTS_Listing_Mar77.pdf [new file with mode: 0644]
emu_pdp11.c [new file with mode: 0644]
simh

index 1aef658..ce7a251 100644 (file)
@@ -16,5 +16,7 @@
 /emu_6809_alt
 /emu_8086
 /emu_8086_alt
+/emu_pdp11
+/emu_pdp11_alt
 /emu_z80
 /emu_z80_alt
diff --git a/DEC-11-AJPB-PB.ptap b/DEC-11-AJPB-PB.ptap
new file mode 100644 (file)
index 0000000..2255860
Binary files /dev/null and b/DEC-11-AJPB-PB.ptap differ
index fd93639..1597125 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_PDP11_CFLAGS=-DALT_BACKEND=1 -Isimh -Isimh/PDP11
 ALT_Z80_CFLAGS=-DALT_BACKEND=1
 
 .PHONY: all
@@ -22,10 +23,13 @@ emu_6809_alt \
 basic_6809.ihx \
 emu_8086_alt \
 msbasic.ihx \
+emu_pdp11_alt \
+basic_pdp11.ihx \
 emu_z80 \
 emu_z80_alt \
 zexall.ihx \
 zexdoc.ihx
+#emu_pdp11
 #emu_68000
 #emu_8086
 
@@ -192,6 +196,39 @@ msbasic.ihx: msbasic.com
        ./entry_point.py 0x50:0x100 __temp__.ihx $@
        rm __temp__.ihx
 
+# PDP-11
+emu_pdp11: emu_pdp11.o cpu_pdp11.o
+       ${CC} ${CFLAGS} -o $@ $^
+
+emu_pdp11.o: cpu_pdp11.h
+
+cpu_pdp11.o: cpu_pdp11.h
+
+emu_pdp11_alt: \
+emu_pdp11_alt.o \
+pdp11_cpu.o \
+pdp11_cis.o \
+pdp11_fp.o
+       ${CC} ${CFLAGS} -o $@ $^
+
+emu_pdp11_alt.o: \
+emu_pdp11.c \
+simh/PDP11/pdp11_defs.h \
+simh/PDP11/pdp11_cpumod.h
+       ${CC} ${CFLAGS} ${ALT_PDP11_CFLAGS} -o $@ -c $<
+
+pdp11_cpu.o: simh/PDP11/pdp11_cpu.c
+       ${CC} ${CFLAGS} ${ALT_PDP11_CFLAGS} -o $@ -c $<
+
+pdp11_cis.o: simh/PDP11/pdp11_cis.c
+       ${CC} ${CFLAGS} ${ALT_PDP11_CFLAGS} -o $@ -c $<
+
+pdp11_fp.o: simh/PDP11/pdp11_fp.c
+       ${CC} ${CFLAGS} ${ALT_PDP11_CFLAGS} -o $@ -c $<
+
+basic_pdp11.ihx: DEC-11-AJPB-PB.ptap
+       ./absolute_loader.py $< $@
+
 # Z80
 emu_z80: emu_z80.o cpu_z80.o
        ${CC} ${CFLAGS} -o $@ $^
@@ -232,7 +269,9 @@ emu_68000_alt \
 emu_6809 \
 emu_6809_alt \
 emu_8086_alt \
+emu_pdp11_alt \
 emu_z80 \
 emu_z80_alt
+#emu_pdp11
 #emu_68000
 #emu_8086
diff --git a/absolute_loader.py b/absolute_loader.py
new file mode 100755 (executable)
index 0000000..a0c5135
--- /dev/null
@@ -0,0 +1,43 @@
+#!/usr/bin/env python3
+
+import sys
+from intelhex import IntelHex
+
+EXIT_SUCCESS = 0
+EXIT_FAILURE = 1
+
+if len(sys.argv) < 3:
+  print(f'usage: {sys.argv[0]:s} in.ptap out.ihx')
+  sys.exit(EXIT_FAILURE)
+in_ptap = sys.argv[1]
+out_ihx = sys.argv[2]
+
+intelhex = IntelHex()
+with open(in_ptap, 'rb') as fin:
+  while True:
+    data = fin.read(1)
+    if len(data) == 0:
+      break
+    #print(f'{data[0]:02x}')
+    if data[0] == 0:
+      continue
+    if data[0] != 1:
+      break
+    data += fin.read(5)
+    assert len(data) == 6
+    assert data[1] == 0
+    load_size = data[2] | (data[3] << 8)
+    load_addr = data[4] | (data[5] << 8)
+    assert load_size >= 6
+    load_size -= 6
+    data += fin.read(load_size + 1)
+    assert len(data) == load_size + 7
+    assert (sum(data) & 0xff) == 0 
+    if load_size == 0:
+      assert (load_addr & 1) == 0
+      print(f'entry_point {load_addr:04x}')
+      intelhex.start_addr = {'EIP': load_addr}
+    else:
+      print(f'[{load_addr:04x}, {load_addr + load_size:04x}]')
+      intelhex.frombytes(data[6:-1], load_addr)
+intelhex.write_hex_file(out_ihx)
diff --git a/doc/BASIC_PTS_Listing_Mar77.pdf b/doc/BASIC_PTS_Listing_Mar77.pdf
new file mode 100644 (file)
index 0000000..0dbae70
Binary files /dev/null and b/doc/BASIC_PTS_Listing_Mar77.pdf differ
diff --git a/emu_pdp11.c b/emu_pdp11.c
new file mode 100644 (file)
index 0000000..5b91393
--- /dev/null
@@ -0,0 +1,385 @@
+#include <endian.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#if ALT_BACKEND
+#include "simh/PDP11/pdp11_defs.h"
+#include "simh/PDP11/pdp11_cpumod.h"
+#undef fprintf
+extern int32 R[8];             /* working registers */
+extern int32 PSW;              /* PSW */
+t_stat cpu_reset (DEVICE *dptr);
+#else
+#include "cpu_pdp11.h"
+#endif
+
+#define EXCEPTION_TRAP_BASE 0x20
+
+#define REG_TRACE 1
+#define MEM_TRACE 1
+
+#define MEM_SIZE 0x8000
+#if __BYTE_ORDER == __BIG_ENDIAN
+#define MEM_SIZE_M1 (MEM_SIZE - 1)
+#define MEM_SIZE_M2 (MEM_SIZE - 2)
+#else
+#define MEM_SIZE_M1 0
+#define MEM_SIZE_M2 0
+#endif
+union {
+  uint8_t b[MEM_SIZE];
+  uint16_t w[MEM_SIZE >> 1];
+} 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;
+#if 0
+    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;
+#endif
+    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 read_byte(void *context, int addr) {
+  int data;
+  switch (addr) {
+  case 0xf000:
+    return 3; // bit 0 = rdrf, bit 1 = tdre
+  case 0xf001:
+    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: %04x\n", addr);
+      data = 0xff;
+    }
+    else
+      data = mem.b[addr ^ MEM_SIZE_M1];
+    break;
+  }
+#if MEM_TRACE
+  fprintf(stderr, "addr=%04x rd=%02x\n", addr, data);
+#endif
+  return data;
+}
+
+int read_word(void *context, int addr) {
+  int data;
+  if (addr < 0 || addr >= MEM_SIZE || (addr & 1)) {
+    fprintf(stderr, "invalid read word: %04x\n", addr);
+    if (addr & 1)
+      exit(EXIT_FAILURE);
+    data = 0xffff;
+  }
+  else
+    data = mem.w[(addr ^ MEM_SIZE_M2) >> 1];
+#if MEM_TRACE
+  fprintf(stderr, "addr=%04x rd=%04x\n", addr, data);
+#endif
+  return data;
+}
+
+void write_byte(void *context, int addr, int data) {
+#if MEM_TRACE
+  fprintf(stderr, "addr=%04x wr=%02x\n", addr, data);
+#endif
+  switch (addr) {
+  case 0xf000:
+    break;
+  case 0xf001:
+    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: %04x\n", addr);
+   else
+      mem.b[addr ^ MEM_SIZE_M1] = data;
+  }
+}
+
+void write_word(void *context, int addr, int data) {
+#if MEM_TRACE
+  fprintf(stderr, "addr=%04x wr=%04x\n", addr, data);
+#endif
+  if (addr < 0 || addr >= MEM_SIZE || (addr & 1)) {
+    fprintf(stderr, "invalid write word: %04x\n", addr);
+    if (addr & 1)
+      exit(EXIT_FAILURE);
+  }
+  else
+    mem.w[(addr ^ MEM_SIZE_M2) >> 1] = data;
+}
+
+#if ALT_BACKEND
+int32 ReadE(int32 va) {
+  return read_word(NULL, va);
+}
+
+int32 ReadW(int32 va) {
+  return read_word(NULL, va);
+}
+
+int32 ReadB(int32 va) {
+  return read_byte(NULL, va);
+}
+
+int32 ReadCW(int32 va) {
+  return read_word(NULL, va);
+}
+
+int32 ReadMW(int32 va) {
+  return read_word(NULL, va);
+}
+
+int32 ReadMB(int32 va) {
+  return read_byte(NULL, va);
+}
+
+int32 PReadW(int32 pa) {
+  return read_word(NULL, pa);
+}
+
+int32 PReadB(int32 pa) {
+  return read_byte(NULL, pa);
+}
+
+void WriteW(int32 data, int32 va) {
+  write_word(NULL, va, data);
+}
+
+void WriteB(int32 data, int32 va) {
+  write_byte(NULL, va, data);
+}
+
+void WriteCW(int32 data, int32 va) {
+  write_word(NULL, va, data);
+}
+
+void PWriteW(int32 data, int32 pa) {
+  write_word(NULL, pa, data);
+}
+
+void PWriteB (int32 data, int32 pa) {
+  write_byte(NULL, pa, data);
+}
+
+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.ihx\n", argv[0]);
+    exit(EXIT_FAILURE);
+  }
+  load_ihx(argv[1]);
+
+#if 0
+  // 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
+#endif
+
+#if ALT_BACKEND
+  cpu_reset(NULL);
+
+  while (true) {
+#if REG_TRACE
+    fprintf(
+      stderr,
+      "pc=%04x r0=%04x r1=%04x r2=%04x r3=%04x r4=%04x r5=%04x sp=%04x psw=%04x nf=%d zf=%d vf=%d cf=%d\n",
+      R[7],
+      R[0],
+      R[1],
+      R[2],
+      R[3],
+      R[4],
+      R[5],
+      R[6],
+      PSW,
+      (PSW >> PSW_V_N) & 1,
+      (PSW >> PSW_V_Z) & 1,
+      (PSW >> PSW_V_V) & 1,
+      (PSW >> PSW_V_C) & 1
+    );
+#endif
+
+    sim_instr();
+  }
+#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;
+}
diff --git a/simh b/simh
index cdcce99..e9e4b95 160000 (submodule)
--- a/simh
+++ b/simh
@@ -1 +1 @@
-Subproject commit cdcce99814c50f5898d6d5671f7ba32ef88c17de
+Subproject commit e9e4b95e19b1c8a9634153eb207c96f9e08ed79b