1
0
mirror of https://github.com/prirun/p50em.git synced 2026-01-13 15:17:32 +00:00

extensive changes to devmt tape controller emulation

changed to use iget16 instead of get16 to fetch instructions
added test in notify for semaphore overflow - call fatal()
This commit is contained in:
Jim 2007-02-07 00:00:00 -05:00
parent 55fc87a975
commit 4703c0b8fd
3 changed files with 545 additions and 54 deletions

View File

@ -101,7 +101,7 @@ labA:
/* here for long, 2-word, V-mode memory reference */
labB:
a = get16(RP);
a = iget16(RP);
RPL++;
if (T_EAV) fprintf(stderr," 2-word format, a=%o\n", a);
y = (inst & 020);

101
em.c
View File

@ -175,6 +175,7 @@ char gen0nam[][5] = {
T_MODE trace CPU mode changes
T_EAAP AP effective address calculation
T_DIO disk I/O
T_TIO tape I/O
T_MAP segmentation
T_PCL PCL instructions
T_FAULT Faults
@ -193,6 +194,7 @@ char gen0nam[][5] = {
#define TB_PCL 0x00000200
#define TB_FAULT 0x00000400
#define TB_PX 0x00000800
#define TB_TIO 0x00001000
#define T_EAR (traceflags & TB_EAR)
#define T_EAV (traceflags & TB_EAV)
@ -206,6 +208,7 @@ char gen0nam[][5] = {
#define T_PCL (traceflags & TB_PCL)
#define T_FAULT (traceflags & TB_FAULT)
#define T_PX (traceflags & TB_PX)
#define T_TIO (traceflags & TB_TIO)
#define T_TRACE1 TB_FLOW | TB_INST | T_DIO | T_PCL | T_FAULT
@ -667,6 +670,31 @@ double get64r(ea_t ea, ea_t rpring) {
}
}
/* Instruction version of get16 (can be replaced by get16 too...)
This needs to be checked more... not sure it actually improves performance
all that much, and it doesn't work for self-modifying code in R-mode or
Ring 0 */
#if 1
unsigned short iget16(ea_t ea) {
static ea_t eanext = -1; /* ea of lower 16 bits in instl */
static unsigned long instl;
if (ea & 0x80000000) { /* check for R-mode inst in register */
eanext = -1;
return get16(ea);
}
if (ea == eanext) {
eanext = -1;
return instl & 0xFFFF;
}
instl = get32(ea);
eanext = INCVA(ea,1);
return instl >> 16;
}
#else
#define iget16(ea) get16((ea))
#endif
put16r(unsigned short value, ea_t ea, ea_t rpring) {
pa_t pa;
@ -950,20 +978,31 @@ long devpoll[64] = {0};
#include "emdev.h"
#if 0
/* this is the "full system" controller configuration */
int (*devmap[64])(short, short, short) = {
/* '0x */ 0,0,0,0,devasr,0,0,devpnc,
/* '1x */ devnone,devnone,0,devmt,devmt,devamlc, devamlc, devamlc,
/* '1x */ devnone,devnone,0,devnone,devmt,devamlc, devamlc, devamlc,
/* '2x */ devcp,0,devdisk,devdisk,devdisk,devdisk,devdisk,devdisk,
/* '3x */ 0,0,devamlc,0,0,devamlc,devnone,devnone,
/* '4x */ 0,0,0,0,0,0,0,0,
/* '5x */ devnone,devnone,devamlc,devamlc,devamlc,0,devnone,0,
/* '6x */ 0,0,0,0,0,0,0,0,
/* '7x */ 0,0,0,0,0,devnone,devnone,0};
#else
/* this is the "minimum system" controller configuration */
int (*devmap[64])(short, short, short) = {
/* '0x */ 0,0,0,0,devasr,0,0,devpnc,
/* '1x */ devnone,devnone,0,devmt,devmt,devnone, devnone, devnone,
/* '2x */ devcp,0,devdisk,devdisk,devdisk,devdisk,devdisk,devdisk,
#if 1
/* '1x */ devnone,devnone,0,devnone,devmt,devnone, devnone, devnone,
#else
/* '1x */ devnone,devnone,0,devnone,devnone,devnone, devnone, devnone,
#endif
/* '2x */ devcp,0,devnone,devnone,devnone,devnone,devdisk,devnone,
/* '3x */ 0,0,devnone,0,0,devnone,devnone,devnone,
/* '4x */ 0,0,0,0,0,0,0,devnone,
/* '5x */ devnone,devnone,devnone,devnone,devamlc,0,devnone,0,
@ -2397,6 +2436,14 @@ nfy(unsigned short inst) {
scount = utempl>>16; /* count (signed) */
bol = utempl & 0xFFFF; /* beginning of wait list */
if (T_PX) fprintf(stderr,"%o/%o: opcode %o %s, ea=%o/%o, count=%d, bol=%o, I am %o\n", RPH, RPL, inst, nfyname[inst-01200], ea>>16, ea&0xFFFF, scount, bol, crs[OWNERL]);
/* on later models, semaphore overflow should cause a fault */
if (scount == -32768) {
printf("NFY: semaphore overflow at ea %o/%o %s\n", ea>>16, ea&0xFFFF, searchloadmap(ea, 'x'));
fatal(NULL);
}
if (scount > 0) {
if (bol == 0) {
printf("NFY: bol is zero, count is %d for semaphore at %o/%o\n", scount, ea>>16, ea&0xFFFF);
@ -2411,6 +2458,7 @@ nfy(unsigned short inst) {
bol = get16r(pcbp+PCBLINK, 0); /* get new beginning of wait list */
resched = ready(pcbp, begend); /* put this pcb on the ready list */
}
scount = scount-1;
#if 0
/* NOTE: shouldn't have to do this if everything is working right, but with
@ -2653,6 +2701,8 @@ main (int argc, char **argv) {
ea_t tempea;
unsigned int ea32; /* full V/I mode eff address */
ea_t ea; /* final MR effective address */
ea_t eanext; /* ea of lower 16 bits in instl */
unsigned long instl;
ea_t earp; /* RP to use for eff address calcs */
unsigned short eabit;
unsigned short opcode;
@ -2772,8 +2822,10 @@ main (int argc, char **argv) {
traceflags |= TB_FAULT;
else if (strcmp(argv[i+1],"px") == 0)
traceflags |= TB_PX;
else if (strcmp(argv[i+1],"tio") == 0)
traceflags |= TB_TIO;
else if (strcmp(argv[i+1],"all") == 0)
traceflags = -1;
traceflags = ~0;
else
fprintf(stderr,"Unrecognized trace flag: %s\n", argv[i+1]);
i++;
@ -2976,7 +3028,8 @@ main (int argc, char **argv) {
if ((ea & 0xFFFF) < 040)
ea = 0x80000000 | (ea & 0xFFFF);
#endif
inst = get16(ea);
inst = iget16(ea);
RPL++;
instcount++;
@ -3054,9 +3107,9 @@ xec:
case 001314:
if (T_FLOW) fprintf(stderr," CGT\n");
tempa = get16(RP); /* get number of words */
tempa = iget16(RP); /* get number of words */
if (1 <= crs[A] && crs[A] < tempa)
RPL = get16(INCVA(RP,crs[A]));
RPL = iget16(INCVA(RP,crs[A]));
else
RPL += tempa;
continue;
@ -3169,7 +3222,7 @@ xec:
}
}
#endif
utempa = get16(RP);
utempa = iget16(RP);
RPL++;
PUTFLR(0,utempa);
utempl = GETFLR(0);
@ -3190,7 +3243,7 @@ xec:
}
}
#endif
utempa = get16(RP);
utempa = iget16(RP);
RPL++;
PUTFLR(1,utempa);
utempl = GETFLR(1);
@ -3903,7 +3956,7 @@ irtn:
if (T_FLOW) fprintf(stderr," BCLT\n");
bclt:
if (crs[KEYS] & 0200)
RPL = get16(RP);
RPL = iget16(RP);
else
RPL++;
continue;
@ -3912,7 +3965,7 @@ bclt:
if (T_FLOW) fprintf(stderr," BCLE\n");
bcle:
if (crs[KEYS] & 0300)
RPL = get16(RP);
RPL = iget16(RP);
else
RPL++;
continue;
@ -3921,7 +3974,7 @@ bcle:
if (T_FLOW) fprintf(stderr," BCEQ\n");
bceq:
if (crs[KEYS] & 0100)
RPL = get16(RP);
RPL = iget16(RP);
else
RPL++;
continue;
@ -3930,7 +3983,7 @@ bceq:
if (T_FLOW) fprintf(stderr," BCNE\n");
bcne:
if (!(crs[KEYS] & 0100))
RPL = get16(RP);
RPL = iget16(RP);
else
RPL++;
continue;
@ -3939,7 +3992,7 @@ bcne:
if (T_FLOW) fprintf(stderr," BCGE\n");
bcge:
if (!(crs[KEYS] & 0200))
RPL = get16(RP);
RPL = iget16(RP);
else
RPL++;
continue;
@ -3948,7 +4001,7 @@ bcge:
if (T_FLOW) fprintf(stderr," BCGT\n");
bcgt:
if (!(crs[KEYS] & 0300))
RPL = get16(RP);
RPL = iget16(RP);
else
RPL++;
continue;
@ -3956,7 +4009,7 @@ bcgt:
case 0141705:
if (T_FLOW) fprintf(stderr," BCR\n");
if (!(crs[KEYS] & 0100000))
RPL = get16(RP);
RPL = iget16(RP);
else
RPL++;
continue;
@ -3964,7 +4017,7 @@ bcgt:
case 0141704:
if (T_FLOW) fprintf(stderr," BCS\n");
if (crs[KEYS] & 0100000)
RPL = get16(RP);
RPL = iget16(RP);
else
RPL++;
continue;
@ -3972,7 +4025,7 @@ bcgt:
case 0141707:
if (T_FLOW) fprintf(stderr," BMLT/BLR\n");
if (!(crs[KEYS] & 020000))
RPL = get16(RP);
RPL = iget16(RP);
else
RPL++;
continue;
@ -3981,7 +4034,7 @@ bcgt:
if (T_FLOW) fprintf(stderr," BLS\n");
bls:
if (crs[KEYS] & 020000)
RPL = get16(RP);
RPL = iget16(RP);
else
RPL++;
continue;
@ -4072,7 +4125,7 @@ bls:
crs[X]++;
bidx:
if (crs[X] != 0)
RPL = get16(RP);
RPL = iget16(RP);
else
RPL++;
continue;
@ -4082,7 +4135,7 @@ bidx:
crs[Y]++;
bidy:
if (crs[Y] != 0)
RPL = get16(RP);
RPL = iget16(RP);
else
RPL++;
continue;
@ -4096,7 +4149,7 @@ bidy:
if (T_FLOW) fprintf(stderr," BDX\n");
crs[X]--;
#if 1
m = get16(RP);
m = iget16(RP);
if (crs[X] > 100 && m == RPL-1) {
struct timeval tv0,tv1;
long delayusec, actualmsec;
@ -4753,7 +4806,7 @@ lcgt:
case 0141711:
if (T_FLOW) fprintf(stderr," BMLE\n");
if (!(crs[KEYS] & 020000))
RPL = get16(RP);
RPL = iget16(RP);
else
goto bceq;
continue;
@ -6406,7 +6459,7 @@ svc() {
/* get svc code word, break into class and function */
code = get16(RP);
code = iget16(RP);
class = (code >> 6) & 077;
if (class == 0)
class = 1;

496
emdev.h
View File

@ -47,8 +47,8 @@
'45 = disk (was D/A converter type 6060 (analog output) - obsolete)
'46 = disk
'47 = #2 PNC
'50 = #1 HSSMLC (cs/slcdim.pma)
'51 = #2 HSSMLC
'50 = #1 HSSMLC/MDLC (cs/slcdim.pma)
'51 = #2 HSSMLC/MDLC
'52 = #3 AMLC or ICS1
'53 = #2 AMLC
'54 = #1 AMLC
@ -56,6 +56,7 @@
'56 = old SMLC (RTOS User Guide, A-1 & Hacker's Guide)
'60-67 = reserved for user devices (GPIB)
'70-'73 = Megatek graphics terminals
'75-'76 = T$GPPI
Devices emulated by Primos in ks/ptrap.ftn for I/O instructions in Ring 3:
'01 = paper tape reader
@ -464,42 +465,467 @@ readasr:
/* Device '14 - magtape controller #1
*/
NOTES:
This code only supports 1 tape controller (4 units), but could be
extended to support 2 controllers (eg, see disk controller code)
All tape files are assumed to be in SimH .TAP format:
- 4-bytes (little endian integer) indicating data record length (in bytes)
- n bytes of data
- repeat of the 4-byte data record length
- odd length records have a pad byte inserted
- if MSB of record length is set, there is an error in the record
- record length of zero indicates a file mark (doesn't repeat)
- record length of all 1's indicates end-of-tape mark (doesn't repeat)
Tape files are named according to the device and unit, like disk drives:
- dev14u0, dev14u1, dev14u2, dev14u3
These names are usually just links to the actual tape file.
Tape status word bits (MSB to LSB):
1 = Vertical Parity Error
2 = Runaway
3 = CRC Error
4 = LRCC Error
5 = False Gap or Insufficient DMX Range
6 = Uncorrectable Read Error
7 = Raw Erorr
8 = Illegal Command
9 = Selected Tape Ready (online and not rewinding)
10 = Selected Tape Online
11 = Selected Tape is at End Of Tape (EOT)
12 = Selected Tape is Rewinding
13 = Selected Tape is at Beginning Of Tape (BOT)
14 = Selected Tape is Write Protected
15 = DMX Overrun
16 = Rewind Interrupt
OTA '01 Motion control A-register bits:
1 = Select Tape Only
2 = 0 for File operation, 1 for Record operation
3 = 1 for Space operation
4 = 1 for Read & Correct
5 = 1 for 7-track
6 = 1 for 9-track
7 = unused
8 = 1 for 2 chars/word, 0 for 1 char/word (not supported here)
9-11 = motion: 100 = Forward, 010 = Reverse, 001 = Rewind
12 = 1 for Write
13 = 1 for Unit 0
14 = 1 for Unit 1
15 = 1 for Unit 2
16 = 1 for Unit 3
*/
int mtread (int fd, unsigned short *iobuf, int nw, int fw, int *mtstat) {
unsigned char buf[4];
int n,reclen,reclen2;
if (T_TIO) fprintf(stderr," mtread initial tape status is 0x%04x\n", *mtstat);
if (fw) {
if (*mtstat & 0x20) /* already at EOT, can't read */
return 0;
n = read(fd, buf, 4);
if (T_TIO) fprintf(stderr," mtread read %d byte for reclen\n", n);
if (n == 0) { /* now we're at EOT */
*mtstat |= 0x20;
return 0;
}
*mtstat &= ~8; /* not at BOT now */
readerr:
if (n == -1) {
perror("Error reading from tape file");
*mtstat = 0;
return 0;
}
if (n < 4) {
fmterr:
warn("Premature EOF: tape file isn't in .TAP format");
*mtstat = 0;
return 0;
}
reclen = buf[0] | (buf[1]<<8) | (buf[2]<<16) | (buf[3]<<24);
if (T_TIO) fprintf(stderr, " mtread reclen = %d bytes\n", reclen);
if (reclen == 0) { /* hit a file mark */
*mtstat |= 0x100;
return 0;
}
if (reclen > 16*1024) {
fprintf(stderr,"em: reclen = %d in tape file - too big!\n");
*mtstat = 0;
return 0;
}
if (reclen & 1)
warn("odd-length record in tape file!");
n = read(fd, iobuf, reclen);
if (T_TIO) fprintf(stderr," mtread read %d bytes of data \n", n);
if (n == -1) goto readerr;
if (n != reclen) goto fmterr;
/* now get the trailing record length */
n = read(fd, buf, 4);
if (T_TIO) fprintf(stderr," mtread read %d bytes for trailer reclen\n", n);
if (n == -1) goto readerr;
if (n != 4) goto fmterr;
reclen2 = buf[0] | (buf[1]<<8) | (buf[2]<<16) | (buf[3]<<24);
if (reclen2 != reclen) goto fmterr;
return (reclen+1)/2;
} else {
warn("Backspace not implemented");
fatal(NULL);
/* if spacing backward, see if we're at BOT */
if (!fw && lseek(fd, 0, SEEK_CUR) == 0)
*mtstat |= 8; /* yep, at BOT */
}
}
int mtwrite (int fd, unsigned short *iobuf, int nw, int *mtstat) {
int n;
n = write(fd, iobuf, nw*2);
if (nw*2 != n) {
perror("Error writing to tape file");
fatal(NULL);
}
*mtstat &= ~8; /* not at BOT now */
}
int devmt (short class, short func, short device) {
static unsigned short mtvec = 0114; /* interrupt vector */
static unsigned short dmxchan = 0; /* dmx channel number */
static unsigned short datareg = 0xFFFF; /* data holding register */
static unsigned short enabled = 0; /* interrupts enabled */
static unsigned short interrupting = 0; /* true if interrupt pending */
static unsigned short usel = 0; /* last unit selected */
static struct {
int fd; /* tape file descriptor */
int mtstat; /* last tape status */
int firstwrite; /* true if next write is the first */
} unit[4];
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 &
end of each record */
unsigned short iobuf[8*1024+4]; /* 16-bit WORDS! */
unsigned short dmxreg; /* DMA/C register address */
unsigned short dmxaddr;
unsigned long dmcpair;
short dmxnw;
int i,n;
char reclen[4];
switch (class) {
case -1:
case -1: /* initialize emulator device */
for (u=0; u<4; u++) {
unit[u].fd = -1;
unit[u].mtstat = 0;
unit[u].firstwrite = 1;
}
return 0;
case 0:
if (T_INST) fprintf(stderr," OCP '%02o%02o\n", func, device);
if (T_INST || T_TIO) fprintf(stderr," OCP '%02o%02o\n", func, device);
if (func == 012 || func == 013) { /* set normal/diag mode - ignored */
;
} 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--;
} else if (func == 015) { /* set interrupt mask */
enabled = 1;
} else if (func == 016) { /* reset interrupt mask */
enabled = 0;
} else if (func == 017) { /* initialize */
mtvec = 014;
dmxchan = 0;
datareg = 0xFFFF;
interrupting = 0;
usel = 0;
} else {
printf("Unimplemented OCP device '%02o function '%02o\n", device, func);
fatal(NULL);
}
break;
case 1:
if (T_INST) fprintf(stderr," SKS '%02o%02o\n", func, device);
if (T_INST || T_TIO) fprintf(stderr," SKS '%02o%02o\n", func, device);
printf("Unimplemented SKS device '%02o function '%02o\n", device, func);
fatal(NULL);
break;
case 2:
if (T_INST) fprintf(stderr," INA '%02o%02o\n", func, device);
if (BLOCKIO) {
printf("Device '%o not supported, so I/O hangs\n", device);
if (T_INST || T_TIO) fprintf(stderr," INA '%02o%02o\n", func, device);
if (func == 0) {
if (datareg == 0xFFFF)
warn("INA 00 on tape device w/o matching OTA!");
crs[A] = datareg;
datareg = 0xFFFF;
IOSKIP;
} else {
printf("Unimplemented INA device '%02o function '%02o\n", device, func);
fatal(NULL);
}
break;
case 3:
if (T_INST) fprintf(stderr," OTA '%02o%02o\n", func, device);
if (BLOCKIO) {
printf("Device '%o not supported, so I/O hangs\n", device);
if (T_INST || T_TIO) fprintf(stderr," 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 (interrupting)
break;
#endif
if (func == 01) {
/* here's the hard part where everything happens... decode unit first */
u = crs[A] & 0xF;
if (u == 1) u = 3;
else if (u == 2) u = 2;
else if (u == 4) u = 1;
else if (u == 8) u = 0;
else
fatal("em: no unit selected on tape OTA '01");
usel = u;
/* if the tape file has never been opened, do it now.
if the tape file inode changes, re-open the file (new tape reel) */
if (unit[u].fd == -1) {
unit[u].mtstat = 0;
unit[u].firstwrite = 1;
snprintf(devfile,sizeof(devfile),"dev%ou%d", device, u);
if (T_TIO) fprintf(stderr," filename for tape dev '%o unit %d is %s\n", device, u, devfile);
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;
break;
}
unit[u].mtstat = 0x00C8; /* Ready, Online, BOT */
}
/* "select only" is ignored. On a real tape controller, this
blocks if the previous tape operation is in progress */
if (crs[A] & 0x8000) {
if (T_TIO) fprintf(stderr," select only\n");
IOSKIP;
break;
}
/* clear "last operation" error bits in mtstat, but preserve status
things like "at BOT", "at EOT", "write protected" */
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. To get around this,
"interrupting" is a counter and we set it to 2 so that when
Primos clears interrupts, the counter is decremented in this
driver, but the interrupt will still occur later */
interrupting = 2;
devpoll[device] = 10;
if ((crs[A] & 0x00E0) == 0x0020) { /* rewind */
if (T_TIO) fprintf(stderr," rewind\n");
if (lseek(unit[u].fd, 0, SEEK_SET) == -1) {
perror("Unable to rewind tape drive file");
fatal(NULL);
}
unit[u].mtstat = 0x00C8; /* Ready, Online, BOT */
IOSKIP;
break;
}
/* Now we're reading, writing, or spacing either a record or file:
- space file/record (forward or backward) = okay
- read record = okay, read file doesn't make sense
- write record = okay, write file = write file mark */
/* write file mark
NOTE: the tape file should probably be truncated on the first
write, not on each file mark */
if ((crs[A] & 0x4010) == 0x0010) {
if (T_TIO) fprintf(stderr," write file mark\n");
*(int *)iobuf = 0;
n = mtwrite(unit[u].fd, iobuf, 2, &unit[u].mtstat);
ftruncate(unit[u].fd, lseek(unit[u].fd, 0, SEEK_CUR));
IOSKIP;
break;
}
/* space forward or backward a record or file at a time */
if (crs[A] & 0x2000) { /* space operation */
if ((crs[A] & 0xC0) == 0)
warn("Motion = 0 for tape spacing operation");
else if (crs[A] & 0x4000) { /* record operation */
if (T_TIO) fprintf(stderr," space record, dir=%x\n", crs[A] & 0x80);
n = mtread(unit[u].fd, iobuf, 0, crs[A] & 0x80, &unit[u].mtstat);
} else {
if (T_TIO) fprintf(stderr," space file, dir=%x\n", crs[A] & 0x80);
n = 1;
while (n != 0) {
n = mtread(unit[u].fd, iobuf, 0, crs[A] & 0x80, &unit[u].mtstat);
}
}
IOSKIP;
break;
}
/* read/write backward aren't supported */
if (((crs[A] & 0x00E0) == 0x0040) && ((crs[A] & 0x2000) == 0)) {
warn("em: read/write reverse not supported for tapes");
unit[u].mtstat = 0;
IOSKIP;
break;
}
/* read/write record */
if (T_TIO) fprintf(stderr," read/write record\n");
dmxreg = dmxchan & 0x7FF;
if (dmxchan & 0x0800) { /* DMC */
dmcpair = get32r(dmxreg, 0); /* fetch begin/end pair */
dmxaddr = dmcpair>>16;
dmxnw = (dmcpair & 0xffff) - dmxaddr + 1;
if (T_INST || T_TIO) fprintf(stderr, " DMC channels: ['%o]='%o, ['%o]='%o, nwords=%d\n", 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];
if (T_INST || T_TIO) fprintf(stderr, " DMA channels: ['%o]='%o, ['%o]='%o, nwords=%d\n", dmxreg, regs.sym.regdmx[dmxreg], dmxreg+1, dmxaddr, dmxnw);
}
if (dmxnw < 0 || dmxnw > sizeof(iobuf)/2) {
fprintf(stderr,"devmt: requested DMX of size %d, emulator buffer is %d words\n", dmxnw, sizeof(iobuf)/2);
fatal(NULL);
}
/* IMPORTANT: for mtwrite, the record length words are stored in
iobuf and the length returned by mtwrite reflects that. But
for mtread, lengths are not stored in iobuf and the actual
data record length is the return value */
if (crs[A] & 0x10) { /* write record */
//traceflags = ~0;
if (T_TIO) fprintf(stderr," write record\n");
for (i=0; i<dmxnw; i++)
iobuf[i+2] = get16r(dmxaddr+i, 0);
n = dmxnw*2;
reclen[0] = n & 0xFF;
reclen[1] = n>>8 & 0xFF;
reclen[2] = n>>16 & 0xFF;
reclen[3] = n>>24 & 0xFF;
*(int *)iobuf = *(int *)reclen;
*(int *)(iobuf+2+dmxnw) = *(int *)reclen;
mtwrite(unit[u].fd, iobuf, dmxnw+4, &unit[u].mtstat);
n = dmxnw;
} else { /* read record */
if (T_TIO) fprintf(stderr," read record\n");
n = mtread(unit[u].fd, iobuf, sizeof(iobuf), 1, &unit[u].mtstat);
if (n > dmxnw) {
if (T_TIO) fprintf(stderr, " DMA Overrun, reclen = %d words, DMA = %d words\n", n, dmxnw);
unit[u].mtstat |= 0x800; /* DMA overrun status bit */
n = dmxnw;
}
for (i=0; i<n; i++)
put16r(iobuf[i], dmxaddr+i, 0);
}
if (T_TIO) fprintf(stderr," read/wrote %d words\n", n);
if (dmxchan & 0x0800) { /* DMC */
put16r(dmxaddr+n, dmxreg, 0); /* update starting address */
} else {
regs.sym.regdmx[dmxreg] += n<<4; /* increment # words */
regs.sym.regdmx[dmxreg+1] += n; /* increment address */
}
IOSKIP;
break;
} else if (func == 02) {
if (T_TIO) fprintf(stderr, " setup INA, A='%06o, 0x%04x\n", crs[A], crs[A]);
if (crs[A] & 0x8000)
datareg = unit[usel].mtstat;
else if (crs[A] & 0x4000)
datareg = 014; /* device ID number */
else if (crs[A] & 0x2000)
datareg = dmxchan;
else if (crs[A] & 0x1000)
datareg = mtvec;
else {
fprintf(stderr,"em: Bad OTA '02 to tape drive\n");
fatal(NULL);
}
if (T_TIO) fprintf(stderr, " datareg='%06o, 0x%04x\n", datareg, datareg);
IOSKIP;
} else if (func == 03) { /* power on */
if (T_TIO) fprintf(stderr, " power on\n");
IOSKIP;
} else if (func == 014) { /* set DMX channel */
dmxchan = crs[A] & 0x0FFF;
if (T_TIO) fprintf(stderr, " dmx channel '%o, 0x%04x\n", dmxchan, dmxchan);
//traceflags = ~TB_MAP;
IOSKIP;
} else if (func == 016) { /* set interrupt vector */
mtvec = crs[A];
if (T_TIO) fprintf(stderr, " interrupt vector '%o\n", mtvec);
IOSKIP;
} else {
printf("Unimplemented OTA device '%02o function '%02o, A='%o\n", device, func, crs[A]);
fatal(NULL);
}
break;
case 4:
if (T_TIO) fprintf(stderr, " POLL device '%02o, enabled=%d, interrupting=%d\n", device, enabled, interrupting);
if (enabled && interrupting) {
if (intvec == -1) {
if (T_TIO) fprintf(stderr, " CPU interrupt to vector '%o\n", mtvec);
intvec = mtvec;
}
/* HACK: keep interrupting because of Primos race bug */
devpoll[device] = 100;
}
}
}
/* Device '20: control panel switches and lights, and realtime clock
OCP '0020 = start Line Frequency Clock, enable mem increment, ack previous overflow (something else on VCP?)
@ -532,7 +958,16 @@ int devmt (short class, short func, short device) {
INA '1620 = read control panel up switches
INA '1720 = read control panel down switches
IMPORTANT NOTE: this device ('20) never skips!
IMPORTANT NOTE 1: this device ('20) never skips!
IMPORTANT NOTE 2: if the host system suspends, then when it awakes,
devcp will make the clock tick faster until it catches up to where
it should be. Fake clock interrupts are injected every 100 Prime
instructions to catch up. These fast interrupts can't occur too
quickly (like every 10 Prime instructions), because CLKDIM can't
execute completely in 10 instructions and the repeated interrupts
will eventually cause the clock semaphore to overflow.
*/
int devcp (short class, short func, short device) {
@ -634,7 +1069,7 @@ int devcp (short class, short func, short device) {
if (func == 02) { /* set PIC interval */
clkpic = *(short *)(crs+A);
SETCLKPOLL;
printf("Clock PIC interval set to %d\n", clkpic);
//printf("Clock PIC interval set to %d\n", clkpic);
} else if (func == 07) {
//printf("Clock control register set to '%o\n", crs[A]);
} else if (func == 013) {
@ -781,6 +1216,9 @@ int devdisk (short class, short func, short device) {
switch (class) {
case -1:
#ifdef DISKSAFE
printf("em: Running in DISKSAFE mode; no changes will be permanent\n");
#endif
for (i=0; i<64; i++) {
dc[i].state = S_HALT;
dc[i].status = 0100000;
@ -890,6 +1328,7 @@ int devdisk (short class, short func, short device) {
case 2: /* SFORM = Format */
case 5: /* SREAD = Read */
case 6: /* SWRITE = Write */
dc[device].status &= ~076000; /* clear bits 2-6 */
m2 = get16r(dc[device].oar++, 0);
recsize = m & 017;
track = m1 & 01777;
@ -937,7 +1376,7 @@ int devdisk (short class, short func, short device) {
hashp = NULL;
#ifndef DISKMOD
#ifdef DISKSAFE
//fprintf(stderr," R/W, modrecs=%p\n", dc[device].unit[u].modrecs);
for (hashp = dc[device].unit[u].modrecs[phyra%HASHMAX]; hashp != NULL; hashp = *((unsigned char **)hashp)) {
//fprintf(stderr," lookup, hashp=%p\n", hashp);
@ -953,7 +1392,7 @@ int devdisk (short class, short func, short device) {
perror("Unable to seek drive file");
fatal(NULL);
}
#ifndef DISKMOD
#ifdef DISKSAFE
} else { /* write */
hashp = malloc(1040*2 + sizeof(void*) + sizeof(int));
*(unsigned char **)hashp = dc[device].unit[u].modrecs[phyra%HASHMAX];
@ -1036,10 +1475,12 @@ int devdisk (short class, short func, short device) {
dc[device].status |= 2; /* select error (right?)... */
break;
}
if (m1 & 0100000)
if (m1 & 0100000) {
track = 0;
else
dc[device].status &= ~4; /* clear bit 14: seek error */
} else {
track = m1 & 01777;
}
if (T_INST || T_DIO) fprintf(stderr," seek track %d, restore=%d, clear=%d\n", track, (m1 & 0100000) != 0, (m1 & 040000) != 0);
dc[device].unit[u].curtrack = track;
break;
@ -1051,10 +1492,11 @@ int devdisk (short class, short func, short device) {
if (T_INST || T_DIO) fprintf(stderr," de-select\n");
break;
}
dc[device].status &= ~3; /* clear 15-16: select err + unavailable */
if (u != 1 && u != 2 && u != 4 && u != 8) {
fprintf(stderr," Device '%o, bad select '%o\n", device, u);
dc[device].usel = -1; /* de-select */
dc[device].status != 2; /* select error */
dc[device].status != 2; /* set bit 15: select error */
break;
}
u = u >> 1; /* unit => 0/1/2/4 */
@ -1066,14 +1508,10 @@ int devdisk (short class, short func, short device) {
if (T_INST || T_DIO) fprintf(stderr," filename for dev '%o unit %d is %s\n", device, u, devfile);
/* NOTE: add O_CREAT to allow creating new drives on the fly */
if ((dc[device].unit[u].devfd = open(devfile, O_RDWR, 0770)) == -1) {
perror ("Unable to open device file");
fprintf(stderr,"em: unable to open disk device file %s for device '%o unit %d; flagging \n", devfile, device, u);
dc[device].status = 0100001; /* not ready */
} else {
/* NOTE: this is a hack, because Primos 20.2.8 doesn't do
a reset if a disk device (file) doesn't exist and is created
under a running simulation */
dc[device].status = 0100000; /* clear status */
#ifndef DISKMOD
#ifdef DISKSAFE
dc[device].unit[u].modrecs = calloc(HASHMAX, sizeof(void *));
//fprintf(stderr," Device '%o, unit %d, modrecs=%p\n", device, u, dc[device].unit[u].modrecs);
#endif
@ -1424,10 +1862,10 @@ int devamlc (short class, short func, short device) {
/* if DTR drops on a connected line, disconnect */
if (!(crs[A] & 0x400) && (dc[device].dss & bitmask16[lx+1])) {
/* see similar code below */
write(dc[device].sockfd[lx], "\r\nDisconnecting on logout\r\n", 27);
write(dc[device].sockfd[lx], "\r\nDisconnecting logged out session\r\n", 36);
close(dc[device].sockfd[lx]);
dc[device].dss &= ~bitmask16[lx+1];
printf("Closing AMLC line %d on device '%o\n", lx, device);
printf("em: closing AMLC line %d on device '%o\n", lx, device);
}
IOSKIP;
@ -1501,7 +1939,7 @@ conloop:
socktoline[fd].line = lx;
dc[newdevice].dss |= bitmask16[lx+1];
dc[newdevice].sockfd[lx] = fd;
printf("AMLC connection, fd=%d, device='%o, line=%d\n", fd, newdevice, lx);
printf("em: AMLC connection, fd=%d, device='%o, line=%d\n", fd, newdevice, lx);
break;
}
if (!newdevice) {