#include "pdp6.h" #include "args.h" #define MAXOPS 20 static char *lp; static int numops; static char *ops[MAXOPS]; /* current apr */ static Apr *apr; static Apr* getapr(void) { Device *dev; if(apr) return apr; for(dev = devlist; dev; dev = dev->next) if(dev->type == apr_ident){ apr = (Apr*)dev; goto found; } err("No APR defined"); found: return apr; } static void skipwhite(void) { while(isspace(*lp)) lp++; } static int isdelim(char c) { return c == '\0' || strchr(" \t\n;'\"", c) != nil; } /* tokenize lp into ops[numops] */ static void splitops(void) { char delim; char *p; numops = 0; for(; *lp; lp++){ skipwhite(); if(*lp == ';' || *lp == '\0') return; if(numops == MAXOPS){ fprintf(stderr, "Too many arguments, ignored <%s>\n", lp); return; } if(*lp == '"' || *lp == '\''){ delim = *lp++; ops[numops++] = p = lp; while(*lp && *lp != delim){ if(*lp == '\\') lp++; *p++ = *lp++; } if(*lp == '\0') lp--; *p = '\0'; }else{ ops[numops++] = p = lp; while(!isdelim(*lp)){ if(*lp == '\\') lp++; *p++ = *lp++; } if(*lp == '\0') lp--; *p = '\0'; } } } /* Parse an octal, decimal or hexadecimal number. * 0nnnn is an octal number. * nnnn. is a decimal number. * 0xnnnn is a hexadecimal number. * returns ~0 on error. */ word parsen(char **sp) { char *s; word dec, oct, hex; char c; int base; s = *sp; base = 0; dec = oct = hex = 0; if(s[0] == '0'){ s++; base = 8; if(*s == 'x' || *s == 'X'){ s++; base = 16; } } while(c = *s++){ if(c >= '0' && c <= '9') c -= '0'; else if(c >= 'A' && c <= 'F') c -= 'A' - 10; else if(c >= 'a' && c <= 'f') c -= 'a' - 10; else if(c == '.'){ /* decimal number, but only if . is at the end */ if(base <= 10 && *s == '\0'){ base = 10; break; }else goto fail; }else break; oct = oct*8 + c; dec = dec*10 + c; hex = hex*16 + c; if(c >= 8){ if(base < 8) base = 8; else goto fail; } if(c >= 10){ if(base < 10) base = 10; else goto fail; } if(c >= 16){ if(base < 16) base = 16; else goto fail; } } *sp = s-1; if(base == 16) return hex; if(base == 10) return dec; return oct; fail: printf("error: number format\n"); return ~0; } static int parserange(char *s, word *start, word *end) { *start = *end = ~0; *start = parsen(&s); if(*start == ~0) return 1; if(*s == '\0'){ *end = *start; return 0; }else if(*s == '-' || *s == ':'){ s++; *end = parsen(&s); if(*end == ~0) return 1; } if(*s){ *start = *end = ~0; printf("error: address format\n"); return 1; } return 0; } enum { FmtOct, FmtMnem }; static void exam(word addr, int fastmem, int format) { word *p; p = getmemref(&getapr()->membus, addr, fastmem, nil); if(p == nil){ printf("Non existent memory\n"); return; } printf("%06lo: ", addr); switch(format){ case FmtOct: default: printf("%012lo\n", *p); break; case FmtMnem: printf("%s\n", disasm(*p)); break; } } static void dep(word addr, int fastmem, word w) { word *p; p = getmemref(&getapr()->membus, addr, fastmem, nil); if(p == nil){ printf("Non existent memory\n"); return; } *p = w; } /* Commands */ typedef struct DevDef DevDef; struct DevDef { char *type; Device *(*make)(int argc, char *argv[]); }; DevDef definitions[] = { { FMEM_IDENT, makefmem }, { CMEM_IDENT, make16kmem }, { MMEM_IDENT, make256kmem }, { APR_IDENT, makeapr }, { TTY_IDENT, maketty }, { PTR_IDENT, makeptr }, { PTP_IDENT, makeptp }, { DC_IDENT, makedc }, { DT_IDENT, makedt }, { DX_IDENT, makedx }, #ifdef GRAPHICS { DIS_IDENT, makedis }, { JOY_IDENT, makejoy }, { OJOY_IDENT, makeojoy }, #endif { NETMEM_IDENT, makenetmem }, { NETCONS_IDENT, makenetcons }, { nil, nil } }; static void c_mkdev(int argc, char *argv[]) { Device *dev; DevDef *def; if(argc < 3){ printf("Not enough arguments\n"); return; } for(def = definitions; def->type; def++) if(strcmp(def->type, argv[2]) == 0){ dev = def->make(argc-3, argv+3); goto found; } printf("No such device type: %s\n", argv[2]); return; found: dev->name = strdup(argv[1]); adddevice(dev); } static void c_mount(int argc, char *argv[]) { Device *dev; if(argc < 3){ printf("Not enough arguments\n"); return; } dev = getdevice(argv[1]); if(dev == nil){ printf("No device: %s\n", argv[1]); return; } if(dev->mount == nil){ printf("No mount for device type %s\n", dev->type); return; } dev->mount(dev, argv[2]); } static void c_unmount(int argc, char *argv[]) { Device *dev; if(argc < 2){ printf("Not enough arguments\n"); return; } dev = getdevice(argv[1]); if(dev == nil){ printf("No device: %s\n", argv[1]); return; } if(dev->unmount == nil){ printf("No unmount for device type %s\n", dev->type); return; } dev->unmount(dev); } /* ioconnect dev busdev */ static void c_ioconnect(int argc, char *argv[]) { Device *dev, *busdev; if(argc < 3){ printf("Not enough arguments\n"); return; } dev = getdevice(argv[1]); if(dev == nil){ printf("No device: %s\n", argv[1]); return; } busdev = getdevice(argv[2]); if(busdev == nil){ printf("No device: %s\n", argv[2]); return; } if(busdev->type != apr_ident){ printf("No bus device: %s\n", busdev->type); return; } if(dev->ioconnect == nil){ printf("No ioconnect for device type %s\n", dev->type); return; } dev->ioconnect(dev, &((Apr*)busdev)->iobus); } /* memconnect dev proc busdev addr */ static void c_memconnect(int argc, char *argv[]) { int proc, addr; Device *dev, *busdev; if(argc < 5){ printf("Not enough arguments\n"); return; } dev = getdevice(argv[1]); if(dev == nil){ printf("No device: %s\n", argv[1]); return; } proc = atoi(argv[2]); busdev = getdevice(argv[3]); if(busdev == nil){ printf("No device: %s\n", argv[3]); return; } if(busdev->type != apr_ident){ printf("No bus device: %s\n", argv[3]); return; } addr = strtol(argv[4], nil, 8); attachmem((Mem*)dev, proc, &((Apr*)busdev)->membus, addr); } /* devconnect dev1 dev2 */ // TODO: not quite sure this is the right way to do it static void c_devconnect(int argc, char *argv[]) { Device *dev1, *dev2; if(argc < 3){ args: printf("Not enough arguments\n"); return; } dev1 = getdevice(argv[1]); if(dev1 == nil){ printf("No device: %s\n", argv[1]); return; } dev2 = getdevice(argv[2]); if(dev2 == nil){ printf("No device: %s\n", argv[2]); return; } if(dev1->type == dc_ident && dev2->type == dt_ident) dtconn((Dc136*)dev1, (Dt551*)dev2); else if(dev1->type == dt_ident && dev2->type == dx_ident){ int n; if(argc < 4) goto args; n = atoi(argv[3]); if(n < 1 || n > 8){ printf("Invalid device number %d\n", n); return; } dxconn((Dt551*)dev1, (Dx555*)dev2, n); }else printf("Cannot connect %s to %s\n", dev2->type, dev1->type); } static void c_ex(int argc, char *argv[]) { char *argv0 = nil; word start, end; int format, fastmem; format = FmtOct; fastmem = 1; ARGBEGIN{ case 's': /* shadow mode */ fastmem = 0; break; case 'm': format = FmtMnem; break; }ARGEND; if(argc < 1) return; if(parserange(argv[0], &start, &end)) return; for(; start <= end; start++) exam(start, fastmem, format); } static void c_dep(int argc, char *argv[]) { char *argv0 = nil; word start, end; word w; int fastmem; fastmem = 1; ARGBEGIN{ case 's': fastmem = 0; break; }ARGEND; if(argc < 2) return; if(parserange(argv[0], &start, &end)) return; w = parsen(&argv[1]); if(w == ~0) return; for(; start <= end; start++) dep(start, fastmem, w); } /* parse "devname.reg". * look up "devname" and return it. * also remove "devname." from original string. * if "devname." is missing, return last device. * return apr if anything goes wrong */ Device* getexdepdev(char *dev) { static Device *exdepdev; char *s; Device *d; s = strchr(dev, '.'); if(s == nil){ if(exdepdev == nil) exdepdev = &getapr()->dev; return exdepdev; } *s++ = '\0'; d = getdevice(dev); exdepdev = d ? d : &getapr()->dev; while(*dev++ = *s++); return exdepdev; } static void c_xex(int argc, char *argv[]) { int i; Device *dev; word w; for(i = 1; i < argc; i++){ dev = getexdepdev(argv[i]); if(dev->examine){ strtolower(argv[i]); w = dev->examine(dev, argv[i]); if(w == ~0) printf("can't examine %s\n", argv[i]); else printf("%012lo\n", w); }else printf("no examine on %s\n", dev->type); } } static void c_xdep(int argc, char *argv[]) { int i; Device *dev; word w; if(argc < 3) return; w = parsen(&argv[argc-1]); if(w == ~0) return; for(i = 1; i < argc-1; i++){ dev = getexdepdev(argv[i]); if(dev->deposit){ strtolower(argv[i]); if(dev->deposit(dev, argv[i], w)) printf("can't deposit %s\n", argv[i]); }else printf("no deposit on %s\n", dev->type); } void updatepanel(Apr *apr); updatepanel(getapr()); } enum { FmtUnk, FmtSav, FmtSblk, FmtRim10 }; static void loadsav(FILE *fp) { word iowd; word w; while(w = readwbak(fp), w != ~0){ if(w >> 27 == 0254){ getapr()->pc = right(w); return; } iowd = w; while(left(iowd) != 0){ iowd += 01000001; w = readwbak(fp); if(w == ~0) goto format; dep(right(iowd), 0, w); } } format: printf("SAV format botch\n"); } static void loadsblk(FILE *fp) { word iowd, w, chk; int d; /* Skip RIM loader */ while(w = readwits(fp), w != ~0) if(w == 0254000000001) goto sblk; goto format; sblk: /* Read a simple block */ while(w = readwits(fp), w != ~0){ /* We expect an AOBJN word here */ if((w & F0) == 0) goto end; iowd = w; chk = iowd; d = right(iowd) != 0; /* 0 is symbol table */ while(left(iowd) != 0){ w = readwits(fp); if(w == ~0) goto format; chk = (chk<<1 | chk>>35) + w & FW; if(d) dep(right(iowd), 0, w); iowd += 01000001; } if(readwits(fp) != chk) goto format; } goto format; end: /* use the first JRST, not the second */ // w = readwits(fp); if(left(w) != 0324000 && left(w) != 0254000) goto format; getapr()->pc = right(w); return; format: printf("SBLK format botch\n"); } static void loadrim10(FILE *fp) { word iowd, w, chk; int nldr; int mac; w = readw(fp); nldr = 0; while(left(w) != 0){ nldr++; readw(fp); w += 01000001; } if(nldr == 017) mac = 1; else if(nldr == 016) mac = 0; else goto format; /* Read a simple block */ while(w = readw(fp), w != ~0){ /* We expect an AOBJN word here */ if((w & F0) == 0) goto end; iowd = w; chk = iowd; while(left(iowd) != 0){ w = readw(fp); if(w == ~0) goto format; if(mac){ chk = (chk<<1 | chk>>35) + w & FW; dep(right(iowd), 0, w); }else{ chk = chk + w & FW; dep(right(iowd+1), 0, w); } iowd += 01000001; } if(readw(fp) != chk) goto format; } goto format; end: /* JRST */ if(left(w) != 0324000 && left(w) != 0254000) goto format; getapr()->pc = right(w); return; format: printf("RIM10 format botch\n"); } static void c_load(int argc, char *argv[]) { char *argv0 = nil; FILE *fp; int fmt; fmt = FmtSav; // assume SAV for now ARGBEGIN{ case 's': fmt = FmtSav; break; case 'b': fmt = FmtSblk; break; case 'r': fmt = FmtRim10; break; }ARGEND; if(argc < 1) return; fp = fopen(argv[0], "rb"); if(fp == nil){ printf("couldn't open file: %s\n", argv[0]); return; } switch(fmt){ case FmtSav: loadsav(fp); break; case FmtSblk: loadsblk(fp); break; case FmtRim10: loadrim10(fp); break; } fclose(fp); } static void c_show(int argc, char *argv[]) { printf("Devices:\n"); showdevices(); printf("Memory:\n"); showmem(&getapr()->membus); } static void c_quit(int argc, char *argv[]) { quit(0); } static void c_help(int argc, char *argv[]); struct { char *cmd; void (*f)(int, char **); char *desc; } cmdtab[] = { { "mkdev", c_mkdev, "make a device: mkdev name type [args]" }, { "mount", c_mount, "mount a file on a device: mount name filename" }, { "unmount", c_unmount, "unmount a file from a device: unmount name" }, { "connectio", c_ioconnect, "connect device to IO bus: connectio devname procname" }, { "connectmem", c_memconnect, "connect memory to mem bus: connectmem memname memport procname address/16k" }, { "connectdev", c_devconnect, "connect devices: connectdev dev1name dev2name [args]" }, { "examine", c_ex, "examine memory: examine [-sm] memrange" }, { "deposit", c_dep, "deposit memory: deposit [-s] memrange word" }, { "xexamine", c_xex, "examine device" }, { "xdeposit", c_xdep, "deposit device" }, { "load", c_load, "load file into memory: load [-sbr] filename" }, { "show", c_show, "show configuration" }, { "quit", c_quit, "quit emulator" }, { "help", c_help, "print help" }, { nil, nil} }; static void c_help(int argc, char *argv[]) { int i; for(i = 0; cmdtab[i].cmd; i++) printf(" %s:\t%s\n", cmdtab[i].cmd, cmdtab[i].desc); } void commandline(char *line) { int i, n; int nfound; size_t l; char *cmd; lp = line; splitops(); if(numops && ops[0][0] != '#'){ nfound = 0; for(i = 0; cmdtab[i].cmd; i++){ cmd = cmdtab[i].cmd; l = strlen(ops[0]); if(strncmp(ops[0], cmd, l) == 0){ n = i; nfound++; } } if(nfound == 0) printf("%s: not found\n", ops[0]); else if(nfound == 1) cmdtab[n].f(numops, ops); else{ printf("Ambiguous command: %s\n", ops[0]); for(i = 0; cmdtab[i].cmd; i++){ cmd = cmdtab[i].cmd; l = strlen(ops[0]); if(strncmp(ops[0], cmd, l) == 0) printf(" %s\n", cmd); } } } } /* If there is no channel, execute immediately. * Otherwise send line through channel so it can * be executed synchronously in the simulation thread. */ void cli(FILE *in, Channel **c) { size_t len; char *line; apr = nil; for(;;){ line = nil; len = 0; if(in == stdin) printf("> "); if(getline(&line, &len, in) < 0) return; if(c){ chansend(c[0], &line); chanrecv(c[1], &line); // wait for completion }else{ commandline(line); free(line); } } } void* cmdthread(void *p) { threadname("cli"); cli(stdin, p); quit(0); return nil; }