mirror of
https://github.com/aap/pdp6.git
synced 2026-01-13 23:37:09 +00:00
773 lines
13 KiB
C
773 lines
13 KiB
C
#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
|
|
};
|
|
|
|
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
|
|
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;
|
|
}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;
|
|
}
|
|
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 [-sb] 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)
|
|
{
|
|
cli(stdin, p);
|
|
quit(0);
|
|
return nil;
|
|
}
|