1
0
mirror of https://github.com/aap/pdp6.git synced 2026-01-18 01:02:44 +00:00
aap.pdp6/fe6/fe.c
2021-04-15 20:35:15 +02:00

959 lines
13 KiB
C

#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
#include <setjmp.h>
#include "fe.h"
struct termios tiosaved;
char erasec, killc, intrc;
jmp_buf errjmp;
int
raw(int fd)
{
struct termios tio;
if(tcgetattr(fd, &tio) < 0) return -1;
tiosaved = tio;
cfmakeraw(&tio);
if(tcsetattr(fd, TCSAFLUSH, &tio) < 0) return -1;
return 0;
}
int
reset(int fd)
{
if(tcsetattr(0, TCSAFLUSH, &tiosaved) < 0) return -1;
return 0;
}
#define ALT 033
char
tyi(void)
{
char c;
read(0, &c, 1);
return c;
}
#define CTL(c) ((c) & 037)
void
tyo_(char c)
{
write(1, &c, 1);
}
void
tyo(char c)
{
static char *alt = "";
static char *ctrl = "";
if(c < 040){
switch(c){
case ALT:
write(1, alt, strlen(alt));
return;
case CTL('J'):
write(1, &c, 1);
c = CTL('M');
case CTL('M'):
case CTL('H'):
case CTL('I'):
write(1, &c, 1);
break;
default:
write(1, ctrl, strlen(ctrl));
c += 0100;
write(1, &c, 1);
}
}else
write(1, &c, 1);
}
#define ADDRMASK 03777777
enum {
BUF_ERR = ~0,
BUF_EMPTY = ~0-1,
/* typeout modes */
MODE_NONE = 0,
MODE_NUM,
MODE_HALF,
MODE_SYM,
MODE_ASCII,
MODE_SIXBIT,
MODE_SQUOZE,
MODE_FLOAT,
/* flags */
CF = 1, // one altmode
CCF = 2, // two altmodes
/* fields */
F_A = 1,
F_I = 2,
F_AC = 4,
F_FW = 8,
/* line modes */
LM_COLON = 1,
LM_LOAD,
LM_DUMP,
LM_MOUNT,
LM_UNMOUNT
};
#define BUFLEN 200
char buf[BUFLEN+1];
char *bufstart;
int nbuf;
int flags;
char ch;
int base = 8;
hword dot;
hword addr;
int opened;
word q;
word number;
int hasnum;
int permmode = MODE_SYM;
int curmode;
struct Assembler
{
word fields[4];
int f;
word exp;
int op;
int gotexp;
};
struct Assembler as;
struct Assembler astack[8];
int asp;
hword starta;
int linemode;
void
typenum(word n)
{
word d;
d = n % base;
n /= base;
if(n)
typenum(n);
tyo(d + '0');
}
void
typestr(char *str)
{
while(*str)
tyo(*str++);
}
void
typestrnl(char *str)
{
while(*str){
if(*str == '\n')
tyo('\r');
tyo(*str++);
}
}
void
typestrnl_(char *str)
{
while(*str){
if(*str == '\n')
tyo_('\r');
tyo_(*str++);
}
}
void
err(char *str)
{
typestr(str);
longjmp(errjmp, 0);
}
void
num(void)
{
number = number*base + ch-'0';
hasnum = 1;
}
void
ins(void)
{
buf[nbuf++] = ch;
nbuf %= BUFLEN;
buf[nbuf] = '\0';
}
word
parsenum(void)
{
word w;
char *c;
w = 0;
for(c = bufstart; c < &buf[nbuf]; c++){
if(*c >= '0' && *c <= '9')
w = w*base + *c-'0';
else
return BUF_ERR;
w &= 0777777777777;
}
return w;
}
typedef struct Symbol Symbol;
struct Symbol
{
char *sym;
word value;
};
Symbol symtab[] = {
{ "SM", 01000000 },
{ "LED", 01001000 },
{ "SW", 01001001 },
/* some more instructions */
{ "HALT", 0254200000000 },
/* devices, decorated with % */
{ "%APR", 0 },
{ "%PI", 4 },
{ "%PTP", 0100 },
{ "%PTR", 0104 },
{ "%TTY", 0120 },
{ "%DIS", 0130 },
{ "%DC", 0200 },
{ "%UTC", 0204 },
{ "%UTS", 0210 },
#define X(str, name) { str, name },
PDP_REGS
PDP_REGS_TEST
#undef X
{ nil, 0 },
};
// for disasm
char *findacsym(word v) { return nil; }
char *findsymval(word v) { return nil; }
word
parsesym(void)
{
word i;
if(strcmp(bufstart, ".") == 0)
return dot;
for(i = 0; symtab[i].sym; i++)
if(strcasecmp(bufstart, symtab[i].sym) == 0)
return symtab[i].value;
for(i = 0; i < 0700; i++)
if(mnemonics[i] && strcasecmp(mnemonics[i], bufstart) == 0)
return i << 27;
for(i = 0; i < 8; i++)
if(strcasecmp(iomnemonics[i], bufstart) == 0)
return (word)0700<<27 | i<<23;
return BUF_ERR;
}
void
trimbuf(void)
{
bufstart = buf;
while(isspace(*bufstart)) bufstart++;
while(&buf[nbuf] > bufstart && isspace(buf[nbuf-1])) nbuf--;
buf[nbuf] = '\0';
}
/* parse input buffer, error if can't parse
* return 0 if empty */
int
parse(word *t)
{
word w;
// this should be unnecessary now
trimbuf();
w = parsenum();
if(bufstart >= &buf[nbuf])
return 0;
if((w = parsenum()) == BUF_ERR && (w = parsesym()) == BUF_ERR)
err("?U? ");
nbuf = 0;
*t = w;
return 1;
}
void
resetexp(void)
{
as.fields[0] = 0;
as.fields[1] = 0;
as.fields[2] = 0;
as.fields[3] = 0;
as.f = 0;
as.exp = 0;
as.op = 0;
as.gotexp = 0;
}
void
pushexp(void)
{
astack[asp] = as;
asp++;
if(asp >= 8)
err(" XXX ");
resetexp();
}
void
popexp(void)
{
if(asp < 0)
err(" XXX ");
asp--;
as = astack[asp];
}
/* combine word t into current expression */
void
combine(word t)
{
switch(as.op){
case '+':
default:
as.exp += t;
break;
case '-':
as.exp -= t;
break;
case '*':
as.exp *= t;
break;
case '|':
as.exp /= t;
break;
}
as.gotexp = 1;
as.op = 0;
}
/* assemble storage word and set *w
* if no word, don't change *w and return 0 */
int
endword(word *w)
{
word t;
if(!parse(&t)){
if(!as.gotexp)
return 0;
t = 0;
}
combine(t);
if(as.f & (F_FW|F_AC)){
// add to address field
as.fields[3] += as.exp;
as.f |= F_A;
}else{
as.fields[0] += as.exp;
as.f |= F_FW;
}
t = as.fields[0];
if(as.f & F_AC){
if((t & 0700000000000) == 0700000000000)
t += (as.fields[1]&0774)<<24;
else
t += (as.fields[1]&017)<<23;
}
if(as.f & F_I)
if((t & 020000000) == 0)
t += as.fields[2]&020000000;
if(as.f & F_A)
t = (t&LT) + ((t+as.fields[3])&RT);
t &= LT|RT;
*w = t;
resetexp();
return 1;
}
void
prword(int mode, word wd)
{
int i;
char c;
char s[7];
double f;
switch(mode){
case MODE_ASCII:
tyo(ALT);
tyo('0' + (wd&1));
tyo('"');
for(i = 0; i < 5; i++){
c = (wd >> ((7*(4-i))+1)) & 0177;
if(c < 040 || c == 0177){
tyo('^');
tyo(c ^ 0100);
}else
tyo(c);
}
tyo(ALT);
break;
case MODE_SIXBIT:
tyo(ALT);
tyo('1');
tyo('\'');
for(i = 0; i < 6; i++){
c = (wd >> (6*(5-i))) & 077;
tyo(c + 040);
}
tyo(ALT);
break;
case MODE_SQUOZE:
tyo(ALT);
typenum(unrad50(wd, s));
tyo('&');
typestr(s);
tyo(ALT);
break;
break;
case MODE_SYM:
typestr(disasm(wd));
break;
case MODE_HALF:
tyo('(');
typenum(wd&0777777);
tyo(')');
typenum((wd>>18)&0777777);
break;
case MODE_FLOAT:
f = pdptod(wd);
printf("%lf", f);
fflush(stdout);
break;
default:
typenum(wd);
}
}
void
typeout(int mode)
{
endword(&q);
prword(mode, q);
typestr(" ");
}
void
propen(hword a)
{
prword(curmode, a);
tyo('/');
}
void
aclose(int ins)
{
// this is a bit ugly...
// we don't want ^M to set Q if no location is open
word t;
t = q;
if(endword(&q) && opened && ins)
deposit(addr, q);
addr = q & ADDRMASK;
if(!opened)
q = t;
opened = 0;
}
void
aopen(int mode)
{
opened = 1;
q = examine(addr);
typestr(" ");
if(mode != MODE_NONE)
typeout(mode);
}
void
modechange(int mode)
{
curmode = mode;
if(flags & CCF){
permmode = mode;
typestr(" ");
}
}
void
runline(void)
{
/* TODO: this can be improved */
switch(linemode){
case LM_COLON:
coloncmd(buf);
break;
case LM_LOAD:
docmd("load", buf);
break;
case LM_DUMP:
docmd("dump", buf);
break;
case LM_MOUNT:
docmd("mount", buf);
break;
case LM_UNMOUNT:
docmd("unmount", buf);
break;
}
}
void
zerocore(void)
{
hword a;
for(a = 0; a < MAXMEM; a++)
deposit(a, 0);
typestr("\r\n");
}
void
quit(void)
{
reset(0);
putchar('\n');
deinit6();
exit(0);
}
int started;
int
threadmain(int argc, char *argv[])
{
char chu;
word t;
init6();
initcrt("soma");
// initnetmem("10.0.0.222", 10006);
raw(0);
erasec = tiosaved.c_cc[VERASE];
killc = tiosaved.c_cc[VKILL];
intrc = tiosaved.c_cc[VINTR];
setjmp(errjmp);
nbuf = 0;
opened = 0;
resetexp();
flags = 0;
curmode = permmode;
number = 0;
hasnum = 0;
linemode = 0;
for(;;){
if(hasinput(0)){
ch = tyi();
}else{
if(started && (!isrunning() || isstopped())){
t = examine(APR_PC);
/* TODO: maybe do something different on stop? */
typestrnl("\n");
typenum(t);
if(!isrunning())
typestr(">>");
else
typestr("<<");
t = examine(t);
prword(MODE_SYM, t);
typestr(" ");
/* show AC or E */
if((t&0700000000000) == 0700000000000){
t = t&RT;
}else{
t = t>>23 & 017;
}
dot = t;
prword(MODE_SYM, dot);
typestr("/ ");
t = examine(t);
prword(MODE_SYM, t);
q = t;
typestr(" ");
started = 0;
}
fe_svc();
// this was commented out, why?
usleep(1000);
continue;
}
chu = toupper(ch);
if(ch == erasec || ch == CTL('H') || ch == CTL('?')){
/* can't backspace in all cases */
if((flags & CF) || nbuf <= 0)
err("\r\n");
typestr("\b \b");
nbuf--;
}else if(ch == killc)
err("");
else if(ch == intrc)
break;
else if(linemode){
if(ch == '\r' || ch == '\n'){
typestrnl("\n");
runline();
err("");
}else{
tyo(ch);
ins();
}
}else{
tyo(ch);
if((flags & CF) == 0 &&
(ch >= 'A' && ch <= 'Z' ||
ch >= 'a' && ch <= 'z')){
ins();
}else switch(chu){
case ':':
linemode = LM_COLON;
nbuf = 0;
break;
case 'L':
tyo(' ');
linemode = LM_LOAD;
nbuf = 0;
break;
case 'Y':
tyo(' ');
linemode = LM_DUMP;
nbuf = 0;
break;
case '?':
typestrnl_(flags & CF ? colhelpstr : helpstr);
break;
case CTL('D'):
err("XXX? ");
break;
case '.': case '%': case '$':
ins();
break;
case '0': case '1': case '2': case '3':
case '4': case '5': case '6': case '7':
case '8': case '9':
if(flags & CF){
num();
goto keepflags;
}
ins();
break;
case 'F':
cpu_printflags();
break;
case 'G':
if(!endword(&t))
t = starta;
if(flags & CCF)
starta = t;
if(hasnum && number == 0)
cpu_setpc(t);
else
cpu_start(t);
started = 1;
break;
case 'R':
if(!endword(&t))
t = starta;
if(flags & CCF)
starta = t;
cpu_readin(t);
started = 1;
break;
case CTL('Z'):
nbuf = 0;
if(flags & CF)
cpu_stopmem();
else
cpu_stopinst();
break;
case 'P':
endword(&t);
cpu_cont();
started = 1;
break;
case CTL('N'):
endword(&t);
if(flags & CF)
cpu_nextmem();
else
cpu_nextinst();
started = 1;
break;
case 'X':
if(!endword(&t))
t = q;
cpu_exec(t);
typestrnl("\n");
break;
case 'I':
nbuf = 0;
cpu_ioreset();
break;
case 'Q':
combine(q);
break;
case 'Z':
if(flags & CCF)
zerocore();
else
err(" ?? ");
break;
case ALT:
if(flags & CF)
flags |= CCF;
flags |= CF;
goto keepflags;
/* expressions */
case '+':
case '-':
case '*':
case '|':
if(!parse(&t)){
if(ch == '-')
as.op = '-';
break;
}
combine(t);
as.op = ch;
break;
case '(':
if(parse(&t))
combine(t);
pushexp();
break;
case ')':
t = 0;
endword(&t);
popexp();
if(as.op == 0){
t = (t&LT)>>18 | (t&RT)<<18;
as.fields[0] += t;
as.gotexp = 1;
}else{
combine(t);
}
as.op = 0;
break;
/* word assembly */
case ' ':
if(!parse(&t))
break;
combine(t);
if(as.f & F_FW){
// add to address field
// if we have a full word already
as.fields[3] += as.exp;
as.f |= F_A;
}else{
as.fields[0] += as.exp;
as.f |= F_FW;
}
as.exp = 0;
break;
case ',':
if(!parse(&t))
break;
combine(t);
if(as.f & F_AC){
// add to address field
// if we have an AC already
as.fields[3] += as.exp;
as.f |= F_A;
}else{
as.fields[1] += as.exp;
as.f |= F_AC;
}
as.exp = 0;
break;
case '@':
as.fields[2] += 020000000;
as.f |= F_I;
as.gotexp = 1;
break;
/* opening */
case '/':
aclose(0);
dot = addr;
aopen(curmode);
break;
case '\\':
aclose(0);
aopen(curmode);
break;
case '[':
if(flags & CF){
tyo(' ');
linemode = LM_MOUNT;
nbuf = 0;
break;
}
aclose(0);
dot = addr;
aopen(MODE_NUM);
break;
case ']':
if(flags & CF){
tyo(' ');
linemode = LM_UNMOUNT;
nbuf = 0;
break;
}
aclose(0);
dot = addr;
aopen(MODE_SYM);
break;
case '!':
aclose(0);
dot = addr;
aopen(MODE_NONE);
break;
case CTL('I'):
typestrnl("\n");
aclose(1);
dot = addr;
propen(addr);
aopen(curmode);
break;
case '^':
typestrnl("\n");
aclose(1);
dot = (dot-1) & ADDRMASK;
addr = dot;
propen(addr);
aopen(curmode);
break;
case CTL('J'):
tyo('\r');
aclose(1);
dot = (dot+1) & ADDRMASK;
addr = dot;
propen(addr);
aopen(curmode);
break;
case CTL('M'):
tyo('\n');
aclose(1);
curmode = permmode;
break;
/* typeout */
case '_':
typeout(MODE_SYM);
break;
case '=':
typeout(MODE_NUM);
break;
case ';':
typeout(MODE_FLOAT);
break;
case '"':
if(flags & CF)
modechange(MODE_ASCII);
else
typeout(MODE_ASCII);
break;
case '\'':
if(flags & CF)
modechange(MODE_SIXBIT);
else
typeout(MODE_SIXBIT);
break;
case '&':
if(flags & CF)
modechange(MODE_SQUOZE);
else
typeout(MODE_SQUOZE);
break;
case 'S':
modechange(MODE_SYM);
break;
case 'C':
modechange(MODE_NUM);
break;
case 'H':
modechange(MODE_HALF);
break;
default:
err(" ?? ");
break;
}
}
flags = 0;
number = 0;
hasnum = 0;
keepflags:;
}
quit();
return 0;
}