mirror of
https://github.com/prirun/p50em.git
synced 2026-04-17 08:01:01 +00:00
Merge branch 'master' of https://github.com/kej715/p50em
This commit is contained in:
4
README.macos_launchd
Normal file
4
README.macos_launchd
Normal 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.
|
||||
94
README.md
94
README.md
@@ -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.
|
||||
|
||||
25
devamlc.h
25
devamlc.h
@@ -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
|
||||
|
||||
17
devpnc.h
17
devpnc.h
@@ -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
272
em.c
@@ -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
40
em19.launchd
Normal 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
17
emdev.h
@@ -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);
|
||||
|
||||
46
makefile
46
makefile
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user