--- /dev/null
+#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;
+}