1
0
mirror of https://github.com/prirun/p50em.git synced 2026-03-24 17:16:05 +00:00

50 Commits

Author SHA1 Message Date
Dennis Boone
74aa7fe23e Trial command line option to suppress updating PRIMOS' DATNOW variable. 2025-09-29 20:38:30 -04:00
Dennis Boone
1819fed512 Error in link to install instructions
The link in README.md to install instructions was either old or
never quite right.  Point to DRB's overview document now, which
points (near) to the version specific ones.
2022-03-21 15:24:01 -04:00
Dennis Boone
64c5970e97 Added tracing for floating point operations. 2021-12-13 14:02:01 -05:00
Dennis Boone
c6c0344004 Additional #ifdef __APPLE__ sections removed
Some DTR handling was still inside of such #ifdefs, and thus
I was unable to make a modem work on a serial port.  I believe
this code is portable to OSX, Linux, FreeBSD, and Solaris.
2021-09-05 16:40:13 -04:00
Dennis Boone
0466b4b603 Emit mark parity space(s) in the "less than three" case, not space
parity ones.  Discovered by Daiyu.
2021-07-27 14:59:27 -04:00
Dennis Boone
256c72ca64 Install git in the vm so the version stamping works. 2021-03-19 01:42:51 -04:00
Dennis Boone
975cc951f8 Really? No "latest" tag or release or whatever, have to use a
numeric version?
2021-03-19 01:34:13 -04:00
Dennis Boone
29fbc31df6 Can the packaging be outside the vm thing? 2021-03-19 01:30:30 -04:00
Dennis Boone
f72b67b876 FreeBSD build, attempt 1. 2021-03-19 01:27:25 -04:00
Dennis Boone
df6529c505 Make these manual. 2021-03-19 01:16:12 -04:00
Dennis Boone
8782e6a63e MacOS builder. 2021-03-19 01:13:11 -04:00
Dennis Boone
88a43363e1 Put repo_token back in
Not sure what this really wants, the action doco doesn't explain it _at all_.
2021-03-19 00:58:53 -04:00
Dennis Boone
9fa1c77bad Flush out the auto-build. No clue if this will work. 2021-03-18 11:58:04 -04:00
Dennis Boone
2d8397d1a4 Create linuxbuild.yml
See if we can build release binaries.
2021-03-18 11:45:44 -04:00
Dennis Boone
244f2f49a2 Remove now-inaccurate comment. 2021-02-21 00:35:31 -05:00
Dennis Boone
af2b015383 Fix for problem where PASCAL install test crashes.
It appears that the emulator needs to check for faulted pointers
after indirection in APs used in e.g. EAFA.  PASCAL triggers this
issue by using EAFA on an indirect pointer to a DYNT.  A two cycle
fault check is done other places, and the approach is repeated here.
2021-02-21 00:06:38 -05:00
Dennis Boone
902f535120 Merge remote-tracking branch 'refs/remotes/origin/master' 2021-02-17 18:11:03 -05:00
Dennis Boone
25febc3947 Merge remote-tracking branch 'refs/remotes/origin/master' 2021-02-11 13:01:51 -05:00
Dennis Boone
84afd8da7b Add an editorconfig file. 2021-02-11 13:01:28 -05:00
Dennis Boone
d6d01cd05c Workaround for locked up AMLC lines
As it was in the real world, especially with dialup ports, it is possible
for an emulator AMLC line to get blocked by stray flow control characters.
The blocked port may appear dead to the next user to connect, if they are
not "serial savvy" enough to try sending an XON character.

A large number of vulnerability scanners have found and have been hitting
the public emulators, injecting HTTP transactions or worse into the AMLC
ports and often locking all of them daily.

This workaround injects an XON (DC1, 0221) and a line kill character as
extracted from the DEFKIL field in FIGCOM, into the AMLC line when a new
connection is received.

There is a -dolineclear command line switch to enable the behavior.
2021-02-07 00:37:15 -05:00
Dennis Boone
ea2edd89de Fix terminal i/o issue in d_hlt
Due to the configuration of the tty, the getchar() call in d_hlt
returns nothing.  Work around this in the same way the SOC console
code does, by opening it on a separate unit, and reading from that
instead.  Without this fix, the code which looks for certain halt
cases and offers "continue or exit" just spins, printing its message
and getting no input.
2021-02-06 17:11:55 -05:00
Dennis Boone
3d71fd85f0 Update copyrights. 2021-02-03 18:51:34 -05:00
Dennis Boone
5d119bb2c5 Update README.md
Github discussions.
2021-01-21 14:24:49 -05:00
Dennis Boone
aa8095217f Merge pull request #5 from kej715/master
Add support for RJE MASTER mode in MDLC/HSSMLC controller, and use TRACE system instead of separate debug tracing.
2020-08-07 10:19:32 -04:00
Kevin Jordan
89043d09a4 Add support for RJE MASTER mode in MDLC/HSSMLC controller, and use
TRACE macro in the controller implementation.
2020-08-06 23:43:50 -04:00
Dennis Boone
e61857e333 Man page enhancements
Credit Kevin Jordan for sync serial support.

Add smlc.cfg info.

Improved explanation for instruction count and octal segno tracing.
2020-06-23 17:53:01 -04:00
Dennis Boone
4771f09e1e Minor tracing enhancements
Deconflict some values of instruction count with some of the
other tracing options by making it require a leading # before
the integer count.

Add/fix support for tracing execution within a designated segno.

Announce to the trace file that tracing is initialized.
2020-06-23 17:35:53 -04:00
Dennis Boone
6bf6e2ea48 Nanosecond log resolution for SMLC debug log
Using clock_gettime(), fetch the realtime clock, which has as much as
nanosecond resolution.  (In reality, it will likely ben more like
microseconds, though it returns the value in nanoseconds.) Use the
nanosecond value in SMLC log entries.  On modern systems and over
ethernet links, second resolution is probably fairly coarse for trying
to debug RJE problems.
2020-06-23 17:15:35 -04:00
Dennis Boone
edf7129651 Merge remote-tracking branch 'refs/remotes/origin/master' 2020-06-22 23:58:37 -04:00
Dennis Boone
543aa3f963 Fix link fail on x64-64
Weird gcc behavior.  Code snippet:

   uint16_t data;
   char ch;
   ...
   i = (data >> 8) & 0xff;
   ch = data & 0xff;
   ...
   fprintf(smlclog, "%s OTA '01%02o line '%02o set special character %d <%02x>\n", smlctimestamp, device, dc[dx].lineno, i, ch);

Output on x86-64 before this change:

    17:13:51 OTA '0150 line '02 set special character 3 <ffffffff>

Output on armhf or on x86-64 after this change:

    19:05:00.130466808 OTA '0150 line '02 set special character 3 <ff>

WTH?  Masking an unsigned with an appropriately sized mask produces a
sign extension?
2020-06-22 23:54:22 -04:00
Dennis Boone
7d7504a02e Clock resolution sniffer
The cmraw tool shows the approximate minimum time a program could
nanosleep, which tends to be quite a bit bigger than the smallest
value one could pass to nanosleep (one ns).  For example, on my
i7-3770 running linux, the results tend to be between 90 and 120 ns.
2020-06-11 13:22:48 -04:00
Dennis Boone
43d2d863d7 Replace tabs with spaces
Mixed tabs/spaces gets hairy.  I've expanded the tabs on the basis
of 8-position tabstops.
2020-05-30 01:42:39 -04:00
Dennis Boone
80c1a57895 Merge pull request #4 from kej715/master
MDLC/HSSMLC controller emulation to support Bisync and HASP for RJE
2020-05-30 00:17:58 -04:00
Kevin Jordan
d566f42ea6 Eliminate compiler warnings. 2020-05-29 22:46:09 -04:00
Kevin Jordan
af1718f699 Merge branch 'master' of https://github.com/kej715/p50em 2020-05-29 22:38:06 -04:00
Kevin Jordan
91bddd02c3 Add emulation of MDLC/HSSMLC controller to support Bisync protocol and enable a Prime
system to operate as a HASP station in an RJE environment. This implementation is
compatible with Bisync/HASP emulation in the Hercules IBM mainframe emulator and the
DtCyber CDC mainframe emulator.
2020-05-29 12:53:58 -04:00
Dennis Boone
4def8fe397 Support for booting DOS pre-Rev20
PRIMOS2 was built to be relocated after being loaded by BOOT.
The build process rewrote the RVEC in the save file.  The SA was anded
with :160000; the result was subtracted from SA and EA in the RVEC,
and stored into RA in the RVEC.  The BOOT program knew to add the RA
value during the load process.

This change causes the emulator to recognize such an RVEC when booting
an R-mode executable from unix disk, and to adjust the RVEC before
actually loading the program.  This fixes failure to boot *DOS64 from
19.2.9, for example.

The code only makes this adjustment if RA is non-zero, and RP is not
between SA and EA.
2020-05-27 19:06:55 -04:00
Jim Wilcoxson
43bbf3cd8f TCP_KEEPALIVE timer not supported on OSX 2020-05-27 18:49:35 -04:00
Dennis Boone
9c6722ffc5 Set the keepalive idle time at accept time
Set the keepalive idle time per connection, at accept time,
using the setsockopt TCP_KEEPIDLE flag.  This seems to be
reasonably portable.
2020-05-17 00:35:04 -04:00
Dennis Boone
7e8e7236ec Turn on keepalive on accepted amlc telnet connections
On linux, keepalive defaults off.  The default timer on linux
is 7200 seconds.  Do we need to make a note in the docs about
setting /proc/sys/net/ipv4/tcp_keepalive_time?
2020-05-16 22:51:14 -04:00
Dennis Boone
07c7232878 Merge remote-tracking branch 'refs/remotes/origin/master' 2020-05-05 18:40:09 -04:00
Dennis Boone
8749def64f Changes to enable building on Solaris, FreeBSD
Solaris (SmartOS) build needs alternative solutions to two termios
things, plus makefile support for additional link libraries.  PNC
support is untested and is likely _very_ slow, since Solaris doesn't
have O_ASYNC.  The usual suggestion is to rewrite such code to use
poll().

Incidental: utilities makefile needed tabs to make `make` happy
on FreeBSD.  Which make?  Don't remember now.
2020-05-05 18:37:08 -04:00
Dennis Boone
633d61f2de Separate binaries package. 2020-05-05 18:29:03 -04:00
Jim Wilcoxson
ebf5511235 em.c: remove mips benchmark
Now that instpermsec is being adjusted every second instead of every
5 seconds, the initial value of instpermsec is not so critical.  This
benchmark gave wildly varying results on different system and compilers.
2020-05-01 16:26:19 -04:00
Jim Wilcoxson
280634b67e em.c: fix precendence bug caught by compiler
Only affects the display of keys (float exception enabled) on a fatal error.
2020-04-26 12:27:48 -04:00
Jim Wilcoxson
33f748f778 em.c: simpler iget16 and iget16t with same performance
Previously iget16 was used for all instruction fetches, with the
assumption that any could trap.  But actually, only the first word of
the instruction stream can trap in V mode, which is why these
instructions must be short.  Both words of a long R-mode instruction
can trap.

Tested change by booting R19 with and without -DFAST, all other revs
with -DFAST, and basic with trace on in R19 (this executes code from
the DFAC)
2020-04-20 16:14:03 +00:00
Jim Wilcoxson
6693ab36e2 Merge branch 'master' of https://github.com/prirun/p50em 2020-04-19 02:04:48 +00:00
Jim Wilcoxson
b3cada1dbf em.c: add a small benchmark loop to set the initial value of instpermsec
to a reasonable value.  This avoids clock anomolies during startup.
2020-04-19 01:14:15 +00:00
Dennis Boone
1d8476a807 Fix crash executing from registers during boot
In slow mode (compiled without -DFAST), Rev. 19 would crash during
boot.  On investigation, the code was branching to '4 after setting
up an instruction there.  However, instruction fetch found a '0
instead of the expected instruction.

Instructions might be fetched from registers via trap.  Fast iget16()
checks for fault.  Slow iget16() does not, and just calls get16() which
does not check.
2020-04-18 01:48:22 -04:00
Jim Wilcoxson
4a1bfefa4a Minimize clock inaccuracies during system boot
The initial estimate for instructions per millisec (instpermses) was
much too low for modern CPUs, and was also only updated every 5
seconds.  Combined, these were causing the clock to be erratic during
the first 5-15 seconds of system boot.

This was easily noticed by running the MIPS benchmark program right
after a system boot, where instead of delaying 5 seconds, it might
only delay 2 seconds, and then would report a bogus MIPS rating.
Waiting a minute and running it again would give an accurate rating.

Adjusting the instpermsec initial value from 2000 to 40000 and
IPMSTIME time from 5000 ms to 1000 ms (every second vs 5 seconds)
fixes the problem.
2020-04-18 03:11:56 +00:00
20 changed files with 4280 additions and 2649 deletions

15
.editorconfig Normal file
View File

@@ -0,0 +1,15 @@
# .editorconfig, Boone, 02/11/21
# Editor behavior settings
root = true
[*]
charset = utf-8
end_of_line = lf
indent_size = 2
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
[makefile]
indent_style = tab

26
.github/workflows/freebsdbuild.yml vendored Normal file
View File

@@ -0,0 +1,26 @@
name: freebsdbuild
on: workflow_dispatch
jobs:
build:
runs-on: macos-latest
steps:
- uses: "actions/checkout@v2"
- uses: "vmactions/freebsd-vm@v0.1.3"
with:
usesh: true
prepare: pkg install -y curl git
run: |
make
- uses: "marvinpinto/action-automatic-releases@latest"
with:
repo_token: "${{ secrets.GITHUB_TOKEN }}"
automatic_release_tag: "latest"
prerelease: false
title: "Bleeding Edge Binary - FreeBSD"
files: |
LICENSE
README.md
em.1
em

22
.github/workflows/linuxbuild.yml vendored Normal file
View File

@@ -0,0 +1,22 @@
name: linuxbuild
on: workflow_dispatch
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: "actions/checkout@v2"
- name: make
run: make
- uses: "marvinpinto/action-automatic-releases@latest"
with:
repo_token: "${{ secrets.GITHUB_TOKEN }}"
automatic_release_tag: "latest"
prerelease: false
title: "Bleeding Edge Binary - Ubuntu"
files: |
LICENSE
README.md
em.1
em

22
.github/workflows/macbuild.yml vendored Normal file
View File

@@ -0,0 +1,22 @@
name: macbuild
on: workflow_dispatch
jobs:
build:
runs-on: macos-latest
steps:
- uses: "actions/checkout@v2"
- name: make
run: make
- uses: "marvinpinto/action-automatic-releases@latest"
with:
repo_token: "${{ secrets.GITHUB_TOKEN }}"
automatic_release_tag: "latest"
prerelease: false
title: "Bleeding Edge Binary - MacOS"
files: |
LICENSE
README.md
em.1
em

1
.gitignore vendored
View File

@@ -3,6 +3,7 @@
*.log *.log
*.swp *.swp
tags tags
cmraw
cscope.* cscope.*
em em
emlink emlink

View File

@@ -22,6 +22,9 @@ the host processor having a large set of general-purpose registers.
Coming soon, we swear! There is a unix man page included in this Coming soon, we swear! There is a unix man page included in this
repository. repository.
Github discussions are now enabled for this repository, and can be
used to ask for help.
## Public Systems ## Public Systems
There are a set of emulators available for public use. These may be There are a set of emulators available for public use. These may be
@@ -52,7 +55,7 @@ business unit ceased to exist. A reformatted copy is available
A growing collection of Prime and related documentation is available A growing collection of Prime and related documentation is available
at [sysovl.info](https://sysovl.info/reference_prime.html). at [sysovl.info](https://sysovl.info/reference_prime.html).
A howto on installing PRIMOS in the emulator is A howto on installing PRIMOS in the emulator is
[here](https://sysovl.info/reference_prime_drb_installing_primos.html). [here](https://sysovl.info/reference_prime_drb_installoview.html).
Discussion of adapting these instructions to Discussion of adapting these instructions to
22.1.4 has been occurring on the [cctalk mailing 22.1.4 has been occurring on the [cctalk mailing
list](http://classiccmp.org/pipermail/cctalk/2020-March/052126.html). list](http://classiccmp.org/pipermail/cctalk/2020-March/052126.html).
@@ -75,6 +78,19 @@ are available from Bitsavers:
* [Rev 22.1.4 repacked](https://yagi.h-net.org/m2214repack.tar.gz) [use this instead, resaved with Rev. 22 magsav] * [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] * [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 ## Sample System Images
A set of sample system images derived from the public emulators can A set of sample system images derived from the public emulators can

View File

@@ -150,6 +150,8 @@ AMLC status word (from AMLCT5):
*/ */
#include <termio.h>
/* this macro closes an AMLC connection - used in several places /* this macro closes an AMLC connection - used in several places
NOTE: don't print disconnect message on dedicated lines */ NOTE: don't print disconnect message on dedicated lines */
@@ -262,6 +264,7 @@ int devamlc (int class, int func, int device) {
unsigned short ctinterrupt; /* 1 bit per line */ unsigned short ctinterrupt; /* 1 bit per line */
unsigned short dss; /* 1 bit per line */ unsigned short dss; /* 1 bit per line */
unsigned short connected; /* 1 bit per line */ unsigned short connected; /* 1 bit per line */
unsigned short dolineclear; /* 1 bit per line */
unsigned short serial; /* true if any CT_SERIAL lines */ unsigned short serial; /* true if any CT_SERIAL lines */
unsigned short dsstime; /* countdown to dss poll */ unsigned short dsstime; /* countdown to dss poll */
short fd[16]; /* Unix fd, 1 per line */ short fd[16]; /* Unix fd, 1 per line */
@@ -329,6 +332,7 @@ int devamlc (int class, int func, int device) {
for (dx=0; dx<MAXBOARDS; dx++) { for (dx=0; dx<MAXBOARDS; dx++) {
dc[dx].deviceid = 0; dc[dx].deviceid = 0;
dc[dx].connected = 0; dc[dx].connected = 0;
dc[dx].dolineclear = 0;
dc[dx].serial = 0; dc[dx].serial = 0;
for (lx = 0; lx < 16; lx++) { for (lx = 0; lx < 16; lx++) {
dc[dx].fd[lx] = -1; dc[dx].fd[lx] = -1;
@@ -557,7 +561,7 @@ int devamlc (int class, int func, int device) {
if (dc[dx].serial) { /* any serial connections? */ if (dc[dx].serial) { /* any serial connections? */
if (--dc[dx].dsstime == 0) { if (--dc[dx].dsstime == 0) {
dc[dx].dsstime = DSSCOUNTDOWN; dc[dx].dsstime = DSSCOUNTDOWN;
#ifdef __APPLE__ /* #ifdef __APPLE__ */
for (lx = 0; lx < 16; lx++) { /* yes, poll them */ for (lx = 0; lx < 16; lx++) { /* yes, poll them */
if (dc[dx].ctype[lx] == CT_SERIAL) { if (dc[dx].ctype[lx] == CT_SERIAL) {
int modemstate; int modemstate;
@@ -573,7 +577,7 @@ int devamlc (int class, int func, int device) {
} }
} }
} }
#endif /* #endif */
} }
} }
//printf("devamlc: dss for device '%o = 0x%x\n", device, dc[dx].dss); //printf("devamlc: dss for device '%o = 0x%x\n", device, dc[dx].dss);
@@ -650,9 +654,22 @@ int devamlc (int class, int func, int device) {
break; break;
} }
memset(&terminfo, 0, sizeof(terminfo)); 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); cfmakeraw(&terminfo);
#endif
baud = baudtable[(getcrs16(A) >> 6) & 7]; baud = baudtable[(getcrs16(A) >> 6) & 7];
#ifdef __sun__
if ((cfsetispeed(&terminfo, baud) == -1) ||
(cfsetospeed(&terminfo, baud) == -1))
#else
if (cfsetspeed(&terminfo, baud) == -1) if (cfsetspeed(&terminfo, baud) == -1)
#endif
perror("em: unable to set AMLC line speed"); perror("em: unable to set AMLC line speed");
else else
printf("em: AMLC line %d set to %d bps\n", dx*16 + lx, baud); printf("em: AMLC line %d set to %d bps\n", dx*16 + lx, baud);
@@ -743,7 +760,6 @@ int devamlc (int class, int func, int device) {
/* set DTR high (02000) or low if it has changed */ /* set DTR high (02000) or low if it has changed */
#ifdef __APPLE__
if ((getcrs16(A) ^ dc[dx].lconf[lx]) & 02000) { if ((getcrs16(A) ^ dc[dx].lconf[lx]) & 02000) {
int modemstate; int modemstate;
//printf("devamlc: DTR state changed\n"); //printf("devamlc: DTR state changed\n");
@@ -756,7 +772,6 @@ int devamlc (int class, int func, int device) {
} }
ioctl(fd, TIOCMSET, &modemstate); ioctl(fd, TIOCMSET, &modemstate);
} }
#endif
break; break;
} }
@@ -872,6 +887,7 @@ int devamlc (int class, int func, int device) {
close(dc[i].fd[lx]); close(dc[i].fd[lx]);
dc[i].dss |= BITMASK16(lx+1); dc[i].dss |= BITMASK16(lx+1);
dc[i].connected |= BITMASK16(lx+1); dc[i].connected |= BITMASK16(lx+1);
dc[i].dolineclear |= BITMASK16(lx+1);
dc[i].fd[lx] = fd; dc[i].fd[lx] = fd;
dc[i].tstate[lx] = TS_DATA; dc[i].tstate[lx] = TS_DATA;
//printf("em: AMLC connection, fd=%d, device='%o, line=%d\n", fd, dc[i].deviceid, lx); //printf("em: AMLC connection, fd=%d, device='%o, line=%d\n", fd, dc[i].deviceid, lx);
@@ -898,6 +914,16 @@ int devamlc (int class, int func, int device) {
optval = 1; optval = 1;
if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &optval, sizeof(optval)) == -1) if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &optval, sizeof(optval)) == -1)
perror("unable to set TCP_NODELAY"); 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 /* these Telnet commands put the connecting telnet client
into character-at-a-time mode and binary mode. Since into character-at-a-time mode and binary mode. Since
@@ -1117,6 +1143,53 @@ dorecv:
if (dc[dx].deviceid == 0 || dc[dx].connected == 0 || dc[dx].eor) if (dc[dx].deviceid == 0 || dc[dx].connected == 0 || dc[dx].eor)
continue; continue;
/* Inject xon / kill if dolineclear is true */
if (dolinecleararg)
for (lx = 0; lx < 16; lx++)
if (dc[dx].dolineclear & BITMASK16(lx+1))
{
unsigned char ch;
unsigned short utemp;
int dmcpair, lcount;
ea_t dmcea, dmcbufbegea, dmcbufendea;
unsigned short dmcnw;
if (dc[dx].bufnum)
dmcea = dc[dx].dmcchan + 2;
else
dmcea = dc[dx].dmcchan;
dmcpair = get32io(dmcea);
dmcbufbegea = dmcpair>>16;
dmcbufendea = dmcpair & 0xffff;
dmcnw = dmcbufendea - dmcbufbegea + 1;
if (dmcnw < 2)
continue;
utemp = lx<<12 | 0x0200 | 0221; /* dc1/xon */
put16io(utemp, dmcbufbegea);
dmcbufbegea = INCVA(dmcbufbegea, 1);
ch = (unsigned char)get16(MAKEVA(014, 0705));
utemp = lx<<12 | 0x0200 | ch; /* kill */
put16io(utemp, dmcbufbegea);
dmcbufbegea = INCVA(dmcbufbegea, 1);
dc[dx].recvlx = lx;
if (dmcbufbegea-1 > dmcbufendea)
fatal("AMLC tumble table overflowed?");
put16io(dmcbufbegea, dmcea);
if (dmcbufbegea > dmcbufendea) { /* end of range has occurred */
dc[dx].bufnum = 1-dc[dx].bufnum;
dc[dx].eor = 1;
neweor = 1;
anyeor = 1;
}
dc[dx].dolineclear &= ~BITMASK16(lx+1);
}
/* select to see which lines have data to be read */ /* select to see which lines have data to be read */
FD_ZERO(&readfds); FD_ZERO(&readfds);

View File

@@ -406,7 +406,11 @@ void pncinitfd(int fd) {
perror("unable to get ts flags for PNC"); perror("unable to get ts flags for PNC");
fatal(NULL); fatal(NULL);
} }
#ifdef __sun__
fdflags |= O_NONBLOCK;
#else
fdflags |= O_NONBLOCK+O_ASYNC; fdflags |= O_NONBLOCK+O_ASYNC;
#endif
if (fcntl(fd, F_SETFL, fdflags) == -1) { if (fcntl(fd, F_SETFL, fdflags) == -1) {
perror("unable to set fdflags for PNC"); perror("unable to set fdflags for PNC");
fatal(NULL); fatal(NULL);

1292
devsmlc.h Normal file

File diff suppressed because it is too large Load Diff

33
em.1
View File

@@ -2,7 +2,7 @@
.\" em.1, Boone, 03/13/20 .\" em.1, Boone, 03/13/20
.\" Man page for Jim Wilcoxson's Prime 50-Series emulator .\" Man page for Jim Wilcoxson's Prime 50-Series emulator
.\" --------------------------------------------------------------------------- .\" ---------------------------------------------------------------------------
.TH em 1 "2020-03-14" "Jim Wilcoxson" "50-Series Emulator" .TH em 1 "2020-06-23" "Jim Wilcoxson" "50-Series Emulator"
.\" --------------------------------------------------------------------------- .\" ---------------------------------------------------------------------------
.SH NAME .SH NAME
em \- Emulator for Prime 50-Series systems em \- Emulator for Prime 50-Series systems
@@ -35,6 +35,8 @@ a tape controller, 4 drives, using the .TAP format
.IP \(bu .IP \(bu
a PNC controller emulating RingNet over TCP/IP a PNC controller emulating RingNet over TCP/IP
.IP \(bu .IP \(bu
two sync serial controllers supporting up to 8 lines
.IP \(bu
a bypass for Primos system serial number checks a bypass for Primos system serial number checks
.IP \(bu .IP \(bu
Unix utilities to read/write physical tapes & Magsav tapes Unix utilities to read/write physical tapes & Magsav tapes
@@ -143,6 +145,15 @@ on the host. Multiple trace types may be listed, separated by
spaces. Tracing may be initially turned off by including the spaces. Tracing may be initially turned off by including the
.I off .I off
trace type. trace type.
.PP
\fB-dolineclear\fR
.IP
Inject an XON (DC1, 0221) character and a line kill character into the
input buffers each time an AMLC line receives an incoming connection.
The system's configured line kill character is fetched from the
DEFKIL field of FIGCOM, at address 14(0)/705. This behavior helps
counteract lines which have been blocked by internet vulnerability
scanners injecting non-telnet data streams.
.\" --------------------------------------------------------------------------- .\" ---------------------------------------------------------------------------
.SH FILES .SH FILES
.TP .TP
@@ -163,6 +174,18 @@ Example:
8 192.168.10.4:9000 # Outbound socket connection e.g. for spooler 8 192.168.10.4:9000 # Outbound socket connection e.g. for spooler
.EE .EE
.TP .TP
smlc.cfg
Used to associate outbound sync serial (MDLC or HSSMLC) lines with destination
IP addresses and port numbers, or to associate inbound sync serial lines
with port numbers. An outbound line corresponds to an RJE site defined as a
SLAVE site, and an inbound line corresponds to an RJE site defined as a MASTER
site. Optional. Comments work the same as amlc.cfg. Example:
.EX
0 10.1.1.3:2554 # Connect to TCP port 2554 of host 10.1.1.3 for outbound line 0
1 9951 # Listen on TCP port 9951 for inbound line 1
.EE
.TP
console.log console.log
All console output is also written to this file. It is overwritten All console output is also written to this file. It is overwritten
at each invocation. Created by at each invocation. Created by
@@ -274,9 +297,12 @@ off|Start with tracing disabled
all|Everything all|Everything
flush|Flush trace file after each write flush|Flush trace file after each write
tlb|STLB and IOTLB changes tlb|STLB and IOTLB changes
smlc|MDLC/HSSMLC device I/O
OWNERL|Execution of this PCB OWNERL|Execution of this PCB
instruction count|Begin after specified number of instructions #instruction count|Begin after specified number of instructions
|(the leading # is literal)
octal segno|Execution in the given segment number octal segno|Execution in the given segment number
|(may interact poorly with "off")
process number|Execution of this user number process number|Execution of this user number
.TE .TE
.\" --------------------------------------------------------------------------- .\" ---------------------------------------------------------------------------
@@ -360,7 +386,8 @@ CMD,1/3/5+1,Cartridge Module 32/64/96 MB
.\" --------------------------------------------------------------------------- .\" ---------------------------------------------------------------------------
.SH AUTHOR .SH AUTHOR
.PP .PP
This emulator was written by Jim Wilcoxson. Man page by Dennis Boone. This emulator was written by Jim Wilcoxson. MDLC/HSSMLC support by
Kevin Jordan. Man page by Dennis Boone.
.\" --------------------------------------------------------------------------- .\" ---------------------------------------------------------------------------
.SH SEE ALSO .SH SEE ALSO
This project is hosted at This project is hosted at

621
em.c
View File

@@ -1,5 +1,5 @@
/* Pr1me Computer emulator, Jim Wilcoxson (prirun@gmail.com), April 4, 2005 /* Pr1me Computer emulator, Jim Wilcoxson (prirun@gmail.com), April 4, 2005
Copyright (C) 2005-2019, Jim Wilcoxson. All Rights Reserved. Copyright (C) 2005-2021, Jim Wilcoxson. All Rights Reserved.
Emulates a Prime Computer system by: Emulates a Prime Computer system by:
- booting from a Prime disk image (normal usage) - booting from a Prime disk image (normal usage)
@@ -132,253 +132,6 @@ gv.tracetriggered = 1; this simulates the Ctrl-t.
#include <sys/file.h> #include <sys/file.h>
#include <glob.h> #include <glob.h>
#define IOTLBENTS 64*4
#define STLBENTS 512
typedef unsigned int ea_t; /* effective address */
typedef unsigned int pa_t; /* physical address */
/* STLB cache structure is defined here; the actual stlb is in gv.
There are several different styles on Prime models. This is
modeled after the 6350 STLB, but is only 1-way associative.
The hit rate has been measured to be over 99.8% with a single
user.
Instead of using a valid/invalid bit, a segment number of 0xFFFF
(note that the fault, ring, and E bits are set) means "invalid",
since it won't match any 12-bit segment number. This eliminates
testing the valid bit in mapva.
As a speed-up, the unmodified bit is stored in access[2], where
Ring 2 access should be kept. This makes the structure exactly 16
bytes, so indexing into the table can use a left shift 4
instruction rather than a multiply by 20. It saves a multiply in
the fast path of a _very_ speed-critical routine (mapva).
*/
#define STLB_UNMODIFIED_BIT 2 /* stored in access[2] */
typedef struct {
unsigned int pmaddr; /* Prime phys addr of page table flag word */
unsigned int ppa; /* physical page address (PPN << 10) */
unsigned short procid; /* process id for segments >= '4000 */
short seg; /* segment number (0-0xFFF), 0xFFFF = invalid */
char access[4]; /* ring n access rights */
//char unmodified; /* 1 = page not modified, 0 = modified */
//char shared; /* 1 if page is shared and can't be cached */
//char valid; /* 1 if STLB entry is valid, zero otherwise */
#ifndef NOTRACE
unsigned int load_ic; /* instruction where STLB was loaded, (debug) */
#endif
} stlbe_t;
/* The IOTLB stores translations for each page of the I/O segments 0-3 */
typedef struct {
unsigned int ppa; /* physical page address (PPN << 10) */
char valid; /* 1 if IOTLB entry is valid, zero otherwise */
} iotlbe_t;
/* the emulator uses a special, very small address translation cache
to hold the virtual page address and corresponding MEM[] pointer
for a few special pages:
- the 4 base registers (PBBR, SBBR, LBBR, XBBR)
- the current instruction page (RPBR)
- sector zero (S0BR) of the current procedure
- field address registers 0 and 1 (F0BR, F1BR)
- "unknown", used for indirect references (UNBR)
When an effective address is calculated, eap is set to point to one
of these cache entries. If the EA, which is a virtual address,
references the same page as the cache entry pointed to by eap, then
the mapva call can be bypassed and the physical memory address
calculated using the cache. If the cache can't be used, either
because the entry is invalid, points to a different virtual page,
or the access isn't allowed (only for writes), mapva is called to
do the mapping and (assuming it doesn't fault) the eap cache entry
is updated.
This special cache is invalidated when the STLB is changed, a ring
change occurs, on process exchange, and on LPSW. For reads, no
access checking is needed when the cache is used: the cache entry
is either valid, meaning that the program has at least read access
to the page, or the cache entry is invalid (special value of
0x000000FF, which is not a virtual page address - the right 10 bits
must be zero for the start of a page), in which case mapva is
called.
The special cache can also be used for write references. Bits 2-4
of the cache entry vpn are the access bits from mapva, so bit 4
being 1 means that writes are allowed.
IMPORTANT NOTE: bit 4 MUST BE CHECKED before all write references!
*/
#define PBBR 0
#define SBBR 1
#define LBBR 2
#define XBBR 3
#define RPBR 4
#define S0BR 5
#define F0BR 6
#define F1BR 7
#define UNBR 8
#define BRP_SIZE 9
/* NOTE: vpn is a segment number/word offset Prime virtual address
corresponding to the physical page of memory in memp. The high-
order fault bit of vpn is never set. The ring and extension bits
are used to store the 3-bit access field from the STLB for the
current ring.
All brp entries are invalidated by PCL and PRTN when the ring
changes, ITLB, PTLB, and LIOT (STLB changes), when OWNERL changes
(process exchange), and on LPSW.
Storing the access bits in the vpn allows use of of the brp cache
for write accesses as well as read accesses. Only bit 4 (write
allowed) is used, but since a 3-bit value comes back from mapva,
it's easier just to put it all in vpn.
IMPORTANT NOTE: the "get" calls do not store the access field in
the brp cache entry; the first write to a page must clear the "page
unmodified" bit in the Primos page maps, and mapva is the only way
to do this. So the first "put" has to go through mapva. An
alternative would be to store the page map pointer in brp and use
bit 3 of vpn as a flag for whether the page map has to be modified,
but this is more work for probably little gain.
*/
typedef struct {
unsigned short *memp; /* MEM[] physical page address */
ea_t vpn; /* corresponding virtual page address */
} brp_t;
/* "gv" is a static structure used to hold "hot" global variables. These
are pointed to by a dedicated register, so that the usual PowerPC global
variable instructions aren't needed: these variables can be directly
referenced by instructions as long as the structure is < 16K bytes. */
typedef struct {
int memlimit; /* user's desired memory limit (-mem) */
int intvec; /* currently raised interrupt (if >= zero) */
unsigned int instcount; /* global instruction count */
brp_t brp[BRP_SIZE]; /* PB, SB, LB, XB, RP, S0, F0, F1, UN */
unsigned short inhcount; /* number of instructions to stay inhibited */
unsigned int instpermsec; /* instructions executed per millisecond */
stlbe_t stlb[STLBENTS]; /* the STLB: Segmentation Translation Lookaside Buffer */
iotlbe_t iotlb[IOTLBENTS]; /* the IOTLB: I/O Translation Lookaside Buffer */
unsigned int prevpc; /* backed program counter */
unsigned short amask; /* address mask */
int pmap32bits; /* true if 32-bit page maps */
int pmap32mask; /* mask for 32-bit page maps */
int csoffset; /* concealed stack segment offset */
unsigned int livereglim; /* 010 if seg enabled, 040 if disabled */
int mapvacalls; /* # of mapva calls */
int mapvamisses; /* STLB misses */
int supercalls; /* brp supercache hits */
int supermisses; /* brp supercache misses */
void *disp_rmr[128]; /* R-mode memory ref dispatch table */
void *disp_vmr[128]; /* V-mode memory ref dispatch table */
#ifndef NOTRACE
/* traceflags is the variable used to test tracing of each instruction
traceuser is the user number to trace, 0 meaning any user
traceseg is the procedure segment number to trace, 0 meaning any
savetraceflags hold the real traceflags, while "traceflags" switches
on and off for each instruction
TRACEUSER is a macro that is true if the current user is being traced
*/
#define MAXRPQ 8
FILE *tracefile; /* trace.log file */
int traceflags; /* each bit is a trace flag */
int savetraceflags;
unsigned short traceuser; /* OWNERL to trace */
short traceseg; /* RPH segment # to trace */
short numtraceprocs; /* # of procedures we're tracing */
unsigned int traceinstcount; /* only trace if instcount > this */
short tracetriggered; /* Ctrl-T on console toggles tracing */
short tracerpqx; /* rpq index to store next RP */
unsigned int tracerpq[MAXRPQ];/* last 16 locations executed */
#endif
} gv_t;
static gv_t gv;
/* trace flags to control aspects of emulator tracing:
T_EAR trace R-mode effective address calculation
T_EAV trace V-mode effective address calculation
T_EAI trace I-mode effective address calculation
T_FLOW instruction summary
T_INST detailed instruction trace
T_MODE trace CPU mode changes
T_EAAP AP effective address calculation
T_DIO disk I/O
T_TIO tape I/O
T_RIO ring network I/O
T_TERM terminal output (tnou[a])
T_MAP segmentation
T_PCL PCL instructions
T_FAULT Faults
T_PX Process exchange
T_LM License manager
T_TLB STLB and IOTLB changes
T_REGS Puts into registers
*/
#define T_EAR 0x00000001
#define T_EAV 0x00000002
#define T_EAI 0x00000004
#define T_INST 0x00000008
#define T_FLOW 0x00000010
#define T_MODE 0x00000020
#define T_EAAP 0x00000040
#define T_DIO 0x00000080
#define T_MAP 0x00000100
#define T_PCL 0x00000200
#define T_FAULT 0x00000400
#define T_PX 0x00000800
#define T_TIO 0x00001000
#define T_TERM 0x00002000
#define T_RIO 0x00004000
#define T_LM 0x00008000
#define T_PUT 0x00010000
#define T_GET 0x00020000
#define T_EAS 0x00040000
#define T_TLB 0x00080000
#define T_REGS 0x00100000
#ifdef NOTRACE
#define TRACE(flags, formatargs...)
#define TRACEA(formatargs...)
#else
#define TRACE(flags, formatargs...) if (gv.traceflags & (flags)) fprintf(gv.tracefile,formatargs)
#define TRACEA(formatargs...) fprintf(gv.tracefile,formatargs)
#endif
/* In SR modes, Prime CPU registers are mapped to memory locations /* In SR modes, Prime CPU registers are mapped to memory locations
0-'37, but only 0-7 are user accessible. In the post-P300 0-'37, but only 0-7 are user accessible. In the post-P300
architecture, these addresses map to the live register file. architecture, these addresses map to the live register file.
@@ -394,6 +147,8 @@ static gv_t gv;
*/ */
#include "regs.h" #include "regs.h"
typedef unsigned int ea_t; /* effective address */
typedef unsigned int pa_t; /* physical address */
/* procs needing forward declarations */ /* procs needing forward declarations */
@@ -513,9 +268,63 @@ static void macheck (unsigned short p300vec, unsigned short chkvec, unsigned int
#define RESTRICTR(rpring) if ((rpring) & RINGMASK32) fault(RESTRICTFAULT, 0, 0); #define RESTRICTR(rpring) if ((rpring) & RINGMASK32) fault(RESTRICTFAULT, 0, 0);
/* trace flags to control aspects of emulator tracing:
T_EAR trace R-mode effective address calculation
T_EAV trace V-mode effective address calculation
T_EAI trace I-mode effective address calculation
T_FLOW instruction summary
T_FP floating point
T_INST detailed instruction trace
T_MODE trace CPU mode changes
T_EAAP AP effective address calculation
T_DIO disk I/O
T_TIO tape I/O
T_RIO ring network I/O
T_TERM terminal output (tnou[a])
T_MAP segmentation
T_PCL PCL instructions
T_FAULT Faults
T_PX Process exchange
T_LM License manager
T_TLB STLB and IOTLB changes
T_SMLC SMLC device I/O
*/
#define T_EAR 0x00000001
#define T_EAV 0x00000002
#define T_EAI 0x00000004
#define T_INST 0x00000008
#define T_FLOW 0x00000010
#define T_MODE 0x00000020
#define T_EAAP 0x00000040
#define T_DIO 0x00000080
#define T_MAP 0x00000100
#define T_PCL 0x00000200
#define T_FAULT 0x00000400
#define T_PX 0x00000800
#define T_TIO 0x00001000
#define T_TERM 0x00002000
#define T_RIO 0x00004000
#define T_LM 0x00008000
#define T_PUT 0x00010000
#define T_GET 0x00020000
#define T_EAS 0x00040000
#define T_TLB 0x00080000
#define T_SMLC 0x00100000
#define T_FP 0x00200000
#define BITMASK16(b) (0x8000 >> ((b)-1)) #define BITMASK16(b) (0x8000 >> ((b)-1))
#define BITMASK32(b) ((unsigned int)(0x80000000) >> ((b)-1)) #define BITMASK32(b) ((unsigned int)(0x80000000) >> ((b)-1))
#ifdef NOTRACE
#define TRACE(flags, formatargs...)
#define TRACEA(formatargs...)
#else
#define TRACE(flags, formatargs...) if (gv.traceflags & (flags)) fprintf(gv.tracefile,formatargs)
#define TRACEA(formatargs...) fprintf(gv.tracefile,formatargs)
#endif
/* traceprocs is an array of (operating system) procedure names we're /* traceprocs is an array of (operating system) procedure names we're
tracing, with flags and associated data tracing, with flags and associated data
@@ -549,10 +358,130 @@ static unsigned short firstbdx = 0; /* backstop sleep flag */
static unsigned short cpuid = 15; /* STPM CPU model, set with -cpuid */ static unsigned short cpuid = 15; /* STPM CPU model, set with -cpuid */
/* STLB cache structure is defined here; the actual stlb is in gv.
There are several different styles on Prime models. This is
modeled after the 6350 STLB, but is only 1-way associative.
The hit rate has been measured to be over 99.8% with a single
user.
Instead of using a valid/invalid bit, a segment number of 0xFFFF
(note that the fault, ring, and E bits are set) means "invalid",
since it won't match any 12-bit segment number. This eliminates
testing the valid bit in mapva.
As a speed-up, the unmodified bit is stored in access[2], where
Ring 2 access should be kept. This makes the structure exactly 16
bytes, so indexing into the table can use a left shift 4
instruction rather than a multiply by 20. It saves a multiply in
the fast path of a _very_ speed-critical routine (mapva).
*/
#define STLBENTS 512
#define STLB_UNMODIFIED_BIT 2 /* stored in access[2] */
typedef struct {
unsigned int pmaddr; /* Prime phys addr of page table flag word */
unsigned int ppa; /* physical page address (PPN << 10) */
unsigned short procid; /* process id for segments >= '4000 */
short seg; /* segment number (0-0xFFF), 0xFFFF = invalid */
char access[4]; /* ring n access rights */
//char unmodified; /* 1 = page not modified, 0 = modified */
//char shared; /* 1 if page is shared and can't be cached */
//char valid; /* 1 if STLB entry is valid, zero otherwise */
#ifndef NOTRACE
unsigned int load_ic; /* instruction where STLB was loaded, (debug) */
#endif
} stlbe_t;
/* The IOTLB stores translations for each page of the I/O segments 0-3 */
#define IOTLBENTS 64*4
typedef struct {
unsigned int ppa; /* physical page address (PPN << 10) */
char valid; /* 1 if IOTLB entry is valid, zero otherwise */
} iotlbe_t;
/* maximum indirect levels is 8 according to T&M CPUT4 */ /* maximum indirect levels is 8 according to T&M CPUT4 */
#define INDLEVELS 8 #define INDLEVELS 8
/* the emulator uses a special, very small address translation cache
to hold the virtual page address and corresponding MEM[] pointer
for a few special pages:
- the 4 base registers (PBBR, SBBR, LBBR, XBBR)
- the current instruction page (RPBR)
- sector zero (S0BR) of the current procedure
- field address registers 0 and 1 (F0BR, F1BR)
- "unknown", used for indirect references (UNBR)
When an effective address is calculated, eap is set to point to one
of these cache entries. If the EA, which is a virtual address,
references the same page as the cache entry pointed to by eap, then
the mapva call can be bypassed and the physical memory address
calculated using the cache. If the cache can't be used, either
because the entry is invalid, points to a different virtual page,
or the access isn't allowed (only for writes), mapva is called to
do the mapping and (assuming it doesn't fault) the eap cache entry
is updated.
This special cache is invalidated when the STLB is changed, a ring
change occurs, on process exchange, and on LPSW. For reads, no
access checking is needed when the cache is used: the cache entry
is either valid, meaning that the program has at least read access
to the page, or the cache entry is invalid (special value of
0x000000FF, which is not a virtual page address - the right 10 bits
must be zero for the start of a page), in which case mapva is
called.
The special cache can also be used for write references. Bits 2-4
of the cache entry vpn are the access bits from mapva, so bit 4
being 1 means that writes are allowed.
IMPORTANT NOTE: bit 4 MUST BE CHECKED before all write references!
*/
#define PBBR 0
#define SBBR 1
#define LBBR 2
#define XBBR 3
#define RPBR 4
#define S0BR 5
#define F0BR 6
#define F1BR 7
#define UNBR 8
#define BRP_SIZE 9
/* NOTE: vpn is a segment number/word offset Prime virtual address
corresponding to the physical page of memory in memp. The high-
order fault bit of vpn is never set. The ring and extension bits
are used to store the 3-bit access field from the STLB for the
current ring.
All brp entries are invalidated by PCL and PRTN when the ring
changes, ITLB, PTLB, and LIOT (STLB changes), when OWNERL changes
(process exchange), and on LPSW.
Storing the access bits in the vpn allows use of of the brp cache
for write accesses as well as read accesses. Only bit 4 (write
allowed) is used, but since a 3-bit value comes back from mapva,
it's easier just to put it all in vpn.
IMPORTANT NOTE: the "get" calls do not store the access field in
the brp cache entry; the first write to a page must clear the "page
unmodified" bit in the Primos page maps, and mapva is the only way
to do this. So the first "put" has to go through mapva. An
alternative would be to store the page map pointer in brp and use
bit 3 of vpn as a flag for whether the page map has to be modified,
but this is more work for probably little gain.
*/
typedef struct {
unsigned short *memp; /* MEM[] physical page address */
ea_t vpn; /* corresponding virtual page address */
} brp_t;
/* the dispatch table for generic instructions: /* the dispatch table for generic instructions:
- bits 1-2 are the class (0-3) - bits 1-2 are the class (0-3)
- bits 3-6 are always zero - bits 3-6 are always zero
@@ -561,6 +490,78 @@ static unsigned short cpuid = 15; /* STPM CPU model, set with -cpuid */
void *disp_gen[4096]; /* generic dispatch table */ void *disp_gen[4096]; /* generic dispatch table */
/* "gv" is a static structure used to hold "hot" global variables. These
are pointed to by a dedicated register, so that the usual PowerPC global
variable instructions aren't needed: these variables can be directly
referenced by instructions as long as the structure is < 16K bytes. */
typedef struct {
int memlimit; /* user's desired memory limit (-mem) */
int intvec; /* currently raised interrupt (if >= zero) */
unsigned int instcount; /* global instruction count */
brp_t brp[BRP_SIZE]; /* PB, SB, LB, XB, RP, S0, F0, F1, UN */
unsigned short inhcount; /* number of instructions to stay inhibited */
unsigned int instpermsec; /* instructions executed per millisecond */
stlbe_t stlb[STLBENTS]; /* the STLB: Segmentation Translation Lookaside Buffer */
iotlbe_t iotlb[IOTLBENTS]; /* the IOTLB: I/O Translation Lookaside Buffer */
unsigned int prevpc; /* backed program counter */
unsigned short amask; /* address mask */
int pmap32bits; /* true if 32-bit page maps */
int pmap32mask; /* mask for 32-bit page maps */
int csoffset; /* concealed stack segment offset */
unsigned int livereglim; /* 010 if seg enabled, 040 if disabled */
int mapvacalls; /* # of mapva calls */
int mapvamisses; /* STLB misses */
int supercalls; /* brp supercache hits */
int supermisses; /* brp supercache misses */
void *disp_rmr[128]; /* R-mode memory ref dispatch table */
void *disp_vmr[128]; /* V-mode memory ref dispatch table */
#ifndef NOTRACE
/* traceflags is the variable used to test tracing of each instruction
traceuser is the user number to trace, 0 meaning any user
traceseg is the procedure segment number to trace, 0 meaning any
savetraceflags hold the real traceflags, while "traceflags" switches
on and off for each instruction
TRACEUSER is a macro that is true if the current user is being traced
*/
#define MAXRPQ 8
FILE *tracefile; /* trace.log file */
int traceflags; /* each bit is a trace flag */
int savetraceflags;
unsigned short traceuser; /* OWNERL to trace */
short traceseg; /* RPH segment # to trace */
short numtraceprocs; /* # of procedures we're tracing */
unsigned int traceinstcount; /* only trace if instcount > this */
short tracetriggered; /* Ctrl-T on console toggles tracing */
short tracerpqx; /* rpq index to store next RP */
unsigned int tracerpq[MAXRPQ];/* last 16 locations executed */
#endif
} gv_t;
static gv_t gv;
brp_t *eap; brp_t *eap;
static jmp_buf jmpbuf; /* for longjumps to the fetch loop */ static jmp_buf jmpbuf; /* for longjumps to the fetch loop */
@@ -658,7 +659,9 @@ static unsigned short *physmem = NULL; /* system's physical memory */
#define LASTFAULT 077 #define LASTFAULT 077
//static ea_t tnoua_ea=0, tnou_ea=0, tsrc_ea=0; //static ea_t tnoua_ea=0, tnou_ea=0, tsrc_ea=0;
static int noclock; /* -noclock arg */
static int domemdump; /* -memdump arg */ static int domemdump; /* -memdump arg */
static int dolinecleararg; /* -dolineclear arg */
static int tport; /* -tport option (incoming terminals) */ static int tport; /* -tport option (incoming terminals) */
static int nport; /* -nport option (PNC/Ringnet) */ static int nport; /* -nport option (PNC/Ringnet) */
@@ -1129,15 +1132,9 @@ static inline unsigned short get16(ea_t ea) {
static unsigned short get16trap(ea_t ea) { static unsigned short get16trap(ea_t ea) {
unsigned short zzz;
ea = ea & 0xFFFF; ea = ea & 0xFFFF;
if (ea < 7) if (ea < 7)
{ return getcrs16(memtocrs[ea]);
zzz = getcrs16(memtocrs[ea]);
TRACE(T_REGS, " get16trap: ea %012o contents %06o\n", ea, zzz);
return zzz;
}
if (ea == 7) /* PC */ if (ea == 7) /* PC */
return RPL; return RPL;
RESTRICTR(RP); RESTRICTR(RP);
@@ -1309,32 +1306,16 @@ static long long get64r(ea_t ea, ea_t rpring) {
page instead of on every 16-bit fetch from the instruction stream. page instead of on every 16-bit fetch from the instruction stream.
*/ */
#ifdef FAST static inline unsigned short iget16t(ea_t ea) {
eap = &gv.brp[RPBR];
unsigned short iget16t(ea_t ea) { return get16t(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 iget16(ea_t ea) { static inline unsigned short iget16(ea_t ea) {
eap = &gv.brp[RPBR];
if ((ea & 0x8FFFFC00) == (gv.brp[RPBR].vpn & 0x0FFFFFFF)) return get16(ea);
return swap16(gv.brp[RPBR].memp[ea & 0x3FF]);
else
return iget16t(ea);
} }
#else
#define iget16(ea) get16((ea))
#define iget16t(ea) get16t((ea))
#endif
static inline void put16(unsigned short value, ea_t ea) { static inline void put16(unsigned short value, ea_t ea) {
unsigned short access; unsigned short access;
@@ -1714,6 +1695,8 @@ static int devpoll[64] = {0};
'35 = devamlc: 4th AMLC (16 lines) '35 = devamlc: 4th AMLC (16 lines)
'45 = devdisk: 7th disk controller (8 drives) '45 = devdisk: 7th disk controller (8 drives)
'46 = devdisk: 8th disk controller (8 drives) '46 = devdisk: 8th disk controller (8 drives)
'50 = devsmlc: 1st HSSMLC/MDLC (4 lines)
'51 = devsmlc: 2nd HSSMLC/MDLC (4 lines)
'52 = devamlc: 3rd AMLC (16 lines) '52 = devamlc: 3rd AMLC (16 lines)
'53 = devamlc: 2nd AMLC (16 lines) '53 = devamlc: 2nd AMLC (16 lines)
'54 = devamlc: 1st AMLC (16 lines) '54 = devamlc: 1st AMLC (16 lines)
@@ -1725,7 +1708,7 @@ static int (*devmap[64])(int, int, int) = {
/* '2x */ devcp,devnone,devdisk,devdisk,devdisk,devdisk,devdisk,devdisk, /* '2x */ devcp,devnone,devdisk,devdisk,devdisk,devdisk,devdisk,devdisk,
/* '3x */ devnone,devnone,devamlc,devnone,devnone,devamlc,devnone,devnone, /* '3x */ devnone,devnone,devamlc,devnone,devnone,devamlc,devnone,devnone,
/* '4x */ devnone,devnone,devnone,devnone,devnone,devdisk,devdisk,devnone, /* '4x */ devnone,devnone,devnone,devnone,devnone,devdisk,devdisk,devnone,
/* '5x */ devnone,devnone,devamlc,devamlc,devamlc,devnone,devnone,devnone, /* '5x */ devsmlc,devsmlc,devamlc,devamlc,devamlc,devnone,devnone,devnone,
/* '6x */ devnone,devnone,devnone,devnone,devnone,devnone,devnone,devnone, /* '6x */ devnone,devnone,devnone,devnone,devnone,devnone,devnone,devnone,
/* '7x */ devnone,devnone,devnone,devnone,devnone,devnone,devnone,devnone}; /* '7x */ devnone,devnone,devnone,devnone,devnone,devnone,devnone,devnone};
@@ -1739,6 +1722,7 @@ static int (*devmap[64])(int, int, int) = {
'20 = devcp: clock / VCP / SOC '20 = devcp: clock / VCP / SOC
'26 = devdisk: 1st disk controller (8 drives) '26 = devdisk: 1st disk controller (8 drives)
'27 = devdisk: 2nd disk controller (8 drives) '27 = devdisk: 2nd disk controller (8 drives)
'50 = devsmlc: 1st HSSMLC/MDLC (4 lines)
'54 = 1st amlc (terminal) controller (16 lines) '54 = 1st amlc (terminal) controller (16 lines)
'53 = devamlc: 2nd AMLC (16 lines) '53 = devamlc: 2nd AMLC (16 lines)
*/ */
@@ -1749,7 +1733,7 @@ static int (*devmap[64])(int, int, int) = {
/* '2x */ devcp,devnone,devnone,devnone,devnone,devnone,devdisk,devdisk, /* '2x */ devcp,devnone,devnone,devnone,devnone,devnone,devdisk,devdisk,
/* '3x */ devnone,devnone,devnone,devnone,devnone,devnone,devnone,devnone, /* '3x */ devnone,devnone,devnone,devnone,devnone,devnone,devnone,devnone,
/* '4x */ devnone,devnone,devnone,devnone,devnone,devnone,devnone,devnone, /* '4x */ devnone,devnone,devnone,devnone,devnone,devnone,devnone,devnone,
/* '5x */ devnone,devnone,devnone,devamlc,devamlc,devnone,devnone,devnone, /* '5x */ devsmlc,devnone,devnone,devamlc,devamlc,devnone,devnone,devnone,
/* '6x */ devnone,devnone,devnone,devnone,devnone,devnone,devnone,devnone, /* '6x */ devnone,devnone,devnone,devnone,devnone,devnone,devnone,devnone,
/* '7x */ devnone,devnone,devnone,devnone,devnone,devnone,devnone,devnone}; /* '7x */ devnone,devnone,devnone,devnone,devnone,devnone,devnone,devnone};
#endif #endif
@@ -1788,7 +1772,7 @@ char *keystring(unsigned short keys) {
*sp++ = 'L'; *sp++ = 'L';
memcpy(sp, modes[(keys>>10) & 7], 3); memcpy(sp, modes[(keys>>10) & 7], 3);
sp += 3; sp += 3;
if (keys & 01000 == 0) /* float exception enabled */ if (!(keys & 01000)) /* float exception enabled */
*sp++ = 'F'; *sp++ = 'F';
if (keys & 0400) /* int exception enabled */ if (keys & 0400) /* int exception enabled */
*sp++ = 'I'; *sp++ = 'I';
@@ -2264,7 +2248,7 @@ special:
#endif #endif
if (class < 2) { /* class 0/1 */ if (class < 2) { /* class 0/1 */
ea = iget16(RP); /* get A from next word */ ea = iget16t(RP); /* get A from next word */
INCRP; INCRP;
TRACE(T_EAR, " Class %d, new ea=%o\n", class, ea); TRACE(T_EAR, " Class %d, new ea=%o\n", class, ea);
if (class == 1) if (class == 1)
@@ -2293,7 +2277,7 @@ special:
} else if (i && x) { /* class 2/3, ix=11 */ } else if (i && x) { /* class 2/3, ix=11 */
TRACE(T_EAR, " class 2/3, ix=11\n"); 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; INCRP;
TRACE(T_EAR, " ea=%o\n", ea); TRACE(T_EAR, " ea=%o\n", ea);
if (class == 3) { if (class == 3) {
@@ -2371,7 +2355,7 @@ special:
static ea_t apea(unsigned short *bitarg) { static ea_t apea(unsigned short *bitarg) {
unsigned short ibr, ea_s, ea_w, bit, br, a; unsigned short ibr, ea_s, ea_w, bit, br, a;
unsigned int utempl; unsigned int utempl;
ea_t ea, ip; ea_t ea, ip, iwea;
eap = &gv.brp[RPBR]; eap = &gv.brp[RPBR];
utempl = get32(RP); utempl = get32(RP);
@@ -2397,8 +2381,11 @@ static ea_t apea(unsigned short *bitarg) {
bit = get16(INCVA(ea,2)) >> 12; bit = get16(INCVA(ea,2)) >> 12;
else else
bit = 0; bit = 0;
iwea = ea;
ea = ip; ea = ip;
TRACE(T_EAAP, " After indirect, AP ea = %o/%o, bit=%d %s\n", ea>>16, ea & 0xFFFF, bit, searchloadmap(ea,' ')); TRACE(T_EAAP, " After indirect, AP ea = %o/%o, bit=%d %s\n", ea>>16, ea & 0xFFFF, bit, searchloadmap(ea,' '));
if (ea & 0x80000000)
fault(POINTERFAULT, ea>>16, iwea);
} }
if (bitarg != NULL) if (bitarg != NULL)
*bitarg = bit; *bitarg = bit;
@@ -4388,7 +4375,7 @@ int main (int argc, char **argv) {
#define XRBRACE 0375 #define XRBRACE 0375
printf("[Prime Emulator ver %s %s]\n", REV, __DATE__); printf("[Prime Emulator ver %s %s]\n", REV, __DATE__);
printf("[Copyright (C) 2005-2019 Jim Wilcoxson prirun@gmail.com]\n"); printf("[Copyright (C) 2005-2021 Jim Wilcoxson prirun@gmail.com]\n");
if (argc > 1 && (strcmp(argv[1],"--version") == 0)) { if (argc > 1 && (strcmp(argv[1],"--version") == 0)) {
exit(0); exit(0);
} }
@@ -4417,12 +4404,16 @@ int main (int argc, char **argv) {
} }
setvbuf(stderr, NULL, _IONBF, 0); 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.intvec = -1; gv.intvec = -1;
gv.instcount = 0; gv.instcount = 0;
gv.inhcount = 0; gv.inhcount = 0;
gv.instpermsec = 2000; gv.instpermsec = 40000;
gv.livereglim = 040; gv.livereglim = 040;
gv.mapvacalls = 0; gv.mapvacalls = 0;
gv.mapvamisses = 0; gv.mapvamisses = 0;
@@ -4466,7 +4457,9 @@ int main (int argc, char **argv) {
#include "dispatch.h" #include "dispatch.h"
noclock = 0;
domemdump = 0; domemdump = 0;
dolinecleararg = 0;
bootarg = NULL; bootarg = NULL;
bootfile[0] = 0; bootfile[0] = 0;
gv.pmap32bits = 0; gv.pmap32bits = 0;
@@ -4483,9 +4476,15 @@ int main (int argc, char **argv) {
while (i+1 < argc && argv[i+1][0] != '-') while (i+1 < argc && argv[i+1][0] != '-')
readloadmap(argv[++i], 1); readloadmap(argv[++i], 1);
} else if (strcmp(argv[i],"-noclock") == 0) {
noclock = 1;
} else if (strcmp(argv[i],"-memdump") == 0) { } else if (strcmp(argv[i],"-memdump") == 0) {
domemdump = 1; domemdump = 1;
} else if (strcmp(argv[i],"-dolineclear") == 0) {
dolinecleararg = 1;
} else if (strcmp(argv[i],"-ss") == 0) { } else if (strcmp(argv[i],"-ss") == 0) {
if (i+1 < argc && argv[i+1][0] != '-') { if (i+1 < argc && argv[i+1][0] != '-') {
sscanf(argv[++i],"%o", &templ); sscanf(argv[++i],"%o", &templ);
@@ -4624,16 +4623,18 @@ int main (int argc, char **argv) {
setlinebuf(gv.tracefile); setlinebuf(gv.tracefile);
else if (strcmp(argv[i],"tlb") == 0) else if (strcmp(argv[i],"tlb") == 0)
gv.traceflags |= T_TLB; gv.traceflags |= T_TLB;
else if (strcmp(argv[i],"regs") == 0) else if (strcmp(argv[i],"smlc") == 0)
gv.traceflags |= T_REGS; gv.traceflags |= T_SMLC;
else if (strcmp(argv[i],"fp") == 0)
gv.traceflags |= T_FP;
else if (isdigit(argv[i][0]) && strlen(argv[i]) <= 3 && sscanf(argv[i],"%d", &templ) == 1) else if (isdigit(argv[i][0]) && strlen(argv[i]) <= 3 && sscanf(argv[i],"%d", &templ) == 1)
gv.traceuser = 0100000 | (templ<<6); /* form OWNERL for user # */ gv.traceuser = 0100000 | (templ<<6); /* form OWNERL for user # */
else if (isdigit(argv[i][0]) && sscanf(argv[i],"%d", &templ) == 1)
gv.traceinstcount = templ;
else if (strlen(argv[i]) == 6 && sscanf(argv[i],"%o", &templ) == 1) else if (strlen(argv[i]) == 6 && sscanf(argv[i],"%o", &templ) == 1)
gv.traceuser = templ; /* specify OWNERL directly */ gv.traceuser = templ; /* specify OWNERL directly */
else if (strlen(argv[i]) == 4 && sscanf(argv[i],"%o", &templ) == 1) else if (strlen(argv[i]) == 4 && sscanf(argv[i],"%o", &templ) == 1)
gv.traceseg = templ; /* specify RPH segno */ gv.traceseg = templ; /* specify RPH segno */
else if (argv[i][0] == '#' && sscanf(argv[i]+1,"%d", &templ) == 1)
gv.traceinstcount = templ;
else if (strlen(argv[i]) <= 8 && argv[i][0] != '-') { else if (strlen(argv[i]) <= 8 && argv[i][0] != '-') {
if (gv.numtraceprocs >= MAXTRACEPROCS) if (gv.numtraceprocs >= MAXTRACEPROCS)
fprintf(stderr,"Only %d trace procs are allowed\n", MAXTRACEPROCS); fprintf(stderr,"Only %d trace procs are allowed\n", MAXTRACEPROCS);
@@ -4724,6 +4725,7 @@ int main (int argc, char **argv) {
/* finish setting up tracing after all options are read, ie, maps */ /* finish setting up tracing after all options are read, ie, maps */
#ifndef NOTRACE #ifndef NOTRACE
TRACEA("Trace file initialized\n");
TRACEA("Trace flags = 0x%x\n", gv.traceflags); TRACEA("Trace flags = 0x%x\n", gv.traceflags);
gv.savetraceflags = gv.traceflags; gv.savetraceflags = gv.traceflags;
gv.traceflags = 0; gv.traceflags = 0;
@@ -4733,6 +4735,14 @@ int main (int argc, char **argv) {
TRACEA("Tracing enabled for all users\n"); TRACEA("Tracing enabled for all users\n");
if (gv.traceinstcount != 0) if (gv.traceinstcount != 0)
TRACEA("Tracing enabled after instruction %u\n", gv.traceinstcount); TRACEA("Tracing enabled after instruction %u\n", gv.traceinstcount);
if (gv.traceseg != 0)
{
TRACEA("Tracing enabled for segment %04o\n", gv.traceseg);
/* This could be splattering other settings that aren't really
compatible with RPH trace. C'est la vie? */
gv.savetraceflags |= T_FLOW;
gv.tracetriggered = 0;
}
for (i=0; i<gv.numtraceprocs; i++) { for (i=0; i<gv.numtraceprocs; i++) {
for (j=0; j<numsyms; j++) { for (j=0; j<numsyms; j++) {
if (strcasecmp(mapsym[j].symname, traceprocs[i].name) == 0 && mapsym[j].symtype == 'e') { if (strcasecmp(mapsym[j].symname, traceprocs[i].name) == 0 && mapsym[j].symtype == 'e') {
@@ -4793,6 +4803,12 @@ int main (int argc, char **argv) {
for (i=0; i<9; i++) for (i=0; i<9; i++)
rvec[i] = swap16(rvec[i]); rvec[i] = swap16(rvec[i]);
if (rvec[3] != 0) {
if (rvec[2] > rvec[1]) {
rvec[0] += rvec[3];
rvec[1] += rvec[3];
}
}
} else { } else {
/* If no filename follows -boot, then the sense switches are used to /* If no filename follows -boot, then the sense switches are used to
@@ -4953,6 +4969,11 @@ fetch:
#endif #endif
#ifndef NOTRACE #ifndef NOTRACE
if (gv.traceseg != 0)
if (gv.traceseg == (RPH & 0xFFF))
gv.tracetriggered = 1;
else
gv.tracetriggered = 0;
gv.traceflags = 0; gv.traceflags = 0;
if (gv.tracetriggered && if (gv.tracetriggered &&
(gv.instcount >= gv.traceinstcount) && (gv.instcount >= gv.traceinstcount) &&
@@ -5164,7 +5185,7 @@ fetch:
inst = iget16(RP | ((RPL >= gv.livereglim || (getcrs16(KEYS) & 0016000) == 010000) ? 0 : 0x80000000)); inst = iget16(RP | ((RPL >= gv.livereglim || (getcrs16(KEYS) & 0016000) == 010000) ? 0 : 0x80000000));
#else #else
inst = iget16(RP); inst = iget16t(RP);
#endif #endif
INCRP; INCRP;
@@ -6293,15 +6314,33 @@ d_hlt: /* 000000 */
if (bootarg) { if (bootarg) {
printf("\nCPU halt, instruction #%u at %o/%o %s: %o %o ^%06o^\nA='%o/%d B='%o/%d L='%o/%d X=%o/%d", gv.instcount, RPH, RPL, searchloadmap(gv.prevpc,' '), get16t(gv.prevpc), get16t(gv.prevpc+1), lights, getcrs16(A), getcrs16s(A), getcrs16(B), getcrs16s(B), getcrs32(A), getcrs32s(A), getcrs16(X), getcrs16s(X)); printf("\nCPU halt, instruction #%u at %o/%o %s: %o %o ^%06o^\nA='%o/%d B='%o/%d L='%o/%d X=%o/%d", gv.instcount, RPH, RPL, searchloadmap(gv.prevpc,' '), get16t(gv.prevpc), get16t(gv.prevpc+1), lights, getcrs16(A), getcrs16s(A), getcrs16(B), getcrs16s(B), getcrs32(A), getcrs32s(A), getcrs16(X), getcrs16s(X));
while (1) { while (1) {
int n;
static int ttydev;
ttydev = open("/dev/tty", O_RDWR, 0);
if (ttydev < 0) {
perror(" error opening /dev/tty");
fatal(NULL);
}
printf("\nPress Enter to continue, h to halt... "); printf("\nPress Enter to continue, h to halt... ");
utempa = getchar(); fflush(stdout);
utempa = ' ';
n = 0;
while (n == 0)
n = read(ttydev, &utempa, 1);
/* utempa = getchar(); */
printf("\n"); printf("\n");
if (utempa == '\r' || utempa == '\n') if (utempa == '\r' || utempa == '\n')
{
close(ttydev);
goto fetch; goto fetch;
}
if (utempa == 'h') if (utempa == 'h')
{
close(ttydev);
break; break;
} }
} }
}
fatal("CPU halt"); fatal("CPU halt");
d_pim: /* 000205 (R-mode) */ d_pim: /* 000205 (R-mode) */

12
emdev.h
View File

@@ -1413,7 +1413,9 @@ void initclock(ea_t datnowea) {
unixtime = time(NULL); unixtime = time(NULL);
tms = localtime(&unixtime); tms = localtime(&unixtime);
datnow = tms->tm_year<<25 | (tms->tm_mon+1)<<21 | tms->tm_mday<<16 | ((tms->tm_hour*3600 + tms->tm_min*60 + tms->tm_sec)/4); datnow = tms->tm_year<<25 | (tms->tm_mon+1)<<21 | tms->tm_mday<<16 | ((tms->tm_hour*3600 + tms->tm_min*60 + tms->tm_sec)/4);
if (! noclock) {
put32r0(datnow, datnowea); put32r0(datnow, datnowea);
}
} }
@@ -1530,15 +1532,15 @@ int devcp (int class, int func, int device) {
else if (cpuid >= 15) /* newer machines: 250 ticks/second */ else if (cpuid >= 15) /* newer machines: 250 ticks/second */
clkpic = -1250; clkpic = -1250;
TRACE(T_INST, "Clock PIC %d requested, set to %d\n", getcrs16s(A), clkpic); TRACE(T_INST, "Clock PIC %d requested, set to %d\n", getcrs16s(A), clkpic);
SETCLKPOLL;
ticks = -1; ticks = -1;
SETCLKPOLL;
} else if (func == 07) { } else if (func == 07) {
TRACE(T_INST, "Clock control register set to '%o\n", getcrs16(A));
if (getcrs16(A) & 020) if (getcrs16(A) & 020)
clkrate = 102.4; clkrate = 102.4;
else else
clkrate = 3.2; clkrate = 3.2;
TRACE(T_INST, "Clock control register set to '%o\n", getcrs16(A));
ticks = -1; ticks = -1;
SETCLKPOLL; SETCLKPOLL;
@@ -1586,7 +1588,7 @@ int devcp (int class, int func, int device) {
targetticks = elapsedms/(-clkpic*clkrate/1000); targetticks = elapsedms/(-clkpic*clkrate/1000);
#if 0 #if 0
if (abs(ticks-targetticks) > 5) if (abs(ticks-targetticks) > 5)
printf("\nClock: target=%d, ticks=%d, offset=%d\n", targetticks, ticks, ticks-targetticks); printf("\nClock: target=%d, ticks=%d, offset=%d, ipms=%d, poll=%d\n", targetticks, ticks, ticks-targetticks, gv.instpermsec, devpoll[device]);
#endif #endif
/* if the clock gets way out of whack (eg, because of a host /* if the clock gets way out of whack (eg, because of a host
@@ -1629,7 +1631,7 @@ int devcp (int class, int func, int device) {
XXX: this code should probably be done whether or not the XXX: this code should probably be done whether or not the
clock is running */ clock is running */
#define IPMTIME 5000 #define IPMTIME 1000
if ((gv.instcount < previnstcount) || (gv.instcount-previnstcount > gv.instpermsec*IPMTIME)) { if ((gv.instcount < previnstcount) || (gv.instcount-previnstcount > gv.instpermsec*IPMTIME)) {
if (gv.instcount-previnstcount > gv.instpermsec*IPMTIME) { if (gv.instcount-previnstcount > gv.instpermsec*IPMTIME) {
@@ -2224,3 +2226,5 @@ int devdisk (int class, int func, int device) {
#include "devpnc.h" #include "devpnc.h"
#include "devamlc.h" #include "devamlc.h"
#include "devsmlc.h"

80
fp.h
View File

@@ -40,6 +40,7 @@ http://tima-cmp.imag.fr/~guyot/Cours/Oparithm/english/Op_Ar2.htm
inline void getdp (unsigned long long p, long long *frac64, int *exp32) { inline void getdp (unsigned long long p, long long *frac64, int *exp32) {
TRACE(T_FP, " getdp(%016llx)\n", p);
*frac64 = p & 0xFFFFFFFFFFFF0000LL; /* unpack fraction */ *frac64 = p & 0xFFFFFFFFFFFF0000LL; /* unpack fraction */
*exp32 = (short) (p & 0xFFFFLL); /* unpack SIGNED exponent */ *exp32 = (short) (p & 0xFFFFLL); /* unpack SIGNED exponent */
} }
@@ -59,22 +60,29 @@ int prieee8(unsigned long long dp, double *d) {
/* unpack Prime DPFP */ /* unpack Prime DPFP */
getdp (dp, &frac64, &exp32); getdp (dp, &frac64, &exp32);
TRACE(T_FP, " prieee8: unpacked frac %016llx exp %08x\n", frac64, exp32);
/* if negative, change to sign-magnitude */ /* if negative, change to sign-magnitude */
sign = 0; sign = 0;
if (frac64 < 0) { if (frac64 < 0) {
TRACE(T_FP, " prieee8: changing to sign-magnitude\n");
/* special case: negative power of 2 */ /* special case: negative power of 2 */
if (frac64 == 0x8000000000000000LL) { if (frac64 == 0x8000000000000000LL) {
TRACE(T_FP, " prieee8: frac is negative power of 2\n");
exp32 += (1023-128); exp32 += (1023-128);
if (exp32 < 0 || exp32 > 0x7fe) if (exp32 < 0 || exp32 > 0x7fe)
{
TRACE(T_FP, " prieee8: exp %x out of range\n", exp32);
return 0; return 0;
}
frac64 |= ((long long)exp32 << 52); frac64 |= ((long long)exp32 << 52);
*d = *(double *)&frac64; *d = *(double *)&frac64;
return 1; return 1;
} else { } else {
TRACE(T_FP, " prieee8: frac is just negative\n");
sign = 0x8000000000000000LL; sign = 0x8000000000000000LL;
frac64 = -frac64; frac64 = -frac64;
} }
@@ -82,6 +90,7 @@ int prieee8(unsigned long long dp, double *d) {
/* special case: zero */ /* special case: zero */
} else if (frac64 == 0) { } else if (frac64 == 0) {
TRACE(T_FP, " prieee8: frac is 0\n");
*d = 0.0; *d = 0.0;
return 1; return 1;
} }
@@ -89,6 +98,7 @@ int prieee8(unsigned long long dp, double *d) {
/* normalize positive fraction until bit 2 is 1 */ /* normalize positive fraction until bit 2 is 1 */
while ((frac64 & 0x4000000000000000LL) == 0) { while ((frac64 & 0x4000000000000000LL) == 0) {
TRACE(T_FP, " prieee8: normalizing positive fraction\n");
frac64 = frac64 << 1; frac64 = frac64 << 1;
exp32--; exp32--;
} }
@@ -98,7 +108,10 @@ int prieee8(unsigned long long dp, double *d) {
exp32 += (1023-128) - 1; exp32 += (1023-128) - 1;
#if 1 #if 1
if (exp32 < 0 || exp32 > 0x7fe) if (exp32 < 0 || exp32 > 0x7fe)
{
TRACE(T_FP, " prieee8: exponent %x out of range\n", exp32);
return 0; return 0;
}
#else #else
if (exp32 < 0) { if (exp32 < 0) {
*d = 0.0; *d = 0.0;
@@ -113,6 +126,7 @@ int prieee8(unsigned long long dp, double *d) {
/* pack into an IEEE DPFP, losing the leading 1 bit in the process */ /* pack into an IEEE DPFP, losing the leading 1 bit in the process */
frac64 = sign | ((long long)exp32 << 52) | ((frac64 >> 10) & 0xfffffffffffffLL); frac64 = sign | ((long long)exp32 << 52) | ((frac64 >> 10) & 0xfffffffffffffLL);
TRACE(T_FP, " prieee8: packed ieee dpfp %016llx\n", frac64);
*d = *(double *)&frac64; *d = *(double *)&frac64;
return 1; return 1;
} }
@@ -134,7 +148,7 @@ retry:
neg = frac64 < 0; neg = frac64 < 0;
exp32 = (frac64 >> 52) & 0x7ff; exp32 = (frac64 >> 52) & 0x7ff;
frac64 &= 0xfffffffffffffLL; frac64 &= 0xfffffffffffffLL;
//printf("dp=%llx, neg=%d, frac64=%llx, exp32=%d, \n", *(long long *)dp, neg, frac64, exp32); TRACE(T_FP, "d=%016llx, neg=%x, frac64=%016llx, exp32=%08x, \n", (long long)d, neg, frac64, exp32);
/* special case: NaN & +-infinity */ /* special case: NaN & +-infinity */
@@ -159,12 +173,21 @@ retry:
and subnormal */ and subnormal */
if (exp32 != 0) /* typical IEEE normalized */ if (exp32 != 0) /* typical IEEE normalized */
{
TRACE(T_FP, " ieeepr8: is normalized\n");
frac64 |= 0x10000000000000LL; frac64 |= 0x10000000000000LL;
else if (frac64 == 0) { /* IEEE +-0.0 (zero exp+frac) */ }
else if (frac64 == 0) /* IEEE +-0.0 (zero exp+frac) */
{
TRACE(T_FP, " ieeepr8: zero exp and frac\n");
*p = 0; /* IEEE and Prime zero are the same */ *p = 0; /* IEEE and Prime zero are the same */
return okay; return okay;
} else }
else
{
TRACE(T_FP, " ieeepr8: subnormal value\n");
; /* subnormal: no hidden 1 bit */ ; /* subnormal: no hidden 1 bit */
}
/* adjust exponent, change sign-magnitude to 2's complement, /* adjust exponent, change sign-magnitude to 2's complement,
and shift fraction into Prime placement (high 48 bits) */ and shift fraction into Prime placement (high 48 bits) */
@@ -173,12 +196,14 @@ retry:
if (neg) if (neg)
frac64 = -frac64; frac64 = -frac64;
frac64 <<= 10; frac64 <<= 10;
TRACE(T_FP, " ieeepr8: de-biased prime-ised frac %016llx exp %08x\n", frac64, exp32);
/* normalize Prime DPFP */ /* normalize Prime DPFP */
while ((frac64 ^ (frac64 << 1)) >= 0) { while ((frac64 ^ (frac64 << 1)) >= 0) {
frac64 = frac64 << 1; frac64 = frac64 << 1;
exp32--; exp32--;
TRACE(T_FP, " ieeepr8: normalized prime frac %016llx exp %08x\n", frac64, exp32);
} }
#if 0 #if 0
@@ -204,8 +229,11 @@ retry:
if (round) if (round)
if ((frac64 & 0x8000) && ((frac64 & 0x7fffffffffff0000LL) != 0x7fffffffffff0000LL)) if ((frac64 & 0x8000) && ((frac64 & 0x7fffffffffff0000LL) != 0x7fffffffffff0000LL))
{
/* XXX: should this be a subtract for negative numbers? */ /* XXX: should this be a subtract for negative numbers? */
frac64 += 0x10000; frac64 += 0x10000;
TRACE(T_FP, " ieeepr8: rounded frac %016llx\n", frac64);
}
*p = swap64((frac64 & 0xffffffffffff0000LL) | (exp32 & 0xffff)); *p = swap64((frac64 & 0xffffffffffff0000LL) | (exp32 & 0xffff));
return okay; return okay;
@@ -260,6 +288,7 @@ unsigned long long dfcm (unsigned long long dp, int *oflow) {
CLEARC; CLEARC;
*oflow = 0; *oflow = 0;
getdp(dp, &frac64, &exp32); getdp(dp, &frac64, &exp32);
TRACE(T_FP, " dfcm: unpacked frac %016llx exp %08x\n", frac64, exp32);
if (frac64 != 0) { /* can't normalize zero */ if (frac64 != 0) { /* can't normalize zero */
if (frac64 == 0x8000000000000000LL) { /* overflow case? */ if (frac64 == 0x8000000000000000LL) { /* overflow case? */
frac64 = 0x4000000000000000LL; /* complement power of 2 */ frac64 = 0x4000000000000000LL; /* complement power of 2 */
@@ -290,6 +319,7 @@ unsigned long long norm(unsigned long long dp, int *oflow) {
*oflow = 0; *oflow = 0;
getdp(dp, &frac64, &exp32); getdp(dp, &frac64, &exp32);
TRACE(T_FP, " norm: unpacked frac %016llx exp %08x\n", frac64, exp32);
while ((frac64 ^ (frac64 << 1)) >= 0) { while ((frac64 ^ (frac64 << 1)) >= 0) {
frac64 = frac64 << 1; frac64 = frac64 << 1;
exp32--; exp32--;
@@ -314,11 +344,16 @@ unsigned long long frn(unsigned long long dp, int *oflow) {
*oflow = 0; *oflow = 0;
getdp(dp, &frac64, &exp32); getdp(dp, &frac64, &exp32);
TRACE(T_FP, " frn: unpacked frac %016llx exp %08x\n", frac64, exp32);
if (frac64 == 0) if (frac64 == 0)
{
TRACE(T_FP, " frn: returning 0\n");
return 0; return 0;
}
else { else {
doround1 = ((frac64 & 0x18000000000LL) != 0); doround1 = ((frac64 & 0x18000000000LL) != 0);
doround2 = ((frac64 & 0x8000000000LL) != 0) && ((frac64 & 0x7FFFFF0000LL) != 0); doround2 = ((frac64 & 0x8000000000LL) != 0) && ((frac64 & 0x7FFFFF0000LL) != 0);
TRACE(T_FP, " frn: doround1 %x doround2 %x\n", doround1, doround2);
if (doround1 || doround2) { if (doround1 || doround2) {
frac64 &= 0xFFFFFF0000000000LL; frac64 &= 0xFFFFFF0000000000LL;
if (frac64 != 0x7FFFFF0000000000LL) if (frac64 != 0x7FFFFF0000000000LL)
@@ -329,8 +364,10 @@ unsigned long long frn(unsigned long long dp, int *oflow) {
} }
frac64 |= (exp32 & 0xFFFF); frac64 |= (exp32 & 0xFFFF);
frac64 = norm(frac64, oflow); frac64 = norm(frac64, oflow);
TRACE(T_FP, " frn: rounded frac %016llx\n", frac64);
return frac64; return frac64;
} }
TRACE(T_FP, " frn: dp %016llx\n", dp);
return dp; return dp;
} }
} }
@@ -356,26 +393,37 @@ int fcs (unsigned long long fac, int fop) {
fop = fop & 0xffffff00; fop = fop & 0xffffff00;
if (fop == 0) /* fix dirty zero */ if (fop == 0) /* fix dirty zero */
fopexp = 0; fopexp = 0;
TRACE(T_FP, " fcs: FAC: %016llx %04x; op: %016llx %04x\n", templ, facexp, fop, fopexp);
if ((templ & 0x80000000) == (fop & 0x80000000)) { /* compare signs */ if ((templ & 0x80000000) == (fop & 0x80000000)) { /* compare signs */
if (facexp == fopexp) /* compare exponents */ if (facexp == fopexp) /* compare exponents */
if (templ == fop) { /* compare fractions */ if (templ == fop) { /* compare fractions */
TRACE(T_FP, " fcs: frac cmp returning eq / skip 1\n");
SETEQ; SETEQ;
return 1; return 1;
} else if (templ < fop) { /* compare fractions */ } else if (templ < fop) { /* compare fractions */
TRACE(T_FP, " fcs: frac cmp returning lt / skip 2\n");
SETLT; /* FAC < operand */ SETLT; /* FAC < operand */
return 2; return 2;
} else } else {
TRACE(T_FP, " fcs: frac cmp returning gt / skip 0\n");
return 0; /* FAC > operand */ return 0; /* FAC > operand */
}
else if (facexp < fopexp) { /* compare exponents */ else if (facexp < fopexp) { /* compare exponents */
TRACE(T_FP, " fcs: exp cmp returning lt / skip 2\n");
SETLT; /* FAC < operand */ SETLT; /* FAC < operand */
return 2; return 2;
} else } else {
TRACE(T_FP, " fcs: exp cmp returning gt / skip 0\n");
return 0; return 0;
}
} else if (templ & 0x80000000) { } else if (templ & 0x80000000) {
TRACE(T_FP, " fcs: sign cmp returning lt / skip 2\n");
SETLT; /* FAC < operand */ SETLT; /* FAC < operand */
return 2; return 2;
} else } else {
TRACE(T_FP, " fcs: sign cmp returning gt / skip 0\n");
return 0; /* FAC > operand */ return 0; /* FAC > operand */
}
} }
@@ -404,27 +452,35 @@ int dfcs (unsigned long long fac, long long fop) {
fop = fop & 0xffffffffffff0000LL; fop = fop & 0xffffffffffff0000LL;
if (fop == 0) /* fix dirty zero */ if (fop == 0) /* fix dirty zero */
fopexp = 0; fopexp = 0;
#if 0 TRACE(T_FP, " dfcs: FAC: %016llx %04x; op: %016llx %04x\n", templl, facexp, fop, fopexp);
printf("dfcs: FAC: %016llx %04x; op: %016llx %04x\n", templl, facexp, fop, fopexp);
#endif
if ((templl & 0x8000000000000000LL) == (fop & 0x8000000000000000LL)) { /* compare signs */ if ((templl & 0x8000000000000000LL) == (fop & 0x8000000000000000LL)) { /* compare signs */
if (facexp == fopexp) /* compare exponents */ if (facexp == fopexp) /* compare exponents */
if (templl == fop) { /* compare fractions */ if (templl == fop) { /* compare fractions */
TRACE(T_FP, " dfcs: frac cmp returning eq / skip 1\n");
SETEQ; SETEQ;
return 1; return 1;
} else if (templl < fop) { /* compare fractions */ } else if (templl < fop) { /* compare fractions */
TRACE(T_FP, " dfcs: frac cmp returning lt / skip 2\n");
SETLT; /* FAC < operand */ SETLT; /* FAC < operand */
return 2; return 2;
} else } else {
TRACE(T_FP, " dfcs: frac cmp returning gt / skip 0\n");
return 0; /* FAC > operand */ return 0; /* FAC > operand */
}
else if (facexp < fopexp) { /* compare exponents */ else if (facexp < fopexp) { /* compare exponents */
TRACE(T_FP, " dfcs: exp cmp returning lt / skip 2\n");
SETLT; /* FAC < operand */ SETLT; /* FAC < operand */
return 2; return 2;
} else } else {
TRACE(T_FP, " dfcs: exp cmp returning eq / skip 1\n");
return 0; return 0;
}
} else if (templl & 0x8000000000000000LL) { } else if (templl & 0x8000000000000000LL) {
TRACE(T_FP, " dfcs: sign cmp returning lt / skip 2\n");
SETLT; /* FAC < operand */ SETLT; /* FAC < operand */
return 2; return 2;
} else } else {
TRACE(T_FP, " dfcs: sign cmp returning gt / skip 0\n");
return 0; /* FAC > operand */ return 0; /* FAC > operand */
}
} }

View File

@@ -7,25 +7,29 @@ all_deps = makefile
em_objs = em.o em em_objs = em.o em
em_deps = \ em_deps = \
em.c regs.h emdev.h ea64v.h ea32i.h fp.h dispatch.h geom.h \ em.c regs.h emdev.h ea64v.h ea32i.h fp.h dispatch.h geom.h \
devpnc.h devamlc.h swap.h devpnc.h devamlc.h devsmlc.h swap.h
CFLAGS =
# Uncomment for building on SmartOS/Solaris:
# CFLAGS += -lsocket -lnsl
.PHONY: emwarn debug trace fixed .PHONY: emwarn debug trace fixed
# normal # normal
em: $(em_deps) $(all_deps) em: $(em_deps) $(all_deps)
$(CC) -DREV=\"${REV}\" -DNOTRACE -DFAST -O -Winline -Wno-return-type em.c -o em $(CC) -DREV=\"${REV}\" ${CFLAGS} -DNOTRACE -DFAST -O -Winline -Wno-return-type em.c -o em
# lots of compiler warnings # lots of compiler warnings
emwarn: $(em_deps) $(all_deps) emwarn: $(em_deps) $(all_deps)
$(CC) -DREV=\"${REV}\" -DNOTRACE -DFAST -O -Wall -Wextra -pedantic -Wconversion em.c -o em $(CC) -DREV=\"${REV}\" ${CFLAGS} -DNOTRACE -DFAST -O -Wall -Wextra -pedantic -Wconversion em.c -o em
# gdb # gdb
debug: $(em_deps) $(all_deps) debug: $(em_deps) $(all_deps)
$(CC) -DREV=\"${REV}\" -DNOTRACE -DFAST -g -O0 em.c -o em $(CC) -DREV=\"${REV}\" ${CFLAGS} -DNOTRACE -DFAST -g -O0 em.c -o em
# tracing # tracing
trace: $(em_deps) $(all_deps) trace: $(em_deps) $(all_deps)
$(CC) -DREV=\"${REV}\" -DFAST -O em.c -o em $(CC) -DREV=\"${REV}\" ${CFLAGS} -DFAST -O em.c -o em
# the fixed clock rate build is useful for making problems reproduceable. # the fixed clock rate build is useful for making problems reproduceable.
# #
@@ -35,7 +39,7 @@ trace: $(em_deps) $(all_deps)
# fixed clock rate # fixed clock rate
fixed: $(em_deps) $(all_deps) fixed: $(em_deps) $(all_deps)
$(CC) -DREV=\"${REV}\" -DFIXEDCLOCK -DNOIDLE -DFAST -O em.c -o em $(CC) -DREV=\"${REV}\" ${CFLAGS} -DFIXEDCLOCK -DNOIDLE -DFAST -O em.c -o em
clean: clean:
rm -f $(em_objs) rm -f $(em_objs)

5
regs.h
View File

@@ -6,8 +6,6 @@
#define REGSETS 10 #define REGSETS 10
#define T_REGS 0x00100000
/* these are 16-bit absolute offsets into the register file */ /* these are 16-bit absolute offsets into the register file */
#define PSWKEYS16 031*2 #define PSWKEYS16 031*2
@@ -294,7 +292,6 @@ static inline uint16_t getcrs16(int offset) { \
/* store 16-bit unsigned at 16-bit offset */ /* store 16-bit unsigned at 16-bit offset */
//#define putcrs16(offset, val) crs[(offset)] = (val) //#define putcrs16(offset, val) crs[(offset)] = (val)
static inline void putcrs16(int offset, uint16_t val) { \ static inline void putcrs16(int offset, uint16_t val) { \
TRACE(T_REGS, " putcrs16: offset %d val %06o\n", offset, val);
crs[(offset)] = swap16(val); \ crs[(offset)] = swap16(val); \
} }
@@ -315,7 +312,6 @@ static inline uint32_t getcrs32(int offset) { \
/* put 32-bit unsigned at 16-bit offset */ /* put 32-bit unsigned at 16-bit offset */
//#define putcrs32(offset, val) *(unsigned int *)(crs+(offset)) = (val) //#define putcrs32(offset, val) *(unsigned int *)(crs+(offset)) = (val)
static inline void putcrs32(int offset, uint32_t val) { \ static inline void putcrs32(int offset, uint32_t val) { \
TRACE(T_REGS, " putcrs32: offset %d val %012lo\n", offset, val);
*(unsigned int *)(crs+offset) = swap32(val); \ *(unsigned int *)(crs+offset) = swap32(val); \
} }
@@ -356,7 +352,6 @@ static inline uint16_t getar16(int offset) { \
/* put 16-bit unsigned at 16-bit absolute register file address */ /* put 16-bit unsigned at 16-bit absolute register file address */
static inline void putar16(int offset, uint16_t val) { \ static inline void putar16(int offset, uint16_t val) { \
TRACE(T_REGS, " putar16: offset %d val %06o\n", offset, val);
regs.u16[(offset)] = swap16(val); \ regs.u16[(offset)] = swap16(val); \
} }

35
util/cmraw.c Normal file
View File

@@ -0,0 +1,35 @@
/* Show approximate minimum clock resolution
Derived from the example in the linux man page for clock_gettime() */
#define _XOPEN_SOURCE 600
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <unistd.h>
static void displayClock(clockid_t clock, char *name)
{
struct timespec ts;
time_t oldsec;
long oldnsec;
clock_gettime(clock, &ts);
oldsec = ts.tv_sec;
oldnsec = ts.tv_nsec;
while (ts.tv_sec == oldsec && ts.tv_nsec == oldnsec)
{
if (clock_gettime(clock, &ts) == -1) {
perror("clock_gettime");
exit(EXIT_FAILURE);
}
}
printf("%-15s: %10ld sec %09ld nsec\n", name,
(long) ts.tv_sec - oldsec, ts.tv_nsec - oldnsec);
}
int main(int argc, char *argv[])
{
displayClock(CLOCK_MONOTONIC_RAW, "CLOCK_MONOTONIC_RAW");
exit(0);
}

View File

@@ -29,7 +29,7 @@ main () {
else { /* not a space character */ else { /* not a space character */
while (space) { /* dump held-back spaces first */ while (space) { /* dump held-back spaces first */
if (space < 3) { /* write regular spaces */ if (space < 3) { /* write regular spaces */
putchar(' '); putchar(0240);
space--; space--;
n++; n++;
} else { /* write compressed spaces */ } else { /* write compressed spaces */