From d907a5a68aee16ab76a12df0cbf394528d1a3027 Mon Sep 17 00:00:00 2001 From: Jim Date: Tue, 13 Mar 2007 00:00:00 -0400 Subject: [PATCH] macros for V-mode branch & logicize began PNC driver put16r0 macro shortcuts like get16r0 branch instructions use inline code (macros) instead of gotos devasr blocks when possible lock disk files to prevent corruption from multiple emulators devamlc select to ensure output socket buffer isn't full devamlc partial write error is now a warning vs fatal ECONNRESET test in devamlc (spurious emulator crashes) lots of changes to PNC driver (not working yet) --- em.c | 474 +++++++++++++++--------------- emdev.h | 894 ++++++++++++++++++++++++++++++++++++++++++-------------- 2 files changed, 914 insertions(+), 454 deletions(-) diff --git a/em.c b/em.c index 7b156f1..798c095 100644 --- a/em.c +++ b/em.c @@ -174,6 +174,28 @@ typedef unsigned int pa_t; /* physical address */ if ((onoff)) crs[KEYS] |= 020000; \ else crs[KEYS] &= ~020000; + +/* these macros are for the VI-mode branch insructions */ + +#define BCLT if (crs[KEYS] & 0200) RPL = iget16(RP); else RPL++ +#define BCLE if (crs[KEYS] & 0300) RPL = iget16(RP); else RPL++ +#define BCEQ if (crs[KEYS] & 0100) RPL = iget16(RP); else RPL++ +#define BCNE if (!(crs[KEYS] & 0100)) RPL = iget16(RP); else RPL++ +#define BCGE if (!(crs[KEYS] & 0200)) RPL = iget16(RP); else RPL++ +#define BCGT if (!(crs[KEYS] & 0300)) RPL = iget16(RP); else RPL++ +#define BLS if (crs[KEYS] & 020000) RPL = iget16(RP); else RPL++ +#define BXNE if (crs[X] != 0) RPL = iget16(RP); else RPL++ +#define BYNE if (crs[Y] != 0) RPL = iget16(RP); else RPL++ + +/* same, but for logicize instructions */ + +#define LCLT crs[A] = ((crs[KEYS] & 0200) != 0) +#define LCLE crs[A] = ((crs[KEYS] & 0300) != 0) +#define LCEQ crs[A] = ((crs[KEYS] & 0100) != 0) +#define LCNE crs[A] = ((crs[KEYS] & 0100) == 0) +#define LCGE crs[A] = !(crs[KEYS] & 0200) +#define LCGT crs[A] = ((crs[KEYS] & 0300) == 0) + /* macro for restricted instructions (uses current program counter) */ #define RESTRICT() if (RP & RINGMASK32) fault(RESTRICTFAULT, 0, 0); @@ -213,7 +235,7 @@ char gen0nam[][5] = { "XVFY"}; -/* trace flags to control various aspects of emulator tracing: +/* trace flags to control aspects of emulator tracing: T_EAR trace R-mode effective address calculation T_EAV trace V-mode effective address calculation @@ -224,6 +246,7 @@ char gen0nam[][5] = { T_EAAP AP effective address calculation T_DIO disk I/O T_TIO tape I/O + T_RIO ring network I/O T_TERM terminal output (tnou[a]) T_MAP segmentation T_PCL PCL instructions @@ -245,24 +268,27 @@ char gen0nam[][5] = { #define TB_PX 0x00000800 #define TB_TIO 0x00001000 #define TB_TERM 0x00002000 +#define TB_RIO 0x00004000 -#define T_EAR TB_EAR -#define T_EAV TB_EAV -#define T_EAI TB_EAI -#define T_INST TB_INST -#define T_FLOW TB_FLOW -#define T_MODE TB_MODE -#define T_EAAP TB_EAAP -#define T_DIO TB_DIO -#define T_MAP TB_MAP -#define T_PCL TB_PCL +#define T_EAR TB_EAR +#define T_EAV TB_EAV +#define T_EAI TB_EAI +#define T_INST TB_INST +#define T_FLOW TB_FLOW +#define T_MODE TB_MODE +#define T_EAAP TB_EAAP +#define T_DIO TB_DIO +#define T_MAP TB_MAP +#define T_PCL TB_PCL #define T_FAULT TB_FAULT -#define T_PX TB_PX -#define T_TIO TB_TIO -#define T_TERM TB_TERM +#define T_PX TB_PX +#define T_TIO TB_TIO +#define T_TERM TB_TERM +#define T_RIO TB_RIO -#if defined(NOTRACE) +#ifdef NOTRACE #define TRACE(flags, formatargs...) + #define TRACEA(formatargs...) #else #define TRACE(flags, formatargs...) if (traceflags & (flags)) fprintf(tracefile,formatargs) #define TRACEA(formatargs...) fprintf(tracefile,formatargs) @@ -276,7 +302,7 @@ char gen0nam[][5] = { on and off for each instruction traceprocs is an array of procedure names we're tracing, with flags - and various associated data + and associated data numtraceprocs is the number of entries in traceprocs, 0=none @@ -333,8 +359,8 @@ unsigned short mem[MEMSIZE]; /* system's physical memory */ #define INCVA(ea,n) (((ea) & 0xFFFF0000) | ((ea)+(n)) & 0xFFFF) /* STLB cache is defined here. There are several different styles on - various Prime models. This is modeled after the 6350 STLB, but is - only 1-way associative and doesn't have slots dedicated to I/O + Prime models. This is modeled after the 6350 STLB, but is only + 1-way associative and doesn't have slots dedicated to I/O segments */ #define STLBENTS 512 @@ -694,8 +720,11 @@ pa_t mapva(ea_t ea, short intacc, unsigned short *access, ea_t rp) { #define get64(ea) (get64r((ea),RP)) #define get64r0(ea) (get64r((ea),0)) #define put16(value, ea) (put16r((value),(ea),RP)) +#define put16r0(value, ea) (put16r((value),(ea),0)) #define put32(value, ea) (put32r((value),(ea),RP)) +#define put32r0(value, ea) (put32r((value),(ea),0)) #define put64(value, ea) (put64r((value),(ea),RP)) +#define put64r0(value, ea) (put64r((value),(ea),0)) unsigned short get16r(ea_t ea, ea_t rpring) { @@ -765,17 +794,17 @@ 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. I tried using get64 (with appropriate mask changes) and - performance was much worse than not prefetching at all on a G4 */ + This needs to be checked more... not sure it actually improves + performance all that much and is potentially dangerous. I tried + using get64 (with appropriate mask changes) and performance was + much worse than not prefetching at all on a G4 */ #if 1 inline unsigned short iget16(ea_t ea) { static ea_t eafirst = -1; /* ea of instruction buffer */ static unsigned short insts[2]; /* instruction buffer */ - if (ea & 0x80000000) { /* check for R-mode inst in register */ + if (!(crs[KEYS] & 010000)) { /* don't buffer in R-mode */ eafirst = -1; return get16(ea); } @@ -886,9 +915,9 @@ void calf(ea_t ea) { /* get concealed stack entry address */ - first = get16r(pcbp+PCBCSFIRST, 0); - next = get16r(pcbp+PCBCSNEXT, 0); - last = get16r(pcbp+PCBCSLAST, 0); + first = get16r0(pcbp+PCBCSFIRST); + next = get16r0(pcbp+PCBCSNEXT); + last = get16r0(pcbp+PCBCSLAST); TRACE(T_FAULT, "CALF: first=%o, next=%o, last=%o\n", first, next, last); if (next == first) this = last; @@ -903,7 +932,7 @@ void calf(ea_t ea) { R-mode I/O instruction occurs under Primos (causing a restricted inst fault that is handled in the outer ring). */ - if (get16r(ea+5, 0) != 0) { + if (get16r0(ea+5) != 0) { printf("CALF ecb at %o/%o has arguments!\n", ea>>16, ea&0xFFFF); fatal(NULL); } @@ -912,8 +941,8 @@ void calf(ea_t ea) { /* get the concealed stack entries and adjust the new stack frame */ - *(unsigned int *)(cs+0) = get32r(csea+0, 0); - *(double *)(cs+2) = get64r(csea+2, 0); + *(unsigned int *)(cs+0) = get32r0(csea+0); + *(double *)(cs+2) = get64r0(csea+2); TRACE(T_FAULT, "CALF: cs entry: retpb=%o/%o, retkeys=%o, fcode=%o, faddr=%o/%o\n", cs[0], cs[1], cs[2], cs[3], cs[4], cs[5]); @@ -926,7 +955,7 @@ void calf(ea_t ea) { /* pop the concealed stack */ - put16r(this, pcbp+PCBCSNEXT, 0); + put16r0(this, pcbp+PCBCSNEXT); } @@ -1003,31 +1032,31 @@ void fault(unsigned short fvec, unsigned short fcode, ea_t faddr) { ring = (RPH>>13) & 3; /* save current ring */ pcbp = *(ea_t *)(crs+OWNER); if (fvec == PROCESSFAULT || fvec == SEMFAULT || fvec == ACCESSFAULT || fvec == STACKFAULT || fvec == SEGFAULT) - pxfvec = get32r(pcbp+PCBFVR0,0); /* use R0 handler */ + pxfvec = get32r0(pcbp+PCBFVR0); /* use R0 handler */ else if (fvec == PAGEFAULT) - pxfvec = get32r(pcbp+PCBFVPF,0); /* use page fault handler, also R0 */ + pxfvec = get32r0(pcbp+PCBFVPF); /* use page fault handler, also R0 */ else { - pxfvec = get32r(pcbp+PCBFVEC+2*ring,0); /* use current ring handler */ + pxfvec = get32r0(pcbp+PCBFVEC+2*ring); /* use current ring handler */ pxfvec |= ((int)ring) << 29; /* weaken */ } /* push a concealed stack entry */ - first = get16r(pcbp+PCBCSFIRST, 0); - next = get16r(pcbp+PCBCSNEXT, 0); - last = get16r(pcbp+PCBCSLAST, 0); + first = get16r0(pcbp+PCBCSFIRST); + next = get16r0(pcbp+PCBCSNEXT); + last = get16r0(pcbp+PCBCSLAST); TRACE(T_FAULT, "fault: PX enabled, pcbp=%o/%o, cs first=%o, next=%o, last=%o\n", pcbp>>16, pcbp&0xFFFF, first, next, last); if (next > last) { TRACE(T_FAULT, "fault: Concealed stack wraparound to first"); next = first; } csea = MAKEVA(crs[OWNERH]+csoffset, next); - put32r(faultrp, csea, 0); - put16r(crs[KEYS], csea+2, 0); - put16r(fcode, csea+3, 0); - put32r(faddr, csea+4, 0); - put16r(next+6, pcbp+PCBCSNEXT, 0); - TRACE(T_FAULT, "fault: updated cs next=%o\n", get16r(pcbp+PCBCSNEXT, 0)); + put32r0(faultrp, csea); + put16r0(crs[KEYS], csea+2); + put16r0(fcode, csea+3); + put32r0(faddr, csea+4); + put16r0(next+6, pcbp+PCBCSNEXT); + TRACE(T_FAULT, "fault: updated cs next=%o\n", get16r0(pcbp+PCBCSNEXT)); /* update RP to jump to the fault vector in the fault table */ @@ -1087,33 +1116,33 @@ int devpoll[64] = {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,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}; +int (*devmap[64])(int, int, int) = { + /* '0x */ devnone,devnone,devnone,devnone,devasr,devnone,devnone,devpnc, + /* '1x */ devnone,devnone,devnone,devnone,devmt,devamlc, devamlc, devamlc, + /* '2x */ devcp,devnone,devdisk,devdisk,devdisk,devdisk,devdisk,devdisk, + /* '3x */ devnone,devnone,devamlc,devnone,devnone,devamlc,devnone,devnone, + /* '4x */ devnone,devnone,devnone,devnone,devnone,devnone,devnone,devnone, + /* '5x */ devnone,devnone,devamlc,devamlc,devamlc,devnone,devnone,devnone, + /* '6x */ devnone,devnone,devnone,devnone,devnone,devnone,devnone,devnone, + /* '7x */ devnone,devnone,devnone,devnone,devnone,devnone,devnone,devnone}; #else /* this is the "minimum system" controller configuration */ int (*devmap[64])(int, int, int) = { - /* '0x */ 0,0,0,0,devasr,0,0,devpnc, + /* '0x */ devnone,devnone,devnone,devnone,devasr,devnone,devnone,devpnc, #if 1 - /* '1x */ devnone,devnone,0,devnone,devmt,devnone, devnone, devnone, + /* '1x */ devnone,devnone,devnone,devnone,devmt,devnone, devnone, devnone, #else - /* '1x */ devnone,devnone,0,devnone,devnone,devnone, devnone, devnone, + /* '1x */ devnone,devnone,devnone,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, - /* '6x */ 0,0,0,0,0,0,0,0, - /* '7x */ 0,0,0,0,0,devnone,devnone,0}; + /* '2x */ devcp,devnone,devnone,devnone,devnone,devnone,devdisk,devnone, + /* '3x */ devnone,devnone,devnone,devnone,devnone,devnone,devnone,devnone, + /* '4x */ devnone,devnone,devnone,devnone,devnone,devnone,devnone,devnone, + /* '5x */ devnone,devnone,devnone,devnone,devamlc,devnone,devnone,devnone, + /* '6x */ devnone,devnone,devnone,devnone,devnone,devnone,devnone,devnone, + /* '7x */ devnone,devnone,devnone,devnone,devnone,devnone,devnone,devnone}; #endif @@ -1487,7 +1516,7 @@ dumpsegs() { } -/* NOTE: this needs get16r */ +/* NOTE: this needs get16r0 */ unsigned short dumppcb(unsigned short pcb) { short i; @@ -1798,8 +1827,6 @@ pcl (ea_t ecbea) { unsigned short tnlen, tnword; unsigned char tnchar; -#define FATAL$ MAKEVA(06,0164600) -#define INIT$3 MAKEVA(013,0174041) #define UNWIND_ MAKEVA(013,0106577) #if 0 @@ -2108,14 +2135,14 @@ pxregsave(unsigned short wait) { for (i=(wait?014:0); i<020; i++) { if (crsl[i] != 0) { mask |= bitmask16[i+1]; - put32r(crsl[i], regp, 0); + put32r0(crsl[i], regp); regp += 2; } } - put16r(mask, pcbp+PCBMASK, 0); - put32r(*(unsigned int *)(crs+TIMER), pcbp+PCBIT, 0); /* save interval timer */ + put16r0(mask, pcbp+PCBMASK); + put32r0(*(unsigned int *)(crs+TIMER), pcbp+PCBIT); /* save interval timer */ crs[KEYS] |= 1; /* set save done bit */ - put16r(crs[KEYS], pcbp+PCBKEYS, 0); + put16r0(crs[KEYS], pcbp+PCBKEYS); } /* pxregload: load pcbp's registers from their pcb to the current @@ -2130,19 +2157,19 @@ pxregload (ea_t pcbp) { TRACE(T_PX, "pxregload loading registers for process %o/%o\n", pcbp>>16, pcbp&0xFFFF); regp = pcbp+PCBREGS; - mask = get16r(pcbp+PCBMASK, 0); + mask = get16r0(pcbp+PCBMASK); for (i=0; i<020; i++) { if (mask & bitmask16[i+1]) { - crsl[i] = get32r(regp, 0); + crsl[i] = get32r0(regp); regp += 2; } else { crsl[i] = 0; } } - newkeys(get16r(pcbp+PCBKEYS, 0)); - *(unsigned int *)(crs+DTAR2) = get32r(pcbp+PCBDTAR2, 0); - *(unsigned int *)(crs+DTAR3) = get32r(pcbp+PCBDTAR3, 0); - *(unsigned int *)(crs+TIMER) = get32r(pcbp+PCBIT, 0); + newkeys(get16r0(pcbp+PCBKEYS)); + *(unsigned int *)(crs+DTAR2) = get32r0(pcbp+PCBDTAR2); + *(unsigned int *)(crs+DTAR3) = get32r0(pcbp+PCBDTAR3); + *(unsigned int *)(crs+TIMER) = get32r0(pcbp+PCBIT); crs[OWNERL] = pcbp & 0xFFFF; TRACE(T_PX, "pxregload: registers loaded, ownerl=%o, modals=%o\n", crs[OWNERL], crs[MODALS]); @@ -2206,15 +2233,11 @@ dispatcher() { if (regs.sym.pla != 0) rlp = MAKEVA(crs[OWNERH], regs.sym.pla); else if (regs.sym.plb != 0) -#if 0 - rlp = MAKEVA(crs[OWNERH], regs.sym.plb); -#else fatal("disp: pla is invalid, plb is valid?"); -#endif else fatal("dispatch: both pla and plb are zero; can't locate ready list"); while(1) { - rlbol = get16r(rlp, 0); + rlbol = get16r0(rlp); if (rlbol != 0) break; rlp += 2; @@ -2231,7 +2254,7 @@ dispatcher() { #if 1 /* debug tests to verify ready list structure */ rlp = MAKEVA(crs[OWNERH], regs.sym.pla); - rlbol = get16r(rlp, 0); + rlbol = get16r0(rlp); if (rlbol != pcbw) { printf("disp: rl bol=%o, != process dispatched=%o\n", rlbol, pcbw); fatal(NULL); @@ -2239,8 +2262,8 @@ dispatcher() { #if 0 /* NOTE: if a running process has its priority changed (in the pcb), this test fails */ - if (get16r(pcbp+PCBLEV, 0) != regs.sym.pla) { - printf("disp: dispatched process level=%o, != pla=%o\n", get16r(pcbp+PCBLEV, 0), regs.sym.pla); + if (get16r0(pcbp+PCBLEV) != regs.sym.pla) { + printf("disp: dispatched process level=%o, != pla=%o\n", get16r0(pcbp+PCBLEV), regs.sym.pla); fatal(NULL); } #endif @@ -2250,7 +2273,7 @@ dispatcher() { definition, this process should not be on any wait lists, so pcb.waitlist(seg) should be zero. Check it */ - utempa = get16r(pcbp+PCBWAIT, 0); + utempa = get16r0(pcbp+PCBWAIT); if (utempa != 0) { printf("disp: pcb %o/%o selected, but wait segno = %o\n", pcbp>>16, pcbp&0xFFFF, utempa); fatal(NULL); @@ -2330,11 +2353,11 @@ dispatcher() { /* if this process' abort flags are set, clear them and take process fault */ - utempa = get16r(pcbp+PCBABT, 0); + utempa = get16r0(pcbp+PCBABT); if (utempa != 0) { TRACE(T_PX, "dispatch: abort flags for %o are %o\n", crs[OWNERL], utempa); //printf("dispatch: abort flags for %o are %o\n", crs[OWNERL], utempa); - put16r(0, pcbp+PCBABT, 0); + put16r0(0, pcbp+PCBABT); fault(PROCESSFAULT, utempa, 0); fatal("fault returned after process fault"); } @@ -2359,7 +2382,7 @@ unready (ea_t waitlist, unsigned short newlink) { pcbp = *(ea_t *)(crs+OWNER); rlp = MAKEVA(crs[OWNERH], regs.sym.pla); - rl = get32r(rlp, 0); + rl = get32r0(rlp); bol = rl >> 16; eol = rl & 0xFFFF; if (bol != (pcbp & 0xFFFF)) { @@ -2370,13 +2393,13 @@ unready (ea_t waitlist, unsigned short newlink) { bol = 0; eol = 0; } else { - bol = get16r(pcbp+1, 0); + bol = get16r0(pcbp+1); } rl = (bol<<16) | eol; - put32r(rl, rlp, 0); /* update ready list */ + put32r0(rl, rlp); /* update ready list */ TRACE(T_PX, "unready: new rl bol/eol = %o/%o\n", rl>>16, rl&0xFFFF); - put16r(newlink, pcbp+1, 0); /* update my pcb link */ - put32r(waitlist, pcbp+2, 0); /* update my pcb wait address */ + put16r0(newlink, pcbp+1); /* update my pcb link */ + put32r0(waitlist, pcbp+2); /* update my pcb wait address */ *(unsigned int *)(crs+PB) = RP; pxregsave(1); regs.sym.pcba = 0; @@ -2402,26 +2425,26 @@ unsigned short ready (ea_t pcbp, unsigned short begend) { fatal("I'm running, but not regs.sym.pcba!"); #endif - level = get16r(pcbp+PCBLEV, 0); + level = get16r0(pcbp+PCBLEV); rlp = MAKEVA(crs[OWNERH],level); - rl = get32r(rlp, 0); + rl = get32r0(rlp); TRACE(T_PX, "ready: pcbp=%o/%o\n", pcbp>>16, pcbp&0xFFFF); TRACE(T_PX, "ready: old bol/eol for level %o = %o/%o\n", level, rl>>16, rl&0xFFFF); pcbw = pcbp; /* pcb word number */ if ((rl>>16) == 0) { /* bol=0: this RL level was empty */ - put32r(0, pcbp+1, 0); /* set link and wait SN in pcb */ + put32r0(0, pcbp+1); /* set link and wait SN in pcb */ rl = (pcbw<<16) | pcbw; /* set beg=end */ } else if (begend) { /* notify to beginning */ - put32r(rl & 0xFFFF0000, pcbp+1, 0); /* set link and wait SN in pcb */ + put32r0(rl & 0xFFFF0000, pcbp+1); /* set link and wait SN in pcb */ rl = (pcbw<<16) | rl&0xFFFF; /* new is bol, eol is unchanged */ } else { /* notify to end */ - put32r(0, pcbp+1, 0); /* set link and wait SN in pcb */ + put32r0(0, pcbp+1); /* set link and wait SN in pcb */ xpcbp = MAKEVA(crs[OWNERH],rl&0xFFFF); /* get ptr to last pcb at this level */ - put16r(pcbw,xpcbp+1, 0); /* set last pcb's forward link */ + put16r0(pcbw,xpcbp+1); /* set last pcb's forward link */ rl = (rl & 0xFFFF0000) | pcbw; /* rl bol is unchanged, eol is new */ } - put32r(rl, rlp, 0); - TRACE(T_PX, "ready: new bol/eol for level %o = %o/%o, pcb's link is %o\n", level, rl>>16, rl&0xFFFF, get16r(pcbp+1, 0)); + put32r0(rl, rlp); + TRACE(T_PX, "ready: new bol/eol for level %o = %o/%o, pcb's link is %o\n", level, rl>>16, rl&0xFFFF, get16r0(pcbp+1)); /* is this new process higher priority than me? If so, return 1 so that the dispatcher is entered. If not, check for new plb/pcbb */ @@ -2454,7 +2477,7 @@ pwait() { ea = apea(NULL); TRACE(T_PX, "%o/%o: wait on %o/%o, pcb %o, keys=%o, modals=%o\n", RPH, RPL, ea>>16, ea&0xFFFF, crs[OWNERL], crs[KEYS], crs[MODALS]); - utempl = get32r(ea, 0); /* get count and BOL */ + utempl = get32r0(ea); /* get count and BOL */ count = utempl>>16; /* count (signed) */ bol = utempl & 0xFFFF; /* beginning of wait list */ TRACE(T_PX, " wait list count was %d, bol was %o\n", count, bol); @@ -2470,18 +2493,18 @@ pwait() { printf("WAIT: pcba=%o != ownerl=%o\n", regs.sym.pcba, crs[OWNERL]); fatal(NULL); } - mylev = get16r(*(ea_t *)(crs+OWNER),0); + mylev = get16r0(*(ea_t *)(crs+OWNER)); if (bol != 0) { pcbp = MAKEVA(crs[OWNERH],bol); - pcblevnext = get32r(pcbp, 0); + pcblevnext = get32r0(pcbp); pcblev = pcblevnext >> 16; } TRACE(T_PX, " my level=%o, pcblev=%o\n", mylev, pcblev); if (count == 1 || mylev < pcblev) { /* add me to the beginning */ utempl = (count<<16) | crs[OWNERL]; - put32r(utempl, ea, 0); /* update semaphore count/bol */ + put32r0(utempl, ea); /* update semaphore count/bol */ } else { /* do a priority scan... */ while (pcblev <= mylev && bol != 0) { @@ -2489,12 +2512,12 @@ pwait() { bol = pcblevnext & 0xFFFF; if (bol != 0) { pcbp = MAKEVA(crs[OWNERH],bol); - pcblevnext = get32r(pcbp, 0); + pcblevnext = get32r0(pcbp); pcblev = pcblevnext >> 16; } } - put16r(crs[OWNERL], prevpcbp+PCBLINK, 0); - put16r(*(unsigned short *)&count, ea, 0); /* update count */ + put16r0(crs[OWNERL], prevpcbp+PCBLINK); + put16r0(*(unsigned short *)&count, ea); /* update count */ TRACE(T_PX, " new count=%d, new link for pcb %o=%o, bol=%o\n", prevpcbp&0xffff, crs[OWNERL], bol); } unready(ea, bol); @@ -2526,7 +2549,7 @@ nfy(unsigned short inst) { fatal(NULL); } ea = apea(NULL); - utempl = get32r(ea, 0); /* get count and BOL */ + utempl = get32r0(ea); /* get count and BOL */ scount = utempl>>16; /* count (signed) */ bol = utempl & 0xFFFF; /* beginning of wait list */ TRACE(T_PX, "%o/%o: opcode %o %s, ea=%o/%o, count=%d, bol=%o, I am %o\n", RPH, RPL, inst, nfyname[inst-01210], ea>>16, ea&0xFFFF, scount, bol, crs[OWNERL]); @@ -2544,12 +2567,12 @@ nfy(unsigned short inst) { fatal(NULL); } pcbp = MAKEVA(crs[OWNERH], bol); - utempl = get32r(pcbp+PCBWAIT, 0); + utempl = get32r0(pcbp+PCBWAIT); if (utempl != ea) { printf("NFY: bol=%o, pcb waiting on %o/%o != ea %o/%o\n", utempl>>16, utempl&0xFFFF, ea>>16, ea&0xFFFF); fatal(NULL); } - bol = get16r(pcbp+PCBLINK, 0); /* get new beginning of wait list */ + bol = get16r0(pcbp+PCBLINK); /* get new beginning of wait list */ resched = ready(pcbp, begend); /* put this pcb on the ready list */ } @@ -2567,7 +2590,7 @@ keys = 14200, modals=137 bol = 0; #endif utempl = (scount<<16) | bol; - put32r(utempl, ea, 0); /* update the semaphore */ + put32r0(utempl, ea); /* update the semaphore */ if (inst & 4) { /* interrupt notify */ if (inst & 2) /* clear active interrupt */ @@ -2881,8 +2904,8 @@ main (int argc, char **argv) { bootfile[0] = 0; pmap32bits = 0; csoffset = 0; - tport = -1; - nport = -1; + tport = 0; + nport = 0; /* check args */ @@ -2951,6 +2974,8 @@ main (int argc, char **argv) { traceflags |= TB_TIO; else if (strcmp(argv[i],"term") == 0) traceflags |= TB_TERM; + else if (strcmp(argv[i],"rio") == 0) + traceflags |= TB_RIO; else if (strcmp(argv[i],"all") == 0) traceflags = ~0; else if (isdigit(argv[i][0]) && strlen(argv[i]) < 2 && sscanf(argv[i],"%d", &templ) == 1) @@ -2994,7 +3019,7 @@ main (int argc, char **argv) { if (traceuser != 0) TRACEA("Tracing enabled for OWNERL %o\n", traceuser); else - TRACEA("Tracing enabled for all users"); + TRACEA("Tracing enabled for all users\n"); savetraceflags = traceflags; for (i=0; i= 26); if ((26 <= cpuid && cpuid <= 29) || cpuid >= 35) csoffset = 1; - if (tport == -1) - tport = 8000; - if (nport == -1) - nport = 8001; /* initialize all devices */ for (i=0; i<64; i++) - if (devmap[i]) - if (devmap[i](-1, 0, i)) { /* if initialization fails, */ - devmap[i] = devnone; /* remove device */ - printf("emulator: device '%o failed initialization - device removed\n", i); - } + if (devmap[i](-1, 0, i)) { /* if initialization fails, */ + devmap[i] = devnone; /* remove device */ + fprintf(stderr, "emulator: device '%o failed initialization - device removed\n", i); + } os_init(); @@ -3220,8 +3240,6 @@ For disk boots, the last 3 digits can be:\n\ for (i=0; i<64; i++) if (devpoll[i] && (--devpoll[i] <= 0)) { - if (!devmap[i]) - fatal("devpoll set but devmap is null"); devmap[i](4, 0, i); } @@ -3309,8 +3327,8 @@ For disk boots, the last 3 digits can be:\n\ if (crs[TIMER] == 0) { TRACE(T_PX, "#%d: pcb %o timer overflow\n", instcount, crs[OWNERL]); ea = *(ea_t *)(crs+OWNER); - m = get16r(ea+4, 0) | 1; /* set process abort flag */ - put16r(m, ea+4, 0); + m = get16r0(ea+4) | 1; /* set process abort flag */ + put16r0(m, ea+4); } } } @@ -4231,6 +4249,7 @@ irtn: } } + if (class == 3) { TRACE(T_INST, " generic class 3\n"); @@ -4238,56 +4257,32 @@ irtn: case 0141604: TRACE(T_FLOW, " BCLT\n"); -bclt: - if (crs[KEYS] & 0200) - RPL = iget16(RP); - else - RPL++; + BCLT; continue; case 0141600: TRACE(T_FLOW, " BCLE\n"); -bcle: - if (crs[KEYS] & 0300) - RPL = iget16(RP); - else - RPL++; + BCLE; continue; case 0141602: TRACE(T_FLOW, " BCEQ\n"); -bceq: - if (crs[KEYS] & 0100) - RPL = iget16(RP); - else - RPL++; + BCEQ; continue; case 0141603: TRACE(T_FLOW, " BCNE\n"); -bcne: - if (!(crs[KEYS] & 0100)) - RPL = iget16(RP); - else - RPL++; + BCNE; continue; case 0141605: TRACE(T_FLOW, " BCGE\n"); -bcge: - if (!(crs[KEYS] & 0200)) - RPL = iget16(RP); - else - RPL++; + BCGE; continue; case 0141601: TRACE(T_FLOW, " BCGT\n"); -bcgt: - if (!(crs[KEYS] & 0300)) - RPL = iget16(RP); - else - RPL++; + BCGT; continue; case 0141705: @@ -4316,118 +4311,122 @@ bcgt: case 0141706: TRACE(T_FLOW, " BLS\n"); -bls: - if (crs[KEYS] & 020000) - RPL = iget16(RP); - else - RPL++; + BLS; continue; case 0140614: TRACE(T_FLOW, " BLT\n"); SETCC_A; - goto bclt; + BCLT; + continue; case 0140610: TRACE(T_FLOW, " BLE\n"); SETCC_A; - goto bcle; + BCLE; + continue; case 0140612: TRACE(T_FLOW, " BEQ\n"); SETCC_A; - goto bceq; + BCEQ; + continue; case 0140613: TRACE(T_FLOW, " BNE\n"); SETCC_A; - goto bcne; + BCNE; + continue; case 0140615: TRACE(T_FLOW, " BGE\n"); SETCC_A; - goto bcge; + BCGE; + continue; case 0140611: TRACE(T_FLOW, " BGT\n"); SETCC_A; - goto bcgt; + BCGT; continue; case 0140700: TRACE(T_FLOW, " BLLE\n"); SETCC_L; - goto bcle; + BCLE; + continue; case 0140702: TRACE(T_FLOW, " BLEQ\n"); SETCC_L; - goto bceq; + BCEQ; + continue; case 0140703: TRACE(T_FLOW, " BLNE\n"); SETCC_L; - goto bcne; + BCNE; + continue; case 0140701: TRACE(T_FLOW, " BLGT\n"); SETCC_L; - goto bcgt; + BCGT; + continue; case 0141614: TRACE(T_FLOW, " BFLT\n"); SETCC_F; - goto bclt; + BCLT; + continue; case 0141610: TRACE(T_FLOW, " BFLE\n"); SETCC_F; - goto bcle; + BCLE; + continue; case 0141612: TRACE(T_FLOW, " BFEQ\n"); SETCC_F; - goto bceq; + BCEQ; + continue; case 0141613: TRACE(T_FLOW, " BFNE\n"); SETCC_F; - goto bcne; + BCNE; + continue; case 0141615: TRACE(T_FLOW, " BFGE\n"); SETCC_F; - goto bcge; + BCGE; + continue; case 0141611: TRACE(T_FLOW, " BFGT\n"); SETCC_F; - goto bcgt; + BCGT; + continue; case 0141334: TRACE(T_FLOW, " BIX\n"); crs[X]++; -bidx: - if (crs[X] != 0) - RPL = iget16(RP); - else - RPL++; + BXNE; continue; case 0141324: TRACE(T_FLOW, " BIY\n"); crs[Y]++; -bidy: - if (crs[Y] != 0) - RPL = iget16(RP); - else - RPL++; + BYNE; continue; case 0140724: TRACE(T_FLOW, " BDY\n"); crs[Y]--; - goto bidy; + BYNE; + continue; case 0140734: TRACE(T_FLOW, " BDX\n"); @@ -4447,8 +4446,8 @@ bidy: value (this is stored as number of instructions left until the timer expires). - NOTE: In practice, the clock device ticks at 330 times a sec, - so we only get to delay about 3ms here. + NOTE: In practice, the clock device ticks at 330 times a sec + under Primos so we only get to delay about 3ms here. */ utempl = instpermsec*10; /* limit delay to 10 millisecs */ @@ -4482,8 +4481,8 @@ bidy: crs[TIMER] += actualmsec; if (crs[TIMER] < utempa) { tempea = *(ea_t *)(crs+OWNER); - utempa = get16r(tempea+4, 0) | 1; /* set process abort flag */ - put16r(utempa, tempea+4, 0); + utempa = get16r0(tempea+4) | 1; /* set process abort flag */ + put16r0(utempa, tempea+4); } } else { crs[TIMERL] += utempl; @@ -4491,14 +4490,9 @@ bidy: instcount += actualmsec*instpermsec; } } - if (crs[X] != 0) - RPL = m; - else - RPL++; - continue; -#else - goto bidx; #endif + BXNE; + continue; case 0141206: TRACE(T_FLOW, " A1A\n"); @@ -4794,119 +4788,129 @@ a1a: case 0141500: TRACE(T_FLOW, " LCLT\n"); -lclt: - crs[A] = ((crs[KEYS] & 0200) != 0); + LCLT; continue; case 0141501: TRACE(T_FLOW, " LCLE\n"); -lcle: - crs[A] = ((crs[KEYS] & 0300) != 0); + LCLE; continue; case 0141503: TRACE(T_FLOW, " LCEQ\n"); -lceq: - crs[A] = ((crs[KEYS] & 0100) != 0); + LCEQ; continue; case 0141502: TRACE(T_FLOW, " LCNE\n"); -lcne: - crs[A] = ((crs[KEYS] & 0100) == 0); + LCNE; continue; case 0141504: TRACE(T_FLOW, " LCGE\n"); -lcge: - crs[A] = !(crs[KEYS] & 0200); + LCGE; continue; case 0141505: TRACE(T_FLOW, " LCGT\n"); -lcgt: - crs[A] = ((crs[KEYS] & 0300) == 0); + LCGT; continue; case 0140410: TRACE(T_FLOW, " LLT\n"); SETCC_A; - goto lclt; + LCLT; + continue; case 0140411: TRACE(T_FLOW, " LLE\n"); SETCC_A; - goto lcle; + LCLE; + continue; case 0140412: TRACE(T_FLOW, " LNE\n"); SETCC_A; - goto lcne; + LCNE; + continue; case 0140413: TRACE(T_FLOW, " LEQ\n"); SETCC_A; - goto lceq; + LCEQ; + continue; case 0140414: TRACE(T_FLOW, " LGE\n"); SETCC_A; - goto lcge; + LCGE; + continue; case 0140415: TRACE(T_FLOW, " LGT\n"); SETCC_A; - goto lcgt; + LCGT; + continue; case 0141511: TRACE(T_FLOW, " LLLE\n"); SETCC_L; - goto lcle; + LCLE; + continue; case 0141513: TRACE(T_FLOW, " LLEQ\n"); SETCC_L; - goto lceq; + LCEQ; + continue; case 0141512: TRACE(T_FLOW, " LLNE\n"); SETCC_L; - goto lcne; + LCNE; + continue; case 0141515: TRACE(T_FLOW, " LLGT\n"); SETCC_L; - goto lcgt; + LCGT; + continue; case 0141110: TRACE(T_FLOW, " LFLT\n"); SETCC_F; - goto lclt; + LCLT; + continue; case 0141111: TRACE(T_FLOW, " LFLE\n"); SETCC_F; - goto lcle; + LCLE; + continue; case 0141113: TRACE(T_FLOW, " LFEQ\n"); SETCC_F; - goto lceq; + LCEQ; + continue; case 0141112: TRACE(T_FLOW, " LFNE\n"); SETCC_F; - goto lcne; + LCNE; + continue; case 0141114: TRACE(T_FLOW, " LFGE\n"); SETCC_F; - goto lcge; + LCGE; + continue; case 0141115: TRACE(T_FLOW, " LFGT\n"); SETCC_F; - goto lcgt; + LCGT; + continue; case 0140550: TRACE(T_FLOW, " FLOT\n"); @@ -5085,7 +5089,7 @@ lcgt: if (!(crs[KEYS] & 020000)) RPL = iget16(RP); else - goto bceq; + BCEQ; continue; #if 0 @@ -5108,8 +5112,9 @@ lcgt: case 0141710: TRACE(T_FLOW, " BMGT\n"); if (crs[KEYS] & 020000) - goto bcne; - RPL++; + BCNE; + else + RPL++; continue; case 0141404: @@ -6427,8 +6432,6 @@ keys = 14200, modals=100177 case 0301: TRACE(T_FLOW, " STLR\n"); - if (MAKEVA(012,037122) <= ea && ea <= MAKEVA(012,042664)) - printf("STLR in PNCDIM at %o/%o, ea=%o/%o, L=%o/%o\n", RPH, RPL-2, ea>>16, ea&0xffff, crs[A], crs[B]); utempa = ea; /* word number portion only */ if (utempa & 040000) { /* absolute RF addressing */ RESTRICT(); @@ -7130,14 +7133,5 @@ pio(unsigned int inst) { func = (inst >> 6) & 017; device = inst & 077; TRACE(T_INST, " pio, class=%d, func='%o, device='%o\n", class, func, device); - if (devmap[device]) - devmap[device](class, func, device); - else { -#if 1 - printf("pio: no handler, class=%d, func='%o, device='%o, A='%o\n", class, func, device, crs[A]); - fatal(NULL); -#else - TRACEA("pio: no handler, class=%d, func='%o, device='%o, A='%o\n", class, func, device, crs[A]); -#endif - } + devmap[device](class, func, device); } diff --git a/emdev.h b/emdev.h index 9268be5..21af376 100644 --- a/emdev.h +++ b/emdev.h @@ -1,6 +1,7 @@ -/* emdev.h, Jim Wilcoxson, April 17, 2005 +/* emdev.h, Jim Wilcoxson (prirun@gmail.com), April 17, 2005 Device handlers for pio instructions. Use devnull as a template for new handlers. + NOTES: OCP instructions never skip @@ -31,11 +32,11 @@ '23 = disk #4 '24 = disk (was Writable Control Store) '25 = disk (was 4000 disk controller) - '26 = 1st disk controller - '27 = 2nd disk controller + '26 = #1 disk controller + '27 = #2 disk controller '30-32 = BPIOC #1-3 (RTOS User Guide, A-1) '32 = AMLC #8 or ICS1 - '33 = #1 Versatec (verdim) + '33 = #1 Versatec '34 = #2 Versatec '35 = #4 AMLC or ICS1 '36-37 = ELFBUS #1 & 2 (ICS1 #1, ICS1 #2) @@ -47,7 +48,7 @@ '45 = disk (was D/A converter type 6060 (analog output) - obsolete) '46 = disk '47 = #2 PNC - '50 = #1 HSSMLC/MDLC (cs/slcdim.pma) + '50 = #1 HSSMLC/MDLC (synchronous comm) '51 = #2 HSSMLC/MDLC '52 = #3 AMLC or ICS1 '53 = #2 AMLC @@ -75,6 +76,7 @@ #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 */ @@ -87,14 +89,14 @@ /* this macro is used to decide whether blocking should be enabled for an I/O operation. The rules are: - - if process exchange is enabled, I/O can never block (check modals) + - if process exchange is disabled then it's safe to block - if the instruction (assumed to be I/O) is followed by JMP *-1 (SR modes) or BCNE *-2 (VI modes), then it's okay to block */ #if 1 #define BLOCKIO \ - (!(crs[MODALS] & 010) && (get16(RP) == 03776 || (get16(RP) == 0141603 && get16(RP+1) == RPL-2))) + (!(crs[MODALS] & 010) && (iget16(RP) == 03776 || (iget16(RP) == 0141603 && iget16(RP+1) == RPL-2))) #else #define BLOCKIO 0 #endif @@ -181,7 +183,7 @@ int devnone (int class, int func, int device) { break; } if (!seen[device]) - fprintf(stderr, " unimplemented device '%o\n", device); + fprintf(stderr, " pio to unimplemented device '%o, class '%o, func '%o\n", device, class, func); seen[device] = 1; } @@ -189,10 +191,11 @@ int devnone (int class, int func, int device) { /* Device '4: system console NOTES: + - this driver only implements the basic needs of the system console - 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 + - issues with setting blocking flags & terminal attributes: doesn't block OCP '0004 = initialize for input only, echoplex, 110 baud, 8-bit, no parity OCP '0104 = same, but for output only @@ -255,15 +258,15 @@ int devasr (int class, int func, int 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; + int doblock; + doblock = BLOCKIO; switch (class) { @@ -284,8 +287,8 @@ int devasr (int class, int func, int device) { fatal(NULL); } - /* NOTE: some of these are not restored after the emulator - is suspended (VSUSP) then restarted, eg, the VSUSP and + /* NOTE: some of these are not restored by the host OS after the + emulator is suspended (VSUSP) then restarted, eg, the VSUSP and VINTR characters */ terminfo.c_iflag &= ~(INLCR | ICRNL); @@ -321,7 +324,10 @@ int devasr (int class, int func, int device) { TRACE(T_INST, " SKS '%02o%02o\n", func, device); if (func == 6) { /* skip if room for a character */ - timeout.tv_sec = 0; + if (crs[MODALS] & 010) /* PX enabled? */ + timeout.tv_sec = 0; /* yes, can't delay */ + else + timeout.tv_sec = 1; /* single user: okay to delay */ timeout.tv_usec = 0; FD_SET(ttydev, &fds); n = select(ttydev+1, NULL, &fds, NULL, &timeout); @@ -358,7 +364,8 @@ int devasr (int class, int func, int device) { case 2: TRACE(T_INST, " INA '%02o%02o\n", func, device); - if (func == 0 || func == 010) { + TRACE(T_INST, "INA, RPH=%o, RPL=%o, [RP]=%o, [RP+1]=%o, BLOCKIO=%d\n", RPH, RPL, iget16(RP), iget16(RP+1),BLOCKIO); + if (func == 0 || func == 010) { /* read a character */ if (BLOCKIO) newflags = ttyflags & ~O_NONBLOCK; else @@ -377,7 +384,12 @@ int devasr (int class, int func, int device) { } readasr: n = read(ttydev, &ch, 1); - if (n < 0) { + if (n == 0) { + if (doblock) { + usleep(500000); + goto readasr; + } + } else if (n < 0) { if (errno != EAGAIN && errno != EIO) { perror(" error reading from tty"); fatal(NULL); @@ -405,7 +417,7 @@ readasr: fflush(conslog); /* immediately flush typing echos */ } IOSKIP; - } else if (n != 0) { + } else { printf("Unexpected error reading from tty, n=%d\n", n); fatal(NULL); } @@ -454,23 +466,10 @@ readasr: } if (!n) return; -#endif -#if 0 - if (atbol && atnewl) - printf("%10d| ", instcount); #endif putchar(ch); - if (ch == 015) - atbol = 1; - else { + if (ch != 015) putc(ch, conslog); - if (ch == 012) - atnewl = 1; - else { - atbol = 0; - atnewl = 0; - } - } needflush = 1; if (devpoll[device] == 0) devpoll[device] = instpermsec*100; @@ -935,7 +934,9 @@ int devmt (int class, int func, int device) { /* write file mark NOTE: the tape file should probably be truncated on the first - write, not on each file mark */ + write, not on each file mark, although this does let us + create garbage tape images for testing by doing a Magsav, + rewind, starting another Magsav, and aborting it. */ if ((crs[A] & 0x4010) == 0x0010) { TRACE(T_TIO, " write file mark\n"); @@ -995,7 +996,7 @@ int devmt (int class, int func, int device) { while (1) { dmxreg = dmxchan & 0x7FF; if (dmxchan & 0x0800) { /* DMC */ - dmcpair = get32r(dmxreg, 0); /* fetch begin/end pair */ + dmcpair = get32r0(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); @@ -1017,20 +1018,20 @@ int devmt (int class, int func, int device) { if (dmxtotnw+dmxnw > MAXTAPEWORDS) fatal("Tape write is too big"); for (n=dmxnw; n > 0; n--) { - *iobufp++ = get16r(dmxaddr+i, 0); + *iobufp++ = get16r0(dmxaddr+i); } dmxtotnw = dmxtotnw + dmxnw; } else { if (dmxnw > dmxtotnw) dmxnw = dmxtotnw; for (n=dmxnw; n > 0; n--) { - put16r(*iobufp++, dmxaddr+i, 0); + put16r0(*iobufp++, dmxaddr+i); } dmxtotnw = dmxtotnw - dmxnw; } TRACE(T_TIO, " read/wrote %d words\n", dmxnw); if (dmxchan & 0x0800) { /* DMC */ - put16r(dmxaddr+dmxnw, dmxreg, 0); /* update starting address */ + put16r0(dmxaddr+dmxnw, dmxreg); /* update starting address */ } else { regs.sym.regdmx[dmxreg] += dmxnw<<4; /* increment # words */ regs.sym.regdmx[dmxreg+1] += dmxnw; /* increment address */ @@ -1180,7 +1181,7 @@ initclock(datnowea) { unixtime = time(NULL); tms = localtime(&unixtime); datnow = tms->tm_year<<25 | (tms->tm_mon+1)<<21 | tms->tm_mday<<16 | ((tms->tm_hour*3600 + tms->tm_min*60 + tms->tm_sec)/4); - put32r(datnow, datnowea, 0); + put32r0(datnow, datnowea); } @@ -1552,9 +1553,9 @@ int devdisk (int class, int func, int device) { 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); - TRACE(T_INST|T_DIO, "\nDIOC %o: %o %o %o\n", dc[device].oar, m, m1, get16r(dc[device].oar+2, 0)); + m = get16r0(dc[device].oar); + m1 = get16r0(dc[device].oar+1); + TRACE(T_INST|T_DIO, "\nDIOC %o: %o %o %o\n", dc[device].oar, m, m1, get16r0(dc[device].oar+2)); dc[device].oar += 2; order = m>>12; @@ -1578,7 +1579,7 @@ int devdisk (int class, int func, int device) { case 5: /* SREAD = Read */ case 6: /* SWRITE = Write */ dc[device].status &= ~076000; /* clear bits 2-6 */ - m2 = get16r(dc[device].oar++, 0); + m2 = get16r0(dc[device].oar++); recsize = m & 017; track = m1 & 01777; rec = m2 >> 8; /* # records for format, rec # for R/W */ @@ -1661,8 +1662,8 @@ int devdisk (int class, int func, int device) { dmanw = regs.sym.regdmx[dmareg]; dmanw = -(dmanw>>4); dmaaddr = regs.sym.regdmx[dmareg+1]; - phyra2 = get16r(dmaaddr+0, 0); - phyra2 = phyra2<<16 | get16r(dmaaddr+1, 0); + phyra2 = get16r0(dmaaddr+0); + phyra2 = phyra2<<16 | get16r0(dmaaddr+1); if (phyra2 != phyra) fprintf(stderr,"devdisk: phyra=%d, phyra2=%d; CRA mismatch (dmanw = %d)!\n", phyra, phyra2, dmanw); } @@ -1693,12 +1694,12 @@ int devdisk (int class, int func, int device) { } if (iobufp == iobuf) for (i=0; i= MAXFD) fatal("New connection fd is too big"); newdevice = 0; - for (i=0; !newdevice && i 0) + if ((nw = write(dc[device].sockfd[lx], buf, n)) != n) { + perror("Writing to AMLC"); + fprintf(stderr," %d bytes written, but %d bytes sent\n", n, nw); } - put16r(0, dc[device].baseaddr + lx, 0); - } - /* need to setup DMT xmit poll here, and/or look for char - time interrupt */ } - - /* NOTE: probably need to check for partial writes here, or put the - socket in blocking mode. Would be best to know how many characters - to remove from the queue in the loop above. */ - if (n > 0) - if ((nw = write(dc[device].sockfd[lx], buf, n)) != n) { - perror("Writing to AMLC"); - fatal("Writing to AMLC"); - } - } + /* process input, but only as much as will fit into the DMC buffer. */ - /* process input, but only as much as will fit into the DMC buffer */ - - if ((dc[device].dss & dc[device].recvenabled & bitmask16[lx+1]) && !dc[device].eor) { - if (dc[device].bufnum) - dmcea = dc[device].dmcchan ^ 2; - else - dmcea = dc[device].dmcchan; - dmcpair = get32r(dmcea, 0); - dmcbufbegea = dmcpair>>16; - dmcbufendea = dmcpair & 0xffff; - dmcnw = dmcbufendea - dmcbufbegea + 1; - if (dmcnw <= 0) - continue; - if (dmcnw > sizeof(buf)) - dmcnw = sizeof(buf); - while ((n = read(dc[device].sockfd[lx], buf, dmcnw)) == -1 && errno == EINTR) - ; - //printf("processing recv on device %o, line %d, b#=%d, dmcnw=%d, n=%d\n", device, lx, dc[device].bufnum, dmcnw, n); - - /* zero length read means the socket has been closed */ - - if (n == 0) { - n = -1; - errno = EPIPE; - } - if (n == -1) { - n = 0; - if (errno == EAGAIN || errno == EWOULDBLOCK) + if ((dc[device].dss & dc[device].recvenabled & bitmask16[lx+1]) && !dc[device].eor) { + if (dc[device].bufnum) + dmcea = dc[device].dmcchan ^ 2; + else + dmcea = dc[device].dmcchan; + dmcpair = get32r0(dmcea); + dmcbufbegea = dmcpair>>16; + dmcbufendea = dmcpair & 0xffff; + dmcnw = dmcbufendea - dmcbufbegea + 1; + if (dmcnw <= 0) + continue; + if (dmcnw > sizeof(buf)) + dmcnw = sizeof(buf); + while ((n = read(dc[device].sockfd[lx], buf, dmcnw)) == -1 && errno == EINTR) ; - else if (errno == EPIPE) { - /* see similar code above */ - close(dc[device].sockfd[lx]); - dc[device].dss &= ~bitmask16[lx+1]; - printf("Closing AMLC line %d on device '%o\n", lx, device); - } else { - perror("Reading AMLC"); - fatal("Reading AMLC"); - } - } + //printf("processing recv on device %o, line %d, b#=%d, dmcnw=%d, n=%d\n", device, lx, dc[device].bufnum, dmcnw, n); - if (n > 0) { - for (i=0; i dmcbufendea) { /* end of range has occurred */ - dc[device].bufnum = 1-dc[device].bufnum; - dc[device].eor = 1; + if (n == -1) { + n = 0; + if (errno == EAGAIN || errno == EWOULDBLOCK) + ; + else if (errno == EPIPE || errno == ECONNRESET) { + + /* see similar code above */ + close(dc[device].sockfd[lx]); + dc[device].dss &= ~bitmask16[lx+1]; + printf("Closing AMLC line %d on device '%o\n", lx, device); + } else { + perror("Reading AMLC"); + } + } + + if (n > 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; + + /* time to interrupt? */ + + if (dc[device].intenable && (dc[device].ctinterrupt || dc[device].eor)) { + if (intvec == -1) { + intvec = dc[device].intvector; + dc[device].interrupting = 1; + } else + devpoll[device] = 100; /* come back soon! */ } - if ((dc[device].ctinterrupt || dc[device].xmitenabled || dc[device].recvenabled) && devpoll[device] == 0) + /* conditions to setup another poll: + - any board with ctinterrupt set on any line is polled + - any board with xmitenabled on any line is polled (to drain output) + - any board with recvenabled on a connected line is polled + - the device must not already be set for a poll + + NOTE: there is always at least one board with ctinterrupt set + (the last board), so it will always be polling and checking + for new incoming connections */ + + if ((dc[device].ctinterrupt || dc[device].xmitenabled || (dc[device].recvenabled & dc[device].dss)) && devpoll[device] == 0) devpoll[device] = AMLCPOLL*instpermsec; /* setup another poll */ break; } @@ -2319,16 +2373,45 @@ conloop: /* PNC (ring net) device handler - PNC ring buffers are 256 words (later version appear to support 256, - 512, and 1024 word packets). "nbkini" (rev 18) allocates 12 ring - buffers + 1 for every 2 nodes in the ring. Both the 1-to-2 queue - for received packets, and 2-to-1 queue for xmit packets have 63 - entries. PNC ring buffers are wired and do not cross page + On a real Prime ring network, each node has a unique node id from 1 + to 254. The node id is configured with software and stored into the + PNC during network initialization. In practice, CONFIG_NET limits + the node id to 247. When a node starts, it sends a message to + itself. If any system acknowledges this packet, it means the node + id is already in use. If this occcurs, the new node will disconnect + from the ring. Since all systems in a ring have to be physically + cabled together, there was usually a common network administration + to ensure that no two nodes had the same node id. + + Node id 255 is the broadcast id - all nodes receive these messages. + Beginning with 19.3, An "I'm alive" broadcast message is sent every + 10 seconds to let all nodes know that a machine is still up. + + For use with the emulator, the unique node id concept doesn't make a + lot of sense. If a couple of guys running the emulator wanted to + have a 2-node network with nodes 1 and 2 (network A), and another + couple of guys had a 2-node network with nodes 1 and 2 (network B), + then it wouldn't be possible for a node in one network to add a node + in the other network without other people redoing their node ids to + ensure uniqueness. To get around this, the emulator has a config + file RING.CFG that lets you say "Here are the guys in *my* ring + (with all unique node ids), here is each one's IP address and port, + my node id on their ring is *blah*, their node id on their ring is + *blah2*"". This allows the node id to be a per-host number that + only needs to be coordinated with two people that want to talk to + each other, and effectively allows one emulator to be in multiple + rings simulataneously. Cool, eh? + + PNC ring buffers are 256 words, with later versions of Primos also + supporting 512, and 1024 word packets. "nbkini" (rev 18) allocates + 12 ring buffers + 1 for every 2 nodes in the ring. Both the 1-to-2 + queue for received packets, and 2-to-1 queue for xmit packets have + 63 entries. PNC ring buffers are wired and never cross page boundaries. - The actual xmit/recv buffers for PNC are 256 words, and each buffer - has an associated "block header", stored in a different location in - system memory, that is 8 words. + The actual xmit/recv buffers for PNC are 256-1024 words, and each + buffer has an associated "block header", stored in a different + location in system memory, that is 8 words. The BH fields are (16-bit words): 0: type field (1) @@ -2337,17 +2420,17 @@ conloop: 4-7 are available for use by the drivers. For PNC: 4: number of words received (calculated by pncdim based on DMA regs) - or number of 5: receive status word 6: data 1 7: data 2 - The PNC data buffer has a 2-word header: - 0: To (left) and From (right) bytes containing node-id + The PNC data buffer has a 2-word header, followed by the data: + 0: To (left) and From (right) bytes containing node-ids 1: "Type" word. - Bit 1 set = odd number of bytes - Bit 8 set = normal data messages (otherwise, a timer message) - + Bit 1 set = odd number of bytes (if set, last word is only 1 byte) + Bit 7 set = normal data messages (otherwise, a timer message) + Bit 16 set = broadcast timer message + PNC data buffers never cross page boundaries. Primos PNC usage: @@ -2388,9 +2471,11 @@ conloop: bit 8 ACK byte check error (parity on bits 1-6) bit 9 recv buffer parity error bit 10 recv busy - bit 11 end of range before end of message - - INA '1407 + bit 11 end of range before end of message (received msg was too big + for the receive buffer) + bits 12-16 unused + + INA '1307 - read xmit status word bit 1 set for ACK bit 2 set for multiple ACK (more than 1 node accepted packet) @@ -2442,76 +2527,302 @@ conloop: int devpnc (int class, int func, int device) { +#define PNCPOLL 100 + /* PNC controller status bits */ #define PNCRCVINT 0x8000 /* bit 1 rcv interrupt (rcv complete) */ #define PNCXMITINT 0x4000 /* bit 2 xmit interrupt (xmit complete) */ -#define PNCBOOSTER 0x2000 /* bit 3 */ -#define PNCINTENAB 0x1000 /* bit 4 (JW usage) */ +#define PNCBOOSTER 0x2000 /* bit 3 PNC booster = 1 */ #define PNCCONNECTED 0x400 /* bit 6 */ #define PNCTWOTOKENS 0x200 /* bit 7, only set after xmit EOR */ #define PNCTOKDETECT 0x100 /* bit 8, only set after xmit EOR */ - static short pncstat; /* controller status word */ - static short recvstat; /* receive status word */ - static short xmitstat; /* xmit status word */ - static short intvec; /* PNC interrupt vector */ + static short configured = 0; /* true if PNC configured */ + static unsigned short pncstat; /* controller status word */ + static unsigned short recvstat; /* receive status word */ + static unsigned short xmitstat; /* xmit status word */ + static unsigned short pncvec; /* PNC interrupt vector */ + static unsigned short myid; /* my PNC node id */ + static unsigned short enabled; /* interrupts enabled flag */ + static int pncfd; /* socket fd for all PNC network connections */ - unsigned short dmachan, dmareg, dmaaddr, dmaword; - short i, dmanw; + /* the ni structure contains the important information for each node + in the network and is indexed by the local node id */ + + static struct { /* node info for each node in my network */ + short cfg; /* true if seen in ring.cfg */ + short fd; /* socket fd for this node, -1 if unconnected */ + char ip[16]; /* IP address of the remote node */ + short port; /* emulator network port on the remote node */ + short myremid; /* my node ID on the remote node's ring network */ + short yourremid; /* remote node's id on its own network */ + } ni[256]; + + /* array to map socket fd's to local node id's for accessing the ni + structure. Not great programming, because the host OS could + choose to use large fd's, which will cause a runtime error */ + +#define FDMAPSIZE 1024 + static short fdnimap[FDMAPSIZE]; + + typedef struct { + unsigned short dmachan, dmareg, dmaaddr; + short dmanw, toid, fromid, remtoid, remfromid; + unsigned short *iobufp; + } t_dma; + static t_dma recv, xmit; + + short i; + unsigned short access, dmaword, dmaword1, dmaword2; + + struct sockaddr_in addr; + int fd, optval, fdflags; + unsigned int addrlen; + + FILE *ringfile; + char *tok, buf[128], *p; + int n, linenum; + int tempid, tempmyremid, tempyourremid, tempport, cfgerrs; + char tempip[22]; /* xxx.xxx.xxx.xxx:ppppp */ +#define DELIM " \t\n" +#define PDELIM ":" //traceflags = ~TB_MAP; + if (nport <= 0) { + if (class == -1) + fprintf(stderr, "-nport is zero, PNC not started\n"); + else + TRACE(T_INST|T_RIO, "nport <= 0, PIO to PNC ignored, class=%d, func='%02o, device=%02o\n", class, func, device); + return -1; + } + switch (class) { case -1: + for (i=0; i 247) { + fprintf(stderr,"Line %d of ring.cfg: node id is out of range 1-247\n", linenum); + continue; + } + if (ni[tempid].cfg) { + fprintf(stderr,"Line %d of ring.cfg: node id occurs twice\n", linenum); + continue; + } + if ((p=strtok(NULL, DELIM)) == NULL) { + fprintf(stderr,"Line %d of ring.cfg: IP address missing\n", linenum); + continue; + } + if (strlen(p) > 21) { + fprintf(stderr,"Line %d of ring.cfg: IP address is too long\n", linenum); + continue; + } + strcpy(tempip, p); + if ((p=strtok(NULL, DELIM)) != NULL) { + tempmyremid = atoi(p); + if (tempmyremid < 1 || tempmyremid > 247) { + fprintf(stderr,"Line %d of ring.cfg: my remote node id out of range 1-247\n", linenum); + continue; + } + } else + tempmyremid = -1; + if ((p=strtok(NULL, DELIM)) != NULL) { + tempyourremid = atoi(p); + if (tempyourremid < 1 || tempyourremid > 247) { + fprintf(stderr,"Line %d of ring.cfg: your remote node id out of range 1-247\n", linenum); + continue; + } + } else + tempyourremid = tempid; + if (tempyourremid == tempmyremid) { + fprintf(stderr,"Line %d of ring.cfg: my remote node id and your remote node id can't be equal\n", linenum); + continue; + } + /* parse the port number from the IP address */ + tempport = -1; + if ((p=strtok(tempip, PDELIM)) != NULL) { + strcpy(ni[tempid].ip, p); + if ((p=strtok(NULL, PDELIM)) != NULL) { + tempport = atoi(p); + if (tempport < 1 || tempport > 32000) + fprintf(stderr,"Line %d of ring.cfg: port number is out of range 1-32000\n", linenum); + } + } + if (tempport == -1) { + fprintf(stderr, "Line %d of ring.cfg: IP should be xxx.xxx.xxx.xxx:pppp\n", linenum); + continue; + } + ni[tempid].cfg = 1; + ni[tempid].myremid = tempmyremid; + ni[tempid].yourremid = tempyourremid; + ni[tempid].port = tempport; + TRACE(T_RIO, "Line %d: id=%d, ip=\"%s\", port=%d, myremid=%d, yourremid=%d\n", linenum, tempid, tempip, tempport, tempmyremid, tempyourremid); + configured = 1; + } + if (!feof(ringfile)) { + perror(" error reading ring.cfg"); + fatal(NULL); + } + fclose(ringfile); + } + if (!configured) { + fprintf(stderr, "PNC not configured because ring.cfg missing or errors occurred.\n"); + return -1; + } return 0; case 0: - TRACE(T_INST, " OCP '%02o%02o\n", func, device); - if (func == 00) { /* disconnect from ring */ + + /* OCP '0700 - disconnect */ + + if (func == 00) { + TRACE(T_INST|T_RIO, " OCP '%02o%02o - disconnect\n", func, device); + if (pncfd >= 0) { + close(pncfd); + pncfd = -1; + } + for (i=0; i<256; i++) { + if (ni[i].fd >= 0) { + fdnimap[ni[i].fd] = -1; + close(ni[i].fd); + ni[i].fd = -1; + } + } pncstat &= ~PNCCONNECTED; + /* OCP '0701 - connect + If any errors occur while trying to setup the listening socket, + it seems reasonable to leave the PNC unconnected and continue, + but this will cause Primos (rev 19) to hang in a spin loop. So + for now, bomb. Later, we could put the PNC in a disabled state, + where INA/OTA don't skip, since Primos handles this better. + */ + } else if (func == 01) { /* connect to the ring */ + TRACE(T_INST|T_RIO, " OCP '%02o%02o - connect\n", func, device); + + /* start listening on the network port */ + + pncfd = socket(AF_INET, SOCK_STREAM, 0); + if (pncfd == -1) { + perror("socket failed for PNC"); + fatal(NULL); + } + if (fcntl(pncfd, F_GETFL, fdflags) == -1) { + perror("unable to get ts flags for PNC"); + fatal(NULL); + } + fdflags |= O_NONBLOCK; + if (fcntl(pncfd, F_SETFL, fdflags) == -1) { + perror("unable to set ts flags for PNC"); + fatal(NULL); + } + optval = 1; + if (setsockopt(pncfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval))) { + perror("setsockopt failed for PNC"); + fatal(NULL); + } + addr.sin_family = AF_INET; + addr.sin_port = htons(nport); + addr.sin_addr.s_addr = INADDR_ANY; + if(bind(pncfd, (struct sockaddr *)&addr, sizeof(addr))) { + perror("bind: unable to bind for PNC"); + fatal(NULL); + } + if(listen(pncfd, 10)) { + perror("listen failed for PNC"); + fatal(NULL); + } pncstat |= PNCCONNECTED; } else if (func == 02) { /* inject a token */ - ; + TRACE(T_INST|T_RIO, " OCP '%02o%02o - inject token\n", func, device); } else if (func == 04) { /* ack xmit (clear xmit int) */ - pncstat &= ~PNCXMITINT; - xmitstat = 0; /* right? */ + TRACE(T_INST|T_RIO, " OCP '%02o%02o - ack xmit int\n", func, device); + pncstat &= ~PNCXMITINT; /* clear "xmit interrupting" */ + pncstat &= ~PNCTOKDETECT; /* clear "token detected" */ + xmitstat = 0; } else if (func == 05) { /* set PNC into "delay" mode */ - printf("Set PNC to delay mode\n"); + TRACE(T_INST|T_RIO, " OCP '%02o%02o - set delay mode\n", func, device); } else if (func == 010) { /* stop xmit in progress */ + TRACE(T_INST|T_RIO, " OCP '%02o%02o - stop xmit\n", func, device); xmitstat = 0; } else if (func == 011) { /* dunno what this is - rev 20 startup */ + TRACE(T_INST|T_RIO, " OCP '%02o%02o - unknown\n", func, device); ; } else if (func == 012) { /* set normal mode */ + TRACE(T_INST|T_RIO, " OCP '%02o%02o - set normal mode\n", func, device); ; } else if (func == 013) { /* set diagnostic mode */ + TRACE(T_INST|T_RIO, " OCP '%02o%02o - set diag mode\n", func, device); ; } else if (func == 014) { /* ack receive (clear rcv int) */ + TRACE(T_INST|T_RIO, " OCP '%02o%02o - ack recv int\n", func, device); pncstat &= ~PNCRCVINT; - recvstat = 0; /* right? */ + recvstat = 0; } else if (func == 015) { /* set interrupt mask (enable int) */ - pncstat |= PNCINTENAB; + TRACE(T_INST|T_RIO, " OCP '%02o%02o - enable interrupts\n", func, device); + enabled = 1; - } else if (func == 016) { /* clear interrupt mask (disenable int) */ - pncstat &= ~PNCINTENAB; + } else if (func == 016) { /* clear interrupt mask (disable int) */ + TRACE(T_INST|T_RIO, " OCP '%02o%02o - disable interrupts\n", func, device); + enabled = 0; } else if (func == 017) { /* initialize */ + TRACE(T_INST|T_RIO, " OCP '%02o%02o - initialize\n", func, device); pncstat = 0; recvstat = 0; xmitstat = 0; - intvec = 0; + pncvec = 0; + enabled = 0; } else { printf("Unimplemented OCP device '%02o function '%02o\n", device, func); @@ -2520,7 +2831,7 @@ int devpnc (int class, int func, int device) { break; case 1: - TRACE(T_INST, " SKS '%02o%02o\n", func, device); + TRACE(T_INST|T_RIO, " SKS '%02o%02o\n", func, device); if (func == 99) IOSKIP; /* assume it's always ready */ else { @@ -2530,20 +2841,27 @@ int devpnc (int class, int func, int device) { break; case 2: - TRACE(T_INST, " INA '%02o%02o\n", func, device); if (func == 011) { /* input ID */ + TRACE(T_INST|T_RIO, " INA '%02o%02o - input ID\n", func, device); crs[A] = 07; IOSKIP; } else if (func == 012) { /* read receive status word */ + TRACE(T_INST|T_RIO, " INA '%02o%02o - get recv status '%o\n", func, device, recvstat); crs[A] = recvstat; IOSKIP; + } else if (func == 013) { /* DIAG - read static register; not impl. */ + crs[A] = 0; + IOSKIP; + } else if (func == 014) { /* read xmit status word */ + TRACE(T_INST|T_RIO, " INA '%02o%02o - get xmit status '%o\n", func, device, xmitstat); crs[A] = xmitstat; IOSKIP; } else if (func == 017) { /* read controller status word */ + TRACE(T_INST|T_RIO, " INA '%02o%02o - get ctrl status '%o\n", func, device, pncstat); crs[A] = pncstat; IOSKIP; @@ -2554,35 +2872,145 @@ int devpnc (int class, int func, int device) { break; case 3: - TRACE(T_INST, " OTA '%02o%02o\n", func, device); - if (func == 014) { /* initiate recv, dma chan in A */ - dmachan = crs[A]; - dmareg = dmachan << 1; - dmanw = regs.sym.regdmx[dmareg]; - dmanw = -(dmanw>>4); - dmaaddr = regs.sym.regdmx[dmareg+1]; - printf("PNC recv: dmachan=%o, dmareg=%o, dmaaddr=%o, dmanw=%d\n", dmachan, dmareg, dmaaddr, dmanw); + TRACE(T_INST|T_RIO, " OTA '%02o%02o\n", func, device); + if (func == 011) { /* DIAG - single step; not impl.*/ + IOSKIP; + + } else if (func == 014) { /* initiate recv, dma chan in A */ + recvstat = 0x0040; /* set receive busy */ + recv.dmachan = crs[A]; + recv.dmareg = recv.dmachan << 1; + recv.dmanw = regs.sym.regdmx[recv.dmareg]; + if (recv.dmanw <= 0) + recv.dmanw = -(recv.dmanw>>4); + else + recv.dmanw = -((recv.dmanw>>4) ^ 0xF000); + recv.dmaaddr = regs.sym.regdmx[recv.dmareg+1]; + recv.iobufp = mem + mapva(recv.dmaaddr, WACC, &access, 0); + TRACE(T_INST|T_RIO, " recv: dmachan=%o, dmareg=%o, dmaaddr=%o, dmanw=%d\n", recv.dmachan, recv.dmareg, recv.dmaaddr, recv.dmanw); + devpoll[device] = 10; IOSKIP; } else if (func == 015) { /* initiate xmit, dma chan in A */ - dmachan = crs[A]; - dmareg = dmachan<<1; - dmanw = regs.sym.regdmx[dmareg]; - dmanw = -(dmanw>>4); - dmaaddr = regs.sym.regdmx[dmareg+1]; - printf("PNC xmit: dmachan=%o, dmareg=%o, dmaaddr=%o, dmanw=%d\n", dmachan, dmareg, dmaaddr, dmanw); - for (i=0; i> 8; + xmit.fromid = dmaword & 0xFF; + TRACE(T_INST|T_RIO, " xmit: dmaword = '%o/%d [%03o %03o]\n", dmaword, *(short *)&dmaword, dmaword>>8, dmaword&0xff); + + /* broadcast packets are "I am up" msgs and are simply "eaten" + here, as this is handled later in the devpnc poll code */ + + if (xmit.toid == 255) { + regs.sym.regdmx[xmit.dmareg+1] += xmit.dmanw; /* bump xmit address */ + regs.sym.regdmx[xmit.dmareg] += xmit.dmanw; /* and count */ + goto xmitdone; } + + /* check for errors, then map to and from node id to remote to + and from node id */ + + if (xmit.toid == 0) + fatal("PNC: xmit.toid is zero"); + if (xmit.fromid == 0) + fatal("PNC: xmit.fromid is zero"); + if (xmit.fromid != myid) { + printf("PNC: xmit fromid=0x%02x != myid=0x%02x\n", xmit.fromid, myid); + fatal(NULL); + } + + if (ni[xmit.fromid].myremid > 0) + xmit.remfromid = ni[xmit.fromid].myremid; + else + xmit.remfromid = myid; + xmit.remtoid = ni[xmit.toid].yourremid; + + dmaword1 = (xmit.remtoid << 8) | xmit.remfromid; + for (i=0; i < xmit.dmanw; i++) { + if (i == 0) + dmaword = dmaword1; + else + dmaword = *xmit.iobufp++; + TRACE(T_INST|T_RIO, " xmit: word %d = '%o/%d [%03o %03o]\n", i, dmaword, *(short *)&dmaword, dmaword>>8, dmaword&0xff); + + /* if this xmit is local and there is a receive pending and there is + room left in the receive buffer, put the word directly in my + receive buffer. If it's local but we can't receive it, set + NACK xmit and receive status */ + + if (xmit.toid == myid) { + if ((recvstat & 0x0040) && regs.sym.regdmx[recv.dmareg]) { + *recv.iobufp++ = dmaword; + regs.sym.regdmx[recv.dmareg]++; /* bump count */ + regs.sym.regdmx[recv.dmareg+1]++; /* bump address */ + } else { + xmitstat |= 0x1000; /* set xmit NACK status */ + recvstat |= 0x20; /* set recv premature EOR */ + } + } else { + /* send remote over sockets */ + } + regs.sym.regdmx[xmit.dmareg+1]++; /* bump xmit address */ + regs.sym.regdmx[xmit.dmareg]++; /* and count */ + } + +xmitdone: + pncstat |= 0x4100; /* set xmit interrupt + token */ + if (xmit.toid == myid) + pncstat |= 0x8000; /* set recv interrupt too */ + if (xmitstat == 0x0040) /* complete w/o errors? */ + xmitstat |= 0x8000; /* yes, set ACK xmit status */ + + devpoll[device] = PNCPOLL*instpermsec; + if (enabled && (pncstat & 0xC000)) + if (intvec == -1) + intvec = pncvec; + else + devpoll[device] = 100; IOSKIP; } else if (func == 016) { /* set interrupt vector */ - intvec = crs[A]; + pncvec = crs[A]; + TRACE(T_INST|T_RIO, " interrupt vector = '%o\n", pncvec); IOSKIP; } else if (func == 017) { /* set my node ID */ - pncstat = (pncstat & 0xFF00) | (crs[A] & 0xFF); + myid = crs[A] & 0xFF; + pncstat = (pncstat & 0xFF00) | myid; + TRACE(T_INST|T_RIO, " my node id is %d\n", myid); + if (ni[myid].fd != -1) { + printf("PNC: my node id of %d is in ring.cfg\n", myid); + fatal(NULL); + } + strcpy(ni[myid].ip, "127.0.0.1"); + ni[myid].port = nport; + ni[myid].myremid = myid; + ni[myid].yourremid = myid; IOSKIP; } else { @@ -2590,5 +3018,43 @@ int devpnc (int class, int func, int device) { fatal(NULL); } break; + + case 4: + TRACE(T_INST|T_RIO, " POLL '%02o%02o\n", func, device); + +#if 0 + while ((fd = accept(pncfd, (struct sockaddr *)&addr, &addrlen)) == -1 && errno == EINTR) + ; + if (fd == -1) { + if (errno != EWOULDBLOCK) { + perror("accept error for PNC"); + } + } else { + if (fd >= MAXFD) + fatal("New connection fd is too big"); + newdevice = 0; + for (i=0; devices[i] && !newdevice && i