/* 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: '00 = polling (?) '01 = paper tape reader '02 = paper tape punch '03 = #1 MPC/URC (line printer/card reader/card punch) '04 = SOC board (system console/user terminal) '05 = #2 MPC/URC (line printer/card reader/card punch) '06 = card punch? (RTOS User Guide, A-1) / IPC (Engr Handbook p.101) '06 = Interproc. Channel (IPC) (R20 Hacker's Guide) '07 = #1 PNC '10 = ICS2 #1 or ICS1 '11 = ICS2 #2 or ICS1 '12 = floppy disk/diskette '13 = #2 magtape controller '14 = #1 magtape controller '15 = #5 AMLC or ICS1 '16 = #6 AMLC or ICS1 '17 = #7 AMLC or ICS1 '20 = control panel / real-time clock '21 = 1st 4002 (Option B') disk controller '22 = disk #3 '23 = disk #4 '24 = disk (was Writable Control Store) '25 = disk (was 4000 disk controller) '26 = 1st disk controller '27 = 2nd disk controller '30-32 = BPIOC #1-3 (RTOS User Guide, A-1) '32 = AMLC #8 or ICS1 '33 = #1 Versatec (verdim) '34 = #2 Versatec '35 = #4 AMLC or ICS1 '36-37 = ELFBUS #1 & 2 (ICS1 #1, ICS1 #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 '47 = #2 PNC '50 = #1 HSSMLC (cs/slcdim.pma) '51 = #2 HSSMLC '52 = #3 AMLC or ICS1 '53 = #2 AMLC '54 = #1 AMLC '55 = MACI autocall unit '56 = old SMLC (RTOS User Guide, A-1 & Hacker's Guide) '60-67 = reserved for user devices (GPIB) '70-'73 = Megatek graphics terminals Devices emulated by Primos in ks/ptrap.ftn for I/O instructions in Ring 3: '01 = paper tape reader '02 = paper tape punch '04 = console '20 = control panel lights & sense switches */ #include #include #include #include #include #include #include #include #include #include /* 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 new device handlers */ int devnew (short class, short func, short device) { switch (class) { case -1: return 0; case 0: if (T_INST) fprintf(stderr," OCP '%02o%02o\n", func, device); if (func == 99) { ; } 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 == 99) 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 == 99) { ; } 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 == 99) { IOSKIP; } else { printf("Unimplemented OTA device '%02o function '%02o, A='%o\n", device, func, crs[A]); fatal(NULL); } break; } } /* this is a template for null (not present) devices */ int devnone (short class, short func, short device) { switch (class) { case -1: return 0; case 0: if (T_INST) fprintf(stderr," OCP '%02o%02o\n", func, device); fprintf(stderr, " unimplemented device '%o\n", device); break; case 1: if (T_INST) fprintf(stderr," SKS '%02o%02o\n", func, device); fprintf(stderr, " unimplemented device '%o\n", device); break; case 2: if (T_INST) fprintf(stderr," INA '%02o%02o\n", func, device); fprintf(stderr, " unimplemented device '%o\n", device); break; case 3: if (T_INST) fprintf(stderr," OTA '%02o%02o\n", func, device); fprintf(stderr, " unimplemented device '%o\n", device); 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 */ int 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: setsid(); 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); } return 0; 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 device '%02o function '%02o, A='%o\n", device, func, crs[A]); 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 */ int devmt (short class, short func, short device) { switch (class) { case -1: return 0; 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! */ int devcp (short class, short func, short device) { static short enabled = 0; static unsigned short clkvec; static short clkpic; int datnow, i; time_t unixtime; ea_t datnowea; struct tm *tms; #define SETCLKPOLL devpoll[device] = instpermsec*(-clkpic*3.2)/1000; switch (class) { case -1: return 0; case 0: if (T_INST) fprintf(stderr," OCP '%02o%02o\n", func, device); if (func == 01) { /* ack PIC interrupt */ ; } else if (func == 015) { /* set interrupt mask */ fprintf(stderr,"Clock interrupt enabled!\n"); /* this enables tracing when the clock process initializes */ //traceflags = ~TB_MAP; enabled = 1; #if 1 SETCLKPOLL; #else devpoll[device] = 600000; #endif datnowea = 0; for (i=0; itm_year<<25 | (tms->tm_mon+1)<<21 | tms->tm_mday<<16 | ((tms->tm_hour*3600 + tms->tm_min*60 + tms->tm_sec)/4); put32(datnow, datnowea); } } 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, A='%o\n", device, func, crs[A]); 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 */ int devdisk (short class, short func, short device) { #define S_HALT 0 #define S_RUN 1 #define S_INT 2 #define MAXDRIVES 8 #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[MAXDRIVES]; } 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; int nb; /* number of bytes returned from read/write */ 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> 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 ((nb=read(dc[device].unit[u].devfd, (char *)iobufp, dmanw*2)) != dmanw*2) { fprintf(stderr, "Disk read error: device='%o, u=%d, fd=%d, nb=%d\n", device, u, dc[device].unit[u].devfd, nb); if (nb == -1) perror("Unable to read drive file"); memset((char *)iobufp, 0, dmanw*2); } if (iobufp == iobuf) for (i=0; i> 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); } } } } /* AMLC I/O operations: OCP '0054 - stop clock OCP '0154 - single step clock OCP '1234 - set normal/DMT mode OCP '1354 - set diagnostic/DMQ mode OCP '1554 - enable interrupts OCP '1654 - disable interrupts OCP '1754 - initialize AMLC SKS '0454 - skip if NOT interrupting INA '0054 - input data set sense bit for all lines (read clear to send) INA '0754 - input AMLC status and clear (always skips) INA '1154 - input AMLC controller ID INA '1454 - input DMA/C channel number INA '1554 - input DMT/DMQ base address INA '1654 - input interrupt vector address OTA '0054 - output line number for INA '0054 (older models only) OTA '0154 - output line configuration for 1 line OTA '0254 - output line control for 1 line OTA '1454 - output DMA/C channel number OTA '1554 - output DMT/DMQ base address OTA '1654 - output interrupt vector address OTA '1754 - output programmable clock constant Primos AMLC usage: OCP '17xx - initialize controller - clear all registers and flip-flops - start line clock - clear all line control bits during the 1st line scan - responds not ready until 1 line scan is complete INA '11xx - read AMLC ID - emulator always returns '20054 (DMQ, 16 lines) OTA '17xx - set AMLC programmable clock constant - ignored by the emulator OTA '14xx - set DMC channel address for double input buffers - emulator stores in a structure OCP '13xx - set dmq mode (used to be "set diagnostic mode") - ignored by the emulator OTA '15xx - set DMT Base Address - also used for DMQ address? - emulator stores in a structure OTA '16xx - set interrupt vector address - emulator stores in a structure OTA '01xx - set line configuration - emulator ignores: all lines are 8-bit raw OTA '02xx - set line control - emulator ignores: all lines are enabled OCP '15xx/'16xx - enable/disable interrupts - emulator stores in a structure */ int devamlc (short class, short func, short device) { #define MAXLINES 128 #define MAXFD MAXLINES+20 #define MAXBOARDS 8 /* AMLC poll rate in milliseconds */ #define AMLCPOLL 50 #if 1 #define QAMLC 020000 /* this is to enable QAMLC/DMQ functionality */ #else #define QAMLC 0 #endif static short inited = 0; static short devices[MAXBOARDS]; /* list of AMLC devices initialized */ static int tsfd; /* socket fd for terminal server */ static struct { /* maps socket fd to device & line */ int device; int line; } socktoline[MAXLINES]; static struct { char dmqmode; /* 0=DMT, 1=DMQ */ char bufnum; /* 0=1st input buffer, 1=2nd */ char eor; /* 1=End of Range on input */ unsigned short dmcchan; /* DMC channel (for input) */ unsigned short baseaddr; /* DMT/Q base address (for output) */ unsigned short intvector; /* interrupt vector */ char intenable; /* interrupts enabled? */ char interrupting; /* am I interrupting? */ unsigned short xmitenabled; /* 1 bit per line */ unsigned short recvenabled; /* 1 bit per line */ unsigned short ctinterrupt; /* 1 bit per line */ unsigned short dss; /* 1 bit per line */ unsigned short sockfd[16]; /* Unix socket fd, 1 per line */ } dc[64]; int lx; unsigned short utempa; unsigned int utempl; ea_t qcbea, dmcea, dmcbufbegea, dmcbufendea; unsigned short dmcnw; int dmcpair; int optval; int tsflags; struct sockaddr_in addr; int fd; unsigned int addrlen; unsigned char buf[1024]; int i, n, nw, newdevice; switch (class) { case -1: /* this part of initialization only occurs once, no matter how many AMLC boards are configured */ if (!inited) { for (i=0; i> 12; //printf("OTA '01%02o: AMLC line %d config = %x\n", device, lx, crs[A]); /* 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); close(dc[device].sockfd[lx]); dc[device].dss &= ~bitmask16[lx+1]; printf("Closing AMLC line %d on device '%o\n", lx, device); } IOSKIP; } else if (func == 02) { /* set line control */ //printf("OTA '02%02o: AMLC line control = %x\n", device, crs[A]); lx = (crs[A]>>12); if (crs[A] & 040) /* character time interrupt enable/disable */ dc[device].ctinterrupt |= bitmask16[lx+1]; else dc[device].ctinterrupt &= ~bitmask16[lx+1]; if (crs[A] & 010) /* transmit enable/disable */ dc[device].xmitenabled |= bitmask16[lx+1]; else dc[device].xmitenabled &= ~bitmask16[lx+1]; if (crs[A] & 01) /* receive enable/disable */ dc[device].recvenabled |= bitmask16[lx+1]; else dc[device].recvenabled &= ~bitmask16[lx+1]; if ((dc[device].ctinterrupt || dc[device].xmitenabled || dc[device].recvenabled) && devpoll[device] == 0) devpoll[device] = AMLCPOLL*instpermsec; /* setup another poll */ IOSKIP; } else if (func == 014) { /* set DMA/C channel (for input) */ dc[device].dmcchan = crs[A] & 0x7ff; //printf("OTA '14%02o: AMLC chan = %o\n", device, dc[device].dmcchan); if (!(crs[A] & 0x800)) fatal("Can't run AMLC in DMA mode!"); IOSKIP; } else if (func == 015) { /* set DMT/DMQ base address (for output) */ dc[device].baseaddr = crs[A]; IOSKIP; } else if (func == 016) { /* set interrupt vector */ dc[device].intvector = crs[A]; IOSKIP; } else if (func == 017) { /* set programmable clock constant */ IOSKIP; } else { printf("Unimplemented OTA device '%02o function '%02o, A='%o\n", device, func, crs[A]); fatal(NULL); } break; case 4: //printf("poll device '%o, cti=%x, xmit=%x, recv=%x, dss=%x\n", device, dc[device].ctinterrupt, dc[device].xmitenabled, dc[device].recvenabled, dc[device].dss); /* check for new connections */ conloop: while ((fd = accept(tsfd, (struct sockaddr *)&addr, &addrlen)) == -1 && errno == EINTR) ; if (fd == -1) { if (errno != EWOULDBLOCK) { perror("accept error for AMLC"); fatal("accept error for AMLC"); } } else { if (fd >= MAXFD) fatal("New connection fd is too big"); newdevice = 0; for (i=0; !newdevice && i 0) { for (i=0; i dmcbufendea) { /* end of range has occurred */ dc[device].bufnum = 1-dc[device].bufnum; dc[device].eor = 1; } } } } if (intvec == -1 && dc[device].intenable && (dc[device].ctinterrupt || dc[device].eor)) { intvec = dc[device].intvector; dc[device].interrupting = 1; } if ((dc[device].ctinterrupt || dc[device].xmitenabled || dc[device].recvenabled) && devpoll[device] == 0) devpoll[device] = AMLCPOLL*instpermsec; /* setup another poll */ break; } }