1
0
mirror of https://github.com/prirun/p50em.git synced 2026-04-17 08:01:01 +00:00
This commit is contained in:
Kevin Jordan
2020-05-29 22:38:06 -04:00
9 changed files with 314 additions and 205 deletions

4
README.macos_launchd Normal file
View File

@@ -0,0 +1,4 @@
Sample startup for the emulator on MacOS
Customize the em19.launchd file, place it in /Library/LaunchDaemons as
com.prirun.em19, reboot, and the emulator should start automagically.

View File

@@ -2,30 +2,30 @@
## What Is This?
This is a software emulator for a minicomputer architecture sold
by Prime Computer from the early 70s through about 1993. Prime's
initial business plan was to make systems compatible with the
Honeywell x16 family, which had then-recently been discontinued.
Prime extended the architecture heavily.
This is a software emulator for a minicomputer architecture sold by
Prime Computer from the early 70s through about 1993. Prime's initial
business plan was to make systems compatible with the Honeywell x16
family, which had then-recently been discontinued. Prime extended
the architecture heavily.
## Emulator History
Beginning in 2005, Jim Wilcoxson developed an emulator for Prime
Computer's 50-Series architecture. The emulator originally ran on
the PowerPC architecture. In late 2011, Jim ported it so it would
run on x86. This entailed solving endianness issues (The 50-Series
is big-endian), as well as re-optimizing the code for performance
without the host processor having a large set of general-purpose
registers.
run on x86. This entailed solving endianness issues (The 50-Series is
big-endian), as well as re-optimizing the code for performance without
the host processor having a large set of general-purpose registers.
## Emulator Documentation
Coming soon, we swear!
Coming soon, we swear! There is a unix man page included in this
repository.
## Public Systems
There are a set of emulators available for public use. These may
be accessed via `telnet` to the appropriate port on `em.prirun.com`.
There are a set of emulators available for public use. These may be
accessed via `telnet` to the appropriate port on `em.prirun.com`.
| PRIMOS Revision | Port |
|-----------------|------|
@@ -50,32 +50,64 @@ business unit ceased to exist. A reformatted copy is available
## Prime Documentation
A growing collection of Prime and related documentation is available
at [sysovl.info](https://sysovl.info/reference_prime.html). A howto
on installing PRIMOS in the emulator is [here](https://sysovl.info/reference_prime_drb_installing_primos.html).
Discussion of adapting these instructions to 22.1.4 has been occurring on the [cctalk mailing list](http://classiccmp.org/pipermail/cctalk/2020-March/052126.html).
at [sysovl.info](https://sysovl.info/reference_prime.html).
A howto on installing PRIMOS in the emulator is
[here](https://sysovl.info/reference_prime_drb_installing_primos.html).
Discussion of adapting these instructions to
22.1.4 has been occurring on the [cctalk mailing
list](http://classiccmp.org/pipermail/cctalk/2020-March/052126.html).
## Getting PRIMOS
Two versions of PRIMOS are available from Bitsavers:
Distribution tape sets for four versions of PRIMOS,
with small sets of layered products, are available from
[sysovl.info](https://sysovl.info/downloads_prime_primedist.html):
* [Rev 22.1.4](http://bitsavers.org/bits/Prime/primos_22.1.4.zip) [has issues]
* [Rev 22.1.4 repacked](https://yagi.h-net.org/m2214repack.tar.gz) [use this]
* [Rev 19.?](http://bitsavers.org/bits/Prime/pps/03_log.tape_I=boot_II=iptpal.tap.gz)
* Rev 21.0.6, including BASIC, FTN, BRMS, INFORMATION
* Rev 23.2.0, including BASIC, FTN, MIDASPLUS, DRB, DTB, FS_RECOVER, PL1_LIBRARY
* Rev 23.4.Y2K,R1, including BASIC, FTN, MIDASPLUS, DRB, DTB, FS_RECOVER, PL1_LIBRARY
* Rev 24.0.0.r15, including BASIC, FTN, MIDASPLUS, DRB, DTB, FS_RECOVER, PL1_LIBRARY
The Rev. 19 tape is a save from an installed system.
Tapes for two versions of PRIMOS (these are not clean distribution sets)
are available from Bitsavers:
* [Rev 22.1.4](http://bitsavers.org/bits/Prime/primos_22.1.4.zip) [has issues, written with newer magsav, can't restore itself]
* [Rev 22.1.4 repacked](https://yagi.h-net.org/m2214repack.tar.gz) [use this instead, resaved with Rev. 22 magsav]
* [Rev 19.?](http://bitsavers.org/bits/Prime/pps/03_log.tape_I=boot_II=iptpal.tap.gz) [a backup from an installed system]
## Pre-compiled binaries
We'd prefer that you build your own from the source tree, but if that's
not possible, a set of pre-compiled binaries is available. Included are:
* Linux i386
* Linux amd64
* Linux armhf (RasPi / BeagleBone)
* FreeBSD amd64
* Solaris amd64
[download](https://sysovl.info/pages/blobs/emulator/embinaries.20200504.tar.gz)
## Sample System Images
A set of sample system images derived from the public emulators can
be downloaded to get you started. The
[tarball](https://yagi.h-net.org/p50em_samplemachines_v3.tar)
is 142882727 bytes, and its sha256sum is
32647dbcc3a0d541209eafc2f78d054e456d58046c9b3c5bc4ca64a8d9fc0037.
be downloaded to get you started. These tarballs preserve sparse
allocation ("holes"), so that uninitialized space in the disk images
does not occupy actual space. You may need to tell `tar` to preserve
this sparse allocation when you extract, e.g. with the `-S` option.
The current
[tarball](https://sysovl.info/pages/blobs/emulator/p50em_samplemachines_v5.tar)
is 150,029,183 bytes, and its sha256sum is
f5b8008d7c53171f50ad95dd5cc537ba48ca419049518f8ea28deee009c6541a.
(gzip compression would only reduce this by ~400 kilobytes.)
V3 removes additional junk, and rebuilds the disk images as 600 MB
drives, split 30/10 heads filesystem/paging. This tarball preserves
sparse allocation ("holes"), so that uninitialized space in the disk
images does not occupy actual space. You may need to tell `tar` to
preserve this sparse allocation when you extract, e.g. with the `-S`
option. Also includes enhancements to the wrapper scripts: directory
independence and the ability to run the `runem` script from a terminal.
V5 adds the source code for PRIMOS 19.2, and the diagnostics programs,
to that image.
V4 corrected ACL problems in the Rev19 and Rev24 images.
V3 removed additional junk, and rebuilt the disk images as 600
MB drives, split 30/10 heads filesystem/paging. It also included
enhancements to the wrapper scripts: directory independence and the
ability to run the `runem` script from a terminal.

View File

@@ -650,9 +650,22 @@ int devamlc (int class, int func, int device) {
break;
}
memset(&terminfo, 0, sizeof(terminfo));
#ifdef __sun__
terminfo.c_iflag &= ~(IMAXBEL|IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
terminfo.c_oflag &= ~OPOST;
terminfo.c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
terminfo.c_cflag &= ~(CSIZE|PARENB);
terminfo.c_cflag |= CS8;
#else
cfmakeraw(&terminfo);
#endif
baud = baudtable[(getcrs16(A) >> 6) & 7];
#ifdef __sun__
if ((cfsetispeed(&terminfo, baud) == -1) ||
(cfsetospeed(&terminfo, baud) == -1))
#else
if (cfsetspeed(&terminfo, baud) == -1)
#endif
perror("em: unable to set AMLC line speed");
else
printf("em: AMLC line %d set to %d bps\n", dx*16 + lx, baud);
@@ -881,7 +894,7 @@ int devamlc (int class, int func, int device) {
endconnect:
if (allbusy) {
//warn("No free AMLC connection");
strcpy(buf,"\r\nAll available connections are in use.\r\n\n");
strncpy(buf,"\r\nAll available connections are in use.\r\n\n", 2047);
write(fd, buf, strlen(buf));
close(fd);
} else {
@@ -898,6 +911,16 @@ int devamlc (int class, int func, int device) {
optval = 1;
if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &optval, sizeof(optval)) == -1)
perror("unable to set TCP_NODELAY");
#ifndef __APPLE__
/* Set 600 second keepalive idle time for this socket */
optval = 600;
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &optval, sizeof(optval)) == -1)
perror("unable to set TCP_KEEPIDLE");
#endif
/* ... and turn on keepalive */
optval = 1;
if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval)) == -1)
perror("unable to set SO_KEEPALIVE");
/* these Telnet commands put the connecting telnet client
into character-at-a-time mode and binary mode. Since

View File

@@ -406,7 +406,11 @@ void pncinitfd(int fd) {
perror("unable to get ts flags for PNC");
fatal(NULL);
}
#ifdef __sun__
fdflags |= O_NONBLOCK;
#else
fdflags |= O_NONBLOCK+O_ASYNC;
#endif
if (fcntl(fd, F_SETFL, fdflags) == -1) {
perror("unable to set fdflags for PNC");
fatal(NULL);
@@ -685,7 +689,7 @@ unsigned short pncxmit1(short nodeid) {
return 0;
}
if (nwritten != ntowrite) {
fprintf(stderr, "devpnc: pncxmit1 wrote %d of %d bytes to node %d %d; disconnecting\n", nwritten, ntowrite, nodeid);
fprintf(stderr, "devpnc: pncxmit1 wrote %d of %d bytes to node %d; disconnecting\n", nwritten, ntowrite, nodeid);
pncdisc(nodeid, "partial packet written");
return 0;
}
@@ -757,7 +761,8 @@ void pncrecv() {
for (nodeid=0; nodeid<=MAXNODEID; nodeid++)
if (ni[nodeid].cstate == PNCCSAUTH) {
fd = ni[nodeid].fd;
FD_SET(fd, &fds);
if (fd != -1)
FD_SET(fd, &fds);
if (fd > n)
n = fd;
}
@@ -961,7 +966,7 @@ int devpnc (int class, int func, int device) {
fprintf(stderr,"Line %d of ring.cfg ignored: IP address too long\n", linenum);
continue;
}
strcpy(temphost, p);
strncpy(temphost, p, MAXHOSTLEN);
if ((p=strtok(NULL, DELIM)) == NULL) {
fprintf(stderr,"Line %d of ring.cfg ignored: unique id/password missing\n", linenum);
@@ -979,14 +984,14 @@ int devpnc (int class, int func, int device) {
}
if (i <= MAXNODEID)
continue;
strcpy(ni[tempid].uid, p);
strncpy(ni[tempid].uid, p, MAXUIDLEN);
/* parse the port number from the IP address */
tempport = 0;
if (strcmp(temphost, "-") != 0) {
if ((p=strtok(temphost, PDELIM)) != NULL) {
strcpy(ni[tempid].host, p);
strncpy(ni[tempid].host, p, MAXHOSTLEN);
if ((p=strtok(NULL, PDELIM)) != NULL) {
tempport = atoi(p);
if (tempport < 1 || tempport > 65000)
@@ -1059,11 +1064,13 @@ int devpnc (int class, int func, int device) {
can be disabled, though that may cause problems for rings where
the max node id > MAXACCEPTTIME because of connect delays */
#ifdef __linux__
optval = MAXNODEID;
if (setsockopt(pncfd, IPPROTO_TCP, TCP_DEFER_ACCEPT, &optval, sizeof(optval))) {
perror("setsockopt TCP_DEFER_ACCEPT failed for PNC");
fatal(NULL);
}
#endif
TRACE(T_RIO, "PNC configured\n");
devpoll[device] = PNCPOLL*gv.instpermsec;

272
em.c
View File

@@ -6,9 +6,6 @@
- booting from a Prime MAGSAV tape
- restoring a Prime R-mode .save image from the host file system
This is a project in development, so please don't publish it or
make it available for others to use.
Comments, suggestions, corrections, and general notes that you're
interested in a Prime emulation project are welcome and
appreciated.
@@ -496,8 +493,6 @@ void *disp_gen[4096]; /* generic dispatch table */
typedef struct {
unsigned short *physmem; /* pointer to Prime physical memory */
int memlimit; /* user's desired memory limit (-mem) */
int intvec; /* currently raised interrupt (if >= zero) */
@@ -569,24 +564,26 @@ static jmp_buf jmpbuf; /* for longjumps to the fetch loop */
static jmp_buf bootjmp; /* for longjumps to the fetch loop */
/* The standard Prime physical memory limit on early machines is 8MB.
Later machines have higher memory capacities, up to 1024MB, using
32-bit page tables.
Later machines have higher memory capacities, up to 1024MB
(unsupported in Primos though), using 32-bit page tables.
NOTE:
NOTE:
- rev 19 custom Primos expands memory limit from 16MB to 32MB
- rev 19 custom Primos expands 9950 limit from 16MB to 32MB
- rev 19 custom Primos *requires* 32MB: Primos memory scan removed
- rev 20 is limited to a max of 32MB
- rev 23.4 is limited to a max of 512MB
- rev 23.4 is limited to a max of 512MB (use -cpuid 5340)
- rev 23.4 RMS_PROCESS scans & adds memory after Primos is booted
"memlimit" is set with the -mem argument, taking an argument which is
the desired memory limit in MB. Setting a memory limit is useful to
speed up system boots and diagnostics during emulator testing.
Prime physical memory size (MB) is set with the -mem argument.
Setting a small memory limit speeds up system boots and diagnostics
during emulator testing.
*/
#define MAXMB 512 /* must be a power of 2 */
#define MEMSIZE MAXMB/2*1024*1024
#define MEMMASK MEMSIZE-1
#define DEFMB 32
#define MEM physmem
static unsigned short physmem[MEMSIZE]; /* system's physical memory */
static unsigned short *physmem = NULL; /* system's physical memory */
#define get16mem(phyaddr) swap16(MEM[(phyaddr)])
#define put16mem(phyaddr, val) MEM[(phyaddr)] = swap16((val))
@@ -667,10 +664,10 @@ static in_addr_t bindaddr = INADDR_ANY; /* -naddr option (PnC/Ringnet) */
/* load map related data, specified with -map */
#define MAXSYMBOLS 15000
#define MAXSYMLEN 9
#define MAXSYMLEN 11
static int numsyms = 0;
static struct {
char symname[MAXSYMLEN];
char symname[MAXSYMLEN+1];
ea_t address;
char symtype; /* o=other, c=common, e=ecb, p=proc, l=linkbase */
} mapsym[MAXSYMBOLS];
@@ -762,7 +759,7 @@ void addsym(char *sym, unsigned short seg, unsigned short word, char type) {
for (ix2 = numsyms; ix2 > ix; ix2--)
mapsym[ix2] = mapsym[ix2-1];
//TRACEA("%s = %o/%o\n", sym, seg, words);
strcpy(mapsym[ix+1].symname, sym);
strncpy(mapsym[ix+1].symname, sym, MAXSYMLEN);
mapsym[ix+1].address = addr;
mapsym[ix+1].symtype = type;
numsyms++;
@@ -1010,13 +1007,11 @@ static pa_t mapva(ea_t ea, ea_t rp, short intacc, unsigned short *access) {
pa = stlbp->ppa | (ea & 0x3FF);
TRACE(T_MAP," for ea %o/%o, iacc=%d, stlbix=%d, pa=%o loaded at #%u\n", ea>>16, ea&0xffff, intacc, stlbix, pa, stlbp->load_ic);
} else {
pa = ea & MEMMASK;
pa = ea;
}
#ifndef NOMEM
if (pa < gv.memlimit)
#endif
return pa;
#ifdef DBG
#if 0
printf(" map: Memory address '%o (%o/%o) is out of range 0-'%o (%o/%o) at #%d!\n", pa, pa>>16, pa & 0xffff, gv.memlimit-1, (gv.memlimit-1)>>16, (gv.memlimit-1) & 0xffff, gv.instcount);
#endif
@@ -1305,32 +1300,16 @@ static long long get64r(ea_t ea, ea_t rpring) {
page instead of on every 16-bit fetch from the instruction stream.
*/
#ifdef FAST
unsigned short iget16t(ea_t ea) {
unsigned short access;
if (*(int *)&ea >= 0) {
gv.brp[RPBR].memp = MEM + (mapva(ea, RP, RACC, &access) & 0xFFFFFC00);
gv.brp[RPBR].vpn = ea & 0x0FFFFC00;
return swap16(gv.brp[RPBR].memp[ea & 0x3FF]);
}
return get16trap(ea);
static inline unsigned short iget16t(ea_t ea) {
eap = &gv.brp[RPBR];
return get16t(ea);
}
static inline unsigned short iget16(ea_t ea) {
if ((ea & 0x8FFFFC00) == (gv.brp[RPBR].vpn & 0x0FFFFFFF))
return swap16(gv.brp[RPBR].memp[ea & 0x3FF]);
else
return iget16t(ea);
eap = &gv.brp[RPBR];
return get16(ea);
}
#else
#define iget16(ea) get16((ea))
#define iget16t(ea) get16t((ea))
#endif
static inline void put16(unsigned short value, ea_t ea) {
unsigned short access;
@@ -1787,7 +1766,7 @@ char *keystring(unsigned short keys) {
*sp++ = 'L';
memcpy(sp, modes[(keys>>10) & 7], 3);
sp += 3;
if (keys & 01000 == 0) /* float exception enabled */
if (!(keys & 01000)) /* float exception enabled */
*sp++ = 'F';
if (keys & 0400) /* int exception enabled */
*sp++ = 'I';
@@ -1874,46 +1853,50 @@ static void fatal(char *msg) {
printf("Fatal error");
if (msg)
printf(": %s", msg);
printf("\ninstruction #%u at %o/%o %s ^%06o^\nA='%o/%d B='%o/%d L='%o/%d X='%o/%d K=%o [%s]\nowner=%o %s, modals=%o [%s]\n", gv.instcount, gv.prevpc >> 16, gv.prevpc & 0xFFFF, searchloadmap(gv.prevpc,' '), lights, getcrs16(A), getcrs16s(A), getcrs16(B), getcrs16s(B), getcrs32(A), getcrs32s(A), getcrs16(X), getcrs16s(X), getcrs16(KEYS), keystring(getcrs16(KEYS)), getcrs16(OWNERL), searchloadmap(getcrs32(OWNER),' '), getcrs16(MODALS), modstring(getcrs16(MODALS)));
printf("\n");
/* dump concealed stack entries */
if (physmem != NULL) {
printf("instruction #%u at %o/%o %s ^%06o^\nA='%o/%d B='%o/%d L='%o/%d X='%o/%d K=%o [%s]\nowner=%o %s, modals=%o [%s]\n", gv.instcount, gv.prevpc >> 16, gv.prevpc & 0xFFFF, searchloadmap(gv.prevpc,' '), lights, getcrs16(A), getcrs16s(A), getcrs16(B), getcrs16s(B), getcrs32(A), getcrs32s(A), getcrs16(X), getcrs16s(X), getcrs16(KEYS), keystring(getcrs16(KEYS)), getcrs16(OWNERL), searchloadmap(getcrs32(OWNER),' '), getcrs16(MODALS), modstring(getcrs16(MODALS)));
if (getcrs16(MODALS) & 010) { /* process exchange is enabled */
pcbp = getcrs32ea(OWNER); /* my pcb pointer */
first = get16r0(pcbp+PCBCSFIRST);
next = get16r0(pcbp+PCBCSNEXT);
last = get16r0(pcbp+PCBCSLAST);
while (next != first) {
this = next-6;
csea = MAKEVA(getcrs16(OWNERH)+gv.csoffset, this);
for (i=0; i<6; i++)
cs[i] = get16r0(csea+i);
printf("Fault: RP=%o/%o, keys=%06o, fcode=%o, faddr=%o/%o\n", cs[0], cs[1], cs[2], cs[3], cs[4], cs[5]);
next = this;
/* dump concealed stack entries */
if (getcrs16(MODALS) & 010) { /* process exchange is enabled */
pcbp = getcrs32ea(OWNER); /* my pcb pointer */
first = get16r0(pcbp+PCBCSFIRST);
next = get16r0(pcbp+PCBCSNEXT);
last = get16r0(pcbp+PCBCSLAST);
while (next != first) {
this = next-6;
csea = MAKEVA(getcrs16(OWNERH)+gv.csoffset, this);
for (i=0; i<6; i++)
cs[i] = get16r0(csea+i);
printf("Fault: RP=%o/%o, keys=%06o, fcode=%o, faddr=%o/%o\n", cs[0], cs[1], cs[2], cs[3], cs[4], cs[5]);
next = this;
}
}
}
#ifndef NOTRACE
printf("RP queue:");
i = gv.tracerpqx;
while(1) {
printf(" %o/%o", gv.tracerpq[i]>>16, gv.tracerpq[i]&0xFFFF);
i = (i+1) & (MAXRPQ-1);
if (i == gv.tracerpqx)
break;
}
printf("\n");
printf("STLB calls: %d misses: %d hitrate: %5.2f%%\n", gv.mapvacalls, gv.mapvamisses, (double)(gv.mapvacalls-gv.mapvamisses)/gv.mapvacalls*100.0);
printf("Supercache calls: %d misses: %d hitrate: %5.2f%%\n", gv.supercalls, gv.supermisses, (double)(gv.supercalls-gv.supermisses)/gv.supercalls*100.0);
printf("RP queue:");
i = gv.tracerpqx;
while(1) {
printf(" %o/%o", gv.tracerpq[i]>>16, gv.tracerpq[i]&0xFFFF);
i = (i+1) & (MAXRPQ-1);
if (i == gv.tracerpqx)
break;
}
printf("\n");
printf("STLB calls: %d misses: %d hitrate: %5.2f%%\n", gv.mapvacalls, gv.mapvamisses, (double)(gv.mapvacalls-gv.mapvamisses)/gv.mapvacalls*100.0);
printf("Supercache calls: %d misses: %d hitrate: %5.2f%%\n", gv.supercalls, gv.supermisses, (double)(gv.supercalls-gv.supermisses)/gv.supercalls*100.0);
#endif
/* should do a register dump, RL dump, PCB dump, etc. here... */
/* should do a register dump, RL dump, PCB dump, etc. here... */
/* call all devices with a request to terminate */
for (i=0; i<64; i++)
devmap[i](-2, 0, i);
/* call all devices with a request to terminate */
for (i=0; i<64; i++)
devmap[i](-2, 0, i);
}
#ifndef NOTRACE
fclose(gv.tracefile);
#endif
@@ -2259,7 +2242,7 @@ special:
#endif
if (class < 2) { /* class 0/1 */
ea = iget16(RP); /* get A from next word */
ea = iget16t(RP); /* get A from next word */
INCRP;
TRACE(T_EAR, " Class %d, new ea=%o\n", class, ea);
if (class == 1)
@@ -2288,7 +2271,7 @@ special:
} else if (i && x) { /* class 2/3, ix=11 */
TRACE(T_EAR, " class 2/3, ix=11\n");
ea = iget16(RP); /* get A from next word */
ea = iget16t(RP); /* get A from next word */
INCRP;
TRACE(T_EAR, " ea=%o\n", ea);
if (class == 3) {
@@ -4382,9 +4365,6 @@ int main (int argc, char **argv) {
#define XJ 0312
#define XRBRACE 0375
struct timeval boot_tv;
struct timezone tz;
printf("[Prime Emulator ver %s %s]\n", REV, __DATE__);
printf("[Copyright (C) 2005-2019 Jim Wilcoxson prirun@gmail.com]\n");
if (argc > 1 && (strcmp(argv[1],"--version") == 0)) {
@@ -4413,14 +4393,18 @@ int main (int argc, char **argv) {
printf("Open returned %d redirecting stderr\n", templ);
exit(1);
}
setvbuf(stderr, NULL, _IONBF, 0);
/* initialize global variables */
/* initialize global variables
NOTE: if instpermsec is off by more than a factor of 2, it causes
some minor clock skew problems during the first few seconds of
system boot. There is debug code in emdev.h/devcp to see this. */
gv.physmem = physmem;
gv.intvec = -1;
gv.instcount = 0;
gv.inhcount = 0;
gv.instpermsec = 2000;
gv.instpermsec = 40000;
gv.livereglim = 040;
gv.mapvacalls = 0;
gv.mapvamisses = 0;
@@ -4464,53 +4448,12 @@ int main (int argc, char **argv) {
#include "dispatch.h"
/* master clear:
- clear all registers
- user register set is 0
- modals:
-- interrupts inhibited
-- standard interrupt mode
-- user register set is 0
-- non-mapped I/O
-- process exchange disabled
-- segmentation disabled
-- machine checks disabled
- keys:
-- C, L, LT, EQ clear
-- single precision
-- 16S mode
-- take fault on FP exception
-- no fault on integer or decimal exception
-- characters have high bit on
-- FP rounding disabled
-- not in dispatcher
-- register set is not saved
- set P to '1000
- all stlb entries are invalid
- all iotlb entries are invalid
- clear 64K words of memory
*/
for (i=0; i < 32*REGSETS; i++)
regs.u32[i] = 0;
crsl = (void *)regs.sym.userregs[0]; /* first user register set */
putcrs16(MODALS, 0); /* interrupts inhibited */
newkeys(0);
RP = 01000;
for (i=0; i < STLBENTS; i++)
gv.stlb[i].seg = 0xFFFF; /* marker for invalid STLB entry */
for (i=0; i < IOTLBENTS; i++)
gv.iotlb[i].valid = 0;
bzero(MEM, 64*1024*2); /* zero first 64K words */
domemdump = 0;
bootarg = NULL;
bootfile[0] = 0;
gv.pmap32bits = 0;
gv.csoffset = 0;
gv.memlimit = MEMSIZE;
gv.memlimit = DEFMB;
tport = 0;
nport = 0;
@@ -4583,17 +4526,15 @@ int main (int argc, char **argv) {
} else
fatal("-cpuid needs an argument\n");
#ifndef NOMEM
} else if (strcmp(argv[i],"-mem") == 0) {
if (i+1 < argc && argv[i+1][0] != '-') {
sscanf(argv[++i],"%d", &templ);
if (1 <= templ && templ <= MAXMB)
gv.memlimit = templ*1024/2*1024;
if (1 <= templ && templ <= 512)
gv.memlimit = templ;
else
fatal("-mem arg range is 1 to 512 (megabytes)\n");
} else
fatal("-mem needs an argument\n");
#endif
} else if (strcmp(argv[i],"-nport") == 0) {
if (i+1 < argc && argv[i+1][0] != '-') {
@@ -4704,6 +4645,54 @@ int main (int argc, char **argv) {
TRACEA("Boot sense switches=%06o, data switches=%06o\n", sswitch, dswitch);
/* master clear:
- clear all registers
- user register set is 0
- modals:
-- interrupts inhibited
-- standard interrupt mode
-- user register set is 0
-- non-mapped I/O
-- process exchange disabled
-- segmentation disabled
-- machine checks disabled
- keys:
-- C, L, LT, EQ clear
-- single precision
-- 16S mode
-- take fault on FP exception
-- no fault on integer or decimal exception
-- characters have high bit on
-- FP rounding disabled
-- not in dispatcher
-- register set is not saved
- set P to '1000
- all stlb entries are invalid
- all iotlb entries are invalid
- clear 64K words of memory
*/
for (i=0; i < 32*REGSETS; i++)
regs.u32[i] = 0;
crsl = (void *)regs.sym.userregs[0]; /* first user register set */
putcrs16(MODALS, 0); /* interrupts inhibited */
newkeys(0);
RP = 01000;
for (i=0; i < STLBENTS; i++)
gv.stlb[i].seg = 0xFFFF; /* marker for invalid STLB entry */
for (i=0; i < IOTLBENTS; i++)
gv.iotlb[i].valid = 0;
gv.memlimit *= 1024 * 1024; /* memory size in bytes */
physmem = malloc(gv.memlimit);
gv.memlimit /= sizeof(*physmem); /* max Prime word address */
if (physmem == NULL) {
perror("Error allocating Prime memory block");
fatal("Can't allocate Prime memory block");
}
bzero(MEM, 64*1024*2); /* zero first 64K words */
/* if no maps were specified on the command line, look for ring0.map and
ring3.map in the current directory and read them */
@@ -4784,6 +4773,12 @@ int main (int argc, char **argv) {
for (i=0; i<9; i++)
rvec[i] = swap16(rvec[i]);
if (rvec[3] != 0) {
if (rvec[2] > rvec[1]) {
rvec[0] += rvec[3];
rvec[1] += rvec[3];
}
}
} else {
/* If no filename follows -boot, then the sense switches are used to
@@ -4917,13 +4912,6 @@ a filename, CPU registers and keys are loaded from the runfile header.\n\
}
RPL = rvec[2];
/* initialize the timer stuff */
if (gettimeofday(&boot_tv, &tz) != 0) {
perror("gettimeofday failed");
fatal(NULL);
}
/* main instruction decode loop */
grp = RP; /* see similar assignments in fault, before longjmp */
@@ -5162,7 +5150,7 @@ fetch:
inst = iget16(RP | ((RPL >= gv.livereglim || (getcrs16(KEYS) & 0016000) == 010000) ? 0 : 0x80000000));
#else
inst = iget16(RP);
inst = iget16t(RP);
#endif
INCRP;
@@ -5237,7 +5225,7 @@ xec:
if ((getcrs16(KEYS) & 016000) == 014000) { /* 64V mode */
ea = ea64v(inst, earp);
TRACE(T_FLOW, " EA: %o/%o %s\n",ea>>16, ea & 0xFFFF, searchloadmap(ea,' '));
TRACE(T_FLOW, " EA: %o/%o %s\n", ea>>16, ea & 0xFFFF, searchloadmap(ea,' '));
goto *(gv.disp_vmr[VMRINSTIX(inst)]);
} else if ((getcrs16(KEYS) & 0016000) == 010000) { /* E32I */
@@ -5249,17 +5237,17 @@ xec:
} else if (getcrs16(KEYS) & 004000) { /* 32R/64R */
ea = ea32r64r(earp, inst);
TRACE(T_FLOW, " EA: %o/%o %s\n",ea>>16, ea & 0xFFFF, searchloadmap(ea,' '));
TRACE(T_FLOW, " EA: %o/%o\n", ea>>16, ea & 0xFFFF);
goto *(gv.disp_rmr[RMRINSTIX(inst)]);
} else if (getcrs16(KEYS) & 002000) {
ea = ea32s(inst);
TRACE(T_FLOW, " EA: %o/%o %s\n",ea>>16, ea & 0xFFFF, searchloadmap(ea,' '));
TRACE(T_FLOW, " EA: %o/%o\n", ea>>16, ea & 0xFFFF);
goto *(gv.disp_rmr[SMRINSTIX(inst)]);
} else if ((getcrs16(KEYS) & 016000) == 0) {
ea = ea16s(inst);
TRACE(T_FLOW, " EA: %o/%o %s\n",ea>>16, ea & 0xFFFF, searchloadmap(ea,' '));
TRACE(T_FLOW, " EA: %o/%o\n", ea>>16, ea & 0xFFFF);
goto *(gv.disp_rmr[SMRINSTIX(inst)]);
} else {

40
em19.launchd Normal file
View File

@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" \
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<dict>
<key>Label</key>
<string>com.prirun.em19</string>
<key>ServiceDescription</key>
<string>Rev 19 Prime Emulator</string>
<key>Disabled</key>
<false/>
<key>OnDemand</key>
<false/>
<key>LowPriorityIO</key>
<true/>
<key>UserName</key>
<string>jim</string>
<key>GroupName</key>
<string>staff</string>
<key>Program</key>
<string>/Users/jim/prime/pub/runem</string>
<key>WorkingDirectory</key>
<string>/Users/jim/prime/pub/</string>
<key>ProgramArguments</key>
<array>
<string>runem</string>
<string>19</string>
<string>8001</string>
<string>8101</string>
</array>
</dict>
</plist>

17
emdev.h
View File

@@ -1530,14 +1530,16 @@ int devcp (int class, int func, int device) {
else if (cpuid >= 15) /* newer machines: 250 ticks/second */
clkpic = -1250;
TRACE(T_INST, "Clock PIC %d requested, set to %d\n", getcrs16s(A), clkpic);
ticks = -1;
SETCLKPOLL;
} else if (func == 07) {
TRACE(T_INST, "Clock control register set to '%o\n", getcrs16(A));
if (getcrs16(A) & 020)
clkrate = 102.4;
else
clkrate = 3.2;
TRACE(T_INST, "Clock control register set to '%o\n", getcrs16(A));
ticks = -1;
SETCLKPOLL;
} else if (func == 013) { /* set interrupt vector */
@@ -1583,9 +1585,8 @@ int devcp (int class, int func, int device) {
elapsedms = (tv.tv_sec-start_tv.tv_sec)*1000.0 + (tv.tv_usec-start_tv.tv_usec)/1000.0;
targetticks = elapsedms/(-clkpic*clkrate/1000);
#if 0
absticks++;
if (absticks%1000 == 0 || abs(ticks-targetticks) > 5)
printf("\nClock: target=%d, ticks=%d, offset=%d\n", targetticks, ticks, ticks-targetticks);
if (abs(ticks-targetticks) > 5)
printf("\nClock: target=%d, ticks=%d, offset=%d, ipms=%d, poll=%d\n", targetticks, ticks, ticks-targetticks, gv.instpermsec, devpoll[device]);
#endif
/* if the clock gets way out of whack (eg, because of a host
@@ -1628,7 +1629,7 @@ int devcp (int class, int func, int device) {
XXX: this code should probably be done whether or not the
clock is running */
#define IPMTIME 5000
#define IPMTIME 1000
if ((gv.instcount < previnstcount) || (gv.instcount-previnstcount > gv.instpermsec*IPMTIME)) {
if (gv.instcount-previnstcount > gv.instpermsec*IPMTIME) {
@@ -1913,11 +1914,11 @@ int devdisk (int class, int func, int device) {
head = m2 & 077;
u = dc[dx].usel;
if (order == 2)
strcpy(ordertext,"Format");
strncpy(ordertext,"Format", 7);
else if (order == 5)
strcpy(ordertext,"Read");
strncpy(ordertext,"Read", 7);
else if (order == 6)
strcpy(ordertext,"Write");
strncpy(ordertext,"Write", 7);
TRACE(T_INST|T_DIO, "%s, head=%d, track=%d, rec=%d, recsize=%d\n", ordertext, head, track, rec, recsize);
if (u == -1) {
fprintf(stderr," Device '%o, order %d with no unit selected\n", device, order);

View File

@@ -2,23 +2,34 @@
REV=${shell [ -d .hg ] && hg id -n || git rev-parse --short HEAD}
.PHONY: em emwarn debug trace fixed
all_deps = makefile
em: # normal
rm -rf em.o
cc -DREV=\"${REV}\" -DNOTRACE -DFAST -DNOMEM -O -Winline -Wno-error=return-type em.c -o em
em_objs = em.o em
em_deps = \
em.c regs.h emdev.h ea64v.h ea32i.h fp.h dispatch.h geom.h \
devpnc.h devamlc.h devsmlc.h swap.h
emwarn: # lots of compiler warnings
rm -rf em.o
cc -DREV=\"${REV}\" -DNOTRACE -DFAST -DNOMEM -O -Wall -Wextra -pedantic -Wconversion em.c -o em
CFLAGS =
# Uncomment for building on SmartOS/Solaris:
# CFLAGS += -lsocket -lnsl
debug: # gdb
rm -rf em.o
cc -DREV=\"${REV}\" -DNOTRACE -DFAST -DNOMEM -g -O0 em.c -o em
.PHONY: emwarn debug trace fixed
trace: # tracing
rm -rf em.o
cc -DREV=\"${REV}\" -DFAST -DNOMEM -O em.c -o em
# normal
em: $(em_deps) $(all_deps)
$(CC) -DREV=\"${REV}\" ${CFLAGS} -DNOTRACE -DFAST -O -Winline -Wno-return-type em.c -o em
# lots of compiler warnings
emwarn: $(em_deps) $(all_deps)
$(CC) -DREV=\"${REV}\" ${CFLAGS} -DNOTRACE -DFAST -O -Wall -Wextra -pedantic -Wconversion em.c -o em
# gdb
debug: $(em_deps) $(all_deps)
$(CC) -DREV=\"${REV}\" ${CFLAGS} -DNOTRACE -DFAST -g -O0 em.c -o em
# tracing
trace: $(em_deps) $(all_deps)
$(CC) -DREV=\"${REV}\" ${CFLAGS} -DFAST -O em.c -o em
# the fixed clock rate build is useful for making problems reproduceable.
#
@@ -26,6 +37,9 @@ trace: # tracing
# PRIMOS.COMI to get a more consistent instruction count for the
# failure, then enable tracing a little before that with -trace <IC - 100>
fixed: # fixed clock rate
rm -rf em.o
cc -DREV=\"${REV}\" -DFIXEDCLOCK -DNOIDLE -DFAST -DNOMEM -O em.c -o em
# fixed clock rate
fixed: $(em_deps) $(all_deps)
$(CC) -DREV=\"${REV}\" ${CFLAGS} -DFIXEDCLOCK -DNOIDLE -DFAST -O em.c -o em
clean:
rm -f $(em_objs)

View File

@@ -2,7 +2,7 @@ default: emlink intsize magrst magsav mtread mtwrite ptextu strip8 \
untap untap16 untap_vin utextp
# Unix version of Prime's magrst
magrst: magrst.c istext.c
magrst: magrst.c istext.c
# Unix version of Prime's magsav
magsav: magsav.c istext.c
magsav: magsav.c istext.c