mirror of
https://github.com/prirun/p50em.git
synced 2026-02-02 22:41:05 +00:00
Garth, FP, XED, devmt, 512MB, sys console full duplex
sent to Garth around 8/15/07 supports 512MB memory device "terminate" call added XED emulation more FP changes changed devmt to emulate Kennedy tape drive & controller devmt changes to support higher revs / fix bugs
This commit is contained in:
226
emdev.h
226
emdev.h
@@ -255,11 +255,12 @@ int devnone (int class, int func, int device) {
|
||||
|
||||
int devasr (int class, int func, int device) {
|
||||
|
||||
static initialized = 0;
|
||||
static FILE *conslog;
|
||||
static int ttydev;
|
||||
static int ttyflags;
|
||||
static int needflush; /* true if data has been written but not flushed */
|
||||
static struct termios terminfo;
|
||||
static struct termios origterminfo, terminfo;
|
||||
static fd_set fds;
|
||||
static short vcptime[8] = {7*0, 1};
|
||||
static short vcptimeix;
|
||||
@@ -276,7 +277,14 @@ int devasr (int class, int func, int device) {
|
||||
|
||||
switch (class) {
|
||||
|
||||
case -1:
|
||||
case -2: /* cleanup */
|
||||
if (!initialized) return;
|
||||
if (tcsetattr(ttydev, TCSANOW, &origterminfo) == -1)
|
||||
perror(" unable to reset tty attributes");
|
||||
fclose(conslog);
|
||||
break;
|
||||
|
||||
case -1: /* initialize */
|
||||
setsid();
|
||||
ttydev = open("/dev/tty", O_RDWR, 0);
|
||||
if (ttydev < 0) {
|
||||
@@ -292,6 +300,10 @@ int devasr (int class, int func, int device) {
|
||||
perror(" unable to get tty attributes");
|
||||
fatal(NULL);
|
||||
}
|
||||
|
||||
/* save initial terminal setup to restore when exiting */
|
||||
|
||||
origterminfo = terminfo;
|
||||
|
||||
/* NOTE: some of these are not restored by the host OS after the
|
||||
emulator is suspended (VSUSP) then restarted, eg, the VSUSP and
|
||||
@@ -322,10 +334,22 @@ int devasr (int class, int func, int device) {
|
||||
fatal(NULL);
|
||||
}
|
||||
setvbuf(conslog, NULL, _IOLBF, 0);
|
||||
initialized = 1;
|
||||
return 0;
|
||||
|
||||
case 0:
|
||||
TRACE(T_INST, " OCP '%02o%02o\n", func, device);
|
||||
if (func == 010) /* enable full duplex */
|
||||
terminfo.c_lflag &= ~ECHO;
|
||||
else if (func == 012) /* enable "echoplex" */
|
||||
terminfo.c_lflag |= ECHO;
|
||||
if (func == 010 | func == 012) {
|
||||
if (tcsetattr(ttydev, TCSANOW, &terminfo) == -1) {
|
||||
perror(" unable to set tty attributes");
|
||||
fatal(NULL);
|
||||
}
|
||||
} else
|
||||
TRACEA("devasr: unrecognized OCP '%02o%02o\n", func, device);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
@@ -352,8 +376,8 @@ int devasr (int class, int func, int device) {
|
||||
timeout.tv_sec = 0; /* yes, can't delay */
|
||||
else {
|
||||
timeout.tv_sec = 1; /* single user: okay to delay */
|
||||
#if 1
|
||||
fflush(tracefile); /* hack to flush for 32i testing */
|
||||
#if 0
|
||||
fflush(tracefile); /* hack to flush for testing */
|
||||
#endif
|
||||
}
|
||||
timeout.tv_usec = 0;
|
||||
@@ -413,6 +437,7 @@ readasr:
|
||||
}
|
||||
} else if (n == 1) {
|
||||
if (ch == '') {
|
||||
printf("Trace owner = %o/%o\n", crs[OWNER], crs[OWNERL]);
|
||||
if (savetraceflags == 0) {
|
||||
TRACEA("\nTRACE ENABLED:\n\n");
|
||||
savetraceflags = ~TB_MAP;
|
||||
@@ -625,21 +650,24 @@ int mtread (int fd, unsigned short *iobuf, int nw, int fw, int *mtstat) {
|
||||
n = read(fd, buf, 4);
|
||||
TRACE(T_TIO, " mtread read foward, %d bytes for reclen\n", n);
|
||||
if (n == 0) { /* now we're at EOT */
|
||||
*mtstat |= 0x20;
|
||||
if (*mtstat & 0x8) /* were we at BOT? */
|
||||
*mtstat |= 0x200; /* yes, return error */
|
||||
else
|
||||
*mtstat |= 0x20; /* no, return EOT */
|
||||
return 0;
|
||||
}
|
||||
*mtstat &= ~8; /* not at BOT now */
|
||||
readerr:
|
||||
if (n == -1) {
|
||||
perror("Error reading from tape file");
|
||||
*mtstat = 0; /* take drive offline */
|
||||
*mtstat |= 0x200; /* raw error */
|
||||
return 0;
|
||||
}
|
||||
if (n < 4) {
|
||||
fprintf(stderr," only read %d bytes for reclen\n", n);
|
||||
fmterr:
|
||||
warn("Tape file isn't in .TAP format");
|
||||
*mtstat = 0;
|
||||
*mtstat |= 0x200; /* raw error */
|
||||
return 0;
|
||||
}
|
||||
reclen = buf[0] | (buf[1]<<8) | (buf[2]<<16) | (buf[3]<<24);
|
||||
@@ -650,11 +678,11 @@ fmterr:
|
||||
}
|
||||
if (reclen == 0xFFFF) { /* hit EOT mark */
|
||||
|
||||
/* NOTE: simh says to backup here, probably to wipe out EOT if
|
||||
more data is written. IMO, EOT should never be written to
|
||||
simulated tape files. */
|
||||
/* NOTE: simh .tap doc says to backup here, probably to wipe out
|
||||
EOT if more data is written. IMO, EOT should never be written
|
||||
to simulated tape files. */
|
||||
|
||||
if (lseek(fd, -4, SEEK_CUR) == -1) {
|
||||
if ((n=lseek(fd, -4, SEEK_CUR)) == -1) {
|
||||
perror("em: unable to backspace over EOT");
|
||||
goto readerr;
|
||||
}
|
||||
@@ -662,7 +690,7 @@ fmterr:
|
||||
return 0;
|
||||
}
|
||||
if (reclen & 0x8000) { /* record marked in error */
|
||||
/* NOTE: simh may have non-zero record length here... */
|
||||
/* NOTE: .tap may have non-zero record length here... */
|
||||
*mtstat |= 0xB600; /* set all error bits */;
|
||||
return 0;
|
||||
}
|
||||
@@ -695,7 +723,7 @@ fmterr:
|
||||
if (n == -1) goto readerr;
|
||||
if (n != bytestoread) goto fmterr;
|
||||
if (bytestoread != reclen) { /* skip the rest of the record */
|
||||
if (lseek(fd, reclen-bytestoread, SEEK_CUR) == -1) {
|
||||
if ((n=lseek(fd, reclen-bytestoread, SEEK_CUR)) == -1) {
|
||||
fprintf(stderr,"em: unable to handle large record\n");
|
||||
goto readerr;
|
||||
}
|
||||
@@ -718,7 +746,7 @@ fmterr:
|
||||
/* spacing backward, see if we're at BOT */
|
||||
|
||||
if ((*mtstat & 8) || (lseek(fd, 0, SEEK_CUR) == 0)) {
|
||||
*mtstat |= 8; /* yep, at BOT */
|
||||
*mtstat = (*mtstat | 8) & ~0x20;; /* at BOT, not EOT */
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -782,10 +810,11 @@ int devmt (int class, int func, int device) {
|
||||
|
||||
static unsigned short mtvec = 0114; /* interrupt vector */
|
||||
static unsigned short dmxchan = 0; /* dmx channel number */
|
||||
static unsigned short datareg = 0; /* data holding register */
|
||||
static unsigned short ready = 0; /* true if datareg valid */
|
||||
static unsigned short datareg = 0; /* INA 00 data register */
|
||||
static unsigned short ready = 0; /* true if INA 00 ready */
|
||||
static unsigned short busy = 0; /* true if MPC busy */
|
||||
static unsigned short enabled = 0; /* interrupts enabled */
|
||||
static unsigned short interrupting = 0; /* true if interrupt pending */
|
||||
static unsigned short interrupting = 0; /* 1 if pending, 2 if active */
|
||||
static unsigned short usel = 0; /* last unit selected */
|
||||
static struct {
|
||||
int fd; /* tape file descriptor */
|
||||
@@ -796,8 +825,8 @@ int devmt (int class, int func, int device) {
|
||||
int u;
|
||||
char devfile[8];
|
||||
|
||||
/* the largest rec size Primos ever supported is 16K bytes, plus 8
|
||||
bytes for the 4-byte .TAP format record length at the beginning &
|
||||
/* the largest rec size Primos ever supported is 8K halfwords, plus
|
||||
4 words for the 4-byte .TAP format record length at the beginning &
|
||||
end of each record */
|
||||
|
||||
#define MAXTAPEWORDS 8*1024
|
||||
@@ -805,13 +834,13 @@ int devmt (int class, int func, int device) {
|
||||
unsigned short iobuf[MAXTAPEWORDS+4]; /* 16-bit WORDS! */
|
||||
unsigned short *iobufp;
|
||||
unsigned short dmxreg; /* DMA/C register address */
|
||||
unsigned short tempdmxchan; /* temp to incr during xfer */
|
||||
short dmxnch; /* number of DMX channels - 1 */
|
||||
unsigned short dmxaddr;
|
||||
unsigned int dmxaddr;
|
||||
unsigned long dmcpair;
|
||||
short dmxnw, dmxtotnw;
|
||||
int i,n;
|
||||
char reclen[4];
|
||||
unsigned short ioword;
|
||||
|
||||
switch (class) {
|
||||
|
||||
@@ -830,23 +859,24 @@ int devmt (int class, int func, int device) {
|
||||
;
|
||||
|
||||
} else if (func == 014) { /* ack interrupt */
|
||||
/* this is a hack because Primos acks interrupts immediately after
|
||||
OTA 01 (due to a tape controller bug) */
|
||||
if (interrupting)
|
||||
interrupting--;
|
||||
interrupting = 0;
|
||||
|
||||
} else if (func == 015) { /* set interrupt mask */
|
||||
enabled = 1;
|
||||
|
||||
if (interrupting == 1) /* if interrupt is pending */
|
||||
devpoll[device] = 10; /* try to interrupt soon */
|
||||
|
||||
} else if (func == 016) { /* reset interrupt mask */
|
||||
enabled = 0;
|
||||
|
||||
} else if (func == 017) { /* initialize */
|
||||
mtvec = 014;
|
||||
mtvec = 0114;
|
||||
dmxchan = 0;
|
||||
datareg = 0;
|
||||
interrupting = 0;
|
||||
enabled = 0;
|
||||
ready = 0;
|
||||
busy = 1;
|
||||
usel = 0;
|
||||
|
||||
} else {
|
||||
@@ -861,9 +891,12 @@ int devmt (int class, int func, int device) {
|
||||
if (ready)
|
||||
IOSKIP;
|
||||
} else if (func == 01) { /* skip if not busy */
|
||||
IOSKIP;
|
||||
if (busy) /* return busy once after init */
|
||||
busy = 0;
|
||||
else
|
||||
IOSKIP;
|
||||
} else if (func == 04) { /* skip if not interrupting */
|
||||
if (!interrupting)
|
||||
if (interrupting != 2)
|
||||
IOSKIP;
|
||||
} else {
|
||||
printf("Unimplemented SKS device '%02o function '%02o\n", device, func);
|
||||
@@ -872,13 +905,22 @@ int devmt (int class, int func, int device) {
|
||||
break;
|
||||
|
||||
case 2:
|
||||
|
||||
/* according to version 0 controller docs, INA '014 should only
|
||||
respond ready one time after an OTA '0214, and should respond
|
||||
not ready otherwise (ie, the INA doesn't skip). But, this
|
||||
causes Primos to lock up, spinning in an INA '014 loop, so the
|
||||
emulator returns 0 on subsequent INA's */
|
||||
|
||||
TRACE(T_INST|T_TIO, " INA '%02o%02o\n", func, device);
|
||||
if (func == 0) {
|
||||
#if 0
|
||||
if (!ready) warn("INA 00 on tape device w/o matching OTA!");
|
||||
#endif
|
||||
crs[A] = datareg;
|
||||
datareg = 0;
|
||||
if (!ready) {
|
||||
TRACE(T_INST|T_TIO, "INA 0 on tape device w/o matching OTA!\n");
|
||||
crs[A] = 0;
|
||||
} else {
|
||||
TRACE(T_INST|T_TIO, " INA 0 returns '%06o 0x%04x\n", datareg, datareg);
|
||||
crs[A] = datareg;
|
||||
}
|
||||
ready = 0;
|
||||
IOSKIP;
|
||||
|
||||
@@ -891,14 +933,14 @@ int devmt (int class, int func, int device) {
|
||||
case 3:
|
||||
TRACE(T_INST|T_TIO, " OTA '%02o%02o, A='%06o %04x\n", func, device, crs[A], crs[A]);
|
||||
|
||||
#if 0
|
||||
/* don't accept any OTA's if we're interrupting */
|
||||
if (func != 0)
|
||||
busy = 0;
|
||||
|
||||
if (interrupting)
|
||||
break;
|
||||
#endif
|
||||
if (func == 00) {
|
||||
datareg = crs[A];
|
||||
IOSKIP;
|
||||
|
||||
if (func == 01) {
|
||||
} else if (func == 01) {
|
||||
|
||||
/* here's the hard part where everything happens... decode unit first */
|
||||
|
||||
@@ -929,6 +971,7 @@ int devmt (int class, int func, int device) {
|
||||
unit[u].firstwrite = 1;
|
||||
snprintf(devfile,sizeof(devfile),"dev%ou%d", device, u);
|
||||
TRACE(T_TIO, " filename for tape dev '%o unit %d is %s\n", device, u, devfile);
|
||||
/* XXX: add code for read-protected tapes */
|
||||
if ((unit[u].fd = open(devfile, O_RDWR, 0770)) == -1) {
|
||||
fprintf(stderr,"em: unable to open tape device file %s for device '%o unit %d for read/write\n", devfile, device, u);
|
||||
IOSKIP;
|
||||
@@ -940,7 +983,7 @@ int devmt (int class, int func, int device) {
|
||||
/* "select only" is ignored. On a real tape controller, this
|
||||
blocks (I think) if the previous tape operation is in progress */
|
||||
|
||||
if (crs[A] & 0x8000) {
|
||||
if ((crs[A] & 0xFFF00) == 0x8000) {
|
||||
TRACE(T_TIO, " select only\n");
|
||||
IOSKIP;
|
||||
break;
|
||||
@@ -951,16 +994,9 @@ int devmt (int class, int func, int device) {
|
||||
|
||||
unit[u].mtstat &= 0x00EC;
|
||||
|
||||
/* for rewind, read, write, & space, setup a completion
|
||||
interrupt if controller interrupts are enabled. NOTE: there
|
||||
is a race condition here. Immediately following OTA 01,
|
||||
Primos clears pending interrupts because of an old tape
|
||||
controller bug. To get around this, "interrupting" is a
|
||||
counter and is set to 2 so that when Primos clears
|
||||
interrupts, the counter is decremented in this driver, but
|
||||
the interrupt will still occur later */
|
||||
/* for rewind, read, write, & space, setup a completion interrupt */
|
||||
|
||||
interrupting = 2;
|
||||
interrupting = 1;
|
||||
devpoll[device] = 10;
|
||||
|
||||
if ((crs[A] & 0x00E0) == 0x0020) { /* rewind */
|
||||
@@ -970,7 +1006,7 @@ int devmt (int class, int func, int device) {
|
||||
perror("Unable to rewind tape drive file");
|
||||
fatal(NULL);
|
||||
}
|
||||
unit[u].mtstat = 0x00C8; /* Ready, Online, BOT */
|
||||
unit[u].mtstat = 0x00D0; /* Ready, Online, Rewinding */
|
||||
IOSKIP;
|
||||
break;
|
||||
}
|
||||
@@ -1032,6 +1068,7 @@ int devmt (int class, int func, int device) {
|
||||
iobuf and the length returned by mtwrite reflects that */
|
||||
|
||||
if (crs[A] & 0x10) { /* write record */
|
||||
TRACE(T_TIO, " write record\n");
|
||||
dmxtotnw = 0;
|
||||
iobufp = iobuf+2;
|
||||
} else {
|
||||
@@ -1048,16 +1085,13 @@ int devmt (int class, int func, int device) {
|
||||
dmcpair = get32io(dmxreg); /* fetch begin/end pair */
|
||||
dmxaddr = dmcpair>>16;
|
||||
dmxnw = (dmcpair & 0xffff) - dmxaddr + 1;
|
||||
TRACE(T_INST|T_TIO, " DMC channels: ['%o]='%o, ['%o]='%o, nwords=%d\n", dmxreg, dmxaddr, dmxreg+1, (dmcpair & 0xffff), dmxnw);
|
||||
TRACE(T_INST|T_TIO, " DMC channels: ['%o]='%o, ['%o]='%o, nwords=%d", dmxreg, dmxaddr, dmxreg+1, (dmcpair & 0xffff), dmxnw);
|
||||
} else { /* DMA */
|
||||
dmxreg = dmxreg << 1;
|
||||
dmxnw = regs.sym.regdmx[dmxreg];
|
||||
if (dmxnw <= 0)
|
||||
dmxnw = -(dmxnw>>4);
|
||||
else
|
||||
dmxnw = -((dmxnw>>4) ^ 0xF000);
|
||||
dmxaddr = regs.sym.regdmx[dmxreg+1];
|
||||
TRACE(T_INST|T_TIO, " DMA channels: ['%o]='%o, ['%o]='%o, nwords=%d\n", dmxreg, regs.sym.regdmx[dmxreg], dmxreg+1, dmxaddr, dmxnw);
|
||||
dmxnw = -((dmxnw>>4) | 0xF000);
|
||||
dmxaddr = ((regs.sym.regdmx[dmxreg] & 3)<<16) | regs.sym.regdmx[dmxreg+1];
|
||||
TRACE(T_INST|T_TIO, " DMA channels: ['%o]='%o, ['%o]='%o/%o, nwords=%d", dmxreg, regs.sym.regdmx[dmxreg], dmxreg+1, dmxaddr>>16, dmxaddr&0xffff, dmxnw);
|
||||
}
|
||||
if (dmxnw < 0) { /* but is legal for >32K DMC transfer... */
|
||||
printf("devmt: requested negative DMX of size %d\n", dmxnw);
|
||||
@@ -1067,22 +1101,32 @@ int devmt (int class, int func, int device) {
|
||||
if (dmxtotnw+dmxnw > MAXTAPEWORDS)
|
||||
fatal("Tape write is too big");
|
||||
for (i=0; i < dmxnw; i++) {
|
||||
*iobufp++ = get16io(dmxaddr+i);
|
||||
ioword = get16io(dmxaddr+i);
|
||||
if (i%10 == 0)
|
||||
TRACE(T_TIO, "\n %04d: ", i);
|
||||
TRACE(T_TIO, " %03o %03o", (unsigned)ioword>>8, ioword&0xff);
|
||||
*iobufp++ = ioword;
|
||||
}
|
||||
TRACE(T_TIO, "\n");
|
||||
dmxtotnw = dmxtotnw + dmxnw;
|
||||
} else {
|
||||
if (dmxnw > dmxtotnw)
|
||||
dmxnw = dmxtotnw;
|
||||
for (i=0; i < dmxnw; i++) {
|
||||
put16io(*iobufp++, dmxaddr+i);
|
||||
ioword = *iobufp++;
|
||||
if (i%10 == 0)
|
||||
TRACE(T_TIO, "\n %04d: ", i);
|
||||
TRACE(T_TIO, " %03o %03o", (unsigned)ioword>>8, ioword&0xff);
|
||||
put16io(ioword, dmxaddr+i);
|
||||
}
|
||||
TRACE(T_TIO, "\n");
|
||||
dmxtotnw = dmxtotnw - dmxnw;
|
||||
}
|
||||
TRACE(T_TIO, " read/wrote %d words\n", dmxnw);
|
||||
if (dmxchan & 0x0800) { /* DMC */
|
||||
TRACE(T_TIO, " transferred %d words\n", dmxnw);
|
||||
if (dmxchan & 0x0800) { /* if DMC... */
|
||||
put16io(dmxaddr+dmxnw, dmxreg); /* update starting address */
|
||||
} else {
|
||||
regs.sym.regdmx[dmxreg] += dmxnw<<4; /* increment # words */
|
||||
} else { /* if DMA...
|
||||
regs.sym.regdmx[dmxreg] += (dmxnw<<4); /* increment # words */
|
||||
regs.sym.regdmx[dmxreg+1] += dmxnw; /* increment address */
|
||||
}
|
||||
|
||||
@@ -1097,7 +1141,6 @@ int devmt (int class, int func, int device) {
|
||||
/* for write record, do the write */
|
||||
|
||||
if (crs[A] & 0x10) { /* write record */
|
||||
TRACE(T_TIO, " write record\n");
|
||||
n = dmxtotnw*2;
|
||||
reclen[0] = n & 0xFF;
|
||||
reclen[1] = n>>8 & 0xFF;
|
||||
@@ -1116,23 +1159,30 @@ int devmt (int class, int func, int device) {
|
||||
break;
|
||||
|
||||
} else if (func == 02) {
|
||||
TRACE(T_TIO, " setup INA, A='%06o, 0x%04x\n", crs[A], crs[A]);
|
||||
if (crs[A] & 0x8000)
|
||||
ready = 1;
|
||||
if (crs[A] & 0x8000) { /* status word 1 */
|
||||
datareg = unit[usel].mtstat;
|
||||
else if (crs[A] & 0x4000)
|
||||
datareg = 0114; /* device ID */
|
||||
|
||||
/* if the tape was rewinding, return rewinding status once, then
|
||||
change it to BOT */
|
||||
|
||||
if (datareg & 0x10)
|
||||
unit[usel].mtstat = unit[usel].mtstat & ~0x10 | 0x8;
|
||||
} else if (crs[A] & 0x4000)
|
||||
datareg = 0214; /* device ID */
|
||||
else if (crs[A] & 0x2000)
|
||||
datareg = dmxchan;
|
||||
else if (crs[A] & 0x1000)
|
||||
datareg = mtvec;
|
||||
else if (crs[A] & 0x800)
|
||||
datareg = 0; /* status word 2 */
|
||||
else {
|
||||
TRACE(T_TIO, " Bad OTA '02 to tape drive, A='%06o, 0x$04x\n", crs[A], crs[A]);
|
||||
if (enabled) {
|
||||
interrupting = 1;
|
||||
devpoll[device] = 10;
|
||||
}
|
||||
datareg = 0;
|
||||
interrupting = 1;
|
||||
devpoll[device] = 10;
|
||||
}
|
||||
TRACE(T_TIO, " datareg='%06o, 0x%04x\n", datareg, datareg);
|
||||
TRACE(T_TIO, " setup INA 0, datareg='%06o, 0x%04x\n", datareg, datareg);
|
||||
IOSKIP;
|
||||
|
||||
} else if (func == 03) { /* power on */
|
||||
@@ -1141,37 +1191,39 @@ int devmt (int class, int func, int device) {
|
||||
|
||||
} else if (func == 05) { /* illegal - DIAG */
|
||||
TRACE(T_TIO, " illegal DIAG OTA '05\n");
|
||||
if (enabled) {
|
||||
interrupting = 1;
|
||||
devpoll[device] = 10;
|
||||
IOSKIP;
|
||||
}
|
||||
interrupting = 1;
|
||||
devpoll[device] = 10;
|
||||
IOSKIP;
|
||||
|
||||
} else if (func == 014) { /* set DMX channel */
|
||||
dmxchan = crs[A];
|
||||
TRACE(T_TIO, " dmx channel '%o, 0x%04x\n", dmxchan, dmxchan);
|
||||
IOSKIP;
|
||||
|
||||
} else if (func == 015) { /* start u-code test */
|
||||
TRACE(T_TIO, " u-code test\n");
|
||||
IOSKIP;
|
||||
|
||||
} else if (func == 016) { /* set interrupt vector */
|
||||
mtvec = crs[A];
|
||||
TRACE(T_TIO, " interrupt vector '%o\n", mtvec);
|
||||
TRACE(T_TIO, " set int vec '%o\n", mtvec);
|
||||
IOSKIP;
|
||||
|
||||
} else {
|
||||
printf("Unimplemented OTA device '%02o function '%02o, A='%o\n", device, func, crs[A]);
|
||||
fatal(NULL);
|
||||
}
|
||||
break;
|
||||
|
||||
case 4:
|
||||
TRACE(T_TIO, " POLL device '%02o, enabled=%d, interrupting=%d\n", device, enabled, interrupting);
|
||||
if (enabled && interrupting) {
|
||||
if (intvec == -1) {
|
||||
if (enabled && (interrupting == 1)) {
|
||||
devpoll[device] = 100; /* assume interrupt will be deferred */
|
||||
if (intvec == -1 && (crs[MODALS] & 0100000) && inhcount == 0) {
|
||||
TRACE(T_TIO, " CPU interrupt to vector '%o\n", mtvec);
|
||||
intvec = mtvec;
|
||||
devpoll[device] = 0;
|
||||
interrupting = 2;
|
||||
}
|
||||
/* HACK: keep interrupting because of Primos/controller race bug */
|
||||
devpoll[device] = 100;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1867,15 +1919,15 @@ int devdisk (int class, int func, int device) {
|
||||
dc[device].status |= 2; /* select error (right?)... */
|
||||
break;
|
||||
}
|
||||
dc[device].status &= ~4; /* clear bit 14: seek error */
|
||||
if (m1 & 0100000) {
|
||||
track = 0;
|
||||
dc[device].status &= ~4; /* clear bit 14: seek error */
|
||||
} else {
|
||||
track = m1 & 03777;
|
||||
}
|
||||
TRACE(T_INST|T_DIO, " seek track %d, restore=%d, clear=%d\n", track, (m1 & 0100000) != 0, (m1 & 040000) != 0);
|
||||
if (track > dc[device].unit[u].maxtrack) {
|
||||
fprintf(stderr," Device '%o, seek to track %d > cylinder limit of %d\n", device, track, dc[device].unit[u].maxtrack);
|
||||
fprintf(stderr," Device '%o, unit %d, seek to track %d > cylinder limit of %d\n", device, u, track, dc[device].unit[u].maxtrack);
|
||||
dc[device].status |= 4; /* set bit 14: seek error */
|
||||
track = -1;
|
||||
}
|
||||
|
||||
220
fp.h
220
fp.h
@@ -20,8 +20,20 @@
|
||||
- and frac = 0 => positive or negative zero, depending on sign
|
||||
- and frac non-zero => subnormal (aka denormal, unnormalized)
|
||||
- subnormals have an implied leading "0" bit (XXX: true??)
|
||||
|
||||
References (no code was used):
|
||||
|
||||
http://en.wikipedia.org/wiki/IEEE_754
|
||||
http://www.psc.edu/general/software/packages/ieee/ieee.html
|
||||
http://www.math.grinnell.edu/~stone/courses/fundamentals/IEEE-reals.html
|
||||
http://www.gnu.org/software/libc/manual/html_node/IEEE-Floating-Point.html
|
||||
http://en.wikipedia.org/wiki/Floating_point
|
||||
http://www.win.ua.ac.be/~cant/arithmos/
|
||||
http://tima-cmp.imag.fr/~guyot/Cours/Oparithm/english/Op_Ar2.htm
|
||||
|
||||
*/
|
||||
|
||||
#define GETFRAC(d) (*(long long *)&(d) & 0xFFFFFFFFFFFF0000LL)
|
||||
|
||||
/* getdp unpacks a Prime DPFP into 48-bit sign + mantissa (left
|
||||
justified in 64 bits) and a 32-bit signed exponent */
|
||||
@@ -61,7 +73,7 @@ int prieee8(void *dp, double *d) {
|
||||
|
||||
if (frac64 == 0x8000000000000000LL) {
|
||||
exp32 += (1023-128);
|
||||
if (exp32 < 0 || exp32 > 0x3ff)
|
||||
if (exp32 < 0 || exp32 > 0x7fe)
|
||||
return 0;
|
||||
frac64 |= ((long long)exp32 << 52);
|
||||
*d = *(double *)&frac64;
|
||||
@@ -88,9 +100,20 @@ int prieee8(void *dp, double *d) {
|
||||
/* adjust exponent bias and check range */
|
||||
|
||||
exp32 += (1023-128) - 1;
|
||||
if (exp32 < 0 || exp32 > 0x7ff)
|
||||
#if 1
|
||||
if (exp32 < 0 || exp32 > 0x7fe)
|
||||
return 0;
|
||||
|
||||
#else
|
||||
if (exp32 < 0) {
|
||||
*d = 0.0;
|
||||
return 1;
|
||||
}
|
||||
if (exp32 > 0x7fe) {
|
||||
exp32 = 0x7fe;
|
||||
frac64 = 0x7fffffffffffffffLL;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* pack into an IEEE DPFP, losing the leading 1 bit in the process */
|
||||
|
||||
frac64 = sign | ((long long)exp32 << 52) | ((frac64 >> 10) & 0xfffffffffffffLL);
|
||||
@@ -98,59 +121,9 @@ int prieee8(void *dp, double *d) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Conversion from Prime SPFP to IEEE DPFP */
|
||||
|
||||
double prieee4(unsigned int sp) {
|
||||
int frac32, sign;
|
||||
long long frac64;
|
||||
int exp32;
|
||||
|
||||
/* unpack Prime SPFP */
|
||||
|
||||
frac32 = sp & 0xFFFFFF00;
|
||||
exp32 = sp & 0xFF;
|
||||
|
||||
/* if negative, change to sign-magnitude */
|
||||
|
||||
sign = 0;
|
||||
if (frac32 < 0) {
|
||||
|
||||
/* special case: negative power of 2 */
|
||||
|
||||
if (frac32 == 0x80000000) {
|
||||
exp32--;
|
||||
frac64 = 0x8000000000000000LL | ((long long)exp32 << 52);
|
||||
return *(double *)&frac64;
|
||||
} else {
|
||||
sign = 0x80000000;
|
||||
frac32 = -frac32;
|
||||
}
|
||||
|
||||
/* special case: zero */
|
||||
|
||||
} else if (frac32 == 0)
|
||||
return 0.0;
|
||||
|
||||
/* normalize positive fraction until bit 2 is 1 */
|
||||
|
||||
while ((frac32 & 0x40000000) == 0) {
|
||||
frac32 = frac32 << 1;
|
||||
exp32--;
|
||||
}
|
||||
|
||||
/* adjust exponent bias and check range */
|
||||
|
||||
exp32 += (1023-128) - 1;
|
||||
|
||||
/* pack into an IEEE DPFP, losing the leading 1 bit in the process */
|
||||
|
||||
frac64 = (long long)sign | ((long long)exp32 << 52) | (((long long)frac32 << 22) & 0xfffffffffffffLL);
|
||||
return *(double *)&frac64;
|
||||
}
|
||||
|
||||
|
||||
/* conversion from IEEE back to Prime. Prime exponents are larger, so
|
||||
this conversion cannot overflow/underflow, but precision is lost */
|
||||
this conversion cannot overflow/underflow, but precision may be
|
||||
lost */
|
||||
|
||||
double ieeepr8(double d) {
|
||||
long long frac64;
|
||||
@@ -263,9 +236,10 @@ double fltl (int int32) {
|
||||
|
||||
dfcm (void *dp) {
|
||||
long long frac64;
|
||||
int exp32;
|
||||
int exp32, oflow;
|
||||
|
||||
CLEARC;
|
||||
oflow = 0;
|
||||
getdp(dp, &frac64, &exp32);
|
||||
if (frac64 != 0) { /* can't normalize zero */
|
||||
if (frac64 == 0x8000000000000000LL) { /* overflow case? */
|
||||
@@ -278,11 +252,12 @@ dfcm (void *dp) {
|
||||
exp32--;
|
||||
}
|
||||
}
|
||||
if (exp32 > 32767 || exp32 < -32768)
|
||||
mathexception('f', FC_DFP_OFLOW, 0);
|
||||
else
|
||||
putdp(dp, frac64, exp32);
|
||||
}
|
||||
putdp(dp, frac64, exp32);
|
||||
oflow = exp32 > 32767 || exp32 < -32768;
|
||||
} else
|
||||
*(double *)dp = 0.0;; /* DFCM is documented to clean up dirty zeroes */
|
||||
if (oflow)
|
||||
mathexception('f', FC_DFP_OFLOW, 0);
|
||||
}
|
||||
|
||||
|
||||
@@ -311,35 +286,132 @@ void norm(void *dp) {
|
||||
/* double->single floating point round (FRN) instruction.
|
||||
|
||||
Passed a pointer to a Prime double precision variable, one of the
|
||||
FACC's, and updates it in place. May also take an arithmetic fault
|
||||
if overflow occurs. For faults, the ea is zero because FACC's
|
||||
don't have an effective address. */
|
||||
FACC's, and updates it in place.
|
||||
|
||||
NOTE: this routine is coded strangely because I ran into compiler
|
||||
bugs (gcc 4.0.1) */
|
||||
|
||||
void frn(void *dp) {
|
||||
long long frac64;
|
||||
int exp32;
|
||||
int origsign, newsign;
|
||||
int doround1, doround2;
|
||||
|
||||
CLEARC;
|
||||
getdp(dp, &frac64, &exp32);
|
||||
if (frac64 == 0)
|
||||
*(long long *)dp = 0;
|
||||
else {
|
||||
origsign = (frac64 < 0);
|
||||
if ((frac64 & 0x18000000000LL)
|
||||
| ((frac64 & 0x8000000000LL) && (frac64 & 0x7FFFFF0000LL))) {
|
||||
frac64 += 0x10000000000LL;
|
||||
doround1 = ((frac64 & 0x18000000000LL) != 0);
|
||||
doround2 = ((frac64 & 0x8000000000LL) != 0) && ((frac64 & 0x7FFFFF0000LL) != 0);
|
||||
if (doround1 || doround2) {
|
||||
frac64 &= 0xFFFFFF0000000000LL;
|
||||
if (frac64 != 0x7FFFFF0000000000LL)
|
||||
frac64 += 0x10000000000LL;
|
||||
else {
|
||||
frac64 = 0x4000000000000000LL;
|
||||
exp32++;
|
||||
}
|
||||
frac64 |= (exp32 & 0xFFFF);
|
||||
norm(&frac64);
|
||||
*(long long *)dp = frac64;
|
||||
newsign = (frac64 < 0);
|
||||
if (newsign != origsign)
|
||||
/* XXX: is this fault code right? */
|
||||
mathexception('f', FC_DFP_OFLOW, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* SPFP comparison, for both FCS (SRV-mode) and FC (I-mode)
|
||||
For I-mode FC instruction, condition codes are used.
|
||||
For SRV-mode FCS instruction, return value is the amount
|
||||
RPL should be advanced.
|
||||
*/
|
||||
|
||||
int fcs (unsigned int *fac, int fop) {
|
||||
int templ;
|
||||
short fopexp, facexp;
|
||||
|
||||
CLEARCC;
|
||||
templ = fac[0] & 0xffffff00; /* FAC SP mantissa */
|
||||
if (templ == 0) /* fix dirty zero */
|
||||
facexp = 0;
|
||||
else
|
||||
facexp = fac[1] & 0xffff; /* FAC exponent */
|
||||
fopexp = fop & 0xff;
|
||||
fop = fop & 0xffffff00;
|
||||
if (fop == 0) /* fix dirty zero */
|
||||
fopexp = 0;
|
||||
if ((templ & 0x80000000) == (fop & 0x80000000)) { /* compare signs */
|
||||
if (facexp == fopexp) /* compare exponents */
|
||||
if (templ == fop) { /* compare fractions */
|
||||
SETEQ;
|
||||
return 1;
|
||||
} else if (templ < fop) { /* compare fractions */
|
||||
SETLT; /* FAC < operand */
|
||||
return 2;
|
||||
} else
|
||||
return 0; /* FAC > operand */
|
||||
else if (facexp < fopexp) { /* compare exponents */
|
||||
SETLT; /* FAC < operand */
|
||||
return 2;
|
||||
} else
|
||||
return 0;
|
||||
} else if (templ & 0x80000000) {
|
||||
SETLT; /* FAC < operand */
|
||||
return 2;
|
||||
} else
|
||||
return 0; /* FAC > operand */
|
||||
}
|
||||
|
||||
|
||||
/* DPFP comparison, for both DFCS (SRV-mode) and DFC (I-mode)
|
||||
For I-mode DFC instruction, condition codes are used.
|
||||
For SRV-mode DFCS instruction, return value is the amount
|
||||
RPL should be advanced.
|
||||
|
||||
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
|
||||
Prime ASCII characters in the DAC) won't convert to IEEE.
|
||||
*/
|
||||
|
||||
int dfcs (unsigned int *fac, long long fop) {
|
||||
long long templl;
|
||||
short fopexp, facexp;
|
||||
|
||||
CLEARCC;
|
||||
templl = *(long long *)fac;
|
||||
facexp = templl & 0xffff; /* FAC exponent */
|
||||
templl = templl & 0xffffffffffff0000LL; /* FAC SP mantissa */
|
||||
if (templl == 0) /* fix dirty zero */
|
||||
facexp = 0;
|
||||
fopexp = fop & 0xffff;
|
||||
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
|
||||
if ((templl & 0x8000000000000000LL) == (fop & 0x8000000000000000LL)) { /* compare signs */
|
||||
if (facexp == fopexp) /* compare exponents */
|
||||
if (templl == fop) { /* compare fractions */
|
||||
SETEQ;
|
||||
return 1;
|
||||
} else if (templl < fop) { /* compare fractions */
|
||||
SETLT; /* FAC < operand */
|
||||
return 2;
|
||||
} else
|
||||
return 0; /* FAC > operand */
|
||||
else if (facexp < fopexp) { /* compare exponents */
|
||||
SETLT; /* FAC < operand */
|
||||
return 2;
|
||||
} else
|
||||
return 0;
|
||||
} else if (templl & 0x8000000000000000LL) {
|
||||
SETLT; /* FAC < operand */
|
||||
return 2;
|
||||
} else
|
||||
return 0; /* FAC > operand */
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
|
||||
/* Prime DPFP multiply */
|
||||
|
||||
Reference in New Issue
Block a user