mirror of
https://github.com/prirun/p50em.git
synced 2026-01-13 15:17:32 +00:00
need to restore interval timer when new process dispatched process level was incorrect when new process dispatched changes setting process abort flag in PCB when timer overflows
902 lines
26 KiB
C
902 lines
26 KiB
C
/* emdev.h, Jim Wilcoxson, April 17, 2005
|
||
Device handlers for pio instructions. Use devnull as a template for
|
||
new handlers.
|
||
NOTES:
|
||
|
||
OCP instructions never skip
|
||
SKS instructions skip on specified conditions
|
||
INA/OTA instructions skip if they succeed (data was read/written)
|
||
|
||
Device numbers:
|
||
'01 = paper tape reader
|
||
'02 = paper tape punch
|
||
'03 = 1st MPC (line printer/card reader/card punch)
|
||
'04 = SOC board (system console/user terminal)
|
||
'05 = 2nd MPC (line printer/card reader/card punch)
|
||
'06 = card punch? (RTOS User Guide, A-1) / IPC (Engr Handbook p.101)
|
||
'07 = PNC
|
||
'12 = diskette
|
||
'13 = 2nd magtape controller
|
||
'14 = 1st magtape controller
|
||
'20 = control panel / real-time clock
|
||
'21 = 1st 4002 disk controller
|
||
'22 = disk
|
||
'23 = disk
|
||
'24 = disk
|
||
'25 = disk
|
||
'26 = 1st disk controller
|
||
'27 = 2nd disk controller
|
||
'30-32 = BPIOC #1-3 (RTOS User Guide, A-1)
|
||
'33 = 1st Versatec (verdim)
|
||
'34 = 2nd Versatec
|
||
'35 = 4th AMLC
|
||
'36-37 = ELFBUS #1 & 2
|
||
'40 = A/D converter type 6000
|
||
'41 = digital input type 6020
|
||
'42 = digital input #2
|
||
'43 = digital output type 6040
|
||
'44 = digital output #2
|
||
'45 = disk (was D/A converter type 6060 (analog output) - obsolete)
|
||
'46 = disk
|
||
'50 = 1st HSSMLC (cs/slcdim.pma)
|
||
'51 = 2nd HSSMLC
|
||
'52 = 3rd AMLC
|
||
'53 = 2nd AMLC
|
||
'54 = 1st AMLC
|
||
'55 = MACI autocall unit
|
||
'56 = old SMLC (RTOS User Guide, A-1)
|
||
'60-67 = reserved for user devices
|
||
'70-'73 = Megatek graphics terminals
|
||
|
||
Devices emulated by Primos in ks/ptrap.ftn:
|
||
'04 = console, '01 = paper tape reader, '02 = paper tape punch,
|
||
'20 = control panel
|
||
*/
|
||
|
||
#include <fcntl.h>
|
||
#include <sys/time.h>
|
||
#include <sys/select.h>
|
||
#include <unistd.h>
|
||
#include <termios.h>
|
||
|
||
/* this macro is used when I/O is successful. In VI modes, it sets
|
||
the EQ condition code bit. In SR modes, it does a skip */
|
||
|
||
#define IOSKIP \
|
||
if (crs[KEYS] & 010000) \
|
||
crs[KEYS] |= 0100; \
|
||
else \
|
||
RPL++
|
||
|
||
/* macro to detect if an I/O instruction is followed by JMP *-1 (SR modes)
|
||
or BCNE *-2 (VI modes). If process exchange is enabled, I/O can never
|
||
block. */
|
||
|
||
#if 1
|
||
#define BLOCKIO \
|
||
(!(crs[MODALS] & 010) && (get16(RP) == 03776 || (get16(RP) == 0141603 && get16(RP+1) == RPL-2)))
|
||
#else
|
||
#define BLOCKIO 0
|
||
#endif
|
||
|
||
|
||
|
||
/* this is a template for device handlers */
|
||
|
||
void devnull (short class, short func, short device) {
|
||
|
||
switch (class) {
|
||
|
||
case 0:
|
||
if (T_INST) fprintf(stderr," OCP '%02o%02o\n", func, device);
|
||
if (func == 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 (func == 0)
|
||
IOSKIP; /* assume it's always ready */
|
||
else {
|
||
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 (func == 0) {
|
||
;
|
||
} 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 (func == 0 | func == 1) {
|
||
IOSKIP; /* OTA '0004 always works on Unix */
|
||
} else {
|
||
printf("Unimplemented OTA device '%02o function '%02o\n", device, func);
|
||
fatal(NULL);
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
|
||
|
||
/* Device '4: system console
|
||
|
||
NOTES:
|
||
- needs to reset tty attributes when emulator shuts down
|
||
- Primos only handles ASRATE 110 (10cps), 1010 (30cps), 3410 (960 cps)
|
||
- input ID is wrong, causes OCP '0477 on clock interrupt
|
||
- instruction counters on output are bogus, need to intercept TNOUA instead
|
||
|
||
OCP '0004 = initialize for input only, echoplex, 110 baud, 8-bit, no parity
|
||
OCP '0104 = same, but for output only
|
||
OCP '0204 = set receive interrupt mask
|
||
OCP '0304 = enable receive DMA/C
|
||
OCP '0404 = reset receive interrupt mask and DMA/C enable
|
||
OCP '0504 = set transmit interrupt mask
|
||
OCP '0604 = enable transmit DMA/C
|
||
OCP '0704 = reset transmit interrupt mask and DMA/C enable
|
||
OCP '1004 = Full duplex; software must echo
|
||
OCP '1104 = output a sync pulse (diagnostics)
|
||
OCP '1204 = Prime normal, independent xmit and recv w/echoplex
|
||
OCP '1304 = Self test mode (internally connects transmitter to receiver)
|
||
OCP '1504 = Set both xmit & rcv interrupt masks
|
||
OCP '1604 = Reset both xmit & rcv interrupt masks
|
||
OCP '1704 = same as '0004, but clears interrupt masks and dma/c enables
|
||
(this is the state after Master Clear)
|
||
|
||
SKS '0004 = skip if either receive or xmit ready, whichever are enabled
|
||
SKS '0104 = skip if not busy
|
||
SKS '0204 = skip if receiver not interrupting
|
||
SKS '0304 = skip if control registers valid
|
||
SKS '0404 = skip if neither xmit nor recv are interrupting
|
||
SKS '0504 = skip if xmit not interrupting
|
||
SKS '0604 = skip is xmit ready (can accept a character)
|
||
SKS '0704 = skip if recv ready (character present)
|
||
SKS '1104-'1404 = skip if input bit 1/2/3/4 marking
|
||
SKS '1504 = skip if parity error
|
||
SKS '1604 = skip if character overrun
|
||
SKS '1704 = skip if framing error
|
||
|
||
INA '0004 = "or" character into right byte of A, not modifying left byte
|
||
INA '0404 = input receive control register 1 (always works)
|
||
INA '0504 = input receive control register 2 (always works)
|
||
INA '0604 = input xmit control register 1 (always works)
|
||
INA '0704 = input xmit control register 2 (always works)
|
||
INA '1004 = like '0004, but clear A first
|
||
INA '1104 = input device ID
|
||
INA '1404 = input recv DMA/C channel address (these last 4 always work)
|
||
INA '1504 = input xmit DMA/C channel address
|
||
INA '1604 = input recv interrupt vector
|
||
INA '1704 = input xmit interrupt vector
|
||
|
||
OTA '0004 = xmit character from A; fails if inited for recv only
|
||
OTA '0404 = output rcv CR 1
|
||
OTA '0504 = output rcv CR 2
|
||
OTA '0604 = output xmit CR 1
|
||
OTA '0704 = output xmit CR 2
|
||
OTA '1404 = output recv DMA/C channel address (these last 4 always work)
|
||
OTA '1504 = output xmit DMA/C channel address
|
||
OTA '1604 = output recv interrupt vector
|
||
OTA '1704 = output xmit interrupt vector
|
||
|
||
*/
|
||
|
||
|
||
void devasr (short class, short func, short device) {
|
||
|
||
static int ttydev;
|
||
static int ttyflags;
|
||
static int needflush; /* true if data has been written but not flushed */
|
||
static int atbol=1; /* true if cursor is at bol */
|
||
static int atnewl=1; /* true if cursor is on a blank line */
|
||
static struct termios terminfo;
|
||
static fd_set fds;
|
||
struct timeval timeout;
|
||
unsigned char ch;
|
||
int newflags;
|
||
int n;
|
||
|
||
|
||
switch (class) {
|
||
|
||
case -1:
|
||
ttydev = open("/dev/tty", O_RDWR, 0);
|
||
if (ttydev < 0) {
|
||
perror(" error opening /dev/tty");
|
||
fatal(NULL);
|
||
}
|
||
if (fcntl(ttydev, F_GETFL, ttyflags) == -1) {
|
||
perror(" unable to get tty flags");
|
||
fatal(NULL);
|
||
}
|
||
FD_ZERO(&fds);
|
||
if (tcgetattr(ttydev, &terminfo) == -1) {
|
||
perror(" unable to get tty attributes");
|
||
fatal(NULL);
|
||
}
|
||
terminfo.c_iflag &= ~(INLCR | ICRNL);
|
||
terminfo.c_lflag &= ~(ECHOCTL | ICANON);
|
||
terminfo.c_cc[VMIN] = 0;
|
||
terminfo.c_cc[VTIME] = 0;
|
||
if (tcsetattr(ttydev, TCSANOW, &terminfo) == -1) {
|
||
perror(" unable to set tty attributes");
|
||
fatal(NULL);
|
||
}
|
||
break;
|
||
|
||
case 0:
|
||
if (T_INST) fprintf(stderr," OCP '%02o%02o\n", func, device);
|
||
break;
|
||
|
||
case 1:
|
||
if (T_INST) fprintf(stderr," SKS '%02o%02o\n", func, device);
|
||
|
||
if (func == 6) { /* skip if room for a character */
|
||
#if 0
|
||
timeout.tv_sec = 0;
|
||
timeout.tv_usec = 0;
|
||
FD_SET(ttydev, &fds);
|
||
n = select(ttydev+1, NULL, &fds, NULL, &timeout);
|
||
if (n == -1) {
|
||
perror(" unable to do write select on ttydev");
|
||
fatal(NULL);
|
||
}
|
||
if (n) {
|
||
IOSKIP;
|
||
}
|
||
#else
|
||
IOSKIP; /* assume there is room under Unix */
|
||
#endif
|
||
|
||
} else if (func == 7) { /* skip if received a char */
|
||
if (crs[MODALS] & 010) /* PX enabled? */
|
||
timeout.tv_sec = 0; /* yes, can't delay */
|
||
else
|
||
timeout.tv_sec = 1;
|
||
timeout.tv_usec = 0;
|
||
FD_SET(ttydev, &fds);
|
||
n = select(ttydev+1, &fds, NULL, NULL, &timeout);
|
||
if (n == -1) {
|
||
perror(" unable to do select on tty");
|
||
fatal(NULL);
|
||
}
|
||
if (n) {
|
||
IOSKIP;
|
||
}
|
||
|
||
} else if (func <= 014)
|
||
IOSKIP; /* assume it's always ready */
|
||
break;
|
||
|
||
case 2:
|
||
if (T_INST) fprintf(stderr," INA '%02o%02o\n", func, device);
|
||
if (func == 0 || func == 010) {
|
||
if (BLOCKIO)
|
||
newflags = ttyflags & ~O_NONBLOCK;
|
||
else
|
||
newflags = ttyflags | O_NONBLOCK;
|
||
if (newflags != ttyflags && fcntl(ttydev, F_SETFL, newflags) == -1) {
|
||
perror(" unable to set tty flags");
|
||
fatal(NULL);
|
||
}
|
||
ttyflags = newflags;
|
||
if (needflush && BLOCKIO) {
|
||
fflush(stdout);
|
||
needflush = 0;
|
||
devpoll[device] = 0;
|
||
}
|
||
readasr:
|
||
n = read(ttydev, &ch, 1);
|
||
if (n < 0) {
|
||
if (errno != EAGAIN) {
|
||
perror(" error reading from tty");
|
||
fatal(NULL);
|
||
}
|
||
} else if (n == 1) {
|
||
if (ch == '') {
|
||
traceflags = ~traceflags;
|
||
traceflags &= ~TB_MAP;
|
||
if (traceflags == 0)
|
||
fprintf(stderr,"\nTRACE DISABLED:\n\n");
|
||
else
|
||
fprintf(stderr,"\nTRACE ENABLED:\n\n");
|
||
//memdump(0, 0xFFFF);
|
||
goto readasr;
|
||
}
|
||
if (func >= 010)
|
||
crs[A] = 0;
|
||
crs[A] = crs[A] | ch;
|
||
if (T_INST) fprintf(stderr," character read=%o: %c\n", crs[A], crs[A] & 0x7f);
|
||
IOSKIP;
|
||
} else if (n != 0) {
|
||
printf("Unexpected error reading from tty, n=%d\n", n);
|
||
fatal(NULL);
|
||
}
|
||
} else if (04 <= func && func <= 07) { /* read control register 1/2 */
|
||
crs[A] = 0;
|
||
IOSKIP;
|
||
} else if (func == 011) { /* read device id? */
|
||
crs[A] = 4;
|
||
IOSKIP;
|
||
} else if (func == 012) { /* read control word */
|
||
crs[A] = 04110;
|
||
IOSKIP;
|
||
} else if (func == 017) { /* read xmit interrupt vector */
|
||
crs[A] = 0;
|
||
IOSKIP;
|
||
} else {
|
||
printf("Unimplemented INA '04 function '%02o\n", func);
|
||
fatal(NULL);
|
||
}
|
||
break;
|
||
|
||
case 3:
|
||
if (T_INST) fprintf(stderr," OTA '%02o%02o\n", func, device);
|
||
if (func == 0) {
|
||
ch = crs[A] & 0x7f;
|
||
if (T_INST) fprintf(stderr," char to write=%o: %c\n", crs[A], ch);
|
||
if (ch == 0 || ch == 0x7f) {
|
||
IOSKIP;
|
||
return;
|
||
}
|
||
#if 0
|
||
timeout.tv_sec = 0;
|
||
timeout.tv_usec = 0;
|
||
FD_SET(2, &fds);
|
||
n = select(2+1, NULL, &fds, NULL, &timeout);
|
||
if (n == -1) {
|
||
perror(" unable to do write select on stdout");
|
||
fatal(NULL);
|
||
}
|
||
if (!n)
|
||
return;
|
||
#endif
|
||
#if 0
|
||
if (atbol && atnewl)
|
||
printf("%10d| ", instcount);
|
||
#endif
|
||
putchar(ch);
|
||
if (ch == 015)
|
||
atbol = 1;
|
||
else if (ch == 012)
|
||
atnewl = 1;
|
||
else {
|
||
atbol = 0;
|
||
atnewl = 0;
|
||
}
|
||
needflush = 1;
|
||
if (devpoll[device] == 0)
|
||
devpoll[device] = instpermsec*100;
|
||
IOSKIP;
|
||
} else if (func == 1) { /* write control word */
|
||
IOSKIP;
|
||
} else if (04 <= func && func <= 07) { /* write control register 1/2 */
|
||
IOSKIP;
|
||
} else if (func == 013) {
|
||
/* NOTE: does this in rev 20 on settime command (Option A maybe?) */
|
||
IOSKIP;
|
||
} else if (func == 017) {
|
||
/* NOTE: 9950 does this in rev 20, others don't */
|
||
IOSKIP;
|
||
} else {
|
||
printf("Unimplemented OTA '04 function '%02o\n", func);
|
||
fatal(NULL);
|
||
}
|
||
break;
|
||
|
||
case 4:
|
||
/* since the tty device is in non-blocking mode under Primos, keep
|
||
flushing output every .1 seconds for smoothest and fastest
|
||
output */
|
||
if (needflush) {
|
||
fflush(stdout);
|
||
needflush = 1;
|
||
devpoll[device] = instpermsec*100;
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
|
||
|
||
/* Device '14 - magtape controller #1
|
||
*/
|
||
|
||
void devmt (short class, short func, short device) {
|
||
|
||
switch (class) {
|
||
|
||
case 0:
|
||
if (T_INST) fprintf(stderr," OCP '%02o%02o\n", func, device);
|
||
break;
|
||
|
||
case 1:
|
||
if (T_INST) fprintf(stderr," SKS '%02o%02o\n", func, device);
|
||
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);
|
||
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);
|
||
fatal(NULL);
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
|
||
|
||
/* Device '20: control panel switches and lights, and realtime clock
|
||
|
||
OCP '0020 = start Line Frequency Clock, enable mem increment, ack previous overflow
|
||
OCP '0120 = ack PIC interrupt
|
||
OCP '0220 = stop LFC, disable mem increment, ack previous overflow
|
||
OCP '0420 = select LFC for memory increment
|
||
OCP '0520 = select external clock for memory increment
|
||
OCP '0620 = starts a new 50-ms watchdog timeout interval
|
||
OCP '0720 = stops the watchdog timer
|
||
OCP '1520 = set interrupt mask
|
||
OCP '1620 = reset interrupt mask
|
||
OCP '1720 = initialize as in Master Clear
|
||
|
||
SKS '0020 = skip if clock IS interrupting
|
||
SKS '0220 = skip if clock IS NOT interrupting
|
||
SKS '0520 = skip if WDT timed out
|
||
SKS '0720 = skip if WDT caused external interrupt (loc '60)
|
||
|
||
OTA '0220 = set PIC Interval register (negative, interrupts on incr to zero)
|
||
OTA '0720 = set control register
|
||
OTA '1220 = set Memory Increment Cell address
|
||
OTA '1320 = set interrupt vector address
|
||
OTA '1720 = write to lights (sets CP fetch address)
|
||
|
||
INA '0220 = read PIC Interval register
|
||
INA '1120 = read device ID, don't clear A first
|
||
INA '1220 = read Memory Increment Cell address
|
||
INA '1320 = read interrupt vector address
|
||
INA '1420 = read location from CP ROM (not implemented, used to boot)
|
||
INA '1620 = read control panel up switches
|
||
INA '1720 = read control panel down switches
|
||
|
||
IMPORTANT NOTE: this device ('20) never skips!
|
||
*/
|
||
|
||
void devcp (short class, short func, short device) {
|
||
static short enabled = 0;
|
||
static unsigned short clkvec;
|
||
static short clkpic;
|
||
|
||
#define SETCLKPOLL devpoll[device] = instpermsec*(-clkpic*3.2)/1000;
|
||
|
||
switch (class) {
|
||
|
||
case 0:
|
||
if (T_INST) fprintf(stderr," OCP '%02o%02o\n", func, device);
|
||
printf("OCP '%02o%02o\n", func, device);
|
||
|
||
if (func == 0 || func == 015) {
|
||
fprintf(stderr,"Clock process initialized!\n");
|
||
/* this enables tracing when the clock process initializes */
|
||
//traceflags = ~TB_MAP;
|
||
enabled = 1;
|
||
#if 0
|
||
SETCLKPOLL;
|
||
#else
|
||
devpoll[device] = 600000;
|
||
#endif
|
||
} else if (func == 016 || func == 017) {
|
||
enabled = 0;
|
||
devpoll[device] = 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);
|
||
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 (func == 011) { /* input ID */
|
||
crs[A] != 0120; /* this is the SOC board */
|
||
} else if (func == 016) {
|
||
crs[A] = sswitch;
|
||
} else if (func == 017) { /* read switches in momentary down position */
|
||
crs[A] = 0;
|
||
} 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 (func == 02) { /* set PIC interval */
|
||
clkpic = *(short *)(crs+A);
|
||
SETCLKPOLL;
|
||
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) {
|
||
clkvec = crs[A];
|
||
printf("Clock interrupt vector address = '%o\n", clkvec);
|
||
} else if (func == 017) { /* write lights */
|
||
} else {
|
||
printf("Unimplemented OTA device '%02o function '%02o\n", device, func);
|
||
fatal(NULL);
|
||
}
|
||
break;
|
||
|
||
case 4:
|
||
#if 1
|
||
if (enabled) {
|
||
if (intvec == -1)
|
||
intvec = clkvec;
|
||
SETCLKPOLL;
|
||
}
|
||
#endif
|
||
break;
|
||
}
|
||
}
|
||
|
||
|
||
/* disk controller at '26 and '27
|
||
|
||
NOTES:
|
||
- in the DSEL disk channel program command, unit number is a 4-bit field,
|
||
with these binary values:
|
||
0001 = unit 0 (pdev 460/461)
|
||
0010 = unit 1 (pdev 462/463)
|
||
0100 = unit 2 (pdev 464/465)
|
||
1000 = unit 3 (pdev 466/467)
|
||
|
||
OCP '1626 = reset interrupt
|
||
OCP '1726 = reset controller
|
||
|
||
INA '0626 = ??
|
||
|
||
INA '1126 = input ID, don't clear A first, fails if no controller
|
||
- bits 1,2,8-16 are significant, bits 8-9 are type, 10-16 are ID
|
||
- 4004 controller responds '26 (type=0)
|
||
- 4005 controller responds '126 (type=1)
|
||
- 2382 controller responds '040100 (type=1)
|
||
- LCDTC controller responds '226 (type=2)
|
||
- 10019 controller responds '040100 (type=1)
|
||
- 6590 controller responds '040100 (type=1)
|
||
|
||
INA '1726 = read oar, fails if controller is not halted
|
||
|
||
OTA '1726 = load OAR (Order Address Register), ie, run channel
|
||
program, address is in A
|
||
|
||
*/
|
||
|
||
void devdisk (short class, short func, short device) {
|
||
|
||
#define S_HALT 0
|
||
#define S_RUN 1
|
||
#define S_INT 2
|
||
|
||
#if 0
|
||
#define CID4005 0100
|
||
#else
|
||
#define CID4005 0
|
||
#endif
|
||
|
||
|
||
static struct {
|
||
unsigned short oar;
|
||
unsigned short state; /* channel program state: S_XXXX */
|
||
unsigned short status; /* controller status */
|
||
short usel; /* unit selected (0-3, -1=none) */
|
||
short dmachan; /* dma channel selected */
|
||
short dmanch; /* number of dma channels-1 */
|
||
struct {
|
||
int rtfd; /* read trace file descriptor */
|
||
unsigned short theads; /* total heads (cfg file) */
|
||
unsigned short spt; /* sectors per track */
|
||
int devfd; /* Unix device file descriptor */
|
||
int readnum; /* increments on each read */
|
||
} unit[4];
|
||
} dc[64];
|
||
|
||
short i,u;
|
||
|
||
/* temps for running channel programs */
|
||
|
||
unsigned short order;
|
||
unsigned short m,m1,m2;
|
||
short head, track, rec, recsize, nwords;
|
||
unsigned short dmareg, dmaaddr;
|
||
unsigned short iobuf[4096]; /* local I/O buf, before mapped I/O */
|
||
unsigned short *iobufp;
|
||
unsigned short access;
|
||
short dmanw, dmanw1, dmanw2;
|
||
unsigned int utempl;
|
||
char ordertext[8];
|
||
int theads, spt, phyra;
|
||
char devfile[8];
|
||
char rtfile[16]; /* read trace file name */
|
||
int rtnw; /* total number of words read (all channels) */
|
||
|
||
switch (class) {
|
||
|
||
case -1:
|
||
for (i=0; i<64; i++) {
|
||
dc[i].state = S_HALT;
|
||
dc[i].status = 0100000;
|
||
dc[i].usel = -1;
|
||
for (u=0; u<4; u++) {
|
||
dc[i].unit[u].rtfd = -1;
|
||
dc[i].unit[u].theads = 40;
|
||
dc[i].unit[u].spt = 9;
|
||
dc[i].unit[u].devfd = -1;
|
||
dc[i].unit[u].readnum = -1;
|
||
}
|
||
}
|
||
break;
|
||
|
||
case 0:
|
||
if (T_INST || T_DIO) fprintf(stderr," OCP '%2o%2o\n", func, device);
|
||
if (func == 016) { /* reset interrupt */
|
||
if (dc[device].state == S_INT) {
|
||
dc[device].state = S_RUN;
|
||
devpoll[device] = 1;
|
||
}
|
||
} else if (func == 017) { /* reset controller */
|
||
dc[i].state = S_HALT;
|
||
dc[i].status = 0100000;
|
||
dc[i].usel = -1;
|
||
} else {
|
||
fprintf(stderr," Unrecognized OCP '%2o%2o\n", func, device);
|
||
fatal(NULL);
|
||
}
|
||
break;
|
||
|
||
case 1:
|
||
if (T_INST || T_DIO) fprintf(stderr," SKS '%2o%2o\n", func, device);
|
||
if (func == 04) { /* skip if not interrupting */
|
||
if (dc[device].state != S_INT)
|
||
IOSKIP;
|
||
} else {
|
||
fprintf(stderr," Unrecognized SKS '%2o%2o\n", func, device);
|
||
fatal(NULL);
|
||
}
|
||
break;
|
||
|
||
case 2:
|
||
if (T_INST || T_DIO) fprintf(stderr," INA '%2o%2o\n", func, device);
|
||
|
||
/* this turns tracing on when the Primos disk processes initialize */
|
||
//traceflags = ~TB_MAP;
|
||
|
||
/* INA's are only accepted when the controller is not busy */
|
||
|
||
if (dc[device].state != S_HALT)
|
||
return;
|
||
|
||
if (func == 01) /* read device id, clear A first */
|
||
crs[A] = CID4005 + device;
|
||
else if (func == 011) /* read device id, don't clear A */
|
||
crs[A] |= (CID4005 + device);
|
||
else if (func == 017) { /* read OAR */
|
||
crs[A] = dc[device].oar;
|
||
} else {
|
||
printf("Unimplemented INA device '%02o function '%02o\n", device, func);
|
||
fatal(NULL);
|
||
}
|
||
IOSKIP;
|
||
break;
|
||
|
||
case 3:
|
||
if (T_INST || T_DIO) fprintf(stderr," OTA '%02o%02o\n", func, device);
|
||
if (func == 017) { /* set OAR (order address register) */
|
||
dc[device].state = S_RUN;
|
||
dc[device].oar = crs[A];
|
||
devpoll[device] = 1;
|
||
} else {
|
||
printf("Unimplemented OTA device '%02o function '%02o\n", device, func);
|
||
fatal(NULL);
|
||
}
|
||
IOSKIP;
|
||
break;
|
||
|
||
case 4: /* poll (run channel program) */
|
||
|
||
while (dc[device].state == S_RUN) {
|
||
m = get16r(dc[device].oar, 0);
|
||
m1 = get16r(dc[device].oar+1, 0);
|
||
if (T_INST || T_DIO) fprintf(stderr,"\nDIOC %o: %o %o %o\n", dc[device].oar, m, m1, get16r(dc[device].oar+2, 0));
|
||
dc[device].oar += 2;
|
||
order = m>>12;
|
||
if (m & 04000) { /* "execute if ..." */
|
||
if (order == 2 || order == 5 || order == 6)
|
||
dc[device].oar++;
|
||
continue;
|
||
}
|
||
|
||
switch (order) {
|
||
|
||
case 0: /* DHLT = Halt */
|
||
dc[device].state = S_HALT;
|
||
devpoll[device] = 0;
|
||
if (T_INST || T_DIO) fprintf(stderr," channel halted at '%o\n", dc[device].oar);
|
||
break;
|
||
|
||
case 2: /* SFORM = Format */
|
||
case 5: /* SREAD = Read */
|
||
case 6: /* SWRITE = Write */
|
||
m2 = get16r(dc[device].oar++, 0);
|
||
recsize = m & 017;
|
||
track = m1 & 01777;
|
||
rec = m2 >> 8; /* # records for format, rec # for R/W */
|
||
head = m2 & 077;
|
||
u = dc[device].usel;
|
||
if (order == 2)
|
||
strcpy(ordertext,"Format");
|
||
else if (order == 5)
|
||
strcpy(ordertext,"Read");
|
||
else if (order == 6)
|
||
strcpy(ordertext,"Write");
|
||
if (T_INST || T_DIO) fprintf(stderr," %s, head=%d, track=%d, rec=%d, recsize=%d\n", ordertext, head, track, rec, recsize);
|
||
if (dc[device].unit[u].devfd == -1) {
|
||
if (T_INST || T_DIO) fprintf(stderr," Unit not selected or not ready\n");
|
||
dc[device].status = 0100001;
|
||
} else if (order == 2) {
|
||
if (T_INST || T_DIO) fprintf(stderr," Format order\n");
|
||
//fatal("DFORMAT channel order not implemented");
|
||
} else { /* order = 5 (read) or 6 (write) */
|
||
|
||
/* translate head/track/sector to drive record address */
|
||
|
||
phyra = (track*dc[device].unit[u].theads*dc[device].unit[u].spt) + head*9 + rec;
|
||
if (T_INST || T_DIO) fprintf(stderr, " Unix ra=%d, byte offset=%d\n", phyra, phyra*2080);
|
||
if (lseek(dc[device].unit[u].devfd, phyra*2080, SEEK_SET) == -1) {
|
||
perror("Unable to seek drive file");
|
||
fatal(NULL);
|
||
}
|
||
while (dc[device].dmanch >= 0) {
|
||
dmareg = ((dc[device].dmachan & 036) << 1) | (dc[device].dmachan & 1);
|
||
dmanw = regs.sym.regdmx[dmareg];
|
||
dmanw = -(dmanw>>4);
|
||
dmaaddr = regs.sym.regdmx[dmareg+1];
|
||
if (T_INST || T_DIO) fprintf(stderr, " DMA channels: nch-1=%d, ['%o]='%o, ['%o]='%o, nwords=%d\n", dc[device].dmanch, dc[device].dmachan, regs.sym.regdmx[dmareg], dc[device].dmachan+1, dmaaddr, dmanw);
|
||
|
||
if (order == 5) {
|
||
if (crs[MODALS] & 020)
|
||
if ((dmaaddr & 01777) || dmanw > 1024)
|
||
iobufp = iobuf;
|
||
else
|
||
iobufp = mem+mapva(dmaaddr, WACC, &access, 0);
|
||
else
|
||
iobufp = mem+dmaaddr;
|
||
if (read(dc[device].unit[u].devfd, (char *)iobufp, dmanw*2) != dmanw*2) {
|
||
perror("Unable to read drive file");
|
||
fatal(NULL);
|
||
}
|
||
if (iobufp == iobuf)
|
||
for (i=0; i<dmanw; i++)
|
||
put16r(iobuf[i], dmaaddr+i, 0);
|
||
} else {
|
||
if (crs[MODALS] & 020) {
|
||
iobufp = iobuf;
|
||
for (i=0; i<dmanw; i++)
|
||
iobuf[i] = get16r(dmaaddr+i, 0);
|
||
} else
|
||
iobufp = mem+dmaaddr;
|
||
if (write(dc[device].unit[u].devfd, (char *)iobufp, dmanw*2) != dmanw*2) {
|
||
perror("Unable to write drive file");
|
||
fatal(NULL);
|
||
}
|
||
}
|
||
regs.sym.regdmx[dmareg] = 0;
|
||
regs.sym.regdmx[dmareg+1] += dmanw;
|
||
dc[device].dmachan += 2;
|
||
dc[device].dmanch--;
|
||
}
|
||
}
|
||
break;
|
||
|
||
/* NOTE: for seek command, the track should probably be stored
|
||
in a state variable, then checked for equality on
|
||
read/write. If not equal, a disk fault should occur rather
|
||
than reading/writing from the wrong cylinder. */
|
||
|
||
case 3: /* SSEEK = Seek */
|
||
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);
|
||
break;
|
||
|
||
case 4: /* DSEL = Select unit */
|
||
u = (m1 & 017) >> 1; /* unit = 0/1/2/4 */
|
||
if (u == 4) u = 3; /* unit = 0/1/2/3 */
|
||
dc[device].usel = u;
|
||
if (dc[device].unit[u].devfd == -1) {
|
||
snprintf(devfile,sizeof(devfile),"dev%ou%d", device, u);
|
||
if (T_INST || T_DIO) fprintf(stderr," filename for unit %d is %s\n", u, devfile);
|
||
if ((dc[device].unit[u].devfd = open(devfile, O_RDWR, 0)) == -1)
|
||
dc[device].status = 0100001; /* not ready */
|
||
}
|
||
if (T_INST || T_DIO) fprintf(stderr," select unit %d\n", u);
|
||
break;
|
||
|
||
case 7: /* DSTALL = Stall */
|
||
if (T_INST || T_DIO) fprintf(stderr," stall\n");
|
||
|
||
/* NOTE: technically, the stall command is supposed to wait
|
||
210 usecs, so that the disk controller doesn't hog the I/O
|
||
bus by looping in a channel program waiting for I/O to
|
||
complete. With the emulator, this delay isn't necessary,
|
||
although it will cause DIAG tests to fail if the delay is
|
||
omitted. Hence the PX test. Ignoring stall gives a 25%
|
||
increase in I/O's per second on a 2GHz Mac (8MB emulator). */
|
||
|
||
if (crs[MODALS] & 010) /* PX enabled? */
|
||
break; /* yes, no stall */
|
||
devpoll[device] = instpermsec/5; /* 200 microseconds, sb 210 */
|
||
return;
|
||
|
||
case 9: /* DSTAT = Store status to memory */
|
||
if (T_INST || T_DIO) fprintf(stderr, " store status='%o to '%o\n", dc[device].status, m1);
|
||
put16r(dc[device].status,m1,0);
|
||
break;
|
||
|
||
case 11: /* DOAR = Store OAR to memory (2 words) */
|
||
if (T_INST || T_DIO) fprintf(stderr, " store OAR='%o to '%o\n", dc[device].oar, m1);
|
||
put16r(dc[device].oar,m1,0);
|
||
break;
|
||
|
||
case 13: /* SDMA = select DMA channel(s) to use */
|
||
dc[device].dmanch = m & 017;
|
||
dc[device].dmachan = m1;
|
||
if (T_INST || T_DIO) fprintf(stderr, " set DMA channels, nch-1=%d, channel='%o\n", dc[device].dmanch, dc[device].dmachan);
|
||
break;
|
||
|
||
case 14: /* DINT = generate interrupt through vector address */
|
||
if (T_INST || T_DIO) fprintf(stderr, " interrupt through '%o\n", m1);
|
||
if (intvec >= 0 || !(crs[MODALS] & 0100000) || inhcount > 0)
|
||
dc[device].oar -= 2; /* can't take interrupt right now */
|
||
else {
|
||
intvec = m1;
|
||
dc[device].state = S_INT;
|
||
}
|
||
//traceflags = ~TB_MAP;
|
||
devpoll[device] = 10;
|
||
return;
|
||
|
||
case 15: /* DTRAN = channel program jump */
|
||
dc[device].oar = m1;
|
||
if (T_INST || T_DIO) fprintf(stderr, " jump to '%o\n", m1);
|
||
break;
|
||
|
||
default:
|
||
printf("Unrecognized channel order = %d\n", order);
|
||
fatal(NULL);
|
||
}
|
||
}
|
||
}
|
||
}
|