mirror of
https://github.com/prirun/p50em.git
synced 2026-02-04 07:12:49 +00:00
1375 lines
32 KiB
C
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");
|
|
}
|