1
0
mirror of https://github.com/prirun/p50em.git synced 2026-03-06 02:38:57 +00:00

23 Commits

Author SHA1 Message Date
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
11 changed files with 661 additions and 363 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

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
repository.
Github discussions are now enabled for this repository, and can be
used to ask for help.
## Public Systems
There are a set of emulators available for public use. These may be

View File

@@ -1,4 +1,4 @@
/*
/*
Implements the AMLC subsystem for Primos. In earlier versions
(r186), a more hardware-centric implementation was used that closely
followed the design of Prime's real AMLC board. For example, every
@@ -52,9 +52,9 @@
NOTE: varying the AMLC poll rates dynamically is the default, but
this can lead to unpredictable performance. If consistent,
predictable performance is more important than absolute
performance, MAXAMLCSPEEDUP can be set to 1. Then this
performance, MAXAMLCSPEEDUP can be set to 1. Then this
implementation will function more or less like a real Prime.
AMLC I/O operations:
@@ -133,7 +133,7 @@
AMLC status word (from AMLCT5):
BITS DESCRIPTION
1 END OF RANGE INTERRUPT
2 CLOCK RUNNING
3-6 LINE # OF LINE WHOSE DATA SET STATUS HAS CHANGED
@@ -150,6 +150,8 @@ AMLC status word (from AMLCT5):
*/
#include <termio.h>
/* this macro closes an AMLC connection - used in several places
NOTE: don't print disconnect message on dedicated lines */
@@ -202,11 +204,11 @@ int devamlc (int class, int func, int device) {
#define DSSCOUNTDOWN 25
/* connection types for each line. This _doesn't_ imply the line is
actually connected, ie, an AMLC line may be tied to a specific
actually connected, ie, an AMLC line may be tied to a specific
serial device (like a USB->serial gizmo), but the USB device may
not be plugged in. The connection type would be CT_SERIAL but
the line's fd would be -1 and the "connected" bit would be 0 */
#define CT_SOCKET 1
#define CT_SERIAL 2
#define CT_DEDIP 3
@@ -262,6 +264,7 @@ int devamlc (int class, int func, int device) {
unsigned short ctinterrupt; /* 1 bit per line */
unsigned short dss; /* 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 dsstime; /* countdown to dss poll */
short fd[16]; /* Unix fd, 1 per line */
@@ -323,12 +326,13 @@ int devamlc (int class, int func, int device) {
int tempport;
struct hostent* host;
char *p;
/* initially, we don't know about any AMLC boards */
for (dx=0; dx<MAXBOARDS; dx++) {
dc[dx].deviceid = 0;
dc[dx].connected = 0;
dc[dx].dolineclear = 0;
dc[dx].serial = 0;
for (lx = 0; lx < 16; lx++) {
dc[dx].fd[lx] = -1;
@@ -344,7 +348,7 @@ int devamlc (int class, int func, int device) {
/* read the amlc.cfg file. This file has 3 uses:
1. maps Prime async lines to real serial devices, like host
serial ports or USB serial boxes.
serial ports or USB serial boxes.
Format: <line #> /dev/<Unix usb device name>
@@ -416,7 +420,7 @@ int devamlc (int class, int func, int device) {
fprintf(stderr,"Line %d of amlc.cfg ignored: IP address too long\n", lc);
continue;
}
/* break out host and port number; no port means this is
an incoming dedicated line: no connects out. With a
port means we need to connect to it. */
@@ -551,13 +555,13 @@ int devamlc (int class, int func, int device) {
/* XXX: this constant is redefined because of a bug in the
OSX Prolific USB serial driver at version 1.2.1r2. They should be
turning on bit 0100, but are turning on 0x0100. */
#define TIOCM_CD 0x0100
#define TIOCM_CD 0x0100
if (func == 00) { /* input Data Set Sense (carrier) */
if (dc[dx].serial) { /* any serial connections? */
if (--dc[dx].dsstime == 0) {
dc[dx].dsstime = DSSCOUNTDOWN;
#ifdef __APPLE__
/* #ifdef __APPLE__ */
for (lx = 0; lx < 16; lx++) { /* yes, poll them */
if (dc[dx].ctype[lx] == CT_SERIAL) {
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);
@@ -632,10 +636,10 @@ int devamlc (int class, int func, int device) {
}
break;
case CT_SERIAL: {
int fd;
/* setup line characteristics if they have changed (check for
something other than DTR changing) */
@@ -705,7 +709,7 @@ int devamlc (int class, int func, int device) {
1_0 - cts/rts flow control (2413 becomes 6413)
1_1 - dsr/dtr flow control (2413 becomes 7413)
NOTE: bit 11 also appears to be free, but Primos doesn't
NOTE: bit 11 also appears to be free, but Primos doesn't
let it flow through to the AMLC controller. :(
*/
@@ -756,7 +760,6 @@ int devamlc (int class, int func, int device) {
/* set DTR high (02000) or low if it has changed */
#ifdef __APPLE__
if ((getcrs16(A) ^ dc[dx].lconf[lx]) & 02000) {
int modemstate;
//printf("devamlc: DTR state changed\n");
@@ -769,7 +772,6 @@ int devamlc (int class, int func, int device) {
}
ioctl(fd, TIOCMSET, &modemstate);
}
#endif
break;
}
@@ -839,7 +841,7 @@ int devamlc (int class, int func, int device) {
double ts;
int neweor, activelines;
//printf("poll device '%o, speedup=%5.2f, cti=%x, xmit=%x, recv=%x, dss=%x\n", device, pollspeedup, dc[dx].ctinterrupt, dc[dx].xmitenabled, dc[dx].recvenabled, dc[dx].dss);
/* check for 1 new telnet connection every AMLCACCEPTSECS seconds
(10 times per second) */
@@ -885,6 +887,7 @@ int devamlc (int class, int func, int device) {
close(dc[i].fd[lx]);
dc[i].dss |= BITMASK16(lx+1);
dc[i].connected |= BITMASK16(lx+1);
dc[i].dolineclear |= BITMASK16(lx+1);
dc[i].fd[lx] = fd;
dc[i].tstate[lx] = TS_DATA;
//printf("em: AMLC connection, fd=%d, device='%o, line=%d\n", fd, dc[i].deviceid, lx);
@@ -1033,7 +1036,7 @@ int devamlc (int class, int func, int device) {
int fd, sockflags;
struct sockaddr_in raddr;
dc[dx].obtimer[lx] = tv.tv_sec + AMLCCONNECT;
//printf("em: trying to connect to 0x%08x:%d\n", dc[dx].obhost[lx], dc[dx].obport[lx]); /***/
fd = socket(AF_INET, SOCK_STREAM, 0);
@@ -1118,7 +1121,7 @@ int devamlc (int class, int func, int device) {
}
/* process input, but only as much as will fit into the DMC
buffer.
buffer.
Because the size of the AMLC tumble tables is limited, this
could pose a denial of service issue. Input is processed in a
@@ -1140,6 +1143,53 @@ dorecv:
if (dc[dx].deviceid == 0 || dc[dx].connected == 0 || dc[dx].eor)
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 */
FD_ZERO(&readfds);

504
devsmlc.h
View File

@@ -1,4 +1,3 @@
#define DEBUG 0
/*
Implements the SMLC subsystem for Primos.
@@ -48,8 +47,10 @@
*/
#include <ctype.h>
#include <stdbool.h>
#include <time.h>
#include <arpa/inet.h>
#define SMLC_DEVICEID 0050 /* HSSMLC/MDLC is '50, SMLC is '56 */
@@ -111,12 +112,12 @@
#define SMLC_ACK0_STATUS (007<<4) /* encoded ACK0 status */
#define SMLC_NAK_STATUS (013<<4) /* encoded NAK status */
#define SMLC_ENQ_STATUS (014<<4) /* encoded soh-ENQ status */
#define SMLC_MASK_STATUS (017<<4) /* encoded status mask */
/* seconds between attempts to make TCP connections */
#define SMLC_CONNECTINTERVAL 15
#if DEBUG
#define HexColumn(x) (3 * (x) + 4)
#define AsciiColumn(x) (HexColumn(16) + 2 + (x))
#define LogLineLength (AsciiColumn(16))
@@ -124,11 +125,9 @@
static void smlclogbytes(unsigned char *bytes, int len);
static void smlclogflush(void);
static FILE *smlclog = NULL;
static char smlclogbuf[LogLineLength + 1];
static int smlclogbytescol = 0;
static char smlctimestamp[20];
#endif
/* connection states */
@@ -144,7 +143,9 @@ typedef enum {
typedef enum {
SMLC_STATERCVSYN = 0,
SMLC_STATERCVDLE,
SMLC_STATERCVCHAR
SMLC_STATERCVCHAR,
SMLC_STATERCVSOH,
SMLC_STATERCVENQ
} SmlcReceiveState;
/* controller interrupt states */
@@ -155,20 +156,18 @@ typedef enum {
SMLC_INTERRUPTPENDING
} SmlcInterruptState;
#if DEBUG
static char *intstates[] = {
"not interrupting",
"interrupting",
"interrupt pending"
};
#endif
/* send/receive buffer */
typedef struct smlcbuffer {
unsigned short in;
unsigned short out;
char data[SMLC_BUFSIZE];
unsigned char data[SMLC_BUFSIZE];
} SmlcBuffer;
/* Simplex Line Control Block. */
@@ -195,7 +194,7 @@ typedef struct plcb { /* physical line control block */
SmlcReceiveState recvstate; /* receive state */
bool starting; /* TRUE if waiting for response to SOH-ENQ */
bool naksent; /* TRUE if last frame sent was a NAK */
int fd; /* Unix fd */
int fd; /* Unix file descriptor of socket */
char *remoteID; /* remote TCP endpoint identifier */
uint32_t host; /* TCP host */
unsigned short port; /* TCP port */
@@ -225,19 +224,12 @@ static void smlcaddstatus(SmlcDCB *dcbp, int line, unsigned short status) {
dmcbufbegea = dmcpair >> 16;
dmcbufendea = dmcpair & 0xffff;
if (dmcbufendea >= dmcbufbegea) {
#if DEBUG
fprintf(smlclog, "%s Interrupt status '%06o added for line %d on device '%02o, next '%06o, last '%06o\n",
TRACE(T_SMLC, "%s Interrupt status '%06o added for line %d on device '%02o, next '%06o, last '%06o\n",
smlctimestamp, status, line, dcbp->deviceid, dmcbufbegea, dmcbufendea);
#endif
put16io(status + (line * 2), dmcbufbegea);
dmcbufbegea = INCVA(dmcbufbegea, 1);
put16io(dmcbufbegea, dcbp->dmcstatus);
dcbp->intstate = SMLC_INTERRUPTPENDING;
#if DEBUG
} else {
fprintf(smlclog, "%s Interrupt status '%06o -NOT- added for line %d on device '%02o, next '%06o, last '%06o\n",
smlctimestamp, status, line, dcbp->deviceid, dmcbufbegea, dmcbufendea);
#endif
}
}
@@ -248,6 +240,8 @@ int devsmlc (int class, int func, int device) {
static int optenable = 1;
static struct timeval timeout = {0, 0};
struct sockaddr_in addr;
socklen_t addrlen;
char *bp;
char buf[SMLC_BUFSIZE];
int bufidx;
@@ -259,11 +253,12 @@ int devsmlc (int class, int func, int device) {
short dmcnw;
unsigned int dmcpair;
int dx;
char *ep;
unsigned char *ep;
int fd;
int flags;
uint32_t hostaddr;
int i;
char *ip;
unsigned char *ip;
PLCB *lp;
bool isetb;
int lx;
@@ -271,11 +266,10 @@ int devsmlc (int class, int func, int device) {
int maxfd;
int n;
int nbytes;
char *np;
char *op;
unsigned char *np;
unsigned char *op;
socklen_t optlen;
int optval;
struct sockaddr_in raddr;
fd_set readfds;
int readycount;
int rc;
@@ -284,16 +278,14 @@ int devsmlc (int class, int func, int device) {
int sx;
unsigned short word;
fd_set writefds;
#if DEBUG
struct tm *tp;
struct timespec sts;
#endif
currenttime = time(0);
#if DEBUG
#ifndef NOTRACE
tp = localtime(&currenttime);
clock_gettime(CLOCK_REALTIME, &sts);
sprintf(smlctimestamp, "%02d:%02d:%02d.%09d", tp->tm_hour, tp->tm_min, tp->tm_sec, sts.tv_nsec);
sprintf(smlctimestamp, "%02d:%02d:%02d.%09ld", tp->tm_hour, tp->tm_min, tp->tm_sec, sts.tv_nsec);
#endif
switch (device) {
@@ -320,20 +312,13 @@ int devsmlc (int class, int func, int device) {
if (!inited) {
FILE *cfgfile;
char devname[32];
int lc;
int tempport;
struct hostent* host;
int lc;
char *p;
char tcpaddr[MAXHOSTLEN + 1];
int tempport;
#if DEBUG
smlclog = fopen("smlc.log", "wt");
if (smlclog == NULL) {
fprintf(stderr, "Failed to create smlclog.txt - aborting\n");
fatal(NULL);
}
smlclogflush(); // initialize log buffer
#endif
smlclogflush(); // initialize log buffer for BSC frames
/* initially, we don't know about any SMLC boards */
@@ -371,57 +356,54 @@ int devsmlc (int class, int func, int device) {
while (fgets(buf, sizeof(buf), cfgfile) != NULL) {
int n;
lc++;
buf[sizeof(devname)] = 0; /* don't let sscanf overwrite anything */
buf[sizeof(tcpaddr)] = 0; /* don't let sscanf overwrite anything */
buf[strlen(buf) - 1] = 0; /* remove trailing newline */
if (buf[0] == '\0' || buf[0] == ';' || buf[0] == '#')
continue;
n = sscanf(buf, "%d %s", &i, devname);
n = sscanf(buf, "%d %s", &i, tcpaddr);
if (n != 2) {
fprintf(stderr, "Can't parse smlc.cfg line #%d: %s\n", lc, buf);
fprintf(stderr, "smlc.cfg[%d] Can't parse: %s\n", lc, buf);
continue;
}
if (i < 0 || i >= SMLC_MAXLINES) {
fprintf(stderr, "Line # %d out of range in smlc.cfg at line #%d: %s\n", i, lc, buf);
fprintf(stderr, "smlc.cfg[%d] SMLC line # %d out of range: %s\n", lc, i, buf);
continue;
}
#if DEBUG
fprintf(smlclog, "%s Line %d, SMLC line %d set to device %s\n", smlctimestamp, lc, i, devname);
#endif
dx = i / SMLC_LINESPERBOARD;
lx = i % SMLC_LINESPERBOARD;
if (strlen(devname) > MAXHOSTLEN) {
fprintf(stderr, "Line %d of smlc.cfg ignored: IP address too long\n", lc);
if (strlen(tcpaddr) > MAXHOSTLEN) {
fprintf(stderr, "smlc.cfg[%d] IP address too long: %s\n", lc, buf);
continue;
}
tempport = 0;
if ((p=strtok(devname, PDELIM)) != NULL) {
host = gethostbyname(p);
host = NULL;
p = index(tcpaddr, ':');
if (p != NULL) {
*p++ = '\0';
host = gethostbyname(tcpaddr);
if (host == NULL) {
fprintf(stderr, "Line %d of smlc.cfg ignored: can't resolve IP address %s\n", lc, p);
fprintf(stderr, "smlc.cfg[%d] Can't resolve IP address of %s\n", lc, tcpaddr);
continue;
}
if ((p=strtok(NULL, DELIM)) != NULL) {
tempport = atoi(p);
if (tempport < 1 || tempport > 65000) {
fprintf(stderr, "Line %d of smlc.cfg ignored: port number %d out of range 1-65000\n", tempport, lc);
continue;
}
}
tempport = atoi(p);
} else {
tempport = atoi(tcpaddr);
}
if (tempport == 0) {
fprintf(stderr, "Line %d of smlc.cfg ignored: no IP address or port number specified\n", lc);
fprintf(stderr, "smlc.cfg[%d] No IP address or port number specified\n", lc);
continue;
} else if (tempport < 1 || tempport > 65535) {
fprintf(stderr, "smlc.cfg[%d] Port number %d out of range 1-65535\n", lc, tempport);
continue;
}
dc[dx].plines[lx].remoteID = (char *)malloc(32);
hostaddr = ntohl(*(unsigned int *)host->h_addr);
hostaddr = (host != NULL) ? ntohl(*(unsigned int *)host->h_addr) : 0;
sprintf(dc[dx].plines[lx].remoteID, "%d.%d.%d.%d:%d", (hostaddr >> 24) & 0xff, (hostaddr >> 16) & 0xff,
(hostaddr >> 8) & 0xff, hostaddr & 0xff, tempport);
dc[dx].plines[lx].host = hostaddr;
dc[dx].plines[lx].port = tempport;
#if DEBUG
fprintf(smlclog, "%s TCP address, host=%x, port=%d, controller=%d, line=%d\n", smlctimestamp, dc[dx].plines[lx].host,
tempport, dx, lx);
#endif
TRACE(T_SMLC, "%s smlc.cfg[%d] controller '%02o, line %d, TCP address %s\n", smlctimestamp, lc, dx + 050, lx,
dc[dx].plines[lx].remoteID);
}
fclose(cfgfile);
}
@@ -445,40 +427,30 @@ int devsmlc (int class, int func, int device) {
switch (func) {
case 000: // enable high-speed SMLC clock
#if DEBUG
fprintf(smlclog, "%s OCP '%02o%02o enable high-speed clock\n", smlctimestamp, func, device);
#endif
TRACE(T_SMLC, "%s OCP '%02o%02o enable high-speed clock\n", smlctimestamp, func, device);
IOSKIP;
break;
case 013: // acknowledge and clear interrupt
#if DEBUG
fprintf(smlclog, "%s OCP '%02o%02o acknowledge and clear interrupt\n", smlctimestamp, func, device);
#endif
TRACE(T_SMLC, "%s OCP '%02o%02o acknowledge and clear interrupt\n", smlctimestamp, func, device);
dc[dx].intstate = SMLC_NOTINTERRUPTING;
IOSKIP;
break;
case 015: // enable interrupts
#if DEBUG
fprintf(smlclog, "%s OCP '%02o%02o enable interrupts\n", smlctimestamp, func, device);
#endif
TRACE(T_SMLC, "%s OCP '%02o%02o enable interrupts\n", smlctimestamp, func, device);
dc[dx].intenabled = 1;
IOSKIP;
break;
case 016: // disable interrupts
#if DEBUG
fprintf(smlclog, "%s OCP '%02o%02o disable interrupts\n", smlctimestamp, func, device);
#endif
TRACE(T_SMLC, "%s OCP '%02o%02o disable interrupts\n", smlctimestamp, func, device);
dc[dx].intenabled = 0;
IOSKIP;
break;
case 017: // initialize controller
#if DEBUG
fprintf(smlclog, "%s OCP '%02o%02o initialize controller\n", smlctimestamp, func, device);
#endif
TRACE(T_SMLC, "%s OCP '%02o%02o initialize controller\n", smlctimestamp, func, device);
dc[dx].intvector = 0;
dc[dx].intenabled = 0;
dc[dx].intstate = SMLC_NOTINTERRUPTING;
@@ -534,9 +506,7 @@ int devsmlc (int class, int func, int device) {
TRACE(T_INST, " SKS '%02o%02o\n", func, device);
if (func == 004) { /* skip if not interrupting */
#if DEBUG
fprintf(smlclog, "%s SKS '02%02o skip if not interrupting, state is %s\n", smlctimestamp, device, intstates[dc[dx].intstate]);
#endif
TRACE(T_SMLC, "%s SKS '02%02o skip if not interrupting, state is %s\n", smlctimestamp, device, intstates[dc[dx].intstate]);
if (dc[dx].intstate == SMLC_INTERRUPTING) {
IOSKIP;
}
@@ -564,16 +534,12 @@ int devsmlc (int class, int func, int device) {
}
}
putcrs16(A, data);
#if DEBUG
fprintf(smlclog, "%s INA '00%02o input status returns 0x%04x\n", smlctimestamp, device, data);
#endif
TRACE(T_SMLC, "%s INA '00%02o input status returns 0x%04x\n", smlctimestamp, device, data);
IOSKIP;
} else if (func == 011) { /* report device ID */
putcrs16(A, SMLC_DEVICEID);
#if DEBUG
fprintf(smlclog, "%s INA '11%02o report device ID returns 0x%04x\n", smlctimestamp, device, getcrs16(A));
#endif
TRACE(T_SMLC, "%s INA '11%02o report device ID returns 0x%04x\n", smlctimestamp, device, getcrs16(A));
IOSKIP;
} else {
@@ -595,9 +561,7 @@ int devsmlc (int class, int func, int device) {
data = getcrs16(A);
dc[dx].fncode = data >> 8;
dc[dx].lineno = data & 0377;
#if DEBUG
fprintf(smlclog, "%s OTA '00%02o set function/line '%03o/'%03o\n", smlctimestamp, device, dc[dx].fncode, dc[dx].lineno);
#endif
TRACE(T_SMLC, "%s OTA '00%02o set function/line '%03o/'%03o\n", smlctimestamp, device, dc[dx].fncode, dc[dx].lineno);
switch (dc[dx].fncode) {
case 000: // set modem controls
@@ -627,15 +591,11 @@ int devsmlc (int class, int func, int device) {
switch (dc[dx].fncode) {
case 000: // set modem controls
#if DEBUG
fprintf(smlclog, "%s OTA '01%02o line '%02o set modem controls '%06o\n", smlctimestamp, device, dc[dx].lineno, data);
fprintf(smlclog, "%s DTR: %d, RTS: %d\n", smlctimestamp, data & 1, (data & 2) == 1);
#endif
TRACE(T_SMLC, "%s OTA '01%02o line '%02o set modem controls '%06o\n", smlctimestamp, device, dc[dx].lineno, data);
TRACE(T_SMLC, "%s DTR: %d, RTS: %d\n", smlctimestamp, data & 1, (data & 2) == 1);
sp->dtr = data & 1;
if ((data & 1) == 0 && lp->fd != -1) {
#if DEBUG
fprintf(smlclog, "%s close connection to %s\n", smlctimestamp, lp->remoteID);
#endif
TRACE(T_SMLC, "%s close connection to %s\n", smlctimestamp, lp->remoteID);
close(lp->fd);
lp->fd = -1;
lp->connstate = SMLC_STATEDISCONNECTED;
@@ -647,9 +607,7 @@ int devsmlc (int class, int func, int device) {
case 010: // set special character
i = (data >> 8) & 0xff;
ch = data & 0xff;
#if DEBUG
fprintf(smlclog, "%s OTA '01%02o line '%02o set special character %d <%02x>\n", smlctimestamp, device, dc[dx].lineno, i, ch);
#endif
TRACE(T_SMLC, "%s OTA '01%02o line '%02o set special character %d <%02x>\n", smlctimestamp, device, dc[dx].lineno, i, ch);
if (i < SMLC_MAXSPCHARS) {
sp->spchars[i] = ch;
} else {
@@ -660,30 +618,26 @@ int devsmlc (int class, int func, int device) {
break;
case 012: // set configuration word
#if DEBUG
fprintf(smlclog, "%s OTA '01%02o line '%02o set configuration word 0x%04x\n", smlctimestamp, device, dc[dx].lineno, data);
#endif
TRACE(T_SMLC, "%s OTA '01%02o line '%02o set configuration word 0x%04x\n", smlctimestamp, device, dc[dx].lineno, data);
sp->configword = data;
IOSKIP;
break;
case 014: // set primary I/O channel
#if DEBUG
fprintf(smlclog, "%s OTA '01%02o line '%02o set primary I/O channel '%06o\n", smlctimestamp, device, dc[dx].lineno, data);
#endif
TRACE(T_SMLC, "%s OTA '01%02o line '%02o set primary I/O channel '%06o\n", smlctimestamp, device, dc[dx].lineno, data);
sp->dmcprimarycount = (data >> 12) + 1;
sp->dmcprimaryidx = 0;
sp->dmcprimary = data & 0x7fe;
if (data != 0) {
if ((data & 04000) == 0) fatal("Can't run SMLC in DMA mode!");
#if DEBUG
fprintf(smlclog, "%s %d buffers\n", smlctimestamp, sp->dmcprimarycount);
#ifndef NOTRACE
TRACE(T_SMLC, "%s %d buffers\n", smlctimestamp, sp->dmcprimarycount);
for (i = 0; i < sp->dmcprimarycount; i++) {
dmcpair = get32io ((data & 0x7fe) + (i * 2));
dmcbufbegea = dmcpair >> 16;
dmcbufendea = dmcpair & 0xffff;
dmcnw = (dmcbufendea - dmcbufbegea) + 1;
fprintf(smlclog, "%s %d: next '%06o, last '%06o, words %d\n", smlctimestamp, i, dmcbufbegea, dmcbufendea, dmcnw);
TRACE(T_SMLC, "%s %d: next '%06o, last '%06o, words %d\n", smlctimestamp, i, dmcbufbegea, dmcbufendea, dmcnw);
}
#endif
}
@@ -691,10 +645,8 @@ int devsmlc (int class, int func, int device) {
break;
case 015: // enable
#if DEBUG
fprintf(smlclog, "%s OTA '01%02o line '%02o enable '%06o (%s %s)\n", smlctimestamp, device, dc[dx].lineno, data,
TRACE(T_SMLC, "%s OTA '01%02o line '%02o enable '%06o (%s %s)\n", smlctimestamp, device, dc[dx].lineno, data,
sx ? "xmit" : "recv", (data & 1) ? "on" : "off");
#endif
sp->enabled = data & 1;
if (sp->enabled) devpoll[device] = 1;
IOSKIP;
@@ -706,22 +658,20 @@ int devsmlc (int class, int func, int device) {
* this OTA function. Consequently, this emulation module does not currently implement any logic to alternate
* storing recceived data between the primary and backup channels.
*/
#if DEBUG
fprintf(smlclog, "%s OTA '01%02o line '%02o set backup I/O channel '%06o\n", smlctimestamp, device, dc[dx].lineno, data);
#endif
TRACE(T_SMLC, "%s OTA '01%02o line '%02o set backup I/O channel '%06o\n", smlctimestamp, device, dc[dx].lineno, data);
sp->dmcbackupcount = (data >> 12) + 1;
sp->dmcbackupidx = 0;
sp->dmcbackup = data & 0x7fe;
if (data != 0) {
if ((data & 04000) == 0) fatal("Can't run SMLC in DMA mode!");
#if DEBUG
fprintf(smlclog, "%s %d buffers\n", smlctimestamp, sp->dmcbackupcount);
#ifndef NOTRACE
TRACE(T_SMLC, "%s %d buffers\n", smlctimestamp, sp->dmcbackupcount);
for (i = 0; i < sp->dmcbackupcount; i++) {
dmcpair = get32io ((data & 0x7fe) + (i * 2));
dmcbufbegea = dmcpair >> 16;
dmcbufendea = dmcpair & 0xffff;
dmcnw = (dmcbufendea - dmcbufbegea) + 1;
fprintf(smlclog, "%s %d: next '%06o, last '%06o, words %d\n", smlctimestamp, i, dmcbufbegea, dmcbufendea, dmcnw);
TRACE(T_SMLC, "%s %d: next '%06o, last '%06o, words %d\n", smlctimestamp, i, dmcbufbegea, dmcbufendea, dmcnw);
}
#endif
}
@@ -729,9 +679,7 @@ int devsmlc (int class, int func, int device) {
break;
case 017: // set status channel
#if DEBUG
fprintf(smlclog, "%s OTA '01%02o line '%02o set status channel '%06o\n", smlctimestamp, device, dc[dx].lineno, data);
#endif
TRACE(T_SMLC, "%s OTA '01%02o line '%02o set status channel '%06o\n", smlctimestamp, device, dc[dx].lineno, data);
if (!(data & 04000) && data != 0)
fatal("Can't run SMLC in DMA mode!");
if (lx != 0 || sx != 0) {
@@ -740,9 +688,7 @@ int devsmlc (int class, int func, int device) {
fatal(NULL);
}
dc[dx].dmcstatus = data & 0x7fe;
#if DEBUG
fprintf(smlclog, "%s next %06o, last %06o\n", smlctimestamp, get32io(dc[dx].dmcstatus) >> 16, get32io(dc[dx].dmcstatus) & 0xffff);
#endif
TRACE(T_SMLC, "%s next %06o, last %06o\n", smlctimestamp, get32io(dc[dx].dmcstatus) >> 16, get32io(dc[dx].dmcstatus) & 0xffff);
IOSKIP;
break;
@@ -754,9 +700,7 @@ int devsmlc (int class, int func, int device) {
break;
case 016: // set interrupt vector
#if DEBUG
fprintf(smlclog, "%s OTA '16%02o set interrupt vector '%06o\n", smlctimestamp, device, getcrs16(A));
#endif
TRACE(T_SMLC, "%s OTA '16%02o set interrupt vector '%06o\n", smlctimestamp, device, getcrs16(A));
dc[dx].intvector = getcrs16(A);
IOSKIP;
break;
@@ -785,9 +729,7 @@ int devsmlc (int class, int func, int device) {
} else {
gv.intvec = dc[dx].intvector;
dc[dx].intstate = SMLC_INTERRUPTING;
#if DEBUG
fprintf(smlclog, "%s Raise interrupt on device '%02o\n", smlctimestamp, device);
#endif
TRACE(T_SMLC, "%s Raise interrupt on device '%02o\n", smlctimestamp, device);
}
return 0;
}
@@ -798,23 +740,41 @@ int devsmlc (int class, int func, int device) {
for (lx = 0; lx < SMLC_LINESPERBOARD; lx++) {
lp = &dc[dx].plines[lx];
if (lp->host == 0) continue;
if (lp->port == 0) continue;
switch (lp->connstate) {
case SMLC_STATEDISCONNECTED:
if (lp->nextconntime <= currenttime && lp->slines[SMLC_RECVIX].dtr != 0) {
if (lp->host != 0) {
//
// Workstation mode.
//
// Periodically attempt to create a connection to the remote host, when
// the DTR modem signal is up.
//
if (lp->slines[SMLC_RECVIX].dtr == 0 || lp->nextconntime > currenttime) continue;
fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (fd < 0) {
fprintf(stderr, "Failed to create socket for SMLC line %d\n", (dx * SMLC_LINESPERBOARD) + lx);
fatal(NULL);
}
setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&optenable, sizeof(optenable));
fcntl(fd, F_SETFL, O_NONBLOCK);
bzero((char *) &raddr, sizeof(raddr));
raddr.sin_family = AF_INET;
raddr.sin_addr.s_addr = htonl(dc[dx].plines[lx].host);
raddr.sin_port = htons(dc[dx].plines[lx].port);
rc = connect(fd, (struct sockaddr *)&raddr, sizeof(raddr));
if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&optenable, sizeof(optenable)) == -1) {
fprintf(stderr, "Failed to set socket option SO_KEEPALICE for SMLC line %d\n", (dx * SMLC_LINESPERBOARD) + lx);
fatal(NULL);
}
if ((flags = fcntl(fd, F_GETFL)) == -1) {
fprintf(stderr, "Failed to get flags for SMLC line %d\n", (dx * SMLC_LINESPERBOARD) + lx);
fatal(NULL);
}
flags |= O_NONBLOCK;
if (fcntl(fd, F_SETFL, flags) == -1) {
fprintf(stderr, "Failed to set flags for SMLC line %d\n", (dx * SMLC_LINESPERBOARD) + lx);
fatal(NULL);
}
bzero((char *) &addr, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(lp->host);
addr.sin_port = htons(lp->port);
rc = connect(fd, (struct sockaddr *)&addr, sizeof(addr));
lp->connstate = SMLC_STATECONNECTING;
if (rc < 0 && errno != EINPROGRESS) {
fprintf(stderr, "Failed to create connection to %s for SMLC line %d\n", lp->remoteID,
@@ -824,18 +784,67 @@ int devsmlc (int class, int func, int device) {
lp->connstate = SMLC_STATEDISCONNECTED;
break;
} else { // connection in progress
#if DEBUG
fprintf(smlclog, "%s Connection initiated to %s for SMLC line %d\n", smlctimestamp, lp->remoteID,
TRACE(T_SMLC, "%s Connection initiated to %s for SMLC line %d\n", smlctimestamp, lp->remoteID,
(dx * SMLC_LINESPERBOARD) + lx);
#endif
lp->fd = fd;
}
} else {
//
// Host mode.
//
// Begin listening for connections.
//
fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd < 0) {
fprintf(stderr, "Failed to create socket for SMLC line %d\n", (dx * SMLC_LINESPERBOARD) + lx);
fatal(NULL);
}
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &optenable, sizeof(optenable)) == -1) {
fprintf(stderr, "Failed to set socket option SO_REUSEADDR for SMLC line %d\n", (dx * SMLC_LINESPERBOARD) + lx);
fatal(NULL);
}
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(lp->port);
if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
fprintf(stderr, "Failed to bind to port %d for SMLC line %d\n", lp->port, (dx * SMLC_LINESPERBOARD) + lx);
fatal(NULL);
}
if (listen(fd, 1) == -1) {
fprintf(stderr, "Failed to listen on port %d for SMLC line %d\n", lp->port, (dx * SMLC_LINESPERBOARD) + lx);
fatal(NULL);
}
if ((flags = fcntl(fd, F_GETFL)) == -1) {
fprintf(stderr, "Failed to get flags for SMLC line %d\n", (dx * SMLC_LINESPERBOARD) + lx);
fatal(NULL);
}
flags |= O_NONBLOCK;
if (fcntl(fd, F_SETFL, flags) == -1) {
fprintf(stderr, "Failed to set flags for SMLC line %d\n", (dx * SMLC_LINESPERBOARD) + lx);
fatal(NULL);
}
lp->fd = fd;
lp->connstate = SMLC_STATECONNECTING;
TRACE(T_SMLC, "%s Listening for connections on %s for SMLC line %d\n", smlctimestamp, lp->remoteID,
(dx * SMLC_LINESPERBOARD) + lx);
}
/* fall through when no error has occurred */
if (lp->connstate != SMLC_STATECONNECTING) break;
case SMLC_STATECONNECTING:
FD_SET(lp->fd, &writefds);
if (lp->fd > maxfd) maxfd = lp->fd;
if (lp->host != 0) {
//
// Workstation mode. Connection is complete when socket is writable.
//
FD_SET(lp->fd, &writefds);
if (lp->fd > maxfd) maxfd = lp->fd;
} else if (lp->slines[SMLC_RECVIX].dtr != 0) {
//
// Host mode. Connection request received when socket is readable.
// However, test for requests only when DTR modem signal is up.
//
FD_SET(lp->fd, &readfds);
if (lp->fd > maxfd) maxfd = lp->fd;
}
break;
case SMLC_STATECONNECTED:
@@ -863,11 +872,9 @@ int devsmlc (int class, int func, int device) {
if (dmcnw <= 0) {
sp->dmcprimaryidx += 1;
} else {
#if DEBUG
fprintf(smlclog, "%s Line %d on device '%02o send to %s from channel '%06o\n", smlctimestamp, lx, device, lp->remoteID,
TRACE(T_SMLC, "%s Line %d on device '%02o send to %s from channel '%06o\n", smlctimestamp, lx, device, lp->remoteID,
sp->dmcprimary);
fprintf(smlclog, "%s next '%06o, last '%06o, words %d\n", smlctimestamp, dmcbufbegea, dmcbufendea, dmcnw);
#endif
TRACE(T_SMLC, "%s next '%06o, last '%06o, words %d\n", smlctimestamp, dmcbufbegea, dmcbufendea, dmcnw);
n = 0;
while (n < dmcnw && np + 1 < ep) {
word = get16io(dmcbufbegea);
@@ -876,10 +883,8 @@ int devsmlc (int class, int func, int device) {
*np++ = word & 0377;
n += 1;
}
#if DEBUG
fprintf(smlclog, "%s %d words transferred to output buffer\n", smlctimestamp, n);
fprintf(smlclog, "%s next '%06o, last '%06o\n", smlctimestamp, dmcbufbegea, dmcbufendea);
#endif
TRACE(T_SMLC, "%s %d words transferred to output buffer\n", smlctimestamp, n);
TRACE(T_SMLC, "%s next '%06o, last '%06o\n", smlctimestamp, dmcbufbegea, dmcbufendea);
put16io(dmcbufbegea, sp->dmcprimary + (sp->dmcprimaryidx * 2));
if (n == dmcnw) {
status = SMLC_EOR | SMLC_XMIT;
@@ -925,7 +930,7 @@ int devsmlc (int class, int func, int device) {
for (lx = 0; lx < SMLC_LINESPERBOARD; lx++) {
lp = &dc[dx].plines[lx];
if (lp->host == 0) continue;
if (lp->port == 0) continue;
switch (lp->connstate) {
case SMLC_STATECONNECTING:
@@ -934,10 +939,7 @@ int devsmlc (int class, int func, int device) {
rc = getsockopt(lp->fd, SOL_SOCKET, SO_ERROR, &optval, &optlen);
if (rc < 0) {
fprintf(stderr, "Failed to get socket status for SMLC line %d\n", (dx * SMLC_LINESPERBOARD) + lx);
close(lp->fd);
lp->fd = -1;
lp->nextconntime = currenttime + SMLC_CONNECTINTERVAL;
lp->connstate = SMLC_STATEDISCONNECTED;
fatal(NULL);
} else if (optval != 0) { // connection failed
fprintf(stderr, "Failed to create connection to %s for SMLC line %d\n", lp->remoteID, (dx * SMLC_LINESPERBOARD) + lx);
close(lp->fd);
@@ -945,47 +947,72 @@ int devsmlc (int class, int func, int device) {
lp->nextconntime = currenttime + SMLC_CONNECTINTERVAL;
lp->connstate = SMLC_STATEDISCONNECTED;
} else {
#if DEBUG
fprintf(smlclog, "%s Connection created to %s for SMLC line %d\n", smlctimestamp, lp->remoteID, (dx * SMLC_LINESPERBOARD) + lx);
#endif
TRACE(T_SMLC, "%s Connection created to %s for SMLC line %d\n", smlctimestamp, lp->remoteID, (dx * SMLC_LINESPERBOARD) + lx);
lp->connstate = SMLC_STATECONNECTED;
lp->recvstate = SMLC_STATERCVSYN;
lp->starting = 0;
lp->naksent = 0;
lp->slines[SMLC_RECVIX].buf.in = 0;
lp->slines[SMLC_RECVIX].buf.out = 0;
lp->slines[SMLC_XMITIX].buf.in = 0;
lp->slines[SMLC_XMITIX].buf.out = 0;
smlcaddstatus(&dc[dx], lx, SMLC_DSS_BIT | SMLC_DSR_BIT | SMLC_CTS_BIT | SMLC_DCD_BIT | SMLC_SQ_BIT);
}
} else if (FD_ISSET(lp->fd, &readfds)) {
addrlen = sizeof(addr);
fd = accept(lp->fd, (struct sockaddr *)&addr, &addrlen);
if (fd >= 0) {
if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&optenable, sizeof(optenable)) == -1) {
fprintf(stderr, "Failed to set socket option SO_KEEPALICE for SMLC line %d\n", (dx * SMLC_LINESPERBOARD) + lx);
fatal(NULL);
}
if ((flags = fcntl(fd, F_GETFL)) == -1) {
fprintf(stderr, "Failed to get flags for SMLC line %d\n", (dx * SMLC_LINESPERBOARD) + lx);
fatal(NULL);
}
flags |= O_NONBLOCK;
if (fcntl(fd, F_SETFL, flags) == -1) {
fprintf(stderr, "Failed to set flags for SMLC line %d\n", (dx * SMLC_LINESPERBOARD) + lx);
fatal(NULL);
}
close(lp->fd);
lp->fd = fd;
lp->connstate = SMLC_STATECONNECTED;
lp->recvstate = SMLC_STATERCVSOH;
strcpy(lp->remoteID, inet_ntoa(addr.sin_addr));
TRACE(T_SMLC, "%s Connection accepted from %s for SMLC line %d\n", smlctimestamp, lp->remoteID,
(dx * SMLC_LINESPERBOARD) + lx);
}
else {
TRACE(T_SMLC, "%s Spurious connection attempt on SMLC line %d\n", smlctimestamp, (dx * SMLC_LINESPERBOARD) + lx);
}
}
if (lp->connstate != SMLC_STATECONNECTED)
break;
if (lp->connstate == SMLC_STATECONNECTED) {
lp->starting = 0;
lp->naksent = 0;
lp->slines[SMLC_RECVIX].buf.in = 0;
lp->slines[SMLC_RECVIX].buf.out = 0;
lp->slines[SMLC_XMITIX].buf.in = 0;
lp->slines[SMLC_XMITIX].buf.out = 0;
smlcaddstatus(&dc[dx], lx, SMLC_DSS_BIT | SMLC_DSR_BIT | SMLC_CTS_BIT | SMLC_DCD_BIT | SMLC_SQ_BIT);
}
break;
case SMLC_STATECONNECTED:
sp = &lp->slines[SMLC_RECVIX];
if (FD_ISSET(lp->fd, &readfds)) {
n = read(lp->fd, &sp->buf.data[sp->buf.in], sizeof(sp->buf.data) - sp->buf.in);
if (n > 0) {
#if DEBUG
fprintf(smlclog, "%s Line %d on device '%02o received %d bytes from %s:\n", smlctimestamp, lx, device, n, lp->remoteID);
#ifndef NOTRACE
TRACE(T_SMLC, "%s Line %d on device '%02o received %d bytes from %s:\n", smlctimestamp, lx, device, n, lp->remoteID);
smlclogbytes(&sp->buf.data[sp->buf.in], n);
smlclogflush();
#endif
sp->buf.in += n;
} else if (n < 0 && (errno == EWOULDBLOCK || errno == EAGAIN || errno == EINTR)) {
n = 0;
#if DEBUG
fprintf(smlclog, "%s recv from %s for line %d returned errno %d (ignored)\n",
TRACE(T_SMLC, "%s read from %s for line %d returned errno %d (ignored)\n",
smlctimestamp, lp->remoteID, (dx * SMLC_LINESPERBOARD) + lx, errno);
#endif
} else {
#if DEBUG
#ifndef NOTRACE
if (n < 0) {
fprintf(smlclog, "%s recv from %s for line %d failed with errno %d\n",
TRACE(T_SMLC, "%s read from %s for line %d failed with errno %d\n",
smlctimestamp, lp->remoteID, (dx * SMLC_LINESPERBOARD) + lx, errno);
} else {
fprintf(smlclog, "%s recv from %s for line %d returned EOF\n", smlctimestamp, lp->remoteID, (dx * SMLC_LINESPERBOARD) + lx);
TRACE(T_SMLC, "%s read from %s for line %d returned EOF\n", smlctimestamp, lp->remoteID, (dx * SMLC_LINESPERBOARD) + lx);
}
#endif
lp->connstate = SMLC_STATEDISCONNECTING;
@@ -1007,17 +1034,15 @@ int devsmlc (int class, int func, int device) {
sp->dmcprimaryidx += 1;
continue;
}
#if DEBUG
fprintf(smlclog, "%s Line %d on device '%02o receive from %s to channel '%06o\n", smlctimestamp, lx, device, lp->remoteID,
TRACE(T_SMLC, "%s Line %d on device '%02o receive from %s to channel '%06o\n", smlctimestamp, lx, device, lp->remoteID,
sp->dmcprimary);
fprintf(smlclog, "%s next '%06o, last '%06o, words %d\n", smlctimestamp, dmcbufbegea, dmcbufendea, dmcnw);
#endif
TRACE(T_SMLC, "%s next '%06o, last '%06o, words %d\n", smlctimestamp, dmcbufbegea, dmcbufendea, dmcnw);
maxbytes = dmcnw * 2;
n = 0;
dmcnw = 0;
status = 0;
isetb = 0;
while (n < maxbytes && np < ip) {
while (n < maxbytes && np < ip && status == 0) {
ch = *np++;
if (lp->recvstate == SMLC_STATERCVSYN) {
if (ch == SMLC_SYN) {
@@ -1030,10 +1055,8 @@ int devsmlc (int class, int func, int device) {
lp->recvstate = SMLC_STATERCVSYN;
break;
} else {
#if DEBUG
fprintf(smlclog, "%s NAK received after sending NAK to %s for line %d\n", smlctimestamp, lp->remoteID,
TRACE(T_SMLC, "%s NAK received after sending NAK to %s for line %d\n", smlctimestamp, lp->remoteID,
(dx * SMLC_LINESPERBOARD) + lx);
#endif
lp->connstate = SMLC_STATEDISCONNECTING;
smlcaddstatus(&dc[dx], lx, SMLC_DSS_BIT);
devpoll[device] = 1; /* return ASAP */
@@ -1052,29 +1075,64 @@ int devsmlc (int class, int func, int device) {
word = ch << 8;
}
n += 1;
if (lp->recvstate == SMLC_STATERCVDLE) {
switch (lp->recvstate) {
case SMLC_STATERCVDLE:
lp->recvstate = SMLC_STATERCVCHAR;
if (ch == SMLC_ACK0) {
switch (ch) {
case SMLC_ACK0:
status |= SMLC_ENCODE_BIT | SMLC_ACK0_STATUS;
lp->recvstate = SMLC_STATERCVSYN;
break;
} else if (ch == SMLC_STX) {
case SMLC_STX:
status |= SMLC_ENCODE_BIT | SMLC_STX_STATUS;
break;
} else if (ch == SMLC_ETB) {
case SMLC_ETB:
status |= SMLC_ENCODE_BIT | SMLC_ETB_STATUS;
lp->recvstate = SMLC_STATERCVSYN;
isetb = 1;
break;
default:
// do nothing
break;
}
} else if (ch == SMLC_DLE) {
lp->recvstate = SMLC_STATERCVDLE;
break;
case SMLC_STATERCVSOH:
if (ch == SMLC_SOH) {
status |= SMLC_ENCODE_BIT | SMLC_SOH_STATUS;
lp->recvstate = SMLC_STATERCVENQ;
} else {
n = 0;
}
break;
case SMLC_STATERCVENQ:
if (ch == SMLC_ENQ) {
status |= SMLC_ENCODE_BIT | SMLC_ENQ_STATUS;
lp->recvstate = SMLC_STATERCVSYN;
} else if (ch == SMLC_SOH) {
status |= SMLC_ENCODE_BIT | SMLC_SOH_STATUS;
} else {
n = 0;
lp->recvstate = SMLC_STATERCVSOH;
}
break;
case SMLC_STATERCVCHAR:
if (ch == SMLC_DLE) {
lp->recvstate = SMLC_STATERCVDLE;
}
break;
default:
fprintf(stderr, "Invalid recv state %d for SMLC line %d\n", lp->recvstate, (dx * SMLC_LINESPERBOARD) + lx);
fatal(NULL);
break;
}
}
if ((n & 1) != 0) { // odd number of bytes processed, so store "incomplete" word, or back up one byte
if (n < maxbytes
&& ((n > 1 && *(np - 1) == SMLC_ETB && *(np - 2) == SMLC_DLE)
|| (n == 1 && *(np - 1) == SMLC_NAK))) {
&& (status & SMLC_ENCODE_BIT) != 0
&& ( (status & SMLC_MASK_STATUS) == SMLC_ETB_STATUS
|| (status & SMLC_MASK_STATUS) == SMLC_SOH_STATUS
|| (status & SMLC_MASK_STATUS) == SMLC_ENQ_STATUS
|| (status & SMLC_MASK_STATUS) == SMLC_NAK_STATUS)) {
put16io(word, dmcbufbegea);
dmcbufbegea = INCVA(dmcbufbegea, 1);
dmcnw += 1;
@@ -1085,10 +1143,8 @@ int devsmlc (int class, int func, int device) {
}
put16io(dmcbufbegea, sp->dmcprimary + (sp->dmcprimaryidx * 2));
sp->dmcprimaryidx += 1;
#if DEBUG
fprintf(smlclog, "%s %d words transferred from input buffer\n", smlctimestamp, dmcnw);
fprintf(smlclog, "%s next '%06o, last '%06o\n", smlctimestamp, dmcbufbegea, dmcbufendea);
#endif
TRACE(T_SMLC, "%s %d words transferred from input buffer\n", smlctimestamp, dmcnw);
TRACE(T_SMLC, "%s next '%06o, last '%06o\n", smlctimestamp, dmcbufbegea, dmcbufendea);
if (n == maxbytes) {
status |= SMLC_EOR;
} else if (n > maxbytes) {
@@ -1117,8 +1173,8 @@ int devsmlc (int class, int func, int device) {
lp->naksent = nbytes >= 5 && sp->buf.data[sp->buf.out + 4] == SMLC_NAK;
n = write(lp->fd, &sp->buf.data[sp->buf.out], nbytes);
if (n >= 0) {
#if DEBUG
fprintf(smlclog, "%s Line %d on device '%02o sent %d bytes to %s:\n", smlctimestamp, lx, device, n, lp->remoteID);
#ifndef NOTRACE
TRACE(T_SMLC, "%s Line %d on device '%02o sent %d bytes to %s:\n", smlctimestamp, lx, device, n, lp->remoteID);
smlclogbytes(&sp->buf.data[sp->buf.out], n);
smlclogflush();
#endif
@@ -1128,10 +1184,8 @@ int devsmlc (int class, int func, int device) {
smlcaddstatus(&dc[dx], lx, SMLC_LCT | SMLC_XMIT);
}
} else if (errno != EWOULDBLOCK && errno != EAGAIN && errno != EINTR) {
#if DEBUG
fprintf(smlclog, "%s Connection failed to %s for line %d with errno %d\n", smlctimestamp, lp->remoteID,
TRACE(T_SMLC, "%s Connection failed to %s for line %d with errno %d\n", smlctimestamp, lp->remoteID,
(dx * SMLC_LINESPERBOARD) + lx, errno);
#endif
lp->connstate = SMLC_STATEDISCONNECTING;
smlcaddstatus(&dc[dx], lx, SMLC_DSS_BIT);
devpoll[device] = 1; /* return ASAP */
@@ -1155,7 +1209,6 @@ int devsmlc (int class, int func, int device) {
return 0;
}
#if DEBUG
static unsigned char ebcdicToAscii[256] = {
/* 00-07 */ 0x00, 0x01, 0x02, 0x03, 0x1a, 0x09, 0x1a, 0x7f,
/* 08-0F */ 0x1a, 0x1a, 0x1a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
@@ -1193,14 +1246,16 @@ static unsigned char ebcdicToAscii[256] = {
};
static void smlclogflush(void) {
#ifndef NOTRACE
if (smlclogbytescol > 0) {
fputs(smlclogbuf, smlclog);
fputc('\n', smlclog);
fflush(smlclog);
fputs(smlclogbuf, gv.tracefile);
fputc('\n', gv.tracefile);
fflush(gv.tracefile);
}
smlclogbytescol = 0;
memset(smlclogbuf, ' ', LogLineLength);
smlclogbuf[LogLineLength] = '\0';
#endif
}
static void smlclogbytes(unsigned char *bytes, int len) {
@@ -1211,24 +1266,27 @@ static void smlclogbytes(unsigned char *bytes, int len) {
int hexCol;
int i;
ascCol = AsciiColumn(smlclogbytescol);
hexCol = HexColumn(smlclogbytescol);
#ifndef NOTRACE
if (gv.traceflags & T_SMLC) {
ascCol = AsciiColumn(smlclogbytescol);
hexCol = HexColumn(smlclogbytescol);
for (i = 0; i < len; i++) {
b = bytes[i];
ac = ebcdicToAscii[b];
if (ac < 0x20 || ac >= 0x7f) {
ac = '.';
}
sprintf(hex, "%02x", b);
memcpy(smlclogbuf + hexCol, hex, 2);
hexCol += 3;
smlclogbuf[ascCol++] = ac;
if (++smlclogbytescol >= 16) {
smlclogflush();
ascCol = AsciiColumn(smlclogbytescol);
hexCol = HexColumn(smlclogbytescol);
for (i = 0; i < len; i++) {
b = bytes[i];
ac = ebcdicToAscii[b];
if (ac < 0x20 || ac >= 0x7f) {
ac = '.';
}
sprintf(hex, "%02x", b);
memcpy(smlclogbuf + hexCol, hex, 2);
hexCol += 3;
smlclogbuf[ascCol++] = ac;
if (++smlclogbytescol >= 16) {
smlclogflush();
ascCol = AsciiColumn(smlclogbytescol);
hexCol = HexColumn(smlclogbytescol);
}
}
}
}
#endif
}

22
em.1
View File

@@ -145,6 +145,15 @@ on the host. Multiple trace types may be listed, separated by
spaces. Tracing may be initially turned off by including the
.I off
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
.TP
@@ -166,13 +175,15 @@ Example:
.EE
.TP
smlc.cfg
Used to associate sync serial (MDLC or HSSMLC) lines with a destination
IP address and port number. Optional. Comments work the same as
amlc.cfg. Example:
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
1 127.0.0.1:2554
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
@@ -286,6 +297,7 @@ off|Start with tracing disabled
all|Everything
flush|Flush trace file after each write
tlb|STLB and IOTLB changes
smlc|MDLC/HSSMLC device I/O
OWNERL|Execution of this PCB
#instruction count|Begin after specified number of instructions
|(the leading # is literal)

232
em.c
View File

@@ -1,5 +1,5 @@
/* 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:
- booting from a Prime disk image (normal usage)
@@ -15,7 +15,7 @@
$ time ./em -tport 8000 -cpuid 5 -boot 14714 -map MFD.2462/PRIRUN/RING0.MAP MFD.2462/PRIRUN/RING3.MAP 2>err
Disk boot device is 14uc4, tape is 10005,
Disk boot device is 14uc4, tape is 10005,
where u=1/3/5/7 for units 0/1/2/3
and c=1/3/5/7 for controller 26/27/...
(See complete boot table below)
@@ -23,7 +23,7 @@
NOTE: the -map command is optional, but is needed to set the
real-time clock automatically for older models (cpuid < 15). If
maps are not available, use the Primos SE command to set the clock
manually after the system boots:
manually after the system boots:
SE -MMDDYY HHMM
-------------
@@ -40,11 +40,11 @@ $ time ./em -cpuid 5 -boot SAM.SAVE 14114 2>err
Enter physical device = 2466
QUICK VERIFY MODE Enabled; Enter 'RESET QVFY' for normal operation.
Enter 'SET DCM' to display CASE messages.
QUICK VERIFY MODE Enabled; Enter 'RESET QVFY' for normal operation.
Enter 'SET DCM' to display CASE messages.
Enter 'LOAD;RUN' for Default Execution
SAM>
SAM>
--------------
Usage: to load initial boot from tape, then prompt for disk pdev
@@ -56,7 +56,7 @@ Sense switches set to 1005 <--- these cause pdev prompt
PHYSICAL DEVICE=2466
DISK ERROR, STATUS: 000001
DISK ERROR, STATUS: 000001
PHYSICAL DEVICE=
---------------
@@ -70,12 +70,12 @@ RUN FILE TREENAME=MFD>DOS>DOS.SAVE
BOOTING FROM MT0 MFD>DOS>DOS.SAVE
PRIMOS II REV 20.0 03/15/85 (AT 170000)
PRIMOS II REV 20.0 03/15/85 (AT 170000)
Copyright (c) Prime Computer, Inc. 1985.
PRIMOS II is being phased out. To boot PRIMOS return to CP mode.
PRIMOS II is being phased out. To boot PRIMOS return to CP mode.
("BOOT 14xxx" will autoboot PRIMOS.)
OK:
OK:
---------------
TRACING:
@@ -138,7 +138,7 @@ gv.tracetriggered = 1; this simulates the Ctrl-t.
Locations '40-'57 are reserved for 8 DMC channels, 2 words each.
Locations '60-'77 are interrupt vectors
Locations '100-'177 are for external device interrupts
Locations '100-'177 are for external device interrupts
see p. A-8 of Sys Arch
In VI mode, locations 0-'17 are trapped and map to the live
@@ -191,7 +191,7 @@ static void macheck (unsigned short p300vec, unsigned short chkvec, unsigned int
/* set condition codes based on V-mode FP accumulator
NOTES:
NOTES:
-Prime considers anything with a zero fraction to be zero,
even if the exponent is non-zero (this is a "dirty zero")
@@ -202,7 +202,7 @@ static void macheck (unsigned short p300vec, unsigned short chkvec, unsigned int
XXX: #define SETCC_F SETCC_32(getcrs32(FLTH))
*/
#define SETCC_F \
CLEARCC; \
if (getcrs32s(FLTH) < 0) \
@@ -274,6 +274,7 @@ static void macheck (unsigned short p300vec, unsigned short chkvec, unsigned int
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
@@ -287,6 +288,7 @@ static void macheck (unsigned short p300vec, unsigned short chkvec, unsigned int
T_PX Process exchange
T_LM License manager
T_TLB STLB and IOTLB changes
T_SMLC SMLC device I/O
*/
#define T_EAR 0x00000001
@@ -309,6 +311,8 @@ static void macheck (unsigned short p300vec, unsigned short chkvec, unsigned int
#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 BITMASK32(b) ((unsigned int)(0x80000000) >> ((b)-1))
@@ -433,7 +437,7 @@ typedef struct {
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.
being 1 means that writes are allowed.
IMPORTANT NOTE: bit 4 MUST BE CHECKED before all write references!
*/
@@ -656,6 +660,7 @@ static unsigned short *physmem = NULL; /* system's physical memory */
//static ea_t tnoua_ea=0, tnou_ea=0, tsrc_ea=0;
static int domemdump; /* -memdump arg */
static int dolinecleararg; /* -dolineclear arg */
static int tport; /* -tport option (incoming terminals) */
static int nport; /* -nport option (PNC/Ringnet) */
@@ -711,7 +716,7 @@ char *brp_name() {
match; if the address isn't found exactly, the index returned
will be the address lower than the requested address, or -1
if the symbol table is empty or the requested address is
lower than any in the symbol table
lower than any in the symbol table
Symbol types:
e = address of ecb
@@ -848,7 +853,7 @@ char *searchloadmap(int addr, char type) {
return buf[bufix];
} else
return mapsym[ix].symname;
} else
} else
return &blank;
}
@@ -1067,11 +1072,11 @@ static unsigned int mapio(ea_t ea) {
#define put64(value, ea) (put64r((value),(ea),RP))
#define put64r0(value, ea) (put64r((value),(ea),0))
/*
/*
get16 handles 16-bit fetches that CANNOT cause address traps.
get16t handles 16-bit fetches that MIGHT cause address traps,
indicated by the sign bit set in EA.
indicated by the sign bit set in EA.
Address traps can occur:
- fetching S/R mode instructions
- fetching V-mode instructions when RPL < 010 or 040 (seg enabled/not)
@@ -1087,7 +1092,7 @@ static unsigned int mapio(ea_t ea) {
get16r handles 16-bit fetches that used a passed-in virtual address;
(only the ring part is used from this address).
VERY IMPORTANT: get16r _cannot_ use the supercache! You don't want to
VERY IMPORTANT: get16r _cannot_ use the supercache! You don't want to
cache Ring 0 accesses to data, then let Ring 3 use the cache! It could
be changed to use a special R0 brp cache entry, but probably not much
gain performance-wise. Might speed up process exchange...
@@ -1487,7 +1492,7 @@ static void put64r(long long value, ea_t ea, ea_t rpring) {
disk buffers and shutdown gracefully.
NOTE: it may take up to 1 minute for Primos to see the sensor
check. User 1 will usually be at the OK, prompt, waiting on
check. User 1 will usually be at the OK, prompt, waiting on
input, and the check will occur when the 1-minute abort occurs.
NOTE: these are inactive if the emulator is using dedicated
@@ -1542,7 +1547,7 @@ void macheck (unsigned short p300vec, unsigned short chkvec, unsigned int dswsta
}
/* queue instructions
/* queue instructions
NOTE: ABQ is typically used in software to add an item to a
hardware (physical) queue and RTQ is used by DMQ hardware to fetch
@@ -1550,7 +1555,7 @@ void macheck (unsigned short p300vec, unsigned short chkvec, unsigned int dswsta
support physical queues, but only ABQ and RTQ currently support
them (they're needed for AMLC boards). If ICS support is added,
the other queue instructions will probably need to support physical
queues.
queues.
The CPU KEYS are not set here because this would not happen on a
DMQ request - only when the instruction is executed by software.
@@ -1736,7 +1741,7 @@ static int (*devmap[64])(int, int, int) = {
static void warn(char *msg) {
printf("emulator warning:\n instruction #%u at %o/%o: %o %o keys=%o, modals=%o\n %s\n", gv.instcount, gv.prevpc >> 16, gv.prevpc & 0xFFFF, get16t(gv.prevpc), get16t(gv.prevpc+1),getcrs16(KEYS), getcrs16(MODALS), msg);
}
/* return a string for the keys
C = C bit set
@@ -1789,7 +1794,7 @@ char *keystring(unsigned short keys) {
/* return a string for the modals
I = interrupts inhibited, bit 1 = 0
E = interrupts enabled, bit 1 = 1
V = vectored interrupt mode, bit 2 = 1
0-7 = CRS, bits 9-11
@@ -1854,7 +1859,7 @@ static void fatal(char *msg) {
if (msg)
printf(": %s", msg);
printf("\n");
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)));
@@ -1896,7 +1901,7 @@ static void fatal(char *msg) {
for (i=0; i<64; i++)
devmap[i](-2, 0, i);
}
#ifndef NOTRACE
fclose(gv.tracefile);
#endif
@@ -1945,7 +1950,7 @@ static void newkeys (unsigned short new) {
}
static void fault(unsigned short fvec, unsigned short fcode, ea_t faddr) {
static unsigned char faultname[LASTFAULT-FIRSTFAULT+2][4] =
static unsigned char faultname[LASTFAULT-FIRSTFAULT+2][4] =
{"RXM", "PRC", "PAG", "SVC", "UII", "SEM", "MCK", "MM", "ILL", "ACC", "ARI", "STK", "SEG", "PTR", "-?-"};
unsigned char *faultnamep;
ea_t pcbp, pxfvec, csea, ea;
@@ -1978,7 +1983,7 @@ static void fault(unsigned short fvec, unsigned short fcode, ea_t faddr) {
regs.sym.pswkeys = crs[KEYS]; /* Prime->Prime: no swap! */
putcrs16(FCODE, fcode);
putcrs32(FADDR, faddr);
if (FIRSTFAULT <= fvec && fvec <= LASTFAULT)
faultnamep = faultname[fvec-FIRSTFAULT];
else
@@ -2070,7 +2075,7 @@ static void fault(unsigned short fvec, unsigned short fcode, ea_t faddr) {
}
/* 16S Addressing Mode
/* 16S Addressing Mode
NOTE: when fetching indirect words via address traps, ie, the word
is in a register, the segment and ring bits are unimportant. But
@@ -2082,7 +2087,7 @@ static void fault(unsigned short fvec, unsigned short fcode, ea_t faddr) {
*/
static ea_t ea16s (unsigned short inst) {
unsigned short rpl, amask, i, x;
int indlevel;
ea_t ea, va;
@@ -2121,7 +2126,7 @@ static ea_t ea16s (unsigned short inst) {
/* 32S Addressing Mode */
static ea_t ea32s (unsigned short inst) {
unsigned short rpl, amask, i, x;
int indlevel;
ea_t ea, va;
@@ -2188,7 +2193,7 @@ static inline ea_t ea32r64r (ea_t earp, unsigned short inst) {
TRACE(T_EAR, " PC relative, P=%o, new ea=%o\n", rpl, ea);
eap = &gv.brp[RPBR];
}
else
else
goto special; /* special cases */
else {
eap = &gv.brp[S0BR];
@@ -2349,7 +2354,7 @@ special:
static ea_t apea(unsigned short *bitarg) {
unsigned short ibr, ea_s, ea_w, bit, br, a;
unsigned int utempl;
ea_t ea, ip;
ea_t ea, ip, iwea;
eap = &gv.brp[RPBR];
utempl = get32(RP);
@@ -2375,8 +2380,11 @@ static ea_t apea(unsigned short *bitarg) {
bit = get16(INCVA(ea,2)) >> 12;
else
bit = 0;
iwea = ea;
ea = ip;
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)
*bitarg = bit;
@@ -2416,7 +2424,7 @@ static inline void mathexception(unsigned char extype, unsigned short fcode, ea_
putcrs16(KEYS, getcrs16(KEYS) | 0x8000);
switch (extype) {
case 'i':
if (getcrs16(KEYS) & 0400)
if (getcrs16(KEYS) & 0400)
fault(ARITHFAULT, fcode, faddr);
break;
case 'd':
@@ -2710,7 +2718,7 @@ static void argt() {
rp = get32(stackfp+2);
/* reload the caller's base registers for EA calculations */
brsave[0] = rp >> 16; brsave[1] = 0;
/* set ring bits on LB and SB to get correct ring on args */
brsave[2] = get16(stackfp+4) | ((rp >> 16) & RINGMASK16);
@@ -2837,7 +2845,7 @@ static void pcl (ea_t ecbea) {
/* get a copy of the ecb. gates must be aligned on a 16-word
boundary, therefore can't cross a page boundary, and mapva has
already ensured that the ecb page is resident. For a non-gate
ecb, check to see if it crosses a page boundary. If not, a
ecb, check to see if it crosses a page boundary. If not, a
memcpy is okay; if it does, do fetches */
if (access == 1 && (ecbea & 0xF) != 0)
@@ -2958,7 +2966,7 @@ static void pcl (ea_t ecbea) {
Y(low) = number of arguments left to transfer (JW hack!) */
/* if a page fault occurs during argument transfer, we need to
/* if a page fault occurs during argument transfer, we need to
make sure to use the current RP, which points to the ARGT
instruction. Otherwise, the return from the page fault
is to the PCL instruction, which has already completed at
@@ -3040,7 +3048,7 @@ static void pcl (ea_t ecbea) {
utempa = get16(get32(ea)); /* 1st arg: key */
TRACEA(" TSRC$$: key = %d\n", utempa);
eatemp = get32(ea+9); /* 4th arg: CP(1..2) */
utempl = get32(eatemp);
utempl = get32(eatemp);
utempa1 = utempl>>16; /* starting cp */
utempa2 = utempl&0xffff; /* # chars */
TRACEA(" cp(0)=%d, cp(1)=%d, loc(cp)=%o/%o\n", utempa1, utempa2, eatemp>>16, eatemp&0xffff);
@@ -3118,7 +3126,7 @@ static void calf(ea_t ea) {
for (i=0; i<6; i++)
cs[i] = get16r0(csea+i);
/* pop the concealed stack
/* pop the concealed stack
XXX: this was after code below. Does it matter? */
put16r0(this, pcbp+PCBCSNEXT);
@@ -3138,7 +3146,7 @@ static void calf(ea_t ea) {
/* process exchange register save: saves the current register
set to the process pcb.
set to the process pcb.
NOTES:
- adding "wait" arg and only saving base registers fixed Case 63
*/
@@ -3160,7 +3168,7 @@ static void pxregsave(unsigned short wait) {
}
TRACE(T_PX, "pxregsave: saving registers owned by %o (wait=%d)\n", getcrs16(OWNERL), wait);
/* NB: I think hardware might save the base registers in a predictable
location in the PCB register save area, rather than compressed in a
random order, because IIRC, Primos sometimes looks at a waiting
@@ -3245,7 +3253,7 @@ static void ors(unsigned short pcbw) {
debug, I wanted to make sure a process never owns 2 register sets */
if (ownerl == pcbw) {
if (ownedx >= 0)
if (ownedx >= 0)
fatal("Process owns more than 1 register set!");
ownedx = rx;
} else if (ownerl == 0)
@@ -3366,7 +3374,7 @@ static void dispatcher() {
fatal(NULL);
}
#endif
/* pcbp now points to the process we're going to run (pcbw is the
16-bit word number that will go in OWNERL). By definition, this
process should not be on any wait lists, so pcb.waitlist(seg)
@@ -3471,7 +3479,7 @@ static void dispatcher() {
TRACE(T_PX, "dispatch: abort flags for %o are %o\n", getcrs16(OWNERL), utempa);
put16r0(0, pcbp+PCBABT);
fault(PROCESSFAULT, utempa, 0);
fatal("fault returned after process fault");
fatal("fault returned after process fault");
}
firstbdx = 1;
@@ -3579,8 +3587,8 @@ static unsigned short ready (ea_t pcbp, unsigned short begend) {
resched = 0;
if (level < getar16(PLA16) || (level == getar16(PLA16) && begend)) {
regs.sym.plb = regs.sym.pla; /* Prime->Prime: no byte swap */
regs.sym.pcbb = regs.sym.pcba; /* Prime->Prime: no byte swap */
regs.sym.plb = regs.sym.pla; /* Prime->Prime: no byte swap */
regs.sym.pcbb = regs.sym.pcba; /* Prime->Prime: no byte swap */
putar16(PLA16, level);
putar16(PCBA16, pcbw);
resched = 1;
@@ -3780,7 +3788,7 @@ static void lpsw() {
if (getcrs16(MODALS) & 4) {
TRACE(T_PX, "Segmentation enabled\n");
gv.livereglim = 010;
} else
} else
gv.livereglim = 040;
if (getcrs16(MODALS) & 010) {
TRACE(T_PX, "Process exchange enabled:\n");
@@ -3823,7 +3831,7 @@ static void sssn() {
put16(0, ea+i);
}
/* if SSSN is from segment 14, it's probably the rev 23.4.Y2K
/* if SSSN is from segment 14, it's probably the rev 23.4.Y2K
system serial number check. Bypass the check loop. */
if (RPH == 014)
@@ -4163,9 +4171,9 @@ static int add32(unsigned int a1, unsigned int a2, unsigned int a3, ea_t ea) {
uresult = utemp; /* truncate result to result size */
retval = uresult;
if (utemp & 0x100000000LL) /* set L-bit if carry occurred */
link = 020000;
link = 020000;
if (uresult == 0) /* set EQ? */
eq = 0100;
eq = 0100;
if (((~a1 ^ a2) & (a1 ^ uresult) & 0x80000000) == 0) {
if (*(int *)&uresult < 0)
lt = 0200;
@@ -4188,11 +4196,11 @@ static int add16(unsigned short a1, unsigned short a2, unsigned short a3, ea_t e
uresult = a1; /* expand to higher precision */
uresult += a2; /* double-precision add */
uresult += a3; /* again, for subtract */
keybits = (uresult & 0x10000) >> 3; /* set L-bit if carry occurred */
keybits = (uresult & 0x10000) >> 3; /* set L-bit if carry occurred */
uresult &= 0xFFFF; /* truncate result */
retval = uresult; /* save result */
if (uresult == 0) /* set EQ? */
keybits |= 0100;
keybits |= 0100;
oflow = (((~a1 ^ a2) & (a1 ^ uresult) & 0x8000) != 0); /* overflow! */
if (oflow)
uresult = ~uresult;
@@ -4366,7 +4374,7 @@ int main (int argc, char **argv) {
#define XRBRACE 0375
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)) {
exit(0);
}
@@ -4449,6 +4457,7 @@ int main (int argc, char **argv) {
#include "dispatch.h"
domemdump = 0;
dolinecleararg = 0;
bootarg = NULL;
bootfile[0] = 0;
gv.pmap32bits = 0;
@@ -4468,6 +4477,9 @@ int main (int argc, char **argv) {
} else if (strcmp(argv[i],"-memdump") == 0) {
domemdump = 1;
} else if (strcmp(argv[i],"-dolineclear") == 0) {
dolinecleararg = 1;
} else if (strcmp(argv[i],"-ss") == 0) {
if (i+1 < argc && argv[i+1][0] != '-') {
sscanf(argv[++i],"%o", &templ);
@@ -4606,6 +4618,10 @@ int main (int argc, char **argv) {
setlinebuf(gv.tracefile);
else if (strcmp(argv[i],"tlb") == 0)
gv.traceflags |= T_TLB;
else if (strcmp(argv[i],"smlc") == 0)
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)
gv.traceuser = 0100000 | (templ<<6); /* form OWNERL for user # */
else if (strlen(argv[i]) == 6 && sscanf(argv[i],"%o", &templ) == 1)
@@ -4676,7 +4692,7 @@ int main (int argc, char **argv) {
regs.u32[i] = 0;
crsl = (void *)regs.sym.userregs[0]; /* first user register set */
putcrs16(MODALS, 0); /* interrupts inhibited */
newkeys(0);
RP = 01000;
@@ -4692,8 +4708,8 @@ int main (int argc, char **argv) {
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
/* if no maps were specified on the command line, look for ring0.map and
ring3.map in the current directory and read them */
if (numsyms == 0) {
@@ -4894,7 +4910,7 @@ a filename, CPU registers and keys are loaded from the runfile header.\n\
close(bootfd);
/* check we got it all */
if (nw2 != nw*2) {
printf("rvec[0]=%d, rvec[1]=%d, nw2=%d, nw=%d, nw*2=%d\n", rvec[0], rvec[1], nw2, nw, nw*2);
fatal("Didn't read entire boot program");
@@ -4905,7 +4921,7 @@ a filename, CPU registers and keys are loaded from the runfile header.\n\
#if 1
/* do a partial sysclr... move it here later if it fixes rev 19
reboot from tape (ctrl-b) */
putcrs16(MODALS, 0); /* interrupts inhibited */
for (i=0; i < STLBENTS; i++)
gv.stlb[i].seg = 0xFFFF; /* marker for invalid STLB entry */
@@ -5131,7 +5147,7 @@ fetch:
/* NOTE: Rev 21 Sys Arch Guide, 2nd Ed, pg 3-32 says:
"When bits 17 to 32 of the program counter contain a value within
the ATR (address trap range) and the processor is reading an
the ATR (address trap range) and the processor is reading an
instruction, an address trap always occurs. The only exception
to this is if the machine is operating in 32I mode."
@@ -5145,7 +5161,7 @@ fetch:
NOTE 10/9/2007 (JW):
iget16 has checks for RP < 0 to indicate an address trap, and all
of the EA calculations can generate traps (and set the high-order
bit) except 32I.
bit) except 32I.
If it's a bad idea for RP to have the high-order bit set, then
this code can be enabled, along with special handling in JMP,
@@ -5244,7 +5260,7 @@ xec:
} else if ((getcrs16(KEYS) & 0016000) == 010000) { /* E32I */
goto imode;
} else if ((inst & 036000) == 030000) { /* check for pio in S/R modes, */
pio(inst); /* before calculating EA */
goto fetch;
@@ -5516,7 +5532,7 @@ d_stac: /* 001200 */
if (get16(ea) == getcrs16(B)) {
put16(getcrs16(A), ea);
SETEQ;
} else
} else
CLEAREQ;
goto fetch;
@@ -5526,7 +5542,7 @@ d_stlc: /* 001204 */
if (get32(ea) == getcrs32(E)){
put32(getcrs32(L), ea);
SETEQ;
} else
} else
CLEAREQ;
goto fetch;
@@ -5656,7 +5672,7 @@ d_zmvd: /* 001115 */
while (utempa--)
*zcp2++ = *zcp1++;
#else
/* this causes error:
/* this causes error:
Coldstarting PRIMOS, Please wait...
Unable to initialize gate segment. (GATE_INIT) */
memcpy(zcp2, zcp1, utempa);
@@ -6054,7 +6070,7 @@ d_wait: /* 000315 */
pwait();
goto fetch;
d_nfy: /* 1210 (nfye), 1211 (nfyb),
d_nfy: /* 1210 (nfye), 1211 (nfyb),
1214 (inen), 1215 (inbn), 1216 (inec), 1217 (inbc) */
TRACE(T_FLOW, " NFY\n", inst);
RESTRICT();
@@ -6254,7 +6270,7 @@ d_svc: /* 000505 */
TRACE(T_FLOW, " SVC\n");
fault(SVCFAULT, 0, 0);
fatal("Returned from SVC fault");
d_cea: /* 000111 */
TRACE(T_FLOW, " CEA\n");
switch ((getcrs16(KEYS) & 016000) >> 10) {
@@ -6293,13 +6309,31 @@ d_hlt: /* 000000 */
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));
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... ");
utempa = getchar();
fflush(stdout);
utempa = ' ';
n = 0;
while (n == 0)
n = read(ttydev, &utempa, 1);
/* utempa = getchar(); */
printf("\n");
if (utempa == '\r' || utempa == '\n')
{
close(ttydev);
goto fetch;
}
if (utempa == 'h')
{
close(ttydev);
break;
}
}
}
fatal("CPU halt");
@@ -6571,7 +6605,7 @@ d_bfgt: /* 0141611 */
d_bix: /* 0141334 */
TRACE(T_FLOW, " BIX\n");
putcrs16(X, getcrs16(X) + 1);
if (getcrs16(X) != 0)
if (getcrs16(X) != 0)
RPL = iget16(RP);
else
INCRP;
@@ -6580,7 +6614,7 @@ d_bix: /* 0141334 */
d_biy: /* 0141324 */
TRACE(T_FLOW, " BIY\n");
putcrs16(Y, getcrs16(Y) + 1);
if (getcrs16(Y) != 0)
if (getcrs16(Y) != 0)
RPL = iget16(RP);
else
INCRP;
@@ -6589,7 +6623,7 @@ d_biy: /* 0141324 */
d_bdy: /* 0140724 */
TRACE(T_FLOW, " BDY\n");
putcrs16(Y, getcrs16(Y) - 1);
if (getcrs16(Y) != 0)
if (getcrs16(Y) != 0)
RPL = iget16(RP);
else
INCRP;
@@ -6598,7 +6632,7 @@ d_bdy: /* 0140724 */
d_bdx: /* 0140734 */
TRACE(T_FLOW, " BDX\n");
putcrs16(X, getcrs16(X) - 1);
if (getcrs16(X) == 0)
if (getcrs16(X) == 0)
INCRP;
else {
m = iget16(RP);
@@ -6608,7 +6642,7 @@ d_bdx: /* 0140734 */
long delayusec, actualmsec;
/* for BDX * loop (backstop process mainly), we want to change
this to a long sleep so that the emulation host's CPU isn't
this to a long sleep so that the emulation host's CPU isn't
pegged the whole time the emulator is running.
So first, check to see if any device times expire sooner than
@@ -6649,7 +6683,7 @@ d_bdx: /* 0140734 */
if (delayusec > 1000) {
if (gettimeofday(&tv0, NULL) != 0)
fatal("em: gettimeofday 0 failed");
/* for some reason, the SIGTERM signal handler gets reset
during emulator initialization; this re-installs it */
@@ -6672,7 +6706,7 @@ d_bdx: /* 0140734 */
TRACEA(" BDX loop at %o/%o, owner=%o, utempl=%d, wanted %d ms, got %d ms\n", gv.prevpc>>16, gv.prevpc&0xffff, getcrs16(OWNERL), utempl, delayusec/1000, actualmsec);
}
#endif
/* do timer bookkeeping that would have occurred if we had
/* do timer bookkeeping that would have occurred if we had
actually looped on BDX utempl times */
for (i=0; i<64; i++)
@@ -6788,14 +6822,14 @@ d_caz: /* 0140214 */
d_irx: /* 0140114 */
TRACE(T_FLOW, " IRX\n");
putcrs16(X, getcrs16(X) + 1);
if (getcrs16(X) == 0)
if (getcrs16(X) == 0)
INCRP;
goto fetch;
d_drx: /* 0140210 */
TRACE(T_FLOW, " DRX\n");
putcrs16(X, getcrs16(X) - 1);
if (getcrs16(X) == 0)
if (getcrs16(X) == 0)
INCRP;
goto fetch;
@@ -7266,7 +7300,7 @@ d_tstq: /* 0141757 */
/* XXX: hack for CPU.FAULT; not sure how to determine
whether an instruction is illegal or unimplemented */
fault(ILLINSTFAULT, RPL, RP);
fatal("Return from 0141700 fault");
@@ -8403,7 +8437,7 @@ dfcmdr:
if (get32(ea) == getgr32(dr+1)) {
put32(getgr32(dr), ea);
SETEQ;
} else
} else
CLEAREQ;
break;
@@ -8413,7 +8447,7 @@ dfcmdr:
if (get16(ea) == (getcrs16(dr*2+1))) {
put16(getgr16(dr), ea);
SETEQ;
} else
} else
CLEAREQ;
break;
@@ -8702,7 +8736,7 @@ dfcmdr:
tempa1 = getgr32(FAC0+dr+1) & 0xffff;
tempa2 = immu64 & 0xffff;
if (abs(tempa1-tempa2) < 48)
if (prieee8(getfr64(dr), &tempd1)
if (prieee8(getfr64(dr), &tempd1)
&& prieee8(immu64, &tempd2)
&& ieeepr8(tempd1+tempd2, (long long *)(crsl+FAC0+dr), 0))
CLEARC;
@@ -8724,7 +8758,7 @@ dfcmdr:
immu64 = get64(ea);
if (immu64 & 0xFFFFFFFF00000000LL)
if (getgr32s(FAC0+dr))
if (prieee8(getfr64(dr), &tempd1)
if (prieee8(getfr64(dr), &tempd1)
&& prieee8(immu64, &tempd2)
&& ieeepr8(tempd1+tempd2, (long long *)(crsl+FAC0+dr), 0))
CLEARC;
@@ -8816,7 +8850,7 @@ dfcmdr:
tempa1 = getgr32(FAC0+dr+1) & 0xffff;
tempa2 = immu64 & 0xffff;
if (abs(tempa1-tempa2) < 48)
if (prieee8(getfr64(dr), &tempd1)
if (prieee8(getfr64(dr), &tempd1)
&& prieee8(immu64, &tempd2)
&& ieeepr8(tempd1-tempd2, (long long *)(crsl+FAC0+dr), 0))
CLEARC;
@@ -8842,7 +8876,7 @@ dfcmdr:
immu64 = get64(ea);
if (immu64 & 0xFFFFFFFF00000000LL)
if (getgr32s(FAC0+dr))
if (prieee8(getfr64(dr), &tempd1)
if (prieee8(getfr64(dr), &tempd1)
&& prieee8(immu64, &tempd2)
&& ieeepr8(tempd1-tempd2, (long long *)(crsl+FAC0+dr), 0))
CLEARC;
@@ -8866,7 +8900,7 @@ dfcmdr:
immu64 = ((immu64 << 32) & 0xffffff0000000000LL) | (immu64 & 0xff);
}
if (immu64 & 0xFFFFFFFF00000000LL)
if (prieee8(immu64, &tempd2)
if (prieee8(immu64, &tempd2)
&& prieee8(getfr64(dr), &tempd1)
&& ieeepr8(tempd1*tempd2, (long long *)(crsl+FAC0+dr), 0))
CLEARC;
@@ -8886,7 +8920,7 @@ dfcmdr:
if (*(int *)&ea >= 0)
immu64 = get64(ea);
if (immu64 & 0xFFFFFFFF00000000LL)
if (prieee8(immu64, &tempd2)
if (prieee8(immu64, &tempd2)
&& prieee8(getfr64(dr), &tempd1)
&& ieeepr8(tempd1*tempd2, (long long *)(crsl+FAC0+dr), 0))
CLEARC;
@@ -8964,7 +8998,7 @@ dfcmdr:
}
if (immu64 & 0xFFFFFFFF00000000LL)
if (getgr32s(FAC0+dr))
if (prieee8(immu64, &tempd2)
if (prieee8(immu64, &tempd2)
&& prieee8(getfr64(dr), &tempd1)
&& ieeepr8(tempd1/tempd2, (long long *)(crsl+FAC0+dr), 1))
CLEARC;
@@ -8984,7 +9018,7 @@ dfcmdr:
immu64 = get64(ea);
if (immu64 & 0xFFFFFFFF00000000LL)
if (getgr32s(FAC0+dr))
if (prieee8(immu64, &tempd2)
if (prieee8(immu64, &tempd2)
&& prieee8(getfr64(dr), &tempd1)
&& ieeepr8(tempd1/tempd2, (long long *)(crsl+FAC0+dr), 1))
CLEARC;
@@ -9528,7 +9562,7 @@ d_adddad: /* 00600 (R-mode) */
putcrs16(KEYS, getcrs16(KEYS) | 020000);
/* NOTE: this EQ test prevents reusing the ADD code :( */
if (getcrs32s(L) == 0) /* set EQ? */
SETEQ;
SETEQ;
if (((~utempa ^ m) & (utempa ^ getcrs16(A))) & 0x8000) {
if (getcrs32s(L) >= 0)
SETLT;
@@ -9563,7 +9597,7 @@ d_subdsb: /* 00700 */
if (utempl & 0x10000) /* set L-bit if carry */
putcrs16(KEYS, getcrs16(KEYS) | 020000);
if (getcrs32s(L) == 0) /* set EQ? */
SETEQ;
SETEQ;
if (((utempa ^ m) & (utempa ^ getcrs16(A))) & 0x8000) {
if (getcrs32s(L) >= 0)
SETLT;
@@ -9615,9 +9649,9 @@ d_cas: /* 01100 */
utempl += 1;
putcrs16(A, utempl); /* truncate results */
if (utempl & 0x10000) /* set L-bit if carry */
putcrs16(KEYS, getcrs16(KEYS) | 020000);
putcrs16(KEYS, getcrs16(KEYS) | 020000);
if (getcrs16(A) == 0) /* set EQ? */
SETEQ;
SETEQ;
if (((utempa ^ m) & (utempa ^ getcrs16(A))) & 0x8000) {
if (getcrs16s(A) >= 0)
SETLT;
@@ -9914,7 +9948,7 @@ d_fad: /* 00601 */
tempa1 = getcrs16(FEXP);
tempa2 = immu64 & 0xffff;
if (abs(tempa1-tempa2) < 48)
if (prieee8(getfr64(2), &tempd1)
if (prieee8(getfr64(2), &tempd1)
&& prieee8(immu64, &tempd2)
&& ieeepr8(tempd1+tempd2, (long long *)(crsl+FAC1), 0))
CLEARC;
@@ -9942,7 +9976,7 @@ d_fdv: /* 01701 */
immu64 = ((immu64 << 32) & 0xffffff0000000000LL) | (immu64 & 0xff);
if (immu64 & 0xFFFFFFFF00000000LL)
if (getgr32s(FAC1))
if (prieee8(immu64, &tempd2)
if (prieee8(immu64, &tempd2)
&& prieee8(getfr64(2), &tempd1)
&& ieeepr8(tempd1/tempd2, (long long *)(crsl+FAC1), 1))
CLEARC;
@@ -9967,7 +10001,7 @@ d_fmp: /* 01601 */
immu64 = get32(ea);
immu64 = ((immu64 << 32) & 0xffffff0000000000LL) | (immu64 & 0xff);
if (immu64 & 0xFFFFFFFF00000000LL)
if (prieee8(immu64, &tempd2)
if (prieee8(immu64, &tempd2)
&& prieee8(getfr64(2), &tempd1)
&& ieeepr8(tempd1*tempd2, (long long *)(crsl+FAC1), 0))
CLEARC;
@@ -9988,7 +10022,7 @@ d_fsb: /* 00701 */
tempa1 = getcrs16(FEXP);
tempa2 = immu64 & 0xffff;
if (abs(tempa1-tempa2) < 48)
if (prieee8(getfr64(2), &tempd1)
if (prieee8(getfr64(2), &tempd1)
&& prieee8(immu64, &tempd2)
&& ieeepr8(tempd1-tempd2, (long long *)(crsl+FAC1), 0))
CLEARC;
@@ -10027,7 +10061,7 @@ d_dfad: /* 0602 */
immu64 = get64(ea);
if (immu64 & 0xFFFFFFFF00000000LL)
if (getgr32s(FAC1))
if (prieee8(getfr64(2), &tempd1)
if (prieee8(getfr64(2), &tempd1)
&& prieee8(immu64, &tempd2)
&& ieeepr8(tempd1+tempd2, (long long *)(crsl+FAC1), 0)) {
CLEARC;
@@ -10053,7 +10087,7 @@ d_dfdv: /* 01702 */
immu64 = get64(ea);
if (immu64 & 0xFFFFFFFF00000000LL)
if (getgr32s(FAC1))
if (prieee8(immu64, &tempd2)
if (prieee8(immu64, &tempd2)
&& prieee8(getfr64(2), &tempd1)
&& ieeepr8(tempd1/tempd2, (long long *)(crsl+FAC1), 1)) {
CLEARC;
@@ -10082,7 +10116,7 @@ d_dfmp: /* 01602 */
if (getgr32s(FAC1)) {
immu64 = get64(ea);
if (immu64 & 0xFFFFFFFF00000000LL)
if (prieee8(immu64, &tempd2)
if (prieee8(immu64, &tempd2)
&& prieee8(getfr64(2), &tempd1)
&& ieeepr8(tempd1*tempd2, (long long *)(crsl+FAC1), 0)) {
CLEARC;
@@ -10101,7 +10135,7 @@ d_dfsb: /* 0702 */
immu64 = get64(ea);
if (immu64 & 0xFFFFFFFF00000000LL)
if (getgr32s(FAC1))
if (prieee8(getfr64(2), &tempd1)
if (prieee8(getfr64(2), &tempd1)
&& prieee8(immu64, &tempd2)
&& ieeepr8(tempd1-tempd2, (long long *)(crsl+FAC1), 0)) {
CLEARC;

84
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) {
TRACE(T_FP, " getdp(%016llx)\n", p);
*frac64 = p & 0xFFFFFFFFFFFF0000LL; /* unpack fraction */
*exp32 = (short) (p & 0xFFFFLL); /* unpack SIGNED exponent */
}
@@ -59,22 +60,29 @@ int prieee8(unsigned long long dp, double *d) {
/* unpack Prime DPFP */
getdp (dp, &frac64, &exp32);
TRACE(T_FP, " prieee8: unpacked frac %016llx exp %08x\n", frac64, exp32);
/* if negative, change to sign-magnitude */
sign = 0;
if (frac64 < 0) {
TRACE(T_FP, " prieee8: changing to sign-magnitude\n");
/* special case: negative power of 2 */
if (frac64 == 0x8000000000000000LL) {
TRACE(T_FP, " prieee8: frac is negative power of 2\n");
exp32 += (1023-128);
if (exp32 < 0 || exp32 > 0x7fe)
{
TRACE(T_FP, " prieee8: exp %x out of range\n", exp32);
return 0;
}
frac64 |= ((long long)exp32 << 52);
*d = *(double *)&frac64;
return 1;
} else {
TRACE(T_FP, " prieee8: frac is just negative\n");
sign = 0x8000000000000000LL;
frac64 = -frac64;
}
@@ -82,13 +90,15 @@ int prieee8(unsigned long long dp, double *d) {
/* special case: zero */
} else if (frac64 == 0) {
TRACE(T_FP, " prieee8: frac is 0\n");
*d = 0.0;
return 1;
}
}
/* normalize positive fraction until bit 2 is 1 */
while ((frac64 & 0x4000000000000000LL) == 0) {
TRACE(T_FP, " prieee8: normalizing positive fraction\n");
frac64 = frac64 << 1;
exp32--;
}
@@ -98,7 +108,10 @@ int prieee8(unsigned long long dp, double *d) {
exp32 += (1023-128) - 1;
#if 1
if (exp32 < 0 || exp32 > 0x7fe)
{
TRACE(T_FP, " prieee8: exponent %x out of range\n", exp32);
return 0;
}
#else
if (exp32 < 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 */
frac64 = sign | ((long long)exp32 << 52) | ((frac64 >> 10) & 0xfffffffffffffLL);
TRACE(T_FP, " prieee8: packed ieee dpfp %016llx\n", frac64);
*d = *(double *)&frac64;
return 1;
}
@@ -134,7 +148,7 @@ retry:
neg = frac64 < 0;
exp32 = (frac64 >> 52) & 0x7ff;
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 */
@@ -159,12 +173,21 @@ retry:
and subnormal */
if (exp32 != 0) /* typical IEEE normalized */
{
TRACE(T_FP, " ieeepr8: is normalized\n");
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 */
return okay;
} else
}
else
{
TRACE(T_FP, " ieeepr8: subnormal value\n");
; /* subnormal: no hidden 1 bit */
}
/* adjust exponent, change sign-magnitude to 2's complement,
and shift fraction into Prime placement (high 48 bits) */
@@ -173,12 +196,14 @@ retry:
if (neg)
frac64 = -frac64;
frac64 <<= 10;
TRACE(T_FP, " ieeepr8: de-biased prime-ised frac %016llx exp %08x\n", frac64, exp32);
/* normalize Prime DPFP */
while ((frac64 ^ (frac64 << 1)) >= 0) {
frac64 = frac64 << 1;
exp32--;
TRACE(T_FP, " ieeepr8: normalized prime frac %016llx exp %08x\n", frac64, exp32);
}
#if 0
@@ -204,8 +229,11 @@ retry:
if (round)
if ((frac64 & 0x8000) && ((frac64 & 0x7fffffffffff0000LL) != 0x7fffffffffff0000LL))
{
/* XXX: should this be a subtract for negative numbers? */
frac64 += 0x10000;
TRACE(T_FP, " ieeepr8: rounded frac %016llx\n", frac64);
}
*p = swap64((frac64 & 0xffffffffffff0000LL) | (exp32 & 0xffff));
return okay;
@@ -260,6 +288,7 @@ unsigned long long dfcm (unsigned long long dp, int *oflow) {
CLEARC;
*oflow = 0;
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 == 0x8000000000000000LL) { /* overflow case? */
frac64 = 0x4000000000000000LL; /* complement power of 2 */
@@ -290,6 +319,7 @@ unsigned long long norm(unsigned long long dp, int *oflow) {
*oflow = 0;
getdp(dp, &frac64, &exp32);
TRACE(T_FP, " norm: unpacked frac %016llx exp %08x\n", frac64, exp32);
while ((frac64 ^ (frac64 << 1)) >= 0) {
frac64 = frac64 << 1;
exp32--;
@@ -314,11 +344,16 @@ unsigned long long frn(unsigned long long dp, int *oflow) {
*oflow = 0;
getdp(dp, &frac64, &exp32);
TRACE(T_FP, " frn: unpacked frac %016llx exp %08x\n", frac64, exp32);
if (frac64 == 0)
{
TRACE(T_FP, " frn: returning 0\n");
return 0;
}
else {
doround1 = ((frac64 & 0x18000000000LL) != 0);
doround2 = ((frac64 & 0x8000000000LL) != 0) && ((frac64 & 0x7FFFFF0000LL) != 0);
TRACE(T_FP, " frn: doround1 %x doround2 %x\n", doround1, doround2);
if (doround1 || doround2) {
frac64 &= 0xFFFFFF0000000000LL;
if (frac64 != 0x7FFFFF0000000000LL)
@@ -329,8 +364,10 @@ unsigned long long frn(unsigned long long dp, int *oflow) {
}
frac64 |= (exp32 & 0xFFFF);
frac64 = norm(frac64, oflow);
TRACE(T_FP, " frn: rounded frac %016llx\n", frac64);
return frac64;
}
TRACE(T_FP, " frn: dp %016llx\n", dp);
return dp;
}
}
@@ -356,26 +393,37 @@ int fcs (unsigned long long fac, int fop) {
fop = fop & 0xffffff00;
if (fop == 0) /* fix dirty zero */
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 (facexp == fopexp) /* compare exponents */
if (templ == fop) { /* compare fractions */
TRACE(T_FP, " fcs: frac cmp returning eq / skip 1\n");
SETEQ;
return 1;
} else if (templ < fop) { /* compare fractions */
TRACE(T_FP, " fcs: frac cmp returning lt / skip 2\n");
SETLT; /* FAC < operand */
return 2;
} else
} else {
TRACE(T_FP, " fcs: frac cmp returning gt / skip 0\n");
return 0; /* FAC > operand */
}
else if (facexp < fopexp) { /* compare exponents */
TRACE(T_FP, " fcs: exp cmp returning lt / skip 2\n");
SETLT; /* FAC < operand */
return 2;
} else
} else {
TRACE(T_FP, " fcs: exp cmp returning gt / skip 0\n");
return 0;
}
} else if (templ & 0x80000000) {
TRACE(T_FP, " fcs: sign cmp returning lt / skip 2\n");
SETLT; /* FAC < operand */
return 2;
} else
} else {
TRACE(T_FP, " fcs: sign cmp returning gt / skip 0\n");
return 0; /* FAC > operand */
}
}
@@ -386,7 +434,7 @@ int fcs (unsigned long long fac, int fop) {
NOTE: This code doesn't pass Prime diagnostics for higher model
CPU's, I'm guessing because comparison is implemented as subtract,
and we can't do that because numbers with huge exponents (and
and we can't do that because numbers with huge exponents (and
Prime ASCII characters in the DAC) won't convert to IEEE.
*/
@@ -404,27 +452,35 @@ int dfcs (unsigned long long fac, long long fop) {
fop = fop & 0xffffffffffff0000LL;
if (fop == 0) /* fix dirty zero */
fopexp = 0;
#if 0
printf("dfcs: FAC: %016llx %04x; op: %016llx %04x\n", templl, facexp, fop, fopexp);
#endif
TRACE(T_FP, " dfcs: FAC: %016llx %04x; op: %016llx %04x\n", templl, facexp, fop, fopexp);
if ((templl & 0x8000000000000000LL) == (fop & 0x8000000000000000LL)) { /* compare signs */
if (facexp == fopexp) /* compare exponents */
if (templl == fop) { /* compare fractions */
TRACE(T_FP, " dfcs: frac cmp returning eq / skip 1\n");
SETEQ;
return 1;
} else if (templl < fop) { /* compare fractions */
TRACE(T_FP, " dfcs: frac cmp returning lt / skip 2\n");
SETLT; /* FAC < operand */
return 2;
} else
} else {
TRACE(T_FP, " dfcs: frac cmp returning gt / skip 0\n");
return 0; /* FAC > operand */
}
else if (facexp < fopexp) { /* compare exponents */
TRACE(T_FP, " dfcs: exp cmp returning lt / skip 2\n");
SETLT; /* FAC < operand */
return 2;
} else
} else {
TRACE(T_FP, " dfcs: exp cmp returning eq / skip 1\n");
return 0;
}
} else if (templl & 0x8000000000000000LL) {
TRACE(T_FP, " dfcs: sign cmp returning lt / skip 2\n");
SETLT; /* FAC < operand */
return 2;
} else
} else {
TRACE(T_FP, " dfcs: sign cmp returning gt / skip 0\n");
return 0; /* FAC > operand */
}
}

View File

@@ -1,5 +1,5 @@
/* utextp.c, Jim Wilcoxson, March 16, 2005
Reads a Unix text file and converts it to a compressed Prime text file.
Reads a Unix text file and converts it to a compressed Prime text file.
This means:
- turn high bit on
- expand Unix tabs to spaces
@@ -29,7 +29,7 @@ main () {
else { /* not a space character */
while (space) { /* dump held-back spaces first */
if (space < 3) { /* write regular spaces */
putchar(' ');
putchar(0240);
space--;
n++;
} else { /* write compressed spaces */