From 319b868345b754c598ee617ae707ed67daaa5a27 Mon Sep 17 00:00:00 2001 From: Jim Date: Thu, 19 Apr 2007 00:00:00 -0400 Subject: [PATCH] added ea32i.h include file, ea64v, devamlc, PNC ea64v never needs to return a bit offset added devamlc feature to set room available in user's input buffer misc PNC changes (not working yet) --- ea32i.h | 113 ++++++++ ea64v.h | 9 +- em.c | 784 +++++++++++++++++++++++++++++++++++++++++--------------- emdev.h | 494 +++++++++++++++++++++-------------- regs.h | 7 +- 5 files changed, 1003 insertions(+), 404 deletions(-) create mode 100644 ea32i.h diff --git a/ea32i.h b/ea32i.h new file mode 100644 index 0000000..423a2aa --- /dev/null +++ b/ea32i.h @@ -0,0 +1,113 @@ +#define IMM_EA 0x80000000 + +inline ea_t ea32i (ea_t earp, unsigned short inst, unsigned long *immu32, unsigned long long *immu64) { + +#ifdef OSX + int tm, sr, br, ring; + unsigned short d; + int temp32; + ea_t ea, ip; + + *immu32 = 0xAAAAAAAA; + *immu64 = 0xAAAAAAAAAAAAAAAALL; + + tm = (inst >> 5) & 3; + sr = (inst >> 2) & 7; + br = inst & 3; + ring = RP & RINGMASK32; + TRACE(T_EAI, " tm=%d, sr=%d, dr=%d, br=%d\n", tm, sr, (inst >> 7) & 7, br); + + switch (tm) { + case 0: + switch (br) { + case 0: /* reg-reg */ + *immu32 = crsl[sr]; + return IMM_EA; + + case 1: + d = iget16(RP); + RPL++; + if (sr == 0) /* imm type 1 */ + *immu32 = d << 16; + else /* imm type 2 */ + *(int *)immu32 = *(short *)&d; + return IMM_EA; + + case 2: + switch (sr) { + case 0: /* imm type 3 */ + d = iget16(RP); + RPL++; + *immu64 = (((long long)(d & 0xFF00)) << 48) | (d & 0xFF); + return IMM_EA; + case 1: /* FAC0 source */ + *immu64 = *(unsigned long long *)(crsl+FAC0); + return IMM_EA; + case 3: /* FAC1 source */ + *immu64 = *(unsigned long long *)(crsl+FAC1); + return IMM_EA; + case 2: + case 4: + case 5: + case 6: + case 7: + fault(UIIFAULT, RPL, RP); + fatal("ea32i: return from UII fault!"); + default: + fatal("ea32i: sr < 0 or > 7?"); + } + fatal("ea32i: case tm=0 br=2 fall-through"); + + case 3: /* GR relative */ + d = iget16(RP); + RPL++; + ea = (crsl[sr] & 0xFFFF0000) | ((crsl[sr] + d) & 0xFFFF); + if (ea & 0x80000000) + fault(POINTERFAULT, ea>>16, ea); + return ea | ring; + + default: + fatal("ea32i: tm=0, br < 0 or > 3?"); + } + fatal("ea32i: tm=0 fall-through"); + + case 1: /* TM=1: Direct and Indexed */ + d = iget16(RP); + RPL++; + if (sr == 0) + ea = (crsl[BR+br] & 0xFFFF0000) | ((crsl[BR+br] + d) & 0xFFFF); + else + ea = (crsl[BR+br] & 0xFFFF0000) | ((crsl[BR+br] + d + crs[sr*2]) & 0xFFFF); + return ea | ring; + + case 2: /* TM=2: Indirect and Indirect Preindexed */ + d = iget16(RP); + RPL++; + if (sr == 0) + ea = (crsl[BR+br] & 0xFFFF0000) | ((crsl[BR+br] + d) & 0xFFFF); + else + ea = (crsl[BR+br] & 0xFFFF0000) | ((crsl[BR+br] + d + crs[sr*2]) & 0xFFFF); + ip = get32(ea | ring); + if (ip & 0x80000000) + fault(POINTERFAULT, ip>>16, ea); + return ip | ring; + + case 3: /* TM=3: Indirect and Indirect Postindexed */ + d = iget16(RP); + RPL++; + ea = (crsl[BR+br] & 0xFFFF0000) | ((crsl[BR+br] + d) & 0xFFFF); + ip = get32(ea | ring); + if (ip & 0x80000000) + fault(POINTERFAULT, ip>>16, ea); + if (sr > 0) + ip = (ip & 0xFFFF0000) | ((ip + crs[sr*2]) & 0xFFFF); + return ip | ring; + + default: + fatal("ea32i: tm out of range!"); + } + fatal("ea32i: main switch fall through"); +#else + fatal("ea32i: not implemented"); +#endif +} diff --git a/ea64v.h b/ea64v.h index 6b2904f..9454dfb 100644 --- a/ea64v.h +++ b/ea64v.h @@ -1,7 +1,7 @@ /* this version is derived from the flowchart in the preliminary P400 release notes */ -inline ea_t ea64v (ea_t earp, unsigned short inst, short x, unsigned short *opcode, unsigned short *bit) { +inline ea_t ea64v (ea_t earp, unsigned short inst, short x, unsigned short *opcode) { ea_t ea; /* full seg/word va */ unsigned short ea_s; /* eff address segno */ @@ -17,7 +17,6 @@ inline ea_t ea64v (ea_t earp, unsigned short inst, short x, unsigned short *opco unsigned short m; unsigned short rph,rpl; - *bit = 0; i = inst & 0100000; /* indirect is bit 1 (left/MS bit) */ /* rph/rpl (and earp) are usually = RPH/RPL in the register file, @@ -131,9 +130,11 @@ labB: fault(POINTERFAULT, m, ea); ea_s = m | (ea_s & RINGMASK16); ea_w = get16(INCVA(ea,1)); +#if 0 if (ea_s & EXTMASK16) - *bit = get16(INCVA(ea,2)) >> 12; - TRACE(T_EAV, " After indirect, ea_s=%o, ea_w=%o, bit=%d\n", ea_s, ea_w, *bit); + warn("em: extension bit set in ea64v"); +#endif + TRACE(T_EAV, " After indirect, ea_s=%o, ea_w=%o\n", ea_s, ea_w); } if (xok) if (ixy == 5) diff --git a/em.c b/em.c index f91b725..5e419d5 100644 --- a/em.c +++ b/em.c @@ -1390,11 +1390,7 @@ special: } #include "ea64v.h" - -unsigned int ea32i (ea_t earp, unsigned short inst, short x) { - TRACE(T_EAI, "Mode 32I not implemented\n"); -} - +#include "ea32i.h" ea_t apea(unsigned short *bitarg) { unsigned short ibr, ea_s, ea_w, bit, br, a; @@ -2857,11 +2853,9 @@ main (int argc, char **argv) { double tempd,tempd1,tempd2; unsigned short tempda[4],tempda1[4]; ea_t tempea; - unsigned int ea32; /* full V/I mode eff address */ ea_t ea; /* final MR effective address */ - ea_t eanext; /* ea of lower 16 bits in instl */ - unsigned long instl; ea_t earp; /* RP to use for eff address calcs */ + int dr; unsigned short eabit; unsigned short opcode; short i,j,x; @@ -2878,6 +2872,8 @@ main (int argc, char **argv) { ea_t trapaddr; unsigned short stpm[8]; unsigned short access; + unsigned long immu32; + unsigned long long immu64; unsigned short zresult, zclen1, zclen2, zaccess; unsigned int zlen1, zlen2; @@ -2886,7 +2882,6 @@ main (int argc, char **argv) { struct timeval boot_tv; struct timezone tz; - float mips; /* open trace log */ @@ -5232,216 +5227,234 @@ a1a: if (class == 1) { - TRACE(T_INST, " shift group\n"); - scount = -inst & 077; - if (scount == 0) - scount = 0100; - switch (inst & 01700) { + if ((crs[KEYS] & 0016000) == 0010000) { /* I-mode Generic */ + switch (inst) { - case 00000: /* LRL */ - TRACE(T_FLOW, " LRL %d\n", scount); - crs[KEYS] &= ~0120000; /* clear C,L */ - if (scount <= 32) { + case 0040300: /* DRN */ + break; + case 0040301: /* DRNP */ + break; + case 0040302: /* DRNZ */ + break; + case 0040310: /* SSSN */ + break; + default: + TRACE(T_INST, " unrecognized I-mode generic class 1 instruction!\n"); + printf(" unrecognized I-mode generic class 1 instruction %o!\n", inst); + fatal(NULL); + } + } else { + TRACE(T_INST, " shift group\n"); + scount = -inst & 077; + if (scount == 0) + scount = 0100; + switch (inst & 01700) { + + case 00000: /* LRL */ + TRACE(T_FLOW, " LRL %d\n", scount); + crs[KEYS] &= ~0120000; /* clear C,L */ + if (scount <= 32) { + utempl = *(unsigned int *)(crs+L); + EXPCL(utempl & bitmask32[33-scount]); + *(unsigned int *)(crs+L) = utempl >> scount; + } else { + *(unsigned int *)(crs+L) = 0; + } + break; + + case 00100: /* LRS (different in R & V modes) */ + TRACE(T_FLOW, " LRS %d\n", scount); + crs[KEYS] &= ~0120000; /* clear C,L */ + if (crs[KEYS] & 010000) { /* V/I mode */ + if (scount <= 32) { + templ = *(int *)(crs+L); + EXPCL(templ & bitmask32[33-scount]); + templ = templ >> scount; + *(int *)(crs+L) = templ; + } else if (crs[A] & 0x8000) { + *(int *)(crs+L) = 0xFFFFFFFF; + SETCL; + } else { + *(int *)(crs+L) = 0; + } + } else { + utempa = crs[B] & 0x8000; /* save B bit 1 */ + if (scount <= 31) { + templ = (crs[A]<<16) | ((crs[B] & 0x7FFF)<<1); + EXPCL(templ & bitmask32[32-scount]); + templ = templ >> (scount+1); + crs[A] = templ >> 15; + crs[B] = (templ & 0x7FFF) | utempa; + } else if (crs[A] & 0x8000) { + *(int *)(crs+A) = 0xFFFF7FFF | utempa; + SETCL; + } else { + *(int *)(crs+A) = utempa; + } + } + break; + + case 00200: /* LRR */ + TRACE(T_FLOW, " LRR %d\n", scount); + crs[KEYS] &= ~0120000; /* clear C,L */ + if (scount > 32) + scount = scount - 32; utempl = *(unsigned int *)(crs+L); EXPCL(utempl & bitmask32[33-scount]); - *(unsigned int *)(crs+L) = utempl >> scount; - } else { - *(unsigned int *)(crs+L) = 0; - } - break; - - case 00100: /* LRS (different in R & V modes) */ - TRACE(T_FLOW, " LRS %d\n", scount); - crs[KEYS] &= ~0120000; /* clear C,L */ - if (crs[KEYS] & 010000) { /* V/I mode */ - if (scount <= 32) { - templ = *(int *)(crs+L); - EXPCL(templ & bitmask32[33-scount]); - templ = templ >> scount; - *(int *)(crs+L) = templ; - } else if (crs[A] & 0x8000) { - *(int *)(crs+L) = 0xFFFFFFFF; - SETCL; - } else { - *(int *)(crs+L) = 0; - } - } else { - utempa = crs[B] & 0x8000; /* save B bit 1 */ - if (scount <= 31) { - templ = (crs[A]<<16) | ((crs[B] & 0x7FFF)<<1); - EXPCL(templ & bitmask32[32-scount]); - templ = templ >> (scount+1); - crs[A] = templ >> 15; - crs[B] = (templ & 0x7FFF) | utempa; - } else if (crs[A] & 0x8000) { - *(int *)(crs+A) = 0xFFFF7FFF | utempa; - SETCL; - } else { - *(int *)(crs+A) = utempa; - } - } - break; - - case 00200: /* LRR */ - TRACE(T_FLOW, " LRR %d\n", scount); - crs[KEYS] &= ~0120000; /* clear C,L */ - if (scount > 32) - scount = scount - 32; - utempl = *(unsigned int *)(crs+L); - EXPCL(utempl & bitmask32[33-scount]); - utempl = (utempl >> scount) | (utempl << (32-scount)); - *(unsigned int *)(crs+L) = utempl; - break; - - case 00300: - if (inst == 040310) { - printf("SSSN @ %o/%o\n", RPH, RPL); - TRACE(T_FLOW, " SSSN\n", inst); - fault(UIIFAULT, RPL, RP); + utempl = (utempl >> scount) | (utempl << (32-scount)); + *(unsigned int *)(crs+L) = utempl; break; - } - goto badshift; - case 00400: /* ARL */ - TRACE(T_FLOW, " ARL %d\n", scount); - crs[KEYS] &= ~0120000; /* clear C,L */ - if (scount <= 16) { + case 00300: + if (inst == 040310) { + printf("SSSN @ %o/%o\n", RPH, RPL); + TRACE(T_FLOW, " SSSN\n", inst); + fault(UIIFAULT, RPL, RP); + break; + } + goto badshift; + + case 00400: /* ARL */ + TRACE(T_FLOW, " ARL %d\n", scount); + crs[KEYS] &= ~0120000; /* clear C,L */ + if (scount <= 16) { + EXPCL(crs[A] & bitmask16[17-scount]); + crs[A] = crs[A] >> scount; + } else { + crs[A] = 0; + } + break; + + case 00500: /* ARS */ + TRACE(T_FLOW, " ARS %d\n", scount); + crs[KEYS] &= ~0120000; /* clear C,L */ + if (scount <= 16) { + tempa = *(short *)(crs+A); + EXPCL(tempa & bitmask16[17-scount]); + tempa = tempa >> scount; + *(short *)(crs+A) = tempa; + } else if (crs[A] & 0x8000) { + *(short *)(crs+A) = 0xFFFF; + SETCL; + } else { + *(short *)(crs+A) = 0; + } + break; + + case 00600: /* ARR */ + TRACE(T_FLOW, " ARR %d\n", scount); + crs[KEYS] &= ~0120000; /* clear C,L */ + scount = ((scount-1)%16)+1; /* make scount 1-16 */ EXPCL(crs[A] & bitmask16[17-scount]); - crs[A] = crs[A] >> scount; - } else { - crs[A] = 0; - } - break; + crs[A] = (crs[A] >> scount) | (crs[A] << (16-scount)); + break; - case 00500: /* ARS */ - TRACE(T_FLOW, " ARS %d\n", scount); - crs[KEYS] &= ~0120000; /* clear C,L */ - if (scount <= 16) { - tempa = *(short *)(crs+A); - EXPCL(tempa & bitmask16[17-scount]); - tempa = tempa >> scount; - *(short *)(crs+A) = tempa; - } else if (crs[A] & 0x8000) { - *(short *)(crs+A) = 0xFFFF; - SETCL; - } else { - *(short *)(crs+A) = 0; - } - break; + case 01000: /* LLL */ + TRACE(T_FLOW, " LLL %d\n", scount); + crs[KEYS] &= ~0120000; /* clear C,L */ + if (scount <= 32) { + utempl = *(unsigned int *)(crs+A); + EXPCL(utempl & bitmask32[scount]); + utempl = utempl << scount; + *(unsigned int *)(crs+A) = utempl; + } else { + *(unsigned int *)(crs+A) = 0; + } + break; - case 00600: /* ARR */ - TRACE(T_FLOW, " ARR %d\n", scount); - crs[KEYS] &= ~0120000; /* clear C,L */ - scount = ((scount-1)%16)+1; /* make scount 1-16 */ - EXPCL(crs[A] & bitmask16[17-scount]); - crs[A] = (crs[A] >> scount) | (crs[A] << (16-scount)); - break; + case 01100: /* LLS (different in R/V modes) */ + TRACE(T_FLOW, " LLS %d\n", scount); + crs[KEYS] &= ~0120000; /* clear C,L */ + if (crs[KEYS] & 010000) { /* V/I mode */ + if (scount < 32) { + templ = 0x80000000; + templ = templ >> scount; /* create mask */ + templ = templ & *(int *)(crs+A); /* grab bits */ + templ = templ >> (31-scount); /* extend them */ + EXPCL(!(templ == -1 || templ == 0)); + *(int *)(crs+A) = *(int *)(crs+A) << scount; + } else { + EXPCL(*(int *)(crs+A) != 0); + *(int *)(crs+A) = 0; + } + } else { + utempa = crs[B] & 0x8000; /* save B bit 1 */ + if (scount < 31) { + utempl = (crs[A]<<16) | ((crs[B] & 0x7FFF)<<1); + templ2 = 0x80000000; + templ2 = templ2 >> scount; /* create mask */ + templ2 = templ2 & utempl; /* grab bits */ + templ2 = templ2 >> (31-scount); /* sign extend them */ + EXPCL(!(templ2 == -1 || templ2 == 0)); + //printf(" before: A=%x, B=%x, utempl=%x, ", crs[A], crs[B], utempl); + utempl = utempl << scount; + crs[A] = utempl >> 16; + crs[B] = ((utempl >> 1) & 0x7FFF) | utempa; + //printf(" after: A=%x, B=%x, utempl=%x\n", crs[A], crs[B], utempl); + } else { + EXPCL(*(unsigned int *)(crs+A) != 0); + *(unsigned int *)(crs+A) = utempa; + } + } + if (crs[KEYS] & 0100000) + mathexception('i', FC_INT_OFLOW, 0); + break; - case 01000: /* LLL */ - TRACE(T_FLOW, " LLL %d\n", scount); - crs[KEYS] &= ~0120000; /* clear C,L */ - if (scount <= 32) { + case 01200: /* LLR */ + TRACE(T_FLOW, " LLR %d\n", scount); + crs[KEYS] &= ~0120000; /* clear C,L */ + if (scount > 32) + scount = scount - 32; utempl = *(unsigned int *)(crs+A); EXPCL(utempl & bitmask32[scount]); - utempl = utempl << scount; + utempl = (utempl << scount) | (utempl >> (32-scount)); *(unsigned int *)(crs+A) = utempl; - } else { - *(unsigned int *)(crs+A) = 0; - } - break; + break; - case 01100: /* LLS (different in R/V modes) */ - TRACE(T_FLOW, " LLS %d\n", scount); - crs[KEYS] &= ~0120000; /* clear C,L */ - if (crs[KEYS] & 010000) { /* V/I mode */ - if (scount < 32) { - templ = 0x80000000; - templ = templ >> scount; /* create mask */ - templ = templ & *(int *)(crs+A); /* grab bits */ - templ = templ >> (31-scount); /* extend them */ - EXPCL(!(templ == -1 || templ == 0)); - *(int *)(crs+A) = *(int *)(crs+A) << scount; + case 01400: /* ALL */ + TRACE(T_FLOW, " ALL %d\n", scount); + crs[KEYS] &= ~0120000; /* clear C,L */ + if (scount <= 16) { + EXPCL(crs[A] & bitmask16[scount]); + crs[A] = crs[A] << scount; } else { - EXPCL(*(int *)(crs+A) != 0); - *(int *)(crs+A) = 0; + crs[A] = 0; } - } else { - utempa = crs[B] & 0x8000; /* save B bit 1 */ - if (scount < 31) { - utempl = (crs[A]<<16) | ((crs[B] & 0x7FFF)<<1); - templ2 = 0x80000000; - templ2 = templ2 >> scount; /* create mask */ - templ2 = templ2 & utempl; /* grab bits */ - templ2 = templ2 >> (31-scount); /* sign extend them */ - EXPCL(!(templ2 == -1 || templ2 == 0)); - //printf(" before: A=%x, B=%x, utempl=%x, ", crs[A], crs[B], utempl); - utempl = utempl << scount; - crs[A] = utempl >> 16; - crs[B] = ((utempl >> 1) & 0x7FFF) | utempa; - //printf(" after: A=%x, B=%x, utempl=%x\n", crs[A], crs[B], utempl); - } else { - EXPCL(*(unsigned int *)(crs+A) != 0); - *(unsigned int *)(crs+A) = utempa; + break; + + case 01500: /* ALS */ + TRACE(T_FLOW, " ALS %d\n", scount); + crs[KEYS] &= ~0120000; /* clear C,L */ + if (scount <= 15) { + tempa = 0100000; + tempa = tempa >> scount; /* create mask */ + tempa = tempa & crs[A]; /* grab bits */ + tempa = tempa >> (15-scount); /* extend them */ + crs[A] = crs[A] << scount; + EXPCL(!(tempa == -1 || tempa == 0)); + } else if (crs[A] != 0) { + crs[A] = 0; + SETCL; } - } - if (crs[KEYS] & 0100000) - mathexception('i', FC_INT_OFLOW, 0); - break; + if (crs[KEYS] & 0100000) + mathexception('i', FC_INT_OFLOW, 0); + break; - case 01200: /* LLR */ - TRACE(T_FLOW, " LLR %d\n", scount); - crs[KEYS] &= ~0120000; /* clear C,L */ - if (scount > 32) - scount = scount - 32; - utempl = *(unsigned int *)(crs+A); - EXPCL(utempl & bitmask32[scount]); - utempl = (utempl << scount) | (utempl >> (32-scount)); - *(unsigned int *)(crs+A) = utempl; - break; - - case 01400: /* ALL */ - TRACE(T_FLOW, " ALL %d\n", scount); - crs[KEYS] &= ~0120000; /* clear C,L */ - if (scount <= 16) { + case 01600: /* ALR */ + TRACE(T_FLOW, " ALR %d\n", scount); + crs[KEYS] &= ~0120000; /* clear C,L */ + scount = ((scount-1)%16)+1; /* make scount 1-16 */ EXPCL(crs[A] & bitmask16[scount]); - crs[A] = crs[A] << scount; - } else { - crs[A] = 0; + crs[A] = (crs[A] << scount) | (crs[A] >> (16-scount)); + break; + + default: + badshift: + printf("emulator warning: unrecognized shift instruction %o at %o/%o\n", inst, RPH, RPL); + TRACE(T_INST, " unrecognized shift instruction!: %o\n", inst); } - break; - - case 01500: /* ALS */ - TRACE(T_FLOW, " ALS %d\n", scount); - crs[KEYS] &= ~0120000; /* clear C,L */ - if (scount <= 15) { - tempa = 0100000; - tempa = tempa >> scount; /* create mask */ - tempa = tempa & crs[A]; /* grab bits */ - tempa = tempa >> (15-scount); /* extend them */ - crs[A] = crs[A] << scount; - EXPCL(!(tempa == -1 || tempa == 0)); - } else if (crs[A] != 0) { - crs[A] = 0; - SETCL; - } - if (crs[KEYS] & 0100000) - mathexception('i', FC_INT_OFLOW, 0); - break; - - case 01600: /* ALR */ - TRACE(T_FLOW, " ALR %d\n", scount); - crs[KEYS] &= ~0120000; /* clear C,L */ - scount = ((scount-1)%16)+1; /* make scount 1-16 */ - EXPCL(crs[A] & bitmask16[scount]); - crs[A] = (crs[A] << scount) | (crs[A] >> (16-scount)); - break; - - default: -badshift: - printf("emulator warning: unrecognized shift instruction %o at %o/%o\n", inst, RPH, RPL); - TRACE(T_INST, " unrecognized shift instruction!: %o\n", inst); + continue; } - continue; } if (class == 2) { @@ -5599,6 +5612,372 @@ keys = 14200, modals=100177 fatal("Coding error: bad generic class"); } + if ((crs[KEYS] & 0016000) != 0010000) goto nonimode; +#ifndef OSX + RESTRICT(); +#endif + ea = ea32i(earp, inst, &immu32, &immu64); + dr = (inst >> 7) & 7; + + switch (inst >> 10) { + + case 000: + fatal("I-mode generic class 0?"); + + case 001: + TRACE(T_INST, " L\n"); + if (*(int *)&ea < 0) + crsl[dr] = immu32; + else + crsl[dr] = get32(ea); + continue; + + case 002: + TRACE(T_INST, " A\n"); + if (*(int *)&ea < 0) + utempl2 = immu32; + else + utempl2 = get32(ea); + crs[KEYS] &= ~0120300; /* clear C, L, LT, EQ */ + utempl = crsl[dr]; /* save orig L for sign check */ + utempll = utempl; /* expand to 64 bits */ + utempll += utempl2; /* 64-bit add */ + crsl[dr] = utempll; /* truncate results */ + if (utempll & 0x100000000LL) /* set L-bit if carry */ + crs[KEYS] |= 020000; + if (crsl[dr] == 0) /* set EQ? */ + crs[KEYS] |= 0100; + if ((~utempl ^ utempl2) & (utempl ^ crsl[dr]) & 0x80000000) { + if (*(int *)(crsl+dr) >= 0) + crs[KEYS] |= 0200; + mathexception('i', FC_INT_OFLOW, 0); + } else if (*(int *)(crsl+dr) < 0) + crs[KEYS] |= 0200; + continue; + + case 003: + TRACE(T_INST, " N\n"); + if (*(int *)&ea < 0) + crsl[dr] &= immu32; + else + crsl[dr] &= get32(ea); + continue; + + case 004: + TRACE(T_INST, " LHL1\n"); + if (*(int *)&ea < 0) + crs[dr*2] = immu32 << 1; + else + crs[dr*2] = get16(ea) << 1; + continue; + + case 005: + TRACE(T_INST, " SHL\n"); + fatal("SHL not implemented"); + continue; + + case 006: /* Special MR FP format */ + /* DFC, DFL, FC, FL */ + continue; + + case 007: + fatal("I-mode opcode 007"); + + case 010: + /* register generic branch */ + continue; + + case 011: + TRACE(T_INST, " LH\n"); + if (*(int *)&ea < 0) + crs[dr*2] = immu32; + else + crs[dr*2] = get16(ea); + continue; + + case 012: + TRACE(T_INST, " AH\n"); + continue; + + case 013: + TRACE(T_INST, " NH\n"); + if (*(int *)&ea < 0) + crs[dr*2] &= immu32; + else + crs[dr*2] &= get16(ea); + continue; + + case 014: + TRACE(T_INST, " LHL2\n"); + if (*(int *)&ea < 0) + crs[dr*2] = immu32 << 2; + else + crs[dr*2] = get16(ea) << 2; + continue; + + case 015: + TRACE(T_INST, " SHA\n"); + fatal("SHA not impl"); + continue; + + case 016: /* Special MR FP format */ + /* DFA, DFST, FA, FST, PCL */ + continue; + + case 017: + fatal("I-mode opcode 017"); + + case 020: + fatal("I-mode generic class 1?"); + + case 021: + TRACE(T_INST, " ST\n"); + if (*(int *)&ea < 0) + fatal("ST imm"); + put32(crsl[dr],ea); + continue; + + case 022: + TRACE(T_INST, " S\n"); + fatal("S not impl"); + continue; + + case 023: + TRACE(T_INST, " O\n"); + if (*(int *)&ea < 0) + crsl[dr] |= immu32; + else + crsl[dr] |= get32(ea); + continue; + + case 024: + TRACE(T_INST, " ROT\n"); + fatal("ROT not impl"); + continue; + + case 025: + fatal("I-mode opcode 025"); + + case 026: /* Special MR FP format */ + /* DFM, DFS, FM, FS */ + continue; + + case 027: + fatal("I-mode opcode 027"); + + case 030: /* register generic */ + switch (inst) { + } + continue; + + case 031: + fatal("I-mode opcode 031"); + + case 032: + TRACE(T_INST, " SH\n"); + continue; + + case 033: + TRACE(T_INST, " OH\n"); + if (*(int *)&ea < 0) + crs[dr*2] |= immu32; + else + crs[dr*2] |= get16(ea); + continue; + + case 034: + TRACE(T_INST, " EIO\n"); + pio(ea & 0xFFFF); + continue; + + case 035: + TRACE(T_INST, " LHL3\n"); + if (*(int *)&ea < 0) + crs[dr*2] = immu32 << 3; + else + crs[dr*2] = get16(ea) << 3; + continue; + + case 036: /* Special MR FP format */ + /* DFD, FD, QFAD, QFLD, QFSB, QFST */ + continue; + + case 037: + fatal("I-mode opcode 031"); + + case 040: /* generic class 2, overlays skip group */ + fatal("I-mode generic class 2?"); + + case 041: + TRACE(T_INST, " I\n"); + utempl = crsl[dr]; + if (*(int *)&ea < 0) { + crsl[dr] = immu32; + crsl[(inst >> 2) & 7] = utempl; + } else { + crsl[dr] = get32(ea); + put32(utempl, ea); + } + continue; + + case 042: + TRACE(T_INST, " M\n"); + continue; + + case 043: + TRACE(T_INST, " X\n"); + if (*(int *)&ea < 0) + crsl[dr] ^= immu32; + else + crsl[dr] ^= get32(ea); + continue; + + case 044: + TRACE(T_INST, " LDAR\n"); + utempa = ea; /* word number portion only */ + if (utempa & 040000) { /* absolute RF addressing */ + RESTRICT(); + utempa &= 0377; + if (utempa == 020) + crsl[dr] = 1; + else if (utempa == 024) + crsl[dr] = -1; + else + crsl[dr] = regs.u32[utempa]; + } else { + utempa &= 037; + if (utempa > 017) RESTRICT(); + crsl[dr] = *(((unsigned int *)crs)+utempa); + } + continue; + + case 045: + TRACE(T_INST, " CCP/LCP\n"); + continue; + + case 046: /* I-mode special MR, GR format */ + /* EALB, IM, QFC, QFDV, QFMP, TM */ + continue; + + case 047: + fatal("I-mode opcode 047"); + + case 050: + fatal("I-mode opcode 050"); + + case 051: + /* IH, STH */ + continue; + + case 052: + TRACE(T_INST, " MH\n"); + continue; + + case 053: + TRACE(T_INST, " XH\n"); + if (*(int *)&ea < 0) + crs[dr*2] ^= immu32; + else + crs[dr*2] ^= get16(ea); + continue; + + case 054: + TRACE(T_INST, " STAR\n"); + utempa = ea; /* word number portion only */ + if (utempa & 040000) { /* absolute RF addressing */ + RESTRICT(); + regs.u32[utempa & 0377] = crsl[dr]; + } else { + utempa &= 037; + if (utempa > 017) RESTRICT(); + *(((unsigned int *)crs)+utempa) = crsl[dr]; + } + continue; + + case 055: + /* ACP, SCC */ + continue; + + case 056: /* I-mode special MR, GR format */ + /* EAXB, IMH, JMP, TMH */ + continue; + + case 057: + fatal("I-mode opcode 057"); + + case 060: + fatal("I-mode generic class 3?"); + + case 061: + TRACE(T_INST, " C\n"); + continue; + + case 062: + TRACE(T_INST, " D\n"); + continue; + + case 063: + TRACE(T_INST, " EAR\n"); + crsl[dr] = ea; + continue; + + case 064: + fatal("I-mode opcode 064"); + + case 065: + TRACE(T_INST, " LIP\n"); + crsl[dr] = get32(ea); + if (crsl[dr] & 0x80000000) + fault(POINTERFAULT, crsl[dr]>>16, ea); + continue; + + case 066: /* I-mode special MR */ + /* DM, JSXB */ + continue; + + case 067: + fatal("I-mode opcode 067"); + + case 070: + fatal("I-mode opcode 067"); + + case 071: + TRACE(T_INST, " CH\n"); + continue; + + case 072: + TRACE(T_INST, " DH\n"); + continue; + + case 073: + TRACE(T_INST, " JSR\n"); + crs[dr*2] = RPH; + RPH = ea; + continue; + + case 074: + fatal("I-mode opcode 074"); + + case 075: + TRACE(T_INST, " AIP\n"); + crsl[dr] += get32(ea); + if (crsl[dr] & 0x80000000) + fault(POINTERFAULT, crsl[dr]>>16, ea); + /* NOTE: ISG says C & L need to be set, ring needs to be weakened */ + continue; + + case 076: + /* DMH, TCNP */ + continue; + + case 077: + fatal("I-mode opcode 077"); + } + fatal("I-mode fall-through?"); + + +nonimode: + /* here for non-generic instructions: memory references or pio */ /* pio can only occur in S/R modes */ @@ -5657,12 +6036,9 @@ keys = 14200, modals=100177 ea = ea32r64r(earp, inst, x, &opcode); break; case 4<<10: /* 32I */ - ea = ea32i(earp, inst, x); - warn("32I mode not supported"); - fault(RESTRICTFAULT, 0, 0); - break; + fatal("32I mode invalid here"); case 6<<10: /* 64V */ - ea = ea64v(earp, inst, x, &opcode, &eabit); + ea = ea64v(earp, inst, x, &opcode); break; default: printf("Bad CPU mode in EA calculation, keys = %o\n", crs[KEYS]); diff --git a/emdev.h b/emdev.h index e3dc69e..c217579 100644 --- a/emdev.h +++ b/emdev.h @@ -2015,6 +2015,7 @@ int devamlc (int class, int func, int device) { unsigned short dss; /* 1 bit per line */ unsigned short sockfd[16]; /* Unix socket fd, 1 per line */ unsigned short tstate[16]; /* terminal state (telnet) */ + unsigned short room[16]; /* room (chars) left in input buffer */ } dc[64]; int lx; @@ -2029,7 +2030,7 @@ int devamlc (int class, int func, int device) { int fd; unsigned int addrlen; unsigned char buf[1024]; /* max size of DMQ buffer */ - int i, n, nw, newdevice; + int i, n, n2, nw, newdevice; fd_set fds; struct timeval timeout; unsigned char ch; @@ -2217,11 +2218,25 @@ int devamlc (int class, int func, int device) { devpoll[device] = AMLCPOLL*instpermsec; /* setup another poll */ IOSKIP; + } else if (func == 03) { /* set room in input buffer */ + lx = (crs[A]>>12); + dc[device].room[lx] = crs[A] & 0xFFF; + //printf("OTA '03%02o: AMLC line %d, room=%d, A=0x%04x\n", device, lx, dc[device].room[lx], crs[A]); + 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!"); +#if 0 + dmcea = dc[device].dmcchan; + dmcpair = get32r0(dmcea); + dmcbufbegea = dmcpair>>16; + dmcbufendea = dmcpair & 0xffff; + dmcnw = dmcbufendea - dmcbufbegea + 1; + printf("AMLC: dmcnw=%d\n", dmcnw); +#endif IOSKIP; } else if (func == 015) { /* set DMT/DMQ base address (for output) */ @@ -2269,6 +2284,7 @@ int devamlc (int class, int func, int device) { dc[newdevice].dss |= bitmask16[lx+1]; dc[newdevice].sockfd[lx] = fd; dc[newdevice].tstate[lx] = TS_DATA; + dc[newdevice].room[lx] = 64; //printf("em: AMLC connection, fd=%d, device='%o, line=%d\n", fd, newdevice, lx); break; } @@ -2302,16 +2318,18 @@ int devamlc (int class, int func, int device) { buf[4] = 251; /* will */ buf[5] = 3; /* supress go ahead */ buf[6] = 255; /* IAC */ - buf[7] = 251; /* will */ + buf[7] = 253; /* do */ buf[8] = 0; /* binary mode */ write(fd, buf, 9); strcpy((void *)buf,"\ \r\n\ Welcome to the Prime Computer 50-series emulator, running Primos rev 19.2!\r\n\ -After logging in, use the Prime HELP command for assistance.\r\n\ -You are welcome to create a directory under GUEST for your files.\r\n\ -To report bugs or contact the author, send email to prirun@gmail.com\r\n\ -Enjoy your time travels! -Jim Wilcoxson\r\n\ +\n\ + After logging in, use the Prime HELP command for assistance.\r\n\ + You are welcome to create a directory under GUEST for your files.\r\n\ + To report bugs or contact the author, send email to prirun@gmail.com\r\n\ +\n\ +Enjoy your time travels! -Jim Wilcoxson aka JIMMY\r\n\ \r\n\ Login please.\r\n\ OK, "); @@ -2319,27 +2337,27 @@ OK, "); } } - /* do a receive/transmit scan loop for every line. This loop is - fairly efficient because Primos turns off xmitenable on DMQ - lines after a short time w/o any output. */ + /* do a transmit scan loop for every line. This loop is fairly + efficient because Primos turns off xmitenable on DMQ lines + after a short time w/o any output. - if (dc[device].xmitenabled != 0 || dc[device].dss != 0) { + NOTE: it's important to do xmit processing even if a line is + not currently connected, to drain the tty output buffer. + Otherwise, the DMQ buffer fills, this stalls the tty output + buffer, and when the next connection occurs on this line, the + output buffer from the previous terminal session will then be + displayed to the new user. */ + + if (dc[device].dss || dc[device].xmitenabled) { for (lx = 0; lx < 16; lx++) { - /* Transmit is first. Lots of opportunity to optimize this: - rather than using the rtq instruction, do a mapva call to - access the qcb directly, pack the queue data to a buffer, - attempt to write the buffer to the socket, and then update - the qcb to reflect how many bytes were actually written. - This would avoid lots of get16/mapva calls and the write - select could be removed. - - NOTE: it's important to do xmit processing even if a line - is not currently connected, to drain the tty output buffer. - Otherwise, the DMQ buffer fills, this stalls the tty output - buffer, and when the next connection occurs on this line, - the output buffer from the previous terminal session will - then be displayed to the new user. */ + /* Lots of opportunity to optimize transmits: rather than + using the rtq instruction, do a mapva call to access the + qcb directly, pack the queue data to a buffer, attempt to + write the buffer to the socket, and then update the qcb to + reflect how many bytes were actually written. This would + avoid lots of get16/mapva calls and the write select could + be removed. */ if (dc[device].xmitenabled & bitmask16[lx+1]) { n = 0; @@ -2383,10 +2401,10 @@ OK, "); the AMLC device is configured as a QAMLC */ } - /* NOTE: on a partial write, terminal data is lost. The best - option would be to find out how much room is left in the - socket buffer and only remove that many characters from the - queue in the loop above. + /* NOTE: on a partial write, data sent to the terminal is + lost. The best option would be to find out how much room + is left in the socket buffer and only remove that many + characters from the queue in the loop above. Mac OSX write selects on sockets will only return true if there are SO_SNDLOWAT (defaults to 1024) bytes available in @@ -2402,112 +2420,132 @@ OK, "); fprintf(stderr," %d bytes written, but %d bytes sent\n", n, nw); } } + } + } - /* process input, but only as much as will fit into the DMC buffer. - NOTE: because the size of the AMLC tumble tables is limited, this - could pose a denial of service issue. Input should probably be - processed in a round to be more fair with this limited resource. + /* process input, but only as much as will fit into the DMC + buffer. NOTE: because the size of the AMLC tumble tables is + limited, this could pose a denial of service issue. Input + should probably be processed in a round to be more fair with + this limited resource, or the tumble table space could be + apportioned for each connected line or each line with data + waiting to be read. - The AMLC tumble tables should never overflow, because we only - read as many characters from the socket buffers as will fit in - the tumble tables. */ + The AMLC tumble tables should never overflow, because we only + read as many characters from the socket buffers as will fit in + the tumble tables. However, the tty line buffers may overflow, + causing data from the terminal to be dropped. To help avoid + this, a new OTA "set room left" has been implemented. If there + is no room left in the tty input buffer, don't read any more + characters from the socket for that line. */ - 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) + if (!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; + + for (lx = 0; lx < 16 && dmcnw > 0; lx++) { + if (!(dc[device].dss & dc[device].recvenabled & bitmask16[lx+1]) + || dc[device].room[lx] < 8) + continue; + + /* dmcnw is the # of characters left in the dmc buffer, but + there may be further size/space restrictions for this line */ + + n2 = dmcnw; + if (n2 > sizeof(buf)) + n2 = sizeof(buf); + if (n2 > dc[device].room[lx]) + n2 = dc[device].room[lx]; + + while ((n = read(dc[device].sockfd[lx], buf, n2)) == -1 && errno == EINTR) + ; + //printf("processing recv on device %o, line %d, b#=%d, n2=%d, n=%d\n", device, lx, dc[device].bufnum, n2, 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) ; - //printf("processing recv on device %o, line %d, b#=%d, dmcnw=%d, n=%d\n", device, lx, dc[device].bufnum, dmcnw, n); + else if (errno == EPIPE || errno == ECONNRESET) { - /* 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) - ; - else if (errno == EPIPE || errno == ECONNRESET) { - - /* see similar code above */ - close(dc[device].sockfd[lx]); - dc[device].dss &= ~bitmask16[lx+1]; - //printf("em: closing AMLC line %d on device '%o\n", lx, device); - } else { - perror("Reading AMLC"); - } - } - - /* very primitive support here for telnet - only enough to - ignore commands sent by the telnet client. Telnet - commands could be split across reads and AMLC interrupts, - so a small state machine is used for each line */ - - if (n > 0) { - state = dc[device].tstate[lx]; - for (i=0; i dmcbufendea) - fatal("AMLC tumble table overflowed?"); - put16r0(dmcbufbegea, dmcea); - if (dmcbufbegea > dmcbufendea) { /* end of range has occurred */ - dc[device].bufnum = 1-dc[device].bufnum; - dc[device].eor = 1; - } + /* see similar code above */ + close(dc[device].sockfd[lx]); + dc[device].dss &= ~bitmask16[lx+1]; + //printf("em: closing AMLC line %d on device '%o\n", lx, device); + } else { + perror("Reading AMLC"); } } + + /* very primitive support here for telnet - only enough to + ignore commands sent by the telnet client. Telnet + commands could be split across reads and AMLC interrupts, + so a small state machine is used for each line */ + + if (n > 0) { + state = dc[device].tstate[lx]; + for (i=0; i dmcbufendea) + fatal("AMLC tumble table overflowed?"); + put16r0(dmcbufbegea, dmcea); + if (dmcbufbegea > dmcbufendea) { /* end of range has occurred */ + dc[device].bufnum = 1-dc[device].bufnum; + dc[device].eor = 1; } } @@ -2529,7 +2567,7 @@ storech: 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 */ + for new incoming connections and incoming data */ if ((dc[device].ctinterrupt || dc[device].xmitenabled || (dc[device].recvenabled & dc[device].dss)) && devpoll[device] == 0) devpoll[device] = AMLCPOLL*instpermsec; /* setup another poll */ @@ -2695,6 +2733,8 @@ storech: int devpnc (int class, int func, int device) { +#if 0 + #define PNCPOLL 100 /* PNC controller status bits */ @@ -2706,6 +2746,15 @@ int devpnc (int class, int func, int device) { #define PNCTWOTOKENS 0x200 /* bit 7, only set after xmit EOR */ #define PNCTOKDETECT 0x100 /* bit 8, only set after xmit EOR */ + /* xmit/recv states */ + +#define XR_IDLE 0 /* initial state: no xmit or recv */ +#define XR_READY 1 /* ready to recv or xmit */ +#define XR_XFER 3 /* transferring data over socket */ + +#define MINCONNTIME 30 /* wait 30 seconds between connect attempts */ +#define MAXPKTBYTES 2048 /* max of 2048 byte packets */ + static short configured = 0; /* true if PNC configured */ static unsigned short pncstat; /* controller status word */ static unsigned short recvstat; /* receive status word */ @@ -2725,6 +2774,7 @@ int devpnc (int class, int func, int device) { 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 */ + time_t conntime; /* time of last connect request */ } ni[256]; /* array to map socket fd's to local node id's for accessing the ni @@ -2735,14 +2785,18 @@ int devpnc (int class, int func, int device) { static short fdnimap[FDMAPSIZE]; typedef struct { + short state, offset; unsigned short dmachan, dmareg, dmaaddr; - short dmanw, toid, fromid, remtoid, remfromid; - unsigned short *iobufp; + short dmanw, dmabytesleft, toid, fromid, remtoid, remfromid; + unsigned char iobuf[MAXPKTBYTES+2]; } t_dma; static t_dma recv, xmit; short i; - unsigned short access, dmaword, dmaword1, dmaword2; + unsigned short access, dmaword; + unsigned short *iobufp; + time_t timenow; + struct hostent* server; struct sockaddr_in addr; int fd, optval, fdflags; @@ -2779,6 +2833,8 @@ int devpnc (int class, int func, int device) { ni[i].myremid = 0; ni[i].yourremid = 0; } + xmit.state = XR_IDLE; + recv.state = XR_IDLE; myid = 0; /* set an invalid node id */ /* read the ring.cfg config file. Each line contains: @@ -2896,6 +2952,8 @@ int devpnc (int class, int func, int device) { ni[i].fd = -1; } } + xmit.state = XR_IDLE; + recv.state = XR_IDLE; pncstat &= ~PNCCONNECTED; /* OCP '0701 - connect @@ -2951,6 +3009,7 @@ int devpnc (int class, int func, int device) { pncstat &= ~PNCXMITINT; /* clear "xmit interrupting" */ pncstat &= ~PNCTOKDETECT; /* clear "token detected" */ xmitstat = 0; + xmit.state = XR_IDLE; } else if (func == 05) { /* set PNC into "delay" mode */ TRACE(T_INST|T_RIO, " OCP '%02o%02o - set delay mode\n", func, device); @@ -2958,6 +3017,7 @@ int devpnc (int class, int func, int device) { } else if (func == 010) { /* stop xmit in progress */ TRACE(T_INST|T_RIO, " OCP '%02o%02o - stop xmit\n", func, device); xmitstat = 0; + xmit.state = XR_IDLE; } else if (func == 011) { /* dunno what this is - rev 20 startup */ TRACE(T_INST|T_RIO, " OCP '%02o%02o - unknown\n", func, device); @@ -2975,6 +3035,7 @@ int devpnc (int class, int func, int device) { TRACE(T_INST|T_RIO, " OCP '%02o%02o - ack recv int\n", func, device); pncstat &= ~PNCRCVINT; recvstat = 0; + recv.active = 0; } else if (func == 015) { /* set interrupt mask (enable int) */ TRACE(T_INST|T_RIO, " OCP '%02o%02o - enable interrupts\n", func, device); @@ -2991,6 +3052,8 @@ int devpnc (int class, int func, int device) { xmitstat = 0; pncvec = 0; enabled = 0; + xmit.state = XR_IDLE; + recv.state = XR_IDLE; } else { printf("Unimplemented OCP device '%02o function '%02o\n", device, func); @@ -3055,11 +3118,17 @@ int devpnc (int class, int func, int device) { recv.dmanw = -((recv.dmanw>>4) ^ 0xF000); recv.dmaaddr = regs.sym.regdmx[recv.dmareg+1]; recv.iobufp = mem + mapva(recv.dmaaddr, WACC, &access, 0); + recv.state = XR_READY; + recv.offset = -1; /* initialize for new packet */ 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 */ + if (xmitstat & 0x0040) { /* already busy? */ + warn("pnc: xmit when already busy!"); + return; /* yes, return and don't skip */ + } xmitstat = 0x0040; /* set xmit busy */ xmit.dmachan = crs[A]; xmit.dmareg = xmit.dmachan<<1; @@ -3071,41 +3140,53 @@ int devpnc (int class, int func, int device) { xmit.dmaaddr = regs.sym.regdmx[xmit.dmareg+1]; TRACE(T_INST|T_RIO, " xmit: dmachan=%o, dmareg=%o, dmaaddr=%o, dmanw=%d\n", xmit.dmachan, xmit.dmareg, xmit.dmaaddr, xmit.dmanw); - /* PNC transmit: - - NOTE: most of this stuff needs to be moved to poll, because - it may not be possible to start/complete a transmit here! - - get a physical pointer to the xmit buffer, which can't cross - a page boundary, then xmit the buffer. Since PNC packets - have a specific size of either 512, 1024, or 2048 bytes, and - we're using a byte-stream for communication, 2 extra header - bytes are sent with the packet length (in words), to let the - other end know how big the packet is. - */ - - xmit.iobufp = mem + mapva(xmit.dmaaddr, RACC, &access, 0); - /* read the first word, the to and from node id's, and map them to the remote hosts' expected to and from node id's */ + xmit.iobufp = mem + mapva(xmit.dmaaddr, RACC, &access, 0); dmaword = *xmit.iobufp++; xmit.toid = dmaword >> 8; xmit.fromid = dmaword & 0xFF; - TRACE(T_INST|T_RIO, " xmit: dmaword = '%o/%d [%03o %03o]\n", dmaword, *(short *)&dmaword, dmaword>>8, dmaword&0xff); + TRACE(T_INST|T_RIO, " xmit: toid=%d, fromid=%d\n", xmit.toid, xmit.fromid); - /* broadcast packets are "I am up" msgs and are simply "eaten" - here, as this is handled later in the devpnc poll code. - XXX: should check that this really is the "I am up" msg */ + /* broadcast packets are "I am up" msgs and are simply discarded + here, with a succesful transmit status. Node up/down is + handled in the devpnc poll code. + + XXX: should check that this really is the "I am up" msg */ 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; + goto xmitdone1; } - /* check for errors, then map to and from node id to remote to - and from node id */ + /* if this xmit is to me and there is a new receive pending and + there is room left in the receive buffer, put the packet + directly in my receive buffer. If we can't receive it now, + set NACK xmit and receive status. If the packet is not to + me, copy it to the xmit.iobuf and add the header */ + + if (xmit.toid == myid) { + if (recv.state == XR_READY && recv.dmanw >= xmit.dmanw) { + memcpy(recv.iobufp, xmit.iobufp, xmit.dmanw*2); + regs.sym.regdmx[recv.dmareg] += xmit.dmanw; /* bump recv count */ + regs.sym.regdmx[recv.dmareg+1] += xmit.dmanw; /* and address */ + pncstat |= 0x8000; /* set recv interrupt too */ + recv.state = XR_IDLE; /* no longer ready to recv */ + +xmitdone1: + regs.sym.regdmx[xmit.dmareg] += xmit.dmanw; /* and xmit count */ + regs.sym.regdmx[xmit.dmareg+1] += xmit.dmanw; /* and address */ + pncstat |= 0x4100; /* set xmit interrupt + token */ + xmitstat |= 0x8000; /* set ACK xmit status */ + goto xmitdone; + + } else { + xmitstat |= 0x1000; /* set xmit NACK status */ + recvstat |= 0x20; /* set recv premature EOR */ + } + } + + /* check for unreasonable situations */ if (xmit.toid == 0) fatal("PNC: xmit.toid is zero"); @@ -3116,49 +3197,18 @@ int devpnc (int class, int func, int device) { fatal(NULL); } + /* map local to and from node id to remote to and from node id */ + 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 */ - } + xmit.state = XR_READY; /* xmit pending */ + xmit.offset = -1; /* initialize for new xmit */ + devpoll[device] = 10; 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; @@ -3192,6 +3242,52 @@ xmitdone: case 4: TRACE(T_INST|T_RIO, " POLL '%02o%02o\n", func, device); + /* if a transmit is pending, start/continue it until completes */ + + if (xmit.state > XR_IDLE) { + if (ni[xmit.toid].fd == -1) { /* not connected yet */ + if (time(&timenow) - ni[xmit.toid].conntime < MINCONNTIME) { + printf("em: waiting for connection timeout to node %d\n", xmit.toid); + goto xmiterr; + } + if ((ni[xmit.toid].fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { + perror ("Unable to create socket"); + exit(1); + } + server = gethostbyname(ni[xmit.toid].ip); + if (server == NULL) { + fprintf(stderr,"pnc: cannot resolve %s\n", ni[xmit.toid].ip); + close(ni[xmit.toid].fd); + ni[xmit.toid].fd = -1; + goto xmiterr; + } + ni[xmit.toid].conntime = timenow; + bzero((char *) &addr, sizeof(addr)); + addr.sin_family = AF_INET; + bcopy((char *)server->h_addr, (char *)&addr.sin_addr.s_addr, server->h_length); + addr.sin_port = htons(ni[xmit.toid].port); + if (connect(ni[xmit.toid].fd, (void *) &addr,(socklen_t) sizeof(addr)) < 0) { + perror("pnc: error connecting to server\n"); + close(ni[xmit.toid].fd); + ni[xmit.toid].fd = -1; + goto xmiterr; + } + xmit.state = XR_XFER; + } + + /* start/continue the transfer */ + + if (xmit.state == XR_XFER) { + if ((n=write(ni[xmit.toid].fd, xmit.iobuf[offset], xmit.dmabytes-xmit.offset)) < 0) { + perror("pnc: write error"); + } else { + xmit.offset += n; + xmit.dmabytesleft -= n; + if (xmit.dmabytesleft == 0) + xmit.state = XR_IDLE; + } + } + #if 0 while ((fd = accept(pncfd, (struct sockaddr *)&addr, &addrlen)) == -1 && errno == EINTR) ; @@ -3202,6 +3298,13 @@ xmitdone: } else { if (fd >= MAXFD) fatal("New connection fd is too big"); + printf("New PNC connection:\n"); + /* +- new connect request came in +- scan host table to find matching IP +- how to figure out when one IP has multiple emulators? port? +- if already connected, display warning error and ignore + newdevice = 0; for (i=0; devices[i] && !newdevice && i