1
0
mirror of https://github.com/prirun/p50em.git synced 2026-02-04 07:12:49 +00:00
Files
prirun.p50em/em.c
2005-04-18 00:00:00 -04:00

1375 lines
32 KiB
C

/* Prime R-mode emulator, Jim Wilcoxson (jim@meritnet.com), April 4, 2005
Copyright (C) 2005, Jim Wilcoxson (jim@meritnet.com). All Rights Reserved.
Restores a Prime R-mode .save image from stdin to memory and
emulates execution.
PLEASE NOTE: this is a very rough prototype still in development.
The main goal for the prototype is to get an understanding of the
kinds of design issues faced by an emulator, before doing a "real"
Prime 50-series emulator.
You are welcome to pass this along to friends for fun and
amusement, but please don't publish it. Comments, suggestions,
corrections, and general notes that you're interested in a Prime
emulation project are also welcome and appreciated.
Usage:
$ ./em <smad.save 2>/dev/null
Lots of instruction details are spewed to stderr.
Left to do (way more to do ... this just gives an idea):
- only runs on a big-endian machine, like the Prime
- nothing has been systematically verified/checked
- many instructions are left to implement
- only 1 32R program has been executed, and it may have issues;
no other CPU modes have been tested at all
- svc doesn't handle alternate returns, LOC(blah) args might not be
handled correctly, and it doesn't handle missing or extra arguments
correctly (by looking for the terminating zero)
- instructions don't update the C-bit, L-bit, and condition codes
- restricted-mode instruction checking is missing, as well as all
restricted-mode instructions and environment (for example, no page
mapping)
*/
#include <stdio.h>
#include <errno.h>
#include "syscom/keys.ins.cc"
#include "syscom/errd.ins.cc"
/* Prime CPU registers are mapped to memory locations 0-'37, but only
0-7 are accessible in SR user mode. In VI user mode, locations
0-'17 are trapped and map to the live register file (p 5-17, Sys Arch)
*/
#define X 0
#define A 1
#define B 2
#define S 3
#define FLTH 4
#define FLTL 5
#define FEXP 6
#define P 7
#define PMAR 010
#define FCODE 011
#define PFAR 012
#define DMA0 020
#define DMA1 022
#define DMA2 024
#define DMA3 026
#define DMA4 030
#define DMA5 032
#define DMA6 034
#define DMA7 036
/* Locations '40-'57 are reserved for 8 DMC channels, 2 words each.
Locations '60-'77 are interrupt vectors
Locations '100-'177 are for external device interrupts
see p. A-8 of Sys Arch
*/
unsigned short mem[64*1024]; /* 64K words of main memory */
signed short tempa;
unsigned short utempa;
signed int templ,templ2;
unsigned int utempl;
unsigned short prevpc,keys; /* program counter, prev pc, keys */
unsigned short amask; /* address mask */
unsigned short ea; /* effective address (word) */
unsigned short opcode;
short i,x;
unsigned short class;
unsigned short xx;
int d; /* displacement */
int verbose;
int memdump; /* -memdump arg */
int scount; /* shift count */
int instcount=0;
/* I/O device map table, containing function pointers to handle device I/O */
#include "emdev.h"
void (*devmap[64])(unsigned short, unsigned short) = {
0,0,0,0,devasr,0,0,0,
0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0};
/* read a short (16-bit) integer in big-endian from stdin */
unsigned short readshort () {
return getchar()<<8 | getchar();
}
/* set new processor keys */
newkeys (unsigned short new) {
keys = new;
switch ((keys & 016000) >> 10) {
case 0: /* 16S */
fprintf(stderr,"Entering 16S mode, keys=%o\n", keys);
amask = 037777;
break;
case 1: /* 32S */
fprintf(stderr,"Entering 32S mode, keys=%o\n", keys);
amask = 077777;
break;
case 2: /* 64R */
fprintf(stderr,"Entering 64R mode, keys=%o\n", keys);
amask = 0177777;
break;
case 3: /* 32R */
fprintf(stderr,"Entering 32R mode, keys=%o\n", keys);
amask = 077777;
break;
case 4: /* 32I */
fprintf(stderr,"Entering 32I mode, keys=%o\n", keys);
amask = 0177777;
break;
case 6: /* 64V */
fprintf(stderr,"Entering 64V mode, keys=%o\n", keys);
amask = 0177777;
break;
default: /* invalid */
fprintf(stderr,"Invalid CPU mode: %o\n", keys);
exit(1);
}
}
/* NOTE: PMA 16S flowcharts say P should point to the current instruction,
but it has already been incremented. Should be using prevpc here instead.
This code is untested!
*/
unsigned short ea16s (unsigned short inst, short i, short x) {
unsigned short ea;
if (inst & 001000)
ea = (mem[P] & 037000) | (inst & 0777); /* current sector */
else
ea = (inst & 0777); /* sector 0 */
while (1) {
if (x) /* indexed */
ea += mem[X];
if (!i) /* not indirect */
break;
i = mem[ea] & 0100000;
x = mem[ea] & 040000;
ea = mem[ea] & 037777; /* go indirect */
}
return ea;
}
/* NOTE: PMA 32S flowcharts say P should point to the current instruction,
but it has already been incremented. Should be using prevpc here instead.
This code is untested!
*/
unsigned short ea32s (unsigned short inst, short i, short x) {
unsigned short ea;
if (inst & 001000)
ea = (mem[P] & 077000) | (inst & 0777); /* current sector */
else {
ea = (inst & 0777); /* sector 0 */
if (ea < 0100 && x) { /* preindex by X */
ea += mem[X];
x = 0;
}
}
while (i) {
i = mem[ea] & 0100000;
ea = mem[ea] & 077777; /* go indirect */
}
if (x) /* postindex */
ea += mem[X];
return ea & amask;
}
/* NOTE: PMA 32R flowcharts say P should point to the current instruction,
but 64R says it should point after the current instruction. Tests show
that using incremented P is correct.
*/
unsigned short ea32r (unsigned short inst, short i, short x, short *opcode) {
unsigned short ea;
fprintf(stderr," ea32r: i=%o, x=%o\n", i!= 0, x!=0);
if (inst & 001000) /* sector bit 7 set? */
if ((inst & 0760) != 0400) { /* PC relative? */
ea = mem[P] + (((short) (inst << 7)) >> 7); /* yes, sign extend D */
fprintf(stderr," PC relative, P=%o, new ea=%o\n", mem[P], ea);
}
else
goto special; /* special cases */
else {
ea = (inst & 0777); /* sector 0 */
fprintf(stderr," Sector 0, new ea=%o\n", ea);
if (ea < 0100 && x) { /* preindex by X */
fprintf(stderr," Preindex, ea=%o, X='%o/%d\n", ea, mem[X], *(short *)(mem+X));
ea += mem[X];
fprintf(stderr," Preindex, new ea=%o\n", ea);
x = 0;
}
}
while (i) {
fprintf(stderr," Indirect, old ea=%o, [ea]=%o\n", ea, mem[ea]);
i = mem[ea] & 0100000;
ea = mem[ea] & 077777; /* go indirect */
fprintf(stderr," Indirect, new i=%d, new ea=%o, [ea]=%o\n", i!=0, ea, mem[ea]);
}
if (x) {
fprintf(stderr," Postindex, old ea=%o, X='%o/%d\n", ea, mem[X], *(short *)(mem+X));
ea += mem[X];
fprintf(stderr," Postindex, new ea=%o\n", ea);
}
return ea & amask;
special:
class = inst & 3; /* class bits = 15 & 16 */
*opcode = *opcode | ((inst >> 2) & 3); /* opcode extension */
fprintf(stderr," special, new opcode=%5#0o, class=%d\n", *opcode, class);
if (class < 2) { /* class 0/1 */
ea = mem[mem[P]++]; /* get A from next word */
fprintf(stderr," Class %d, new ea=%o\n", class, ea);
if (class == 1)
ea += mem[S];
if (x) {
fprintf(stderr," Preindex, ea=%o, X='%o/%d\n", ea, mem[X], *(short *)(mem+X));
ea += mem[X];
fprintf(stderr," Preindex, new ea=%o\n", ea);
}
while (i) {
fprintf(stderr," Indirect, old ea=%o, [ea]=%o\n", ea, mem[ea]);
i = mem[ea] & 0100000;
ea = mem[ea] & 077777;
fprintf(stderr," Indirect, new i=%d, new ea=%o, [ea]=%o\n", i!=0, ea, mem[ea]);
}
} else if (i && x) { /* class 2/3, ix=11 */
fprintf(stderr," class 2/3, ix=11\n");
ea = mem[mem[P]++]; /* get A from next word */
fprintf(stderr," ea=%o\n", ea);
if (class == 3)
ea += (short) mem[S];
while (i) {
fprintf(stderr," Indirect, ea=%o, [ea]=%o\n", ea, mem[ea]);
i = mem[ea] & 0100000;
ea = mem[ea] & 077777;
fprintf(stderr," Indirect, new i=%d, new ea=%o, [ea]=%o\n", i!=0, ea, mem[ea]);
}
fprintf(stderr," Postindex, old ea=%o, X='%o/%d\n", ea, mem[X], *(short *)(mem+X));
ea += (short) mem[X];
fprintf(stderr," Postindex, new ea=%o\n", ea);
} else { /* class 2/3, ix != 11 */
if (class == 2)
ea = mem[S]++;
else
ea = --mem[S];
fprintf(stderr," Class 2/3, new ea=%o, new S=%o\n", ea, mem[S]);
exit(1);
if (x) {
i = mem[ea] & 0100000;
ea = mem[ea] & 077777;
}
while (i) {
i = mem[ea] & 0100000;
ea = mem[ea] & 077777;
}
if (x)
ea += mem[X];
}
return ea & 077777;
}
unsigned short ea64r (unsigned short inst, short i, short x, short *opcode) {
unsigned short ea;
fprintf(stderr,"Mode 64R not implemented\n");
}
unsigned short ea64v (unsigned short inst, short i, short x, short *opcode) {
fprintf(stderr,"Mode 64V not implemented\n");
}
unsigned short ea32i (unsigned short inst, short i, short x) {
fprintf(stderr,"Mode 32I not implemented\n");
}
main (int argc, char **argv) {
int i;
int nw;
unsigned short rvec[9]; /* SA, EA, P, A, B, X, keys, dummy, dummy */
unsigned short inst;
verbose = 0;
memdump = 0;
/* check args */
for (i=1; i<argc; i++) {
if (strcmp(argv[i],"-vv") == 0)
verbose = 2;
else if (strcmp(argv[i],"-v") == 0)
verbose = 1;
else if (strcmp(argv[i],"-memdump") == 0)
memdump = 1;
else if (argv[i][0] == '-')
fprintf(stderr,"Unrecognized argument: %s\n", argv[i]);
}
/* read 9-word rvec header */
for (i=0; i<9; i++)
rvec[i] = readshort();
fprintf(stderr,"SA=%o, EA=%o, P=%o, A=%o, B=%o, X=%o, K=%o\n\n", rvec[0], rvec[1],
rvec[2], rvec[3], rvec[4], rvec[5], rvec[6]);
if (rvec[2] > rvec[1]) {
fprintf(stderr,"Program start > EA: runfile is trashed\n");
exit(1);
}
/* read memory image from SA to EA inclusive */
nw = rvec[1]-rvec[0]+1;
if (fread(mem+rvec[0], sizeof(short), nw, stdin) != nw) {
perror("Error reading memory image");
exit(1);
}
/* setup execution (registers, keys, address mask, etc.) from rvec */
mem[A] = rvec[3];
mem[B] = rvec[4];
mem[X] = rvec[5];
newkeys(rvec[6]);
mem[P] = rvec[2];
if (memdump) {
/* dump sector zero for debugging */
fprintf(stderr,"\nSector 0:\n");
for (i=0; i<01000; i=i+8)
if (mem[i]|mem[i+1]|mem[i+2]|mem[i+3]|mem[i+4]|mem[i+5]|mem[i+6]|mem[i+7])
fprintf(stderr,"%3o: %6o %6o %6o %6o %6o %6o %6o %6o\n", i, mem[i], mem[i+1], mem[i+2], mem[i+3], mem[i+4], mem[i+5], mem[i+6], mem[i+7]);
/* dump main memory for debugging */
fprintf(stderr,"\nMain memory:\n");
for (i=rvec[2]; i<rvec[1]; i=i+8)
if (mem[i]|mem[i+1]|mem[i+2]|mem[i+3]|mem[i+4]|mem[i+5]|mem[i+6]|mem[i+7])
fprintf(stderr,"%o: %6o %6o %6o %6o %6o %6o %6o %6o\n", i, mem[i], mem[i+1], mem[i+2], mem[i+3], mem[i+4], mem[i+5], mem[i+6], mem[i+7]);
}
/* main instruction decode loop */
while (1) {
if (mem[P] > rvec[1]) { /* hack for testing */
fprintf(stderr,"\nOOPS! Program counter %o > EA %o\n", mem[P], rvec[1]);
exit(1);
}
prevpc = mem[P];
inst = mem[mem[P]];
instcount++;
fprintf(stderr,"\n%o: %o A='%o/%:0d, B='%o/%d, X=%o/%d instcount=%d\n", mem[P], inst, mem[A], *(short *)(mem+A), mem[B], *(short *)(mem+B), mem[X], *(short *)(mem+X), instcount);
mem[P]++;
/* generic? */
if ((inst & 036000) == 0) {
fprintf(stderr," generic\n");
class = inst>>14;
if (class == 0 || class == 3) {
fprintf(stderr," generic class %d\n", class);
if (inst == 000005) { /* SGL */
fprintf(stderr," SGL\n");
newkeys(keys & ~040000);
continue;
}
if (inst == 000011) { /* E16S */
fprintf(stderr," E16S\n");
newkeys(keys & 0161777);
continue;
}
if (inst == 000013) { /* E32S */
fprintf(stderr," E32S\n");
newkeys((keys & 0161777) | 1<<10);
continue;
}
if (inst == 001013) { /* E32R */
fprintf(stderr," E32R\n");
newkeys((keys & 0161777) | 3<<10);
continue;
}
if (inst == 001011) { /* E64R */
fprintf(stderr," E64R\n");
newkeys((keys & 0161777) | 2<<10);
continue;
}
if (inst == 000010) { /* E64V */
fprintf(stderr," E64V\n");
newkeys((keys & 0161777) | 6<<10);
continue;
}
if (inst == 001010) { /* E32I */
fprintf(stderr," E32I\n");
newkeys((keys & 0161777) | 4<<10);
continue;
}
if (inst == 000505) { /* SVC */
fprintf(stderr," SVC\n");
svc();
continue;
}
if (inst == 0141206) { /* A1A */
fprintf(stderr," A1A\n");
mem[A]++;
continue;
}
if (inst == 0140110) { /* S1A */
fprintf(stderr," S1A\n");
mem[A]--;
continue;
}
if (inst == 0140310) { /* S2A */
fprintf(stderr," S2A\n");
mem[A] -= 2;
continue;
}
if (inst == 0141050) { /* CAL */
fprintf(stderr," CAL\n");
mem[A] &= 0xFF;
continue;
}
if (inst == 0141044) { /* CAR */
fprintf(stderr," CAR\n");
mem[A] &= 0xFF00;
continue;
}
if (inst == 0140040) { /* CRA */
fprintf(stderr," CRA\n");
mem[A] = 0;
continue;
}
if (inst == 0140010) { /* CRL */
fprintf(stderr," CRL\n");
mem[A] = 0; mem[B] = 0;
continue;
}
if (inst == 0140214) { /* CAZ */
fprintf(stderr," CAZ\n");
if (mem[A] == 0)
mem[P]++;
else if (*(short *)(mem+A) < 0)
mem[P] += 2;
continue;
}
/* NOTE: using "if mem[X]++ == 0" doesn't work because of unsigned
short type promotion! */
if (inst == 0140114) { /* IRX */
fprintf(stderr," IRX\n");
mem[X]++;
if (mem[X] == 0)
mem[P]++;
continue;
}
if (inst == 0140210) { /* DRX */
fprintf(stderr," DRX\n");
mem[X]--;
if (mem[X] == 0)
mem[P]++;
continue;
}
/* NOTE: needs to check for various CPU modes */
if (inst == 000111) {
fprintf(stderr," CEA\n");
i = mem[A] & 0100000;
ea = mem[A] & amask;
while (i) {
fprintf(stderr," Indirect, old ea=%o, [ea]=%o\n", ea, mem[ea]);
i = mem[ea] & 0100000;
ea = mem[ea] & 077777; /* go indirect */
fprintf(stderr," Indirect, new i=%d, new ea=%o, [ea]=%o\n", i!=0, ea, mem[ea]);
}
mem[A] = ea;
continue;
}
if (inst == 000000) { /* HLT :( */
fprintf(stderr," HLT\n");
fprintf(stderr,"\nProgram halt at %o\n", prevpc);
exit(1);
}
if (inst == 0141240) { /* ICR */
fprintf(stderr," ICR\n");
mem[A] = mem[A] << 8;
continue;
}
if (inst == 000205) { /* PIM (R-mode) */
fprintf(stderr," PIM\n");
mem[A] = mem[B] | (mem[A] << 15);
continue;
}
if (inst == 000211) { /* PID (R-mode) */
fprintf(stderr," PID\n");
mem[B] = mem[A] & 0x7fff;
if (mem[A] & 0x8000)
mem[A] = -1;
else
mem[A] = 0;
continue;
}
if (inst == 0140417) { /* LT */
fprintf(stderr," LT\n");
mem[A] = 1;
continue;
}
if (inst == 0140416) { /* LF */
fprintf(stderr," LF\n");
mem[A] = 0;
continue;
}
if (inst == 0140410) { /* LLT */
fprintf(stderr," LLT\n");
if (*(short *)(mem+A) < 0)
mem[A] = 1;
else
mem[A] = 0;
continue;
}
if (inst == 0140411) { /* LLE */
fprintf(stderr," LLE\n");
if (*(short *)(mem+A) <= 0)
mem[A] = 1;
else
mem[A] = 0;
continue;
}
if (inst == 0140412) { /* LNE */
fprintf(stderr," LNE\n");
if (*(short *)(mem+A) != 0)
mem[A] = 1;
else
mem[A] = 0;
continue;
}
if (inst == 0140413) { /* LEQ */
fprintf(stderr," LEQ\n");
if (*(short *)(mem+A) == 0)
mem[A] = 1;
else
mem[A] = 0;
continue;
}
if (inst == 0140414) { /* LGE */
fprintf(stderr," LGE\n");
if (*(short *)(mem+A) >= 0)
mem[A] = 1;
else
mem[A] = 0;
continue;
}
if (inst == 0140411) { /* LGT */
fprintf(stderr," LGT\n");
if (*(short *)(mem+A) > 0)
mem[A] = 1;
else
mem[A] = 0;
continue;
}
if (inst == 000007) { /* DBL */
fprintf(stderr," DBL\n");
newkeys(keys | 040000);
continue;
}
if (inst == 0141034) { /* TXA */
fprintf(stderr," TXA\n");
mem[A] = mem[X];
continue;
}
if (inst == 0140407) {
fprintf(stderr," TCA\n");
*(short *)(mem+A) = - (*(short *)(mem+A));
continue;
}
if (inst == 0140200) {
fprintf(stderr," RCB\n");
newkeys(keys & 077777);
continue;
}
fprintf(stderr," unrecognized generic class 0/3 instruction!\n");
exit(1);
}
if (class == 1) {
fprintf(stderr," shift group\n");
scount = -inst & 077;
switch (inst & 01700) {
case 00000: /* LRL */
fprintf(stderr," LRL %d\n", scount);
utempl = *(unsigned int *)(mem+A);
utempl = utempl >> scount;
*(int *)(mem+A) = utempl;
break;
case 00100: /* LRS (s.b. different in R/V modes) */
fprintf(stderr," LRS %d\n", scount);
templ = *(int *)(mem+A);
templ = templ >> scount;
*(int *)(mem+A) = templ;
break;
case 00200: /* LRR */
fprintf(stderr," LRR %d\n", scount);
exit(1);
break;
case 00400: /* ARL */
fprintf(stderr," ARL %d\n", scount);
utempa = mem[A];
utempa = utempa >> scount;
mem[A] = utempa;
break;
case 00500: /* ARS */
fprintf(stderr," ARS %d\n", scount);
tempa = *(short *)(mem+A);
tempa = tempa >> scount;
*(short *)(mem+A) = tempa;
break;
case 00600: /* ARR */
fprintf(stderr," ARR %d\n", scount);
exit(1);
break;
case 01000: /* LLL */
fprintf(stderr," LLL %d\n", scount);
utempl = *(unsigned int *)(mem+A);
utempl = utempl << scount;
*(unsigned int *)(mem+A) = utempl;
break;
case 01100: /* LLS (s.b. different in R/V modes) */
fprintf(stderr," LLS %d\n", scount);
templ = *(int *)(mem+A);
templ = templ << scount;
*(int *)(mem+A) = templ;
break;
case 01200: /* LLR */
fprintf(stderr," LLR %d\n", scount);
exit(1);
break;
case 01400: /* ALL */
case 01500: /* ALS */
fprintf(stderr," ALL/ALS %d\n", scount);
utempa = *(short *)(mem+A);
utempa = utempa << scount;
*(short *)(mem+A) = utempa;
break;
case 01600: /* ALR */
fprintf(stderr," ALR %d\n", scount);
exit(1);
break;
default:
fprintf(stderr," unrecognized shift instruction\n");
exit(1);
}
continue;
}
if (class == 2) {
fprintf(stderr," skip group\n");
if (inst == 0101000) {
fprintf(stderr," NOP\n");
continue;
}
if (inst == 0100000) {
fprintf(stderr," SKP\n");
mem[P]++;
continue;
}
if (inst == 0101400) {
fprintf(stderr," SMI\n");
if (*(short *)(mem+A) < 0)
mem[P]++;
continue;
}
if (inst == 0100400) {
fprintf(stderr," SPL\n");
if (*(short *)(mem+A) >= 0)
mem[P]++;
continue;
}
if (inst == 0101100) {
fprintf(stderr," SLN\n");
if (mem[A] & 1)
mem[P]++;
continue;
}
if (inst == 0100100) {
fprintf(stderr," SLZ\n");
if (!(mem[A] & 1))
mem[P]++;
continue;
}
if (inst == 0101040) {
fprintf(stderr," SNZ\n");
if (mem[A] != 0)
mem[P]++;
continue;
}
if (inst == 0100040) {
fprintf(stderr," SZE\n");
if (mem[A] == 0)
mem[P]++;
continue;
}
if (inst == 0101220) {
fprintf(stderr," SLE\n");
if (*(short *)(mem+A) <= 0)
mem[P]++;
continue;
}
if (inst == 0100220) {
fprintf(stderr," SGT\n");
if (*(short *)(mem+A) > 0)
mem[P]++;
continue;
}
fprintf(stderr," unrecognized skip instruction\n");
exit(1);
}
}
/* here for non-generic instructions: memory references or pio */
if (keys & 010000) {
fprintf(stderr," VI-mode MR decode\n");
exit(1);
}
fprintf(stderr," SR-mode MR decode\n");
/* pio? */
if ((inst & 036000) == 030000) {
pio(inst);
continue;
}
/* get ix bits and adjust opcode so that PMA manual opcode
references can be used directly, ie, if the PMA manual says the
opcode is '15 02, then 01502 can be used here. If the PMA
manual says the opcode is '11, then use 01100 (the XX extended
opcode bits are zero) */
i = inst & 0100000; /* indirect is bit 1 (left/MS bit) */
x = inst & 040000; /* indexed is bit 2 */
opcode = (inst & 036000) >> 4; /* isolate opcode bits */
/* fix ldx/stx (opcode '15): these instructions cannot be indexed
by X, so if an instruction specifies indexing by X, it acts
like an opcode extension. Opcodes listed as '35 02 for example
(sty in V-mode, jdx in R-mode) have X=1 with the 4 opcode bits
1101 ('15)
x=0, opcode='15 -> stx (SRV)
x=1, opcode='15 -> ldx (SRV) (aka '35)
x=0, opcode='15 01 -> flx (RV)
x=1, opcode='15 01 -> ldy (V) (aka '35 01)
x=0, opcode='15 02 -> dflx (V)
x=1, opcode='15 02 -> sty (V) (aka '35 02)
x=1, opcode='15 02 -> jdx (R) (aka '35 02)
x=0, opcode='15 03 -> jix (R)
x=1, opcode='15 03 -> jsx (RV) (aka '35 03)
*/
if (opcode == 01500) { /* '15 = ldx/stx form, depending on X */
opcode = opcode | ((inst & 040000)>>4); /* if X set, expand opcode */
x = 0; /* clear X bit (these can't be indexed) */
fprintf(stderr," ldx/stx opcode adjusted\n");
}
fprintf(stderr," opcode=%5#0o, i=%o, x=%o\n", opcode, i, x);
switch ((keys & 016000) >> 10) {
case 0: /* 16S */
ea = ea16s(inst, i, x);
break;
case 1: /* 32S */
ea = ea32s(inst, i, x);
break;
case 2: /* 64R */
ea = ea64r(inst, i, x, &opcode);
exit(1);
break;
case 3: /* 32R */
ea = ea32r(inst, i, x, &opcode);
break;
case 4: /* 32I */
ea = ea32i(inst, i, x);
exit(1);
break;
case 6: /* 64V */
ea = ea64v(inst, i, x, &opcode);
exit(1);
break;
default:
fprintf(stderr,"Bad CPU mode in EA calculation, keys = %o\n", keys);
exit(1);
}
fprintf(stderr," ea=%o, [ea]='%o/%d\n", ea, mem[ea], *(short *)(mem+ea));
if (opcode == 00100) {
fprintf(stderr," JMP\n");
mem[P] = ea;
continue;
}
if (opcode == 00200) {
if (keys & 040000) {
fprintf(stderr," DLD\n");
mem[A] = mem[ea];
mem[B] = mem[ea+1];
} else {
fprintf(stderr," LDA\n");
mem[A] = mem[ea];
}
continue;
}
if (opcode == 00300) {
fprintf(stderr," ANA\n");
mem[A] &= mem[ea];
continue;
}
if (opcode == 00400) {
if (keys & 040000) {
fprintf(stderr," DST\n");
mem[ea] = mem[A];
mem[ea+1] = mem[B];
} else {
fprintf(stderr," STA\n");
mem[ea] = mem[A];
}
continue;
}
if (opcode == 00500) {
fprintf(stderr," ERA\n");
mem[A] ^= mem[ea];
continue;
}
if (opcode == 00600) {
fprintf(stderr," ADD\n");
tempa = mem[A];
tempa += mem[ea];
mem[A] = tempa;
continue;
}
if (opcode == 00700) {
fprintf(stderr," SUB\n");
tempa = mem[A];
tempa -= mem[ea];
mem[A] = tempa;
continue;
}
if (opcode == 01000) {
fprintf(stderr," JST\n");
mem[ea] = mem[P];
mem[P] = ea+1;
continue;
}
if (opcode == 01100) {
fprintf(stderr," CAS\n");
if (mem[A] == mem[ea])
mem[P]++;
else if (*(short*)(mem+A) < *(short *)(mem+ea))
mem[P] += 2;
continue;
}
if (opcode == 01200) {
fprintf(stderr," IRS\n");
mem[ea]++;
if (mem[ea] == 0)
mem[P]++;
continue;
}
if (opcode == 01300) {
fprintf(stderr," IMA\n");
tempa = mem[ea];
mem[ea] = mem[A];
mem[A] = tempa;
continue;
}
if (opcode == 01400) {
/* if V-mode, JSY, else LDX in R-mode */
fprintf(stderr," JSY/LDX\n");
exit(1);
}
if (opcode == 01500) {
fprintf(stderr," STX\n");
mem[ea] = mem[X];
continue;
}
if (opcode == 01600) {
fprintf(stderr," MPY\n");
templ = *(short *)(mem+A) * *(short *)(mem+ea);
if (keys & 010000) { /* V/I mode */
*(int *)(mem+A) = templ;
} else { /* R/S mode */
mem[A] = (templ >> 15);
mem[B] = templ & 077777;
}
continue;
}
if (opcode == 01700) {
fprintf(stderr," DIV\n");
if (keys & 010000) { /* V/I mode */
templ = *(int *)(mem+A);
} else { /* R/S mode */
if (mem[A] != 0 && mem[A] != 0xffff) {
fprintf(stderr," Register A should be 0 or -1 before R-mode DIV\n");
exit(1);
}
if (mem[A]) { /* dividend was negative before PID */
tempa = mem[B] | 0x8000; /* make 16-bit negative again */
templ = tempa; /* extend to 32-bit negative */
} else
templ = *(short *)(mem+B);/* make 32-bit positive */
}
mem[A] = templ / *(short *)(mem+ea);
mem[B] = templ % *(short *)(mem+ea);
continue;
}
if (opcode == 03500) {
fprintf(stderr," LDX instruction, new value=%o\n", mem[ea]);
mem[X] = mem[ea];
continue;
}
if (opcode == 00101) {
fprintf(stderr," EAA\n");
mem[A] = ea;
continue;
}
if (opcode == 00203) {
fprintf(stderr," JEQ\n");
if (*(short *)(mem+A) == 0)
mem[P] = ea;
continue;
}
if (opcode == 00703) {
fprintf(stderr," JGE\n");
if (*(short *)(mem+A) >= 0)
mem[P] = ea;
continue;
}
if (opcode == 00503) {
fprintf(stderr," JGT\n");
if (*(short *)(mem+A) > 0)
mem[P] = ea;
continue;
}
if (opcode == 00403) {
fprintf(stderr," JLE\n");
if (*(short *)(mem+A) <= 0)
mem[P] = ea;
continue;
}
if (opcode == 00603) {
fprintf(stderr," JLT\n");
if (*(short *)(mem+A) < 0)
mem[P] = ea;
continue;
}
if (opcode == 00303) {
fprintf(stderr," JNE\n");
if (*(short *)(mem+A) != 0)
mem[P] = ea;
continue;
}
fprintf(stderr," UNKNOWN OPCODE: %o\n", opcode);
exit(1);
}
}
/* Handle SVC instruction. For real hardware emulation on an R-mode
such as the P300, SVC would interrupt (JST*) through location '75
(in vectored mode) or would fault on the P400.
Since we're running programs without an OS underneath us, handle
the SVC's here.
Typical usage:
CALL TNOUA ('MESSAGE', 7)
JST TNOUA
DAC =C'MESSAGE'
DAC =7
OCT 0
...
The library would resolve the TNOUA reference like this:
TNOUA DAC ** RETURN ADDRESS
SVC ENTER THE OS
OCT 140703
JMP# BADCALL HERE FOR BAD SVC
...
The SVC code word follows the SVC instruction:
'100000 = this is an interlude; the actual arguments are found
at the address preceeding the SVC instruction (ie, the caller)
'040000 = there is an instruction following the SVC code, before
the argument list. If there is an SVC error, like a bad function
code, the SVC handler will return to the location following the
code word.
Bits 3-4 are ignored
Bits 5-10 are the SVC group, 1-15.
Bits 11-16 are the SVC function within the group.
Interludes are used because at the time a program is compiled, the
compiler doesn't know whether a call is going to the OS or not. So the
call is compiled like any other. It's dumb for the library TNOUA to
muck around with all the arguments, so bit 1 is set as a shortcut and
the address of the argument list is just before the SVC itself.
If a program knows it's going to call the OS, a direct SVC can be used,
with bit 1 clear:
SVC
OCT '040703
JMP# BADSVC
DAC =C'MESSAGE'
DAC =7
OCT 0
GOOD ...
BADSVC EQU *
*/
svc() {
unsigned short code; /* code word following SVC instruction */
unsigned short argl; /* address of argument list */
unsigned short a1,a2,a3,a4,a5,a6; /* args */
short i;
char *bp;
short class; /* SVC class, from bits 5-10 */
short func; /* SVC func within class, bits 11-16 */
code = mem[mem[P]];
if (code & 0100000)
argl = mem[mem[P]-2];
else if (code & 040000)
argl = mem[P]+2;
else
argl = mem[P]+1;
class = (code >> 6) & 077;
func = code & 077;
fprintf(stderr," code=%o, class=%o, func=%o, arglist=%o\n", code, class, func, argl);
switch (class) {
case 0: /* same as class 1 */
case 1: /* funcs 0-'15 */
switch (func) {
case 5: /* EXIT */
fprintf(stderr,"\nProgram called EXIT\n");
exit(1);
default:
goto unimp;
}
break;
case 2: /* funcs 0-3 */
goto unimp;
break;
case 3: /* func 0 = PRWFIL */
goto unimp;
break;
case 4: /* func 0 = FAMSVC (obsolete) */
goto unimp;
break;
case 5: /* funcs 0-'17 */
goto unimp;
break;
case 6: /* funcs 0-5 */
switch (func) {
case 0: /* comanl, 0 args */
goto unimp;
case 1: /* c1in, 1 arg */
fprintf(stderr," c1in - enter a character!\n");
goto badsvc;
a1 = mem[argl++];
tempa=getchar() | 0x80; /* WRONG - needs to read from /dev/tty */
mem[a1] = tempa;
break;
default:
goto unimp;
}
break;
case 7:
switch (func) {
case 0:
fprintf(stderr," t1in\n");
exit(1);
break;
case 1:
fprintf(stderr," t1ou\n");
a1 = mem[argl++]; /* get address of arg1 */
a1 = mem[a1]; /* get the character to print */
putchar(a1 & 0x7f); /* write the character w/o parity */
break;
case 2: /* TNOU */
case 3: /* TNOUA */
a1 = mem[argl++];
a2 = mem[argl++];
bp = (char *)(mem+a1);
fprintf(stderr," tnou/a, arg1 @ %o, arg2 @ %o\n", a1, a2);
for (i=0; i < mem[a2]; i++,bp++)
putchar(*bp & 0x7f);
if (func == 2)
putchar('\n');
fflush(stdout);
break;
case 4:
fprintf(stderr," tooct\n");
exit(1);
break;
case 5:
fprintf(stderr," duplx$\n");
exit(1);
break;
default:
goto unimp;
}
break;
case 010:
goto unimp;
break;
case 011:
goto unimp;
break;
case 012:
goto unimp;
break;
case 013:
goto unimp;
break;
case 014:
goto unimp;
break;
case 015:
switch (func) {
case 024: /* ERKL$$(KEY,ERASECH,KILLCH,CODE) */
fprintf(stderr," erlk$$\n");
a1 = mem[argl++];
a2 = mem[argl++];
a3 = mem[argl++];
a4 = mem[argl++];
if (mem[a1] == k$read) {
mem[a2] = 0210;
mem[a3] = 0377;
}
mem[a4] = 0;
break;
default:
goto unimp;
}
break;
default:
goto unimp; /* bad class */
}
/* after the SVC, argl is the return address; look for zero arg list
terminator and ignore it) */
if (mem[argl] == 0)
argl++;
fprintf(stderr," returning from SVC to %o\n", argl);
mem[P] = argl;
return;
unimp:
fprintf(stderr," svc class '%o function '%o is not implemented\n", class, func);
exit(1);
/* here on a bad svc; if the bounce bit (bit 2) is set in the code word,
jump to the location following the code word (which is typically a
JMP instruction). If the bounce bit isn't set, we have to halt */
badsvc:
if (code & 040000) {
mem[P]++;
fprintf(stderr," bouncing svc error to address %o\n", mem[P]);
return;
}
fprintf(stderr," halting on bad svc\n");
exit(1);
}
/* here for PIO instructions: OCP, SKS, INA, OTA. The instruction
word is passed in as an argument to handle EIO (Execute I/O) in
V-mode.
*/
pio(unsigned int inst) {
unsigned short class;
unsigned short func;
unsigned short device;
class = inst >> 14;
func = (inst >> 6) & 017;
device = inst & 077;
fprintf(stderr," pio, class=%d, func=%o, device=%o\n", class, func, device);
if (devmap[device])
devmap[device](class, func);
else
fprintf(stderr," no handler for device; instruction ignored\n");
}