// vim: set noexpandtab sw=8 : /* * PDP-8 format converter. * * Usage: p8bin2hex tape.bin > tape.hex * * Assumes tape.bin is a SIMH-compatible paper tape image in "BIN" or "RIM" * formats. * * RIM format: * 10.000.000 leader/trailer (at least an inch of these) * 01.aaa.aaa address hi (bits 5:0 contain address 11:6) * 00.aaa.aaa address lo (bits 5:0 contain address 5:0) * 00.xxx.xxx data hi (bits 5:0 contain data 11:6) * 00.xxx.xxx data lo (bits 5:0 contain data 5:0) * * * FIXME: RIM format currently almost always fails with bad checksum * * BIN format: * 10.000.000 leader/trailer (at least an inch of these) * 11.011.000 "field" (bits 6:4 specify address 14:12) * 01.000.010 origin hi bits: bits 5:0 contain origin 11:6 * 00.011.100 origin lo bits: bits 5:0 contain origin 5:0 * 00.xxx.xxx data hi bits: bits 5:0 contain data word 11:6 * 00.xxx.xxx data lo bits: bits 5:0 contain data word 5:0 * * 00.xxx.xxx chucksum hi - sum of all data and origin frames, but * 00.xxx.xxx chucksum lo - excluding field and leader/trailer * * Some tapes may contain assembler error messages. These are are text * delimited by 0377 bytes. The BIN loader ignores bytes from starting * at any 0377 byte, until another 0377 byte is seen. */ #include #include #include #include #include #include #include uint16_t mem[32768]; int load(FILE *fp) { bool binfile = false; bool sawdata = false; enum { Addr, Data, Leader, Fetch, Skip , Start } s = Start; int c = 0, n, v = 0; uint16_t a = 0, d = 0, t = 0, u = 0; for (n = 0; (c = getc(fp)) != EOF; ++n) { switch (s) { case Addr: u += c; a |= c; s = Fetch; break; case Data: u += c; d |= 0x8000 | c; s = Fetch; break; case Fetch: fetch: if (c == 0200) { if ((d & 0x8000) != 0) { if (binfile && (d & 07777) != (t & 07777)) fprintf(stderr, "\nbad checksum %05o\n", t & 07777); else v = 1; } goto leadout; } t += u; u = 0; if (d & 0x8000) { mem[a] = d & 07777; binfile |= sawdata; sawdata = true; a = (a & ~07777) | ((a + 1) & 07777); d = 0; } if ((c & 0300) == 0000) { u = c; d = (c & 077) << 6; s = Data; break; } if ((c & 0300) == 0100) { u = c; a = (a & ~07777) | (c & 077) << 6; sawdata = false; s = Addr; break; } if ((c & 0307) == 0300) { a = ((c & 0070) << 9) | (a & 07777); break; } if (c == 0377) { s = Skip; break; } break; case Leader: leader: if (c != 0200) { s= Fetch; goto fetch; } break; case Skip: if (c == 0377) s = Fetch; break; case Start: if (c == 0200) { s = Leader; goto leader; } break; } } leadout: while ((c = getc(fp)) != EOF) ; return v; } #define MAX_LINE_SIZE 63 char buf[MAX_LINE_SIZE * 2] = {0}; void print(const char *fmt, ...) { va_list args; va_start(args, fmt); int size = vsnprintf(NULL, 0, fmt, args) + 1; va_end(args); char tmp[size]; va_start(args, fmt); vsnprintf(tmp, size, fmt, args); va_end(args); strncat(buf, tmp, MAX_LINE_SIZE * 2 - 1); if (strlen(buf) > MAX_LINE_SIZE) { char *space = strrchr(buf, ' '); assert(space); assert(space - buf <= MAX_LINE_SIZE); *space = 0; ++space; assert(strlen(buf) <= MAX_LINE_SIZE); printf("%s\n", buf); memmove(buf, space, buf + sizeof(buf) - space); } } void flush() { if (buf[0]) printf("%s\n", buf); buf[0] = 0; } void dump(unsigned int wordsperline) { unsigned int nextaddr = 0; for (uint_fast32_t a = 0; a < 32768; a += wordsperline) { uint16_t words[wordsperline]; bool allzero = true; for (uint_fast32_t i = 0; i < wordsperline; ++i) { words[i] = mem[a+i]; allzero &= words[i] == 0; } if (allzero) continue; if (nextaddr != a) { if (nextaddr + wordsperline == a) print("= "); else print("!%x ", (unsigned int)((a - nextaddr) / wordsperline)); } print("="); for (uint_fast32_t i = 0; i < wordsperline; ++i) print(i == 0 ? "%x" : ":%04x", (unsigned int)words[i]); print(" "); nextaddr = a + wordsperline; } if (nextaddr != 32768) print("!%x\n", (unsigned int)((32768 - nextaddr) / wordsperline)); flush(); } int main(int argc, char *argv[]) { if (argc < 3) { fprintf(stderr, "usage: p8 wordsperline filename.bin|rim [... filename.bin|rim]\n"); exit(EXIT_FAILURE); } char *end; unsigned int wordsperline = strtol(argv[1], &end, 0); if (*end) { perror(argv[1]); exit(EXIT_FAILURE); } for (int i = 2; i < argc; ++i) { fprintf(stderr, "Loading %s...\n", argv[i]); FILE *fp = fopen(argv[i], "rb"); if (!fp) { perror(argv[i]); exit(EXIT_FAILURE); } if (!load(fp)) exit(EXIT_FAILURE); fclose(fp); } dump(wordsperline); exit(EXIT_SUCCESS); }