diff --git a/em.c b/em.c index 39b74e7..e181e19 100644 --- a/em.c +++ b/em.c @@ -1,12 +1,14 @@ /* Pr1me Computer emulator, Jim Wilcoxson (prirun@gmail.com), April 4, 2005 - Copyright (C) 2005, Jim Wilcoxson (prirun@gmail.com). All Rights Reserved. + Copyright (C) 2005-2007, Jim Wilcoxson. All Rights Reserved. Emulates a Prime Computer system by: - booting from a Prime disk image (normal usage) - booting from a Prime MAGSAV tape - restoring a Prime R-mode .save image from the host file system - This is a project in development, so please don't publish it. + This is a project in development, so please don't publish it or + make it available for others to use. + Comments, suggestions, corrections, and general notes that you're interested in a Prime emulation project are welcome and appreciated. @@ -115,6 +117,11 @@ OK: typedef unsigned int ea_t; /* effective address */ typedef unsigned int pa_t; /* physical address */ +/* procs needing forward declarations */ + +void fault(unsigned short fvec, unsigned short fcode, ea_t faddr); + + /* the live program counter register is aka microcode scratch register TR7 */ #define RP regs.sym.tr7 @@ -184,7 +191,7 @@ typedef unsigned int pa_t; /* physical address */ #define SETC crs[KEYS] |= 0100000 #define CLEARC crs[KEYS] &= 077777 -/* XEXPC, XSETC, XCLEARC are stuubs to indicate that the C-bit may not be set correctly */ +/* XEXPC, XSETC, XCLEARC are stubs to indicate that the C-bit may not be set correctly */ #define XEXPC EXPC #define XCLEARC CLEARC @@ -367,7 +374,7 @@ int intvec=-1; /* currently raised interrupt (if >= zero) /* NOTE: Primos II gives "NOT FOUND" on STARTUP 2460 command if sense switches are set to 014114. But DIAGS like this setting. :( */ -unsigned short sswitch = 014114; /* sense switches, set with -ss & -boot*/ +unsigned short sswitch = 014114; /* sense switches, set with -ss & -boot */ /* NOTE: the default cpuid is a P750: 1 MIPS, 8MB of memory */ @@ -387,11 +394,17 @@ jmp_buf jmpbuf; /* for longjumps to the fetch loop */ NOTE: - rev 20 is limited to a max of 32MB - - rev 23.4 is limited to a max of 128MB + - rev 23.4 is limited to a max of 512MB + + "memlimit" is set with the -mem argument, taking an argument which is + the desired memory limit in MB. Setting a memory limit is useful to + speed up system boots and diagnostics during emulator testing. */ -#define MEMSIZE 256*1024*1024 /* 128 MB */ -unsigned short mem[MEMSIZE]; /* system's physical memory */ +#define MEMSIZE 512/2*1024*1024 /* 512 MB */ + +unsigned short mem[MEMSIZE]; /* system's physical memory */ +int memlimit; /* user's desired memory limit (-mem) */ #define MAKEVA(seg,word) ((((int)(seg))<<16) | (word)) @@ -413,7 +426,7 @@ typedef struct { char access[4]; /* ring n access rights */ unsigned short procid; /* process id for segments >= '4000 */ unsigned short seg; /* segment number */ - unsigned int ppn; /* physical page number (15 bits = 64MB limit) */ + unsigned int ppn; /* physical page number */ unsigned short *pmep; /* pointer to page table flag word */ unsigned long load_ic; /* instruction where STLB was loaded (for debug) */ } stlbe_t; @@ -426,7 +439,7 @@ stlbe_t stlb[STLBENTS]; typedef struct { char valid; /* 1 if IOTLB entry is valid, zero otherwise */ - unsigned int ppn; /* physical page (16 bits = 128MB limit) */ + unsigned int ppn; /* physical page number */ } iotlbe_t; iotlbe_t iotlb[IOTLBENTS]; @@ -498,6 +511,7 @@ ea_t tnoua_ea=0, tnou_ea=0; int verbose; /* -v (not used anymore) */ int domemdump; /* -memdump arg */ int pmap32bits; /* true if 32-bit page maps */ +int pmap32mask; /* mask for 32-bit page maps */ int csoffset; /* concealed stack segment offset */ int tport; /* -tport option (incoming terminals) */ int nport; /* -nport option (PNC/Ringnet) */ @@ -657,8 +671,6 @@ char *searchloadmap(int addr, char type) { #define WACC 3 #define XACC 4 -void fault(unsigned short fvec, unsigned short fcode, ea_t faddr); - /* NOTE: this is the 6350 STLB hash function, giving a 9-bit index 0-511 */ @@ -720,7 +732,15 @@ pa_t mapva(ea_t ea, short intacc, unsigned short *access, ea_t rp) { if (pmap32bits) { pmaddr = ptaddr + 2*PAGENO(ea); pte = mem[pmaddr]; - ppn = mem[pmaddr+1]; + + /* this is probably correct (don't have any references) for + the 53xx and later machines that support more than 128MB of + physical memory, but it can't be used for earlier machines + like the 9950 or they can't run older software (rev 19 for + example). Need to have a mask for each CPU type to make it + technically correct. */ + + ppn = ((mem[pmaddr] & pmap32mask) << 16) | mem[pmaddr+1]; } else { pmaddr = ptaddr + PAGENO(ea); pte = mem[pmaddr]; @@ -763,11 +783,12 @@ pa_t mapva(ea_t ea, short intacc, unsigned short *access, ea_t rp) { pa = (stlbp->ppn << 10) | (ea & 0x3FF); TRACE(T_MAP," for ea %o/%o, stlbix=%d, pa=%o loaded at #%d\n", ea>>16, ea&0xffff, stlbix, pa, stlbp->load_ic); } else { + /* XXX: this test looks bogus and should be removed, but causes boot failures related to disk I/O if removed */ pa = ea & (MEMSIZE-1); } - if (pa <= MEMSIZE) + if (pa < memlimit) return pa; - printf(" map: Memory address %o (%o/%o) is out of range!\n", ea, ea>>16, ea & 0xffff); + printf(" map: Memory address '%o (%o/%o) is out of range 0-'%o!\n", ea, ea>>16, ea & 0xffff, memlimit-1); fatal(NULL); /* NOTE: could also take a missing memory check here... */ } @@ -994,10 +1015,52 @@ put64r(double value, ea_t ea, ea_t rpring) { } +/* I/O device map table, containing function pointers to handle device I/O */ + +int devpoll[64] = {0}; + +#include "emdev.h" + +#if 0 + +/* this is the "full system" controller configuration */ + +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 */ devnone,devnone,devnone,devnone,devasr,devnone,devnone,devpnc, +#if 1 + /* '1x */ devnone,devnone,devnone,devnone,devmt,devnone, devnone, devnone, +#else + /* '1x */ devnone,devnone,devnone,devnone,devnone,devnone, devnone, devnone, +#endif + /* '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 + + + fatal(char *msg) { ea_t pcbp, csea; unsigned short first,next,last,this; unsigned short cs[6]; + int i; printf("Fatal error: instruction #%d at %o/%o %s: %o %o\nowner=%o %s, keys=%o, modals=%o\n", instcount, prevpc >> 16, prevpc & 0xFFFF, searchloadmap(prevpc,' '), get16(prevpc), get16(prevpc+1), crs[OWNERL], searchloadmap(*(unsigned int *)(crs+OWNER),' '), crs[KEYS], crs[MODALS]); @@ -1021,7 +1084,13 @@ fatal(char *msg) { if (msg) printf("%s\n", msg); /* should do a register dump, RL dump, PCB dump, etc. here... */ - close(tracefile); + + /* call all devices with a request to terminate */ + + for (i=0; i<64; i++) + devmap[i](-2, 0, i); + + fclose(tracefile); exit(1); } @@ -1241,47 +1310,6 @@ void fault(unsigned short fvec, unsigned short fcode, ea_t faddr) { } -/* I/O device map table, containing function pointers to handle device I/O */ - -int devpoll[64] = {0}; - -#include "emdev.h" - -#if 0 - -/* this is the "full system" controller configuration */ - -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 */ devnone,devnone,devnone,devnone,devasr,devnone,devnone,devpnc, -#if 1 - /* '1x */ devnone,devnone,devnone,devnone,devmt,devnone, devnone, devnone, -#else - /* '1x */ devnone,devnone,devnone,devnone,devnone,devnone, devnone, devnone, -#endif - /* '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 - - - /* 16S Addressing Mode */ ea_t ea16s (unsigned short inst, short x) { @@ -1700,8 +1728,6 @@ ea_t stex(unsigned int extsize) { stackrootseg = get16((*(unsigned int *)(crs+SB))+1); stackrootp = MAKEVA(stackrootseg,0); stackfp = get32(stackrootp); - if (stackfp == 0) - fatal("stex: stack free pointer is zero"); /* find a stack segment where this extension will fit */ @@ -1956,7 +1982,7 @@ pcl (ea_t ecbea) { ea_t ea; ea_t rp; /* return pointer */ short stackrootseg; - short stacksize; + unsigned short stacksize; short store; /* true if store bit set on AP */ short storedargs; /* # of arguments that have been stored */ short lastarg; /* true if "last" bit seen in PCL arglist */ @@ -2831,18 +2857,6 @@ nfy(unsigned short inst) { } scount = scount-1; -#if 0 - /* NOTE: shouldn't have to do this if everything is working right, but with - PRIMOS we get: -CFatal error: instruction #81929908 at 6/54311: 315 4400 -WAIT: count == 1 but bol != 0 -keys = 14200, modals=137CFatal error: instruction #81929908 at 6/54311: 315 4400 -WAIT: count == 1 but bol != 0 -keys = 14200, modals=137 - */ - if (scount <= 0) - bol = 0; -#endif utempl = (scount<<16) | bol; put32r0(utempl, ea); /* update the semaphore */ @@ -3446,7 +3460,7 @@ int ldar(ea_t ea) { RESTRICT(); if ((ea & 0777) > 0477) { - printf("em: LDLR ea '%o is out of range\n", ea); + printf("em: LDLR ea '%o is out of range for this CPU model\n", ea); fatal(NULL); } ea &= 0777; @@ -3470,7 +3484,7 @@ star(unsigned int val32, ea_t ea) { if (ea & 040000) { /* absolute RF addressing */ RESTRICT(); if ((ea & 0777) > 0477) { - printf("em: STLR ea '%o is out of range; this -cpuid may not be supported by this version of software\n", ea); + printf("em: STLR ea '%o is out of range for this cpu model.\nThis -cpuid may not be supported by this version of software\nTry a lower -cpuid", ea); fatal(NULL); } regs.u32[ea & 0777] = val32; @@ -3499,9 +3513,7 @@ main (int argc, char **argv) { long long templl,templl1,templl2; unsigned long long utempll, utempll1, utempll2; unsigned int utempl,utempl1,utempl2,utempl3,utempl4; - float tempf,tempf1,tempf2; double tempd,tempd1,tempd2; - unsigned short tempda[4],tempda1[4]; ea_t tempea; ea_t ea; /* final MR effective address */ ea_t earp; /* RP to use for eff address calcs */ @@ -3515,7 +3527,7 @@ main (int argc, char **argv) { int nw,nw2; unsigned short rvec[9]; /* SA, EA, P, A, B, X, keys, dummy, dummy */ unsigned short inst; - unsigned short m,m1,m2; + unsigned short m,m2; unsigned short qtop,qbot,qseg,qmask,qtemp; ea_t qea; short scount; /* shift count */ @@ -3526,16 +3538,28 @@ main (int argc, char **argv) { unsigned long immu32; unsigned long long immu64; short fcode; - long long frac64; - int exp32; unsigned short zresult, zclen1, zclen2, zaccess; unsigned int zlen1, zlen2; ea_t zea1, zea2; unsigned char zch1, zch2, *zcp1, *zcp2, zspace; + unsigned char xsc, xfc, xsign, xsig; + + /* Prime ASCII constants for decimal instructions */ + +#define XPLUS 0253 +#define XMINUS 0255 +#define XZERO 0260 +#define XONE 0261 +#define XJ 0312 +#define XRBRACE 0375 struct timeval boot_tv; struct timezone tz; + /* ignore SIGPIPE signals (sockets) or they'll kill the emulator */ + + signal (SIGPIPE, SIG_IGN); + /* open trace log */ if ((tracefile=fopen("trace.log", "w")) == NULL) { @@ -3591,9 +3615,11 @@ main (int argc, char **argv) { bootarg = NULL; bootfile[0] = 0; pmap32bits = 0; + pmap32mask = 0; csoffset = 0; tport = 0; nport = 0; + memlimit = MEMSIZE; /* check args */ @@ -3616,21 +3642,33 @@ main (int argc, char **argv) { } else if (strcmp(argv[i],"-cpuid") == 0) { if (i+1 < argc && argv[i+1][0] != '-') { sscanf(argv[++i],"%d", &templ); - cpuid = templ; + if (0 <= templ && templ <= 44) + cpuid = templ; + else + fatal("-cpuid arg range is 0 to 44\n"); } else - fprintf(stderr,"-cpuid needs an argument\n"); + fatal("-cpuid needs an argument\n"); + } else if (strcmp(argv[i],"-mem") == 0) { + if (i+1 < argc && argv[i+1][0] != '-') { + sscanf(argv[++i],"%d", &templ); + if (1 <= templ && templ < 1024) + memlimit = templ*1024*1024/2; + else + fatal("-mem arg range is 1 to 1024 (megabytes)\n"); + } else + fatal("-mem needs an argument\n"); } else if (strcmp(argv[i],"-tport") == 0) { if (i+1 < argc && argv[i+1][0] != '-') { sscanf(argv[++i],"%d", &templ); tport = templ; } else - fprintf(stderr,"-tport needs an argument\n"); + fatal("-tport needs an argument\n"); } else if (strcmp(argv[i],"-nport") == 0) { if (i+1 < argc && argv[i+1][0] != '-') { sscanf(argv[++i],"%d", &templ); nport = templ; } else - fprintf(stderr,"-nport needs an argument\n"); + fatal("-nport needs an argument\n"); } else if (strcmp(argv[i],"-trace") == 0) while (i+1 < argc && argv[i+1][0] != '-') { i++; @@ -3704,8 +3742,8 @@ main (int argc, char **argv) { } } else { - fprintf(stderr,"Unrecognized argument: %s\n", argv[i]); printf("Unrecognized argument: %s\n", argv[i]); + fatal(NULL); } } @@ -3727,21 +3765,20 @@ main (int argc, char **argv) { break; } } - if (j == numsyms) + if (j == numsyms) { fprintf(stderr,"Can't find procedure %s in load maps for tracing.\n", traceprocs[i].name); + printf("Can't find procedure %s in load maps for tracing.\n", traceprocs[i].name); + } } /* set some vars after the options have been read */ pmap32bits = (cpuid == 15 || cpuid == 18 || cpuid == 19 || cpuid == 24 || cpuid >= 26); + if (cpuid == 33 || cpuid == 37 || cpuid == 39 || cpuid >= 43) + pmap32mask = 0x3; if ((26 <= cpuid && cpuid <= 29) || cpuid >= 35) csoffset = 1; - /* the emulator has occasionally exited to command level; try - ignoring SIGPIPE to see if that is the problem */ - - signal (SIGPIPE, SIG_IGN); - /* initialize all devices */ for (i=0; i<64; i++) @@ -4288,7 +4325,7 @@ stfa: case 001015: TRACE(T_FLOW, " TAK\n"); - newkeys(crs[A] & 0177774); + newkeys(crs[A] & 0177770); continue; case 000001: @@ -4384,7 +4421,22 @@ stfa: calf(ea); continue; - /* Decimal and character instructions */ + /* Decimal and character instructions + + IMPORTANT NOTE: when using the ZGETC and ZPUTC macros, + be sure to use curly braces, ie, + + Instead of: + + if (cond) + ZPUTC ... + + use: + + if (cond) { + ZPUTC ... + } + */ #define ZGETC(zea, zlen, zcp, zclen, zch) \ if (zclen == 0) { \ @@ -4417,7 +4469,7 @@ stfa: else \ zea = (zea & 0xEFFF0000) | ((zea+0x400) & 0xFC00); \ } \ - *zcp = zch; \ + *zcp = (zch); \ zcp++; \ zclen--; \ zlen-- @@ -4657,6 +4709,182 @@ stfa: break; } continue; + + case 001112: + + /* XED has some support for chars w/o parity by checking the + keys before setting the zero suppress character, but it's + not clear if it should ignore all character parity */ + + TRACE(T_FLOW, " XED\n"); + zlen1 = zlen2 = 128*1024; + zea1 = crsl[FAR0]; + if (crsl[FLR0] & 0x8000) + zea1 |= EXTMASK32; + zea2 = crsl[FAR1]; + if (crsl[FLR1] & 0x8000) + zea2 |= EXTMASK32; + zclen1 = 0; + zclen2 = 0; + TRACE(T_INST, " ea1=%o/%o, len1=%d, ea2=%o/%o, len2=%d\n", zea1>>16, zea1&0xffff, zlen1, zea2>>16, zea2&0xffff, zlen2); + if (crs[KEYS] & 020) + xsc = 040; + else + xsc = 0240; + xfc = 0; + ZGETC(zea1, zlen1, zcp1, zclen1, zch1); + //printf("xed: first char = '%o\n", zch1); + xsign = (zch1 == XMINUS); + xsig = 0; + ea = *(ea_t *)(crs+XB); + for (i=0; i < 32767; i++) { /* do edit pgms have a size limit? */ + utempa = get16(INCVA(ea, i)); + m = utempa & 0xFF; + //printf("\nxed: %d: opcode = %o, m=%o\n", i, (utempa>>8) & 037, m); + switch ((utempa >> 8) & 037) { + case 0: /* Zero Suppress */ + while (m) { + ZGETC(zea1, zlen1, zcp1, zclen1, zch1); + if (!xsig) + if (zch1 == XZERO) + zch1 = xsc; + else { + xsig = 1; + if (xfc) { + ZPUTC(zea2, zlen2, zcp2, zclen2, xfc); + } + } + ZPUTC(zea2, zlen2, zcp2, zclen2, zch1); + m--; + } + break; + + case 1: /* insert character M */ + ZPUTC(zea2, zlen2, zcp2, zclen2, m); + break; + + case 2: /* set supression character */ + xsc = m; + break; + + case 3: /* insert character */ + if (xsig) + zch1 = m; + else + zch1 = xsc; + ZPUTC(zea2, zlen2, zcp2, zclen2, zch1); + break; + + case 4: /* insert digits */ + if (!xsig && xfc) { + ZPUTC(zea2, zlen2, zcp2, zclen2, xfc); + } + while (m) { + ZGETC(zea1, zlen1, zcp1, zclen1, zch1); + ZPUTC(zea2, zlen2, zcp2, zclen2, zch1); + m--; + } + xsig = 1; + break; + + case 5: /* insert char if minus */ + if (xsign) + zch1 = m; + else + zch1 = xsc; + ZPUTC(zea2, zlen2, zcp2, zclen2, zch1); + break; + + case 6: /* insert char if plus */ + if (!xsign) + zch1 = m; + else + zch1 = xsc; + ZPUTC(zea2, zlen2, zcp2, zclen2, zch1); + break; + + case 7: /* set floating char */ + xfc = m; + break; + + case 010: /* set floating if plus */ + if (!xsign) + xfc = m; + else + xfc = xsc; + break; + + case 011: /* set floating if minus */ + if (xsign) + xfc = m; + else + xfc = xsc; + break; + + case 012: /* set floating to sign */ + if (xsign) + xfc = XMINUS; + else + xfc = XPLUS; + break; + + case 013: /* jump if zero */ + if (crs[A]) + i += m; + break; + + case 014: /* fill with suppress */ + while (m) { + ZPUTC(zea2, zlen2, zcp2, zclen2, xsc); + m--; + } + break; + + case 015: /* set significance */ + if (!xsig && xfc) { + ZPUTC(zea2, zlen2, zcp2, zclen2, xfc); + } + xsig = 1; + break; + + case 016: /* insert sign */ + if (xsign) + zch1 = XMINUS; + else + zch1 = XPLUS; + ZPUTC(zea2, zlen2, zcp2, zclen2, zch1); + break; + + case 017: /* suppress digits */ + while (m) { + ZGETC(zea1, zlen1, zcp1, zclen1, zch1); + if (zch1 == XZERO) + zch1 = xsc; + ZPUTC(zea2, zlen2, zcp2, zclen2, zch1); + m--; + } + break; + + case 020: /* embed sign */ + while (m) { + ZGETC(zea1, zlen1, zcp1, zclen1, zch1); + if (xsign) + if (zch1 == XZERO) + zch1 = XRBRACE; + else + zch1 = zch1-XONE+XJ; + ZPUTC(zea2, zlen2, zcp2, zclen2, zch1); + m--; + } + break; + + default: + warn("xed: unrecognized subprogram opcode ignored"); + } + if (utempa & 0x8000) + break; + } + continue; #endif /* 001100 = XAD* @@ -4666,7 +4894,7 @@ stfa: 001107 = XDV* 001110 = ZTRN 001111 = ZED - 001112 = XED* + 001112 = XED 001114 = ZMV 001115 = ZMVD 001116 = ZFIL @@ -4718,7 +4946,9 @@ stfa: case 001212: case 001213: - fatal("Unrecognized NFY instruction"); + warn("Unrecognized NFY instruction"); + fault(ILLINSTFAULT, RPL, RP); + continue; case 001315: TRACE(T_FLOW, " STEX\n"); @@ -5675,12 +5905,13 @@ a1a: case 0140534: TRACE(T_FLOW, " FRN\n"); + CLEARC; frn(crsl+FAC1); continue; case 0140574: TRACE(T_FLOW, " DFCM\n"); - dfcm(crs+FLTH); + dfcm(crsl+FAC1); continue; case 0141000: @@ -5690,7 +5921,7 @@ a1a: case 0140530: TRACE(T_FLOW, " FCM\n"); - dfcm(crs+FLTH); + dfcm(crsl+FAC1); continue; case 0140510: @@ -6565,6 +6796,7 @@ keys = 14200, modals=100177 case 0107: TRACE(T_FLOW, " FRN\n"); + CLEARC; frn(crsl+FAC0+dr); break; @@ -7185,27 +7417,21 @@ keys = 14200, modals=100177 case 6: dr &= 2; TRACE(T_INST, " FC\n"); - CLEARCC; - if (*(int *)&ea < 0) { - if (!prieee8(&immu64, &tempd2)) - mathexception('f', FC_SFP_OFLOW, ea); - } else - tempd2 = prieee4(get32(ea)); - if (prieee8(crsl+FAC0+dr, &tempd1)) - if (tempd1 == tempd2) - SETEQ; - else if (tempd1 < tempd2) - SETLT; - else - ; + if (*(int *)&ea < 0) + utempl = ((immu64 >> 32) & 0xffffff00) | (immu64 & 0xff); else - mathexception('f', FC_SFP_OFLOW, ea); + utempl = get32(ea); + fcs(crsl+FAC0+dr, utempl); break; case 5: case 7: dr &= 2; TRACE(T_INST, " DFC\n"); + if (*(int *)&ea >= 0) + *(double *)&immu64 = get64(ea); + dfcs(crsl+FAC0+dr, immu64); +#if 0 CLEARCC; if (*(int *)&ea < 0) tempd2 = *(double *)&immu64; @@ -7220,7 +7446,33 @@ keys = 14200, modals=100177 ; else mathexception('f', FC_DFP_OFLOW, ea); - break; +#endif +#if 0 + /* this is the "compare signs, exponents, fraction" method. + See similar code in V-mode DFCS */ + + CLEARCC; + if (*(int *)&ea < 0) + utempll = immu64; + else + *(double *)&utempll = get64(ea); + CLEARCC; + if ((crsl[FAC0+dr] & 0x80000000) == (*(unsigned int *)&utempll & 0x80000000)) { + m = utempll & 0xffff; /* m = operand exponent */ + m2 = crsl[FAC0+dr+1] & 0xffff; /* m2 = FAC exponent */ + if (m2 == m) + if (crsl[FAC0+dr] == *(unsigned int *)&utempll) + SETEQ; + else if (*(int *)(crsl+FAC0+dr) < *(int *)&utempll) + SETLT; + else + ; /* FAC > mem: next instruction */ + else if (*(short *)&m2 < *(short *)&m) + SETLT; + } else if (crsl[FAC0+dr] & 0x80000000) /* FAC < mem */ + SETLT; +#endif + break; default: warn("I-mode 006 switch?"); @@ -7304,14 +7556,15 @@ keys = 14200, modals=100177 case 2: dr &= 2; TRACE(T_INST, " FST\n"); - if (*(int *)&ea >= 0) - if ((crsl[FAC0+dr+1] & 0xFF00) == 0) { - /* XXX: round here if enabled in keys */ + CLEARC; + if (*(int *)&ea >= 0) { + if (crs[KEYS] & 010) + frn(crsl+FAC0+dr); + if ((crsl[FAC0+dr+1] & 0xFF00) == 0) put32((crsl[FAC0+dr] & 0xFFFFFF00) | (crsl[FAC0+dr+1] & 0xFF), ea); - CLEARC; - } else + else mathexception('f', FC_SFP_STORE, ea); - else { + } else { warn("I-mode immediate FST?"); fault(ILLINSTFAULT, RPL, RP); } @@ -7333,31 +7586,47 @@ keys = 14200, modals=100177 case 6: dr &= 2; TRACE(T_INST, " FA\n"); - if (*(int *)&ea < 0) { - if (!prieee8(&immu64, &tempd2)) - mathexception('f', FC_SFP_OFLOW, ea); + CLEARC; + if (*(int *)&ea >= 0) { + immu64 = get32(ea); + immu64 = ((immu64 << 32) & 0xffffff0000000000LL) | (immu64 & 0xff); + } + if (*(int *)&immu64) + if (*(int *)(crsl+FAC0+dr)) { + tempa1 = crsl[FAC0+dr+1] & 0xffff; + tempa2 = immu64 & 0xffff; + if (abs(tempa1-tempa2) < 48) + if (prieee8(crsl+FAC0+dr, &tempd1) && prieee8(&immu64, &tempd2)) { + *(double *)(crsl+FAC0+dr) = ieeepr8(tempd1+tempd2); + XCLEARC; /* XXX: test overflow */ + } else + mathexception('f', FC_SFP_OFLOW, ea); + else if (tempa1 < tempa2) + *(double *)(crsl+FAC0+dr) = *(double *)&immu64; } else - tempd2 = prieee4(get32(ea)); - if (prieee8(crsl+FAC0+dr, &tempd1)) { - *(double *)(crsl+FAC0+dr) = ieeepr8(tempd1+tempd2); - XCLEARC; - } else - mathexception('f', FC_DFP_OFLOW, ea); + *(double *)(crsl+FAC0+dr) = *(double *)&immu64; + else if (*(int *)(crsl+FAC0+dr) == 0) + *(double *)(crsl+FAC0+dr) = 0.0; break; case 5: case 7: dr &= 2; TRACE(T_INST, " DFA\n"); - if (*(int *)&ea < 0) - tempd2 = *(double *)&immu64; - else - tempd2 = get64(ea); - if (prieee8(crsl+FAC0+dr, &tempd1) && prieee8(&tempd2, &tempd2)) { - *(double *)(crsl+FAC0+dr) = ieeepr8(tempd1+tempd2); - XCLEARC; - } else - mathexception('f', FC_DFP_OFLOW, ea); + CLEARC; + if (*(int *)&ea >= 0) + *(double *)&immu64 = get64(ea); + if (*(int *)&immu64) + if (*(int *)(crsl+FAC0+dr)) + if (prieee8(crsl+FAC0+dr, &tempd1) && prieee8(&immu64, &tempd2)) { + *(double *)(crsl+FAC0+dr) = ieeepr8(tempd1+tempd2); + XCLEARC; /* XXX: test overflow */ + } else + mathexception('f', FC_DFP_OFLOW, ea); + else + *(double *)(crsl+FAC0+dr) = *(double *)&immu64; + else if (*(int *)(crsl+FAC0+dr) == 0) + *(double *)(crsl+FAC0+dr) = 0.0; break; default: @@ -7434,62 +7703,95 @@ keys = 14200, modals=100177 case 2: dr &= 2; TRACE(T_INST, " FS\n"); - if (*(int *)&ea < 0) { - if (!prieee8(&immu64, &tempd2)) - mathexception('f', FC_SFP_OFLOW, ea); - } else - tempd2 = prieee4(get32(ea)); - if (prieee8(crsl+FAC0+dr, &tempd1)) { - *(double *)(crsl+FAC0+dr) = ieeepr8(tempd1-tempd2); - XCLEARC; - } else - mathexception('f', FC_SFP_OFLOW, ea); + CLEARC; + if (*(int *)&ea >= 0) { + immu64 = get32(ea); + immu64 = ((immu64 << 32) & 0xffffff0000000000LL) | (immu64 & 0xff); + } + if (*(int *)&immu64) + if (*(int *)(crsl+FAC0+dr)) { + tempa1 = crsl[FAC0+dr+1] & 0xffff; + tempa2 = immu64 & 0xffff; + if (abs(tempa1-tempa2) < 48) + if (prieee8(crsl+FAC0+dr, &tempd1) && prieee8(&immu64, &tempd2)) { + *(double *)(crsl+FAC0+dr) = ieeepr8(tempd1-tempd2); + XCLEARC; /* XXX: test overflow */ + } else + mathexception('f', FC_SFP_OFLOW, ea); + else if (tempa1 < tempa2) { + *(double *)(crsl+FAC0+dr) = *(double *)&immu64; + dfcm(crsl+FAC0+dr); + } + } else { + *(double *)(crsl+FAC0+dr) = *(double *)&immu64; + dfcm(crsl+FAC0+dr); + } + else if (*(int *)(crsl+FAC0+dr) == 0) + *(double *)(crsl+FAC0+dr) = 0.0; break; case 1: case 3: dr &= 2; TRACE(T_INST, " DFS\n"); - if (*(int *)&ea < 0) - tempd2 = *(double *)&immu64; - else - tempd2 = get64(ea); - if (prieee8(crsl+FAC0+dr, &tempd1) && prieee8(&tempd2, &tempd2)) { - *(double *)(crsl+FAC0+dr) = ieeepr8(tempd1-tempd2); - XCLEARC; - } else - mathexception('f', FC_DFP_OFLOW, ea); + CLEARC; + if (*(int *)&ea >= 0) + *(double *)&immu64 = get64(ea); + if (*(int *)&immu64) + if (*(int *)(crsl+FAC0+dr)) + if (prieee8(crsl+FAC0+dr, &tempd1) && prieee8(&immu64, &tempd2)) { + *(double *)(crsl+FAC0+dr) = ieeepr8(tempd1-tempd2); + XCLEARC; /* XXX: test overflow */ + } else + mathexception('f', FC_DFP_OFLOW, ea); + else { + *(double *)(crsl+FAC0+dr) = *(double *)&immu64; + dfcm(crsl+FAC0+dr); + } + else if (*(int *)(crsl+FAC0+dr) == 0) + *(double *)(crsl+FAC0+dr) = 0.0; break; case 4: case 6: dr &= 2; TRACE(T_INST, " FM\n"); - if (*(int *)&ea < 0) { - if (!prieee8(&immu64, &tempd2)) - mathexception('f', FC_SFP_OFLOW, ea); - } else - tempd2 = prieee4(get32(ea)); - if (prieee8(crsl+FAC0+dr, &tempd1)) { - *(double *)(crsl+FAC0+dr) = ieeepr8(tempd1*tempd2); - XCLEARC; - } else - mathexception('f', FC_SFP_OFLOW, ea); + CLEARC; + if (*(int *)(crsl+FAC0+dr)) { + if (*(int *)&ea >= 0) { + immu64 = get32(ea); + immu64 = ((immu64 << 32) & 0xffffff0000000000LL) | (immu64 & 0xff); + } + if (*(int *)&immu64) + if (prieee8(&immu64, &tempd2) && prieee8(crsl+FAC0+dr, &tempd1)) { + *(double *)(crsl+FAC0+dr) = ieeepr8(tempd1*tempd2); + XCLEARC; /* XXX: test overflow */ + } else + mathexception('f', FC_SFP_OFLOW, ea); + else /* operand = 0.0: no multiply */ + *(double *)(crsl+FAC0+dr) = 0.0; + } else /* clean up (maybe) dirty zero */ + *(double *)(crsl+FAC0+dr) = 0.0; break; case 5: case 7: dr &= 2; TRACE(T_INST, " DFM\n"); - if (*(int *)&ea < 0) - tempd2 = *(double *)&immu64; - else - tempd2 = get64(ea); - if (prieee8(crsl+FAC0+dr, &tempd1) && prieee8(&tempd2, &tempd2)) { - *(double *)(crsl+FAC0+dr) = ieeepr8(tempd1*tempd2); - XCLEARC; + CLEARC; + if (*(int *)(crsl+FAC0+dr)) { + if (*(int *)&ea >= 0) + *(double *)&immu64 = get64(ea); + if (*(int *)&immu64) + if (prieee8(&immu64, &tempd2) && prieee8(crsl+FAC0+dr, &tempd1)) { + *(double *)(crsl+FAC0+dr) = ieeepr8(tempd1*tempd2); + XCLEARC; /* XXX: test overflow */ + } else + mathexception('f', FC_DFP_OFLOW, ea); + else /* operand = 0.0: no multiply */ + *(double *)(crsl+FAC0+dr) = 0.0; } else - mathexception('f', FC_DFP_OFLOW, ea); + *(double *)(crsl+FAC0+dr) = 0.0; break; default: @@ -7551,37 +7853,42 @@ keys = 14200, modals=100177 case 2: dr &= 2; TRACE(T_INST, " FD\n"); - if (*(int *)&ea < 0) { - if (!prieee8(&immu64, &tempd2)) - mathexception('f', FC_SFP_OFLOW, ea); - } else - tempd2 = prieee4(get32(ea)); - if (tempd2 != 0.0) - if (prieee8(crsl+FAC0+dr, &tempd1)) { - *(double *)(crsl+FAC0+dr) = ieeepr8(tempd1/tempd2); - XCLEARC; - } else - mathexception('f', FC_SFP_OFLOW, ea); - else - mathexception('f', FC_SFP_ZDIV, ea); + CLEARC; + if (*(int *)(crsl+FAC0+dr)) { + if (*(int *)&ea >= 0) { + immu64 = get32(ea); + immu64 = ((immu64 << 32) & 0xffffff0000000000LL) | (immu64 & 0xff); + } + if (*(int *)&immu64) + if (prieee8(&immu64, &tempd2) && prieee8(crsl+FAC0+dr, &tempd1)) { + *(double *)(crsl+FAC0+dr) = ieeepr8(tempd1/tempd2); + XCLEARC; /* XXX: test overflow */ + } else + mathexception('f', FC_SFP_OFLOW, ea); + else /* operand = 0.0 */ + mathexception('f', FC_SFP_ZDIV, ea); + } else /* clean up (maybe) dirty zero */ + *(double *)(crsl+FAC0+dr) = 0.0; break; case 1: case 3: dr &= 2; TRACE(T_INST, " DFD\n"); - if (*(int *)&ea < 0) - tempd2 = *(double *)&immu64; - else - tempd2 = get64(ea); - if (prieee8(crsl+FAC0+dr, &tempd1) && prieee8(&tempd2, &tempd2)) - if (tempd2 != 0.0) { - *(double *)(crsl+FAC0+dr) = ieeepr8(tempd1/tempd2); - XCLEARC; - } else - mathexception('f', FC_DFP_OFLOW, ea); - else - mathexception('f', FC_DFP_ZDIV, ea); + CLEARC; + if (*(int *)(crsl+FAC0+dr)) { + if (*(int *)&ea >= 0) + *(double *)&immu64 = get64(ea); + if (*(int *)&immu64) + if (prieee8(&immu64, &tempd2) && prieee8(crsl+FAC0+dr, &tempd1)) { + *(double *)(crsl+FAC0+dr) = ieeepr8(tempd1/tempd2); + XCLEARC; /* XXX: test overflow */ + } else + mathexception('f', FC_DFP_OFLOW, ea); + else + mathexception('f', FC_DFP_ZDIV, ea); + } else + *(double *)(crsl+FAC0+dr) = 0.0; break; case 4: /* QFLD */ @@ -7901,13 +8208,11 @@ imodepcl: continue; case 065: - /* XXX: DIAG test CPU.AMGRR for cpuid=26 indicates IP needs to - be weakened */ TRACE(T_FLOW, " LIP\n"); utempl = get32(ea); if (utempl & 0x80000000) fault(POINTERFAULT, utempl>>16, ea); - crsl[dr] = utempl; + crsl[dr] = utempl | (RP & RINGMASK32); /* CPU.AMGRR, cpuid=26+ */ continue; case 066: /* I-mode special MR: DM, JSXB */ @@ -8551,43 +8856,51 @@ nonimode: case 00601: TRACE(T_FLOW, " FAD\n"); - tempd2 = prieee4(get32(ea)); - if (prieee8(crs+FLTH, &tempd1)) { - *(double *)(crs+FLTH) = ieeepr8(tempd1+tempd2); - XCLEARC; - } else - mathexception('f', FC_SFP_OFLOW, ea); + CLEARC; + immu64 = get32(ea); + immu64 = ((immu64 << 32) & 0xffffff0000000000LL) | (immu64 & 0xff); + if (*(int *)&immu64) + if (*(int *)(crsl+FAC1)) { + tempa1 = crs[FEXP]; + tempa2 = immu64 & 0xffff; + if (abs(tempa1-tempa2) < 48) + if (prieee8(crsl+FAC1, &tempd1) && prieee8(&immu64, &tempd2)) { + *(double *)(crsl+FAC1) = ieeepr8(tempd1+tempd2); + XCLEARC; /* XXX: test overflow */ + } else + mathexception('f', FC_SFP_OFLOW, ea); + else if (tempa1 < tempa2) + *(double *)(crsl+FAC1) = *(double *)&immu64; + } else + *(double *)(crsl+FAC1) = *(double *)&immu64; + else if (*(int *)(crsl+FAC1) == 0) + *(double *)(crsl+FAC1) = 0.0; continue; /* this is implemented as a subtract on some models */ case 01101: TRACE(T_FLOW, " FCS\n"); - CLEARCC; - tempd2 = prieee4(get32(ea)); - if (prieee8(crs+FLTH, &tempd1)) { - if (tempd1 == tempd2) { - RPL++; - SETEQ; - } else if (tempd1 < tempd2) { - RPL += 2; - SETLT; - } - } else - mathexception('f', FC_SFP_OFLOW, ea); + templ = get32(ea); + RPL += fcs(crsl+FAC1, templ); continue; case 01701: TRACE(T_FLOW, " FDV\n"); - tempd2 = prieee4(get32(ea)); - if (prieee8(crs+FLTH, &tempd1)) - if (tempd2 != 0.0) { - *(double *)(crs+FLTH) = ieeepr8(tempd1/tempd2); - XCLEARC; - } else + CLEARC; + if (*(int *)(crsl+FAC1)) { + immu64 = get32(ea); + immu64 = ((immu64 << 32) & 0xffffff0000000000LL) | (immu64 & 0xff); + if (*(int *)&immu64) + if (prieee8(&immu64, &tempd2) && prieee8(crsl+FAC1, &tempd1)) { + *(double *)(crsl+FAC1) = ieeepr8(tempd1/tempd2); + XCLEARC; /* XXX: test overflow */ + } else + mathexception('f', FC_SFP_OFLOW, ea); + else /* operand = 0.0 */ mathexception('f', FC_SFP_ZDIV, ea); - else - mathexception('f', FC_SFP_OFLOW, ea); + } else /* clean up (maybe) dirty zero */ + *(double *)(crsl+FAC1) = 0.0; continue; case 0201: @@ -8599,48 +8912,88 @@ nonimode: case 01601: TRACE(T_FLOW, " FMP\n"); - tempd2 = prieee4(get32(ea)); - if (prieee8(crs+FLTH, &tempd1)) { - *(double *)(crs+FLTH) = ieeepr8(tempd1*tempd2); - XCLEARC; - } else - mathexception('f', FC_SFP_OFLOW, ea); + CLEARC; + if (*(int *)(crsl+FAC1)) { + immu64 = get32(ea); + immu64 = ((immu64 << 32) & 0xffffff0000000000LL) | (immu64 & 0xff); + if (*(int *)&immu64) + if (prieee8(&immu64, &tempd2) && prieee8(crsl+FAC1, &tempd1)) { + *(double *)(crsl+FAC1) = ieeepr8(tempd1*tempd2); + XCLEARC; /* XXX: test overflow */ + } else + mathexception('f', FC_SFP_OFLOW, ea); + else /* operand = 0.0: no multiply */ + *(double *)(crsl+FAC1) = 0.0; + } else /* clean up (maybe) dirty zero */ + *(double *)(crsl+FAC1) = 0.0; continue; case 00701: TRACE(T_FLOW, " FSB\n"); - tempd2 = prieee4(get32(ea)); - if (prieee8(crs+FLTH, &tempd1)) { - *(double *)(crs+FLTH) = ieeepr8(tempd1-tempd2); - XCLEARC; - } else - mathexception('f', FC_SFP_OFLOW, ea); + CLEARC; + immu64 = get32(ea); + immu64 = ((immu64 << 32) & 0xffffff0000000000LL) | (immu64 & 0xff); + if (*(int *)&immu64) + if (*(int *)(crsl+FAC1)) { + tempa1 = crs[FEXP]; + tempa2 = immu64 & 0xffff; + if (abs(tempa1-tempa2) < 48) + if (prieee8(crsl+FAC1, &tempd1) && prieee8(&immu64, &tempd2)) { + *(double *)(crsl+FAC1) = ieeepr8(tempd1-tempd2); + XCLEARC; /* XXX: test overflow */ + } else + mathexception('f', FC_SFP_OFLOW, ea); + else if (tempa1 < tempa2) { + *(double *)(crsl+FAC1) = *(double *)&immu64; + dfcm(crsl+FAC1); + } + } else { + *(double *)(crsl+FAC1) = *(double *)&immu64; + dfcm(crsl+FAC1); + } + else if (*(int *)(crsl+FAC1) == 0) + *(double *)(crsl+FAC1) = 0.0; continue; case 0401: TRACE(T_FLOW, " FST\n"); - if ((crsl[FAC1+1] & 0xFF00) == 0) { - /* XXX: round here if enabled in keys */ + CLEARC; + if (crs[KEYS] & 010) + frn(crsl+FAC1); + if ((crsl[FAC1+1] & 0xFF00) == 0) put32((crsl[FAC1] & 0xFFFFFF00) | (crsl[FAC1+1] & 0xFF), ea); - CLEARC; - } else + else mathexception('f', FC_SFP_STORE, ea); continue; case 0602: TRACE(T_FLOW, " DFAD\n"); - XCLEARC; - tempd2 = get64(ea); - if (prieee8(crs+FLTH, &tempd1) && prieee8(&tempd2, &tempd2)) - *(double *)(crs+FLTH) = ieeepr8(tempd1+tempd2); - else - mathexception('f', FC_DFP_OFLOW, ea); + CLEARC; + *(double *)&immu64 = get64(ea); + if (*(int *)&immu64) + if (*(int *)(crsl+FAC1)) + if (prieee8(crsl+FAC1, &tempd1) && prieee8(&immu64, &tempd2)) { + *(double *)(crsl+FAC1) = ieeepr8(tempd1+tempd2); + XCLEARC; /* XXX: test overflow */ + } else + mathexception('f', FC_DFP_OFLOW, ea); + else + *(double *)(crsl+FAC1) = *(double *)&immu64; + else if (*(int *)(crsl+FAC1) == 0) + *(double *)(crsl+FAC1) = 0.0; continue; case 01102: TRACE(T_FLOW, " DFCS\n"); - CLEARCC; -#if 1 + *(double *)&templl = get64(ea); + RPL += dfcs(crsl+FAC1, templl); +#if 0 + /* the subtract method for DFCS doesn't work so well. Some + Prime software (DSM for example) used DFCS to compare 8-byte + ASCII strings for equal or not equal. These strings will not + convert to IEEE floating point and comparisons tend to + fail. */ + tempd2 = get64(ea); if (prieee8(crs+FLTH, &tempd1) && prieee8(&tempd2, &tempd2)) { if (tempd1 == tempd2) { @@ -8652,38 +9005,44 @@ nonimode: } } else mathexception('f', FC_DFP_OFLOW, ea); -#else - m = get16(ea); - if ((crs[FLTH] & 0x8000) == (m & 0x8000)) { - m1 = get16(INCVA(ea,3)); - if (m1 == crs[FEXP]) { - if (m == crs[FLTH]) { - utempl = get32(INCVA(ea,1)); - if ((unsigned int)((crs[FLTL]<<16) | crs[FLTD]) == utempl) - RPL += 1; - /* XXX: does this next test need to be reversed for negative numbers? */ - else if ((unsigned int)((crs[FLTL]<<16) | crs[FLTD]) < utempl) - RPL += 2; - } else if (crs[FLTH] < m) +#endif +#if 0 + /* this is the "compare signs, exponents, fraction" method. + See similar code in I-mode DFC */ + + utempl = get32(ea); + if ((crsl[FAC1] & 0x80000000) == (utempl & 0x80000000)) { + m = get16(INCVA(ea,3)); /* m = FAC exponent */ + if (crs[FEXP] == m) + if (crsl[FAC1] == utempl) + RPL += 1; + else if (*(int *)(crsl+FAC1) < *(int *)&utempl) RPL += 2; - } else if (crs[FEXP] < m1) /* this line breaks CPU.FLOAT.V */ + else + ; /* FAC > mem: next instruction */ + else if (*(short *)(crs+FEXP) < *(short *)&m) RPL += 2; - } else if (crs[FLTH] & 0x8000) /* DAC < mem */ + } else if (crsl[FAC1] & 0x80000000) /* FAC < mem */ RPL += 2; #endif continue; case 01702: TRACE(T_FLOW, " DFDV\n"); - tempd2 = get64(ea); - if (prieee8(crs+FLTH, &tempd1) && prieee8(&tempd2, &tempd2)) - if (tempd2 != 0.0) { - *(double *)(crs+FLTH) = ieeepr8(tempd1/tempd2); - XCLEARC; - } else + CLEARC; + if (*(int *)(crsl+FAC1)) { + if (*(int *)&ea >= 0) + *(double *)&immu64 = get64(ea); + if (*(int *)&immu64) + if (prieee8(&immu64, &tempd2) && prieee8(crsl+FAC1, &tempd1)) { + *(double *)(crsl+FAC1) = ieeepr8(tempd1/tempd2); + XCLEARC; /* XXX: test overflow */ + } else + mathexception('f', FC_DFP_OFLOW, ea); + else mathexception('f', FC_DFP_ZDIV, ea); - else - mathexception('f', FC_DFP_OFLOW, ea); + } else + *(double *)(crsl+FAC1) = 0.0; continue; case 0202: @@ -8693,22 +9052,38 @@ nonimode: case 01602: TRACE(T_FLOW, " DFMP\n"); - tempd2 = get64(ea); - if (prieee8(crs+FLTH, &tempd1) && prieee8(&tempd2, &tempd2)) { - *(double *)(crs+FLTH) = ieeepr8(tempd1*tempd2); - XCLEARC; + CLEARC; + if (*(int *)(crsl+FAC1)) { + *(double *)&immu64 = get64(ea); + if (*(int *)&immu64) + if (prieee8(&immu64, &tempd2) && prieee8(crsl+FAC1, &tempd1)) { + *(double *)(crsl+FAC1) = ieeepr8(tempd1*tempd2); + XCLEARC; /* XXX: test overflow */ + } else + mathexception('f', FC_DFP_OFLOW, ea); + else /* operand = 0.0: no multiply */ + *(double *)(crsl+FAC1) = 0.0; } else - mathexception('f', FC_DFP_OFLOW, ea); + *(double *)(crsl+FAC1) = 0.0; continue; case 0702: TRACE(T_FLOW, " DFSB\n"); - tempd2 = get64(ea); - if (prieee8(crs+FLTH, &tempd1) && prieee8(&tempd2, &tempd2)) { - *(double *)(crs+FLTH) = ieeepr8(tempd1-tempd2); - XCLEARC; - } else - mathexception('f', FC_DFP_OFLOW, ea); + CLEARC; + *(double *)&immu64 = get64(ea); + if (*(int *)&immu64) + if (*(int *)(crsl+FAC1)) + if (prieee8(crsl+FAC1, &tempd1) && prieee8(&immu64, &tempd2)) { + *(double *)(crsl+FAC1) = ieeepr8(tempd1-tempd2); + XCLEARC; /* XXX: test overflow */ + } else + mathexception('f', FC_DFP_OFLOW, ea); + else { + *(double *)(crsl+FAC1) = *(double *)&immu64; + dfcm(crsl+FAC1); + } + else if (*(int *)(crsl+FAC1) == 0) + *(double *)(crsl+FAC1) = 0.0; continue; case 0402: @@ -8758,7 +9133,8 @@ nonimode: continue; default: - printf("Unknown memory reference opcode: %o\n", opcode); + printf("em: unknown memory reference opcode: %o\n", opcode); + fault(UIIFAULT, RPL, RP); fatal(NULL); } } diff --git a/emdev.h b/emdev.h index 8e1e648..08850a4 100644 --- a/emdev.h +++ b/emdev.h @@ -255,11 +255,12 @@ int devnone (int class, int func, int device) { int devasr (int class, int func, int device) { + static initialized = 0; static FILE *conslog; static int ttydev; static int ttyflags; static int needflush; /* true if data has been written but not flushed */ - static struct termios terminfo; + static struct termios origterminfo, terminfo; static fd_set fds; static short vcptime[8] = {7*0, 1}; static short vcptimeix; @@ -276,7 +277,14 @@ int devasr (int class, int func, int device) { switch (class) { - case -1: + case -2: /* cleanup */ + if (!initialized) return; + if (tcsetattr(ttydev, TCSANOW, &origterminfo) == -1) + perror(" unable to reset tty attributes"); + fclose(conslog); + break; + + case -1: /* initialize */ setsid(); ttydev = open("/dev/tty", O_RDWR, 0); if (ttydev < 0) { @@ -292,6 +300,10 @@ int devasr (int class, int func, int device) { perror(" unable to get tty attributes"); fatal(NULL); } + + /* save initial terminal setup to restore when exiting */ + + origterminfo = terminfo; /* NOTE: some of these are not restored by the host OS after the emulator is suspended (VSUSP) then restarted, eg, the VSUSP and @@ -322,10 +334,22 @@ int devasr (int class, int func, int device) { fatal(NULL); } setvbuf(conslog, NULL, _IOLBF, 0); + initialized = 1; return 0; case 0: TRACE(T_INST, " OCP '%02o%02o\n", func, device); + if (func == 010) /* enable full duplex */ + terminfo.c_lflag &= ~ECHO; + else if (func == 012) /* enable "echoplex" */ + terminfo.c_lflag |= ECHO; + if (func == 010 | func == 012) { + if (tcsetattr(ttydev, TCSANOW, &terminfo) == -1) { + perror(" unable to set tty attributes"); + fatal(NULL); + } + } else + TRACEA("devasr: unrecognized OCP '%02o%02o\n", func, device); break; case 1: @@ -352,8 +376,8 @@ int devasr (int class, int func, int device) { timeout.tv_sec = 0; /* yes, can't delay */ else { timeout.tv_sec = 1; /* single user: okay to delay */ -#if 1 - fflush(tracefile); /* hack to flush for 32i testing */ +#if 0 + fflush(tracefile); /* hack to flush for testing */ #endif } timeout.tv_usec = 0; @@ -413,6 +437,7 @@ readasr: } } else if (n == 1) { if (ch == '') { + printf("Trace owner = %o/%o\n", crs[OWNER], crs[OWNERL]); if (savetraceflags == 0) { TRACEA("\nTRACE ENABLED:\n\n"); savetraceflags = ~TB_MAP; @@ -625,21 +650,24 @@ int mtread (int fd, unsigned short *iobuf, int nw, int fw, int *mtstat) { n = read(fd, buf, 4); TRACE(T_TIO, " mtread read foward, %d bytes for reclen\n", n); if (n == 0) { /* now we're at EOT */ - *mtstat |= 0x20; + if (*mtstat & 0x8) /* were we at BOT? */ + *mtstat |= 0x200; /* yes, return error */ + else + *mtstat |= 0x20; /* no, return EOT */ return 0; } *mtstat &= ~8; /* not at BOT now */ readerr: if (n == -1) { perror("Error reading from tape file"); - *mtstat = 0; /* take drive offline */ + *mtstat |= 0x200; /* raw error */ return 0; } if (n < 4) { fprintf(stderr," only read %d bytes for reclen\n", n); fmterr: warn("Tape file isn't in .TAP format"); - *mtstat = 0; + *mtstat |= 0x200; /* raw error */ return 0; } reclen = buf[0] | (buf[1]<<8) | (buf[2]<<16) | (buf[3]<<24); @@ -650,11 +678,11 @@ fmterr: } if (reclen == 0xFFFF) { /* hit EOT mark */ - /* NOTE: simh says to backup here, probably to wipe out EOT if - more data is written. IMO, EOT should never be written to - simulated tape files. */ + /* NOTE: simh .tap doc says to backup here, probably to wipe out + EOT if more data is written. IMO, EOT should never be written + to simulated tape files. */ - if (lseek(fd, -4, SEEK_CUR) == -1) { + if ((n=lseek(fd, -4, SEEK_CUR)) == -1) { perror("em: unable to backspace over EOT"); goto readerr; } @@ -662,7 +690,7 @@ fmterr: return 0; } if (reclen & 0x8000) { /* record marked in error */ - /* NOTE: simh may have non-zero record length here... */ + /* NOTE: .tap may have non-zero record length here... */ *mtstat |= 0xB600; /* set all error bits */; return 0; } @@ -695,7 +723,7 @@ fmterr: if (n == -1) goto readerr; if (n != bytestoread) goto fmterr; if (bytestoread != reclen) { /* skip the rest of the record */ - if (lseek(fd, reclen-bytestoread, SEEK_CUR) == -1) { + if ((n=lseek(fd, reclen-bytestoread, SEEK_CUR)) == -1) { fprintf(stderr,"em: unable to handle large record\n"); goto readerr; } @@ -718,7 +746,7 @@ fmterr: /* spacing backward, see if we're at BOT */ if ((*mtstat & 8) || (lseek(fd, 0, SEEK_CUR) == 0)) { - *mtstat |= 8; /* yep, at BOT */ + *mtstat = (*mtstat | 8) & ~0x20;; /* at BOT, not EOT */ return 0; } @@ -782,10 +810,11 @@ int devmt (int class, int func, int device) { static unsigned short mtvec = 0114; /* interrupt vector */ static unsigned short dmxchan = 0; /* dmx channel number */ - static unsigned short datareg = 0; /* data holding register */ - static unsigned short ready = 0; /* true if datareg valid */ + static unsigned short datareg = 0; /* INA 00 data register */ + static unsigned short ready = 0; /* true if INA 00 ready */ + static unsigned short busy = 0; /* true if MPC busy */ static unsigned short enabled = 0; /* interrupts enabled */ - static unsigned short interrupting = 0; /* true if interrupt pending */ + static unsigned short interrupting = 0; /* 1 if pending, 2 if active */ static unsigned short usel = 0; /* last unit selected */ static struct { int fd; /* tape file descriptor */ @@ -796,8 +825,8 @@ int devmt (int class, int func, int device) { int u; char devfile[8]; - /* the largest rec size Primos ever supported is 16K bytes, plus 8 - bytes for the 4-byte .TAP format record length at the beginning & + /* the largest rec size Primos ever supported is 8K halfwords, plus + 4 words for the 4-byte .TAP format record length at the beginning & end of each record */ #define MAXTAPEWORDS 8*1024 @@ -805,13 +834,13 @@ int devmt (int class, int func, int device) { unsigned short iobuf[MAXTAPEWORDS+4]; /* 16-bit WORDS! */ unsigned short *iobufp; unsigned short dmxreg; /* DMA/C register address */ - unsigned short tempdmxchan; /* temp to incr during xfer */ short dmxnch; /* number of DMX channels - 1 */ - unsigned short dmxaddr; + unsigned int dmxaddr; unsigned long dmcpair; short dmxnw, dmxtotnw; int i,n; char reclen[4]; + unsigned short ioword; switch (class) { @@ -830,23 +859,24 @@ int devmt (int class, int func, int device) { ; } else if (func == 014) { /* ack interrupt */ - /* this is a hack because Primos acks interrupts immediately after - OTA 01 (due to a tape controller bug) */ - if (interrupting) - interrupting--; + interrupting = 0; } else if (func == 015) { /* set interrupt mask */ enabled = 1; - + if (interrupting == 1) /* if interrupt is pending */ + devpoll[device] = 10; /* try to interrupt soon */ + } else if (func == 016) { /* reset interrupt mask */ enabled = 0; } else if (func == 017) { /* initialize */ - mtvec = 014; + mtvec = 0114; dmxchan = 0; datareg = 0; interrupting = 0; + enabled = 0; ready = 0; + busy = 1; usel = 0; } else { @@ -861,9 +891,12 @@ int devmt (int class, int func, int device) { if (ready) IOSKIP; } else if (func == 01) { /* skip if not busy */ - IOSKIP; + if (busy) /* return busy once after init */ + busy = 0; + else + IOSKIP; } else if (func == 04) { /* skip if not interrupting */ - if (!interrupting) + if (interrupting != 2) IOSKIP; } else { printf("Unimplemented SKS device '%02o function '%02o\n", device, func); @@ -872,13 +905,22 @@ int devmt (int class, int func, int device) { break; case 2: + + /* according to version 0 controller docs, INA '014 should only + respond ready one time after an OTA '0214, and should respond + not ready otherwise (ie, the INA doesn't skip). But, this + causes Primos to lock up, spinning in an INA '014 loop, so the + emulator returns 0 on subsequent INA's */ + TRACE(T_INST|T_TIO, " INA '%02o%02o\n", func, device); if (func == 0) { -#if 0 - if (!ready) warn("INA 00 on tape device w/o matching OTA!"); -#endif - crs[A] = datareg; - datareg = 0; + if (!ready) { + TRACE(T_INST|T_TIO, "INA 0 on tape device w/o matching OTA!\n"); + crs[A] = 0; + } else { + TRACE(T_INST|T_TIO, " INA 0 returns '%06o 0x%04x\n", datareg, datareg); + crs[A] = datareg; + } ready = 0; IOSKIP; @@ -891,14 +933,14 @@ int devmt (int class, int func, int device) { case 3: TRACE(T_INST|T_TIO, " OTA '%02o%02o, A='%06o %04x\n", func, device, crs[A], crs[A]); -#if 0 - /* don't accept any OTA's if we're interrupting */ + if (func != 0) + busy = 0; - if (interrupting) - break; -#endif + if (func == 00) { + datareg = crs[A]; + IOSKIP; - if (func == 01) { + } else if (func == 01) { /* here's the hard part where everything happens... decode unit first */ @@ -929,6 +971,7 @@ int devmt (int class, int func, int device) { unit[u].firstwrite = 1; snprintf(devfile,sizeof(devfile),"dev%ou%d", device, u); TRACE(T_TIO, " filename for tape dev '%o unit %d is %s\n", device, u, devfile); + /* XXX: add code for read-protected tapes */ if ((unit[u].fd = open(devfile, O_RDWR, 0770)) == -1) { fprintf(stderr,"em: unable to open tape device file %s for device '%o unit %d for read/write\n", devfile, device, u); IOSKIP; @@ -940,7 +983,7 @@ int devmt (int class, int func, int device) { /* "select only" is ignored. On a real tape controller, this blocks (I think) if the previous tape operation is in progress */ - if (crs[A] & 0x8000) { + if ((crs[A] & 0xFFF00) == 0x8000) { TRACE(T_TIO, " select only\n"); IOSKIP; break; @@ -951,16 +994,9 @@ int devmt (int class, int func, int device) { unit[u].mtstat &= 0x00EC; - /* for rewind, read, write, & space, setup a completion - interrupt if controller interrupts are enabled. NOTE: there - is a race condition here. Immediately following OTA 01, - Primos clears pending interrupts because of an old tape - controller bug. To get around this, "interrupting" is a - counter and is set to 2 so that when Primos clears - interrupts, the counter is decremented in this driver, but - the interrupt will still occur later */ + /* for rewind, read, write, & space, setup a completion interrupt */ - interrupting = 2; + interrupting = 1; devpoll[device] = 10; if ((crs[A] & 0x00E0) == 0x0020) { /* rewind */ @@ -970,7 +1006,7 @@ int devmt (int class, int func, int device) { perror("Unable to rewind tape drive file"); fatal(NULL); } - unit[u].mtstat = 0x00C8; /* Ready, Online, BOT */ + unit[u].mtstat = 0x00D0; /* Ready, Online, Rewinding */ IOSKIP; break; } @@ -1032,6 +1068,7 @@ int devmt (int class, int func, int device) { iobuf and the length returned by mtwrite reflects that */ if (crs[A] & 0x10) { /* write record */ + TRACE(T_TIO, " write record\n"); dmxtotnw = 0; iobufp = iobuf+2; } else { @@ -1048,16 +1085,13 @@ int devmt (int class, int func, int device) { dmcpair = get32io(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); + TRACE(T_INST|T_TIO, " DMC channels: ['%o]='%o, ['%o]='%o, nwords=%d", dmxreg, dmxaddr, dmxreg+1, (dmcpair & 0xffff), dmxnw); } else { /* DMA */ dmxreg = dmxreg << 1; dmxnw = regs.sym.regdmx[dmxreg]; - if (dmxnw <= 0) - dmxnw = -(dmxnw>>4); - else - dmxnw = -((dmxnw>>4) ^ 0xF000); - dmxaddr = regs.sym.regdmx[dmxreg+1]; - TRACE(T_INST|T_TIO, " DMA channels: ['%o]='%o, ['%o]='%o, nwords=%d\n", dmxreg, regs.sym.regdmx[dmxreg], dmxreg+1, dmxaddr, dmxnw); + dmxnw = -((dmxnw>>4) | 0xF000); + dmxaddr = ((regs.sym.regdmx[dmxreg] & 3)<<16) | regs.sym.regdmx[dmxreg+1]; + TRACE(T_INST|T_TIO, " DMA channels: ['%o]='%o, ['%o]='%o/%o, nwords=%d", dmxreg, regs.sym.regdmx[dmxreg], dmxreg+1, dmxaddr>>16, dmxaddr&0xffff, dmxnw); } if (dmxnw < 0) { /* but is legal for >32K DMC transfer... */ printf("devmt: requested negative DMX of size %d\n", dmxnw); @@ -1067,22 +1101,32 @@ int devmt (int class, int func, int device) { if (dmxtotnw+dmxnw > MAXTAPEWORDS) fatal("Tape write is too big"); for (i=0; i < dmxnw; i++) { - *iobufp++ = get16io(dmxaddr+i); + ioword = get16io(dmxaddr+i); + if (i%10 == 0) + TRACE(T_TIO, "\n %04d: ", i); + TRACE(T_TIO, " %03o %03o", (unsigned)ioword>>8, ioword&0xff); + *iobufp++ = ioword; } + TRACE(T_TIO, "\n"); dmxtotnw = dmxtotnw + dmxnw; } else { if (dmxnw > dmxtotnw) dmxnw = dmxtotnw; for (i=0; i < dmxnw; i++) { - put16io(*iobufp++, dmxaddr+i); + ioword = *iobufp++; + if (i%10 == 0) + TRACE(T_TIO, "\n %04d: ", i); + TRACE(T_TIO, " %03o %03o", (unsigned)ioword>>8, ioword&0xff); + put16io(ioword, dmxaddr+i); } + TRACE(T_TIO, "\n"); dmxtotnw = dmxtotnw - dmxnw; } - TRACE(T_TIO, " read/wrote %d words\n", dmxnw); - if (dmxchan & 0x0800) { /* DMC */ + TRACE(T_TIO, " transferred %d words\n", dmxnw); + if (dmxchan & 0x0800) { /* if DMC... */ put16io(dmxaddr+dmxnw, dmxreg); /* update starting address */ - } else { - regs.sym.regdmx[dmxreg] += dmxnw<<4; /* increment # words */ + } else { /* if DMA... + regs.sym.regdmx[dmxreg] += (dmxnw<<4); /* increment # words */ regs.sym.regdmx[dmxreg+1] += dmxnw; /* increment address */ } @@ -1097,7 +1141,6 @@ int devmt (int class, int func, int device) { /* for write record, do the write */ if (crs[A] & 0x10) { /* write record */ - TRACE(T_TIO, " write record\n"); n = dmxtotnw*2; reclen[0] = n & 0xFF; reclen[1] = n>>8 & 0xFF; @@ -1116,23 +1159,30 @@ int devmt (int class, int func, int device) { break; } else if (func == 02) { - TRACE(T_TIO, " setup INA, A='%06o, 0x%04x\n", crs[A], crs[A]); - if (crs[A] & 0x8000) + ready = 1; + if (crs[A] & 0x8000) { /* status word 1 */ datareg = unit[usel].mtstat; - else if (crs[A] & 0x4000) - datareg = 0114; /* device ID */ + + /* if the tape was rewinding, return rewinding status once, then + change it to BOT */ + + if (datareg & 0x10) + unit[usel].mtstat = unit[usel].mtstat & ~0x10 | 0x8; + } else if (crs[A] & 0x4000) + datareg = 0214; /* device ID */ else if (crs[A] & 0x2000) datareg = dmxchan; else if (crs[A] & 0x1000) datareg = mtvec; + else if (crs[A] & 0x800) + datareg = 0; /* status word 2 */ else { TRACE(T_TIO, " Bad OTA '02 to tape drive, A='%06o, 0x$04x\n", crs[A], crs[A]); - if (enabled) { - interrupting = 1; - devpoll[device] = 10; - } + datareg = 0; + interrupting = 1; + devpoll[device] = 10; } - TRACE(T_TIO, " datareg='%06o, 0x%04x\n", datareg, datareg); + TRACE(T_TIO, " setup INA 0, datareg='%06o, 0x%04x\n", datareg, datareg); IOSKIP; } else if (func == 03) { /* power on */ @@ -1141,37 +1191,39 @@ int devmt (int class, int func, int device) { } else if (func == 05) { /* illegal - DIAG */ TRACE(T_TIO, " illegal DIAG OTA '05\n"); - if (enabled) { - interrupting = 1; - devpoll[device] = 10; - IOSKIP; - } + interrupting = 1; + devpoll[device] = 10; + IOSKIP; } else if (func == 014) { /* set DMX channel */ dmxchan = crs[A]; TRACE(T_TIO, " dmx channel '%o, 0x%04x\n", dmxchan, dmxchan); IOSKIP; + } else if (func == 015) { /* start u-code test */ + TRACE(T_TIO, " u-code test\n"); + IOSKIP; + } else if (func == 016) { /* set interrupt vector */ mtvec = crs[A]; - TRACE(T_TIO, " interrupt vector '%o\n", mtvec); + TRACE(T_TIO, " set int vec '%o\n", mtvec); IOSKIP; } else { printf("Unimplemented OTA device '%02o function '%02o, A='%o\n", device, func, crs[A]); - fatal(NULL); } break; case 4: TRACE(T_TIO, " POLL device '%02o, enabled=%d, interrupting=%d\n", device, enabled, interrupting); - if (enabled && interrupting) { - if (intvec == -1) { + if (enabled && (interrupting == 1)) { + devpoll[device] = 100; /* assume interrupt will be deferred */ + if (intvec == -1 && (crs[MODALS] & 0100000) && inhcount == 0) { TRACE(T_TIO, " CPU interrupt to vector '%o\n", mtvec); intvec = mtvec; + devpoll[device] = 0; + interrupting = 2; } - /* HACK: keep interrupting because of Primos/controller race bug */ - devpoll[device] = 100; } } } @@ -1867,15 +1919,15 @@ int devdisk (int class, int func, int device) { dc[device].status |= 2; /* select error (right?)... */ break; } + dc[device].status &= ~4; /* clear bit 14: seek error */ if (m1 & 0100000) { track = 0; - dc[device].status &= ~4; /* clear bit 14: seek error */ } else { track = m1 & 03777; } TRACE(T_INST|T_DIO, " seek track %d, restore=%d, clear=%d\n", track, (m1 & 0100000) != 0, (m1 & 040000) != 0); if (track > dc[device].unit[u].maxtrack) { - fprintf(stderr," Device '%o, seek to track %d > cylinder limit of %d\n", device, track, dc[device].unit[u].maxtrack); + fprintf(stderr," Device '%o, unit %d, seek to track %d > cylinder limit of %d\n", device, u, track, dc[device].unit[u].maxtrack); dc[device].status |= 4; /* set bit 14: seek error */ track = -1; } diff --git a/fp.h b/fp.h index ef05d75..a473839 100644 --- a/fp.h +++ b/fp.h @@ -20,8 +20,20 @@ - and frac = 0 => positive or negative zero, depending on sign - and frac non-zero => subnormal (aka denormal, unnormalized) - subnormals have an implied leading "0" bit (XXX: true??) + + References (no code was used): + +http://en.wikipedia.org/wiki/IEEE_754 +http://www.psc.edu/general/software/packages/ieee/ieee.html +http://www.math.grinnell.edu/~stone/courses/fundamentals/IEEE-reals.html +http://www.gnu.org/software/libc/manual/html_node/IEEE-Floating-Point.html +http://en.wikipedia.org/wiki/Floating_point +http://www.win.ua.ac.be/~cant/arithmos/ +http://tima-cmp.imag.fr/~guyot/Cours/Oparithm/english/Op_Ar2.htm + */ +#define GETFRAC(d) (*(long long *)&(d) & 0xFFFFFFFFFFFF0000LL) /* getdp unpacks a Prime DPFP into 48-bit sign + mantissa (left justified in 64 bits) and a 32-bit signed exponent */ @@ -61,7 +73,7 @@ int prieee8(void *dp, double *d) { if (frac64 == 0x8000000000000000LL) { exp32 += (1023-128); - if (exp32 < 0 || exp32 > 0x3ff) + if (exp32 < 0 || exp32 > 0x7fe) return 0; frac64 |= ((long long)exp32 << 52); *d = *(double *)&frac64; @@ -88,9 +100,20 @@ int prieee8(void *dp, double *d) { /* adjust exponent bias and check range */ exp32 += (1023-128) - 1; - if (exp32 < 0 || exp32 > 0x7ff) +#if 1 + if (exp32 < 0 || exp32 > 0x7fe) return 0; - +#else + if (exp32 < 0) { + *d = 0.0; + return 1; + } + if (exp32 > 0x7fe) { + exp32 = 0x7fe; + frac64 = 0x7fffffffffffffffLL; + } +#endif + /* pack into an IEEE DPFP, losing the leading 1 bit in the process */ frac64 = sign | ((long long)exp32 << 52) | ((frac64 >> 10) & 0xfffffffffffffLL); @@ -98,59 +121,9 @@ int prieee8(void *dp, double *d) { return 1; } -/* Conversion from Prime SPFP to IEEE DPFP */ - -double prieee4(unsigned int sp) { - int frac32, sign; - long long frac64; - int exp32; - - /* unpack Prime SPFP */ - - frac32 = sp & 0xFFFFFF00; - exp32 = sp & 0xFF; - - /* if negative, change to sign-magnitude */ - - sign = 0; - if (frac32 < 0) { - - /* special case: negative power of 2 */ - - if (frac32 == 0x80000000) { - exp32--; - frac64 = 0x8000000000000000LL | ((long long)exp32 << 52); - return *(double *)&frac64; - } else { - sign = 0x80000000; - frac32 = -frac32; - } - - /* special case: zero */ - - } else if (frac32 == 0) - return 0.0; - - /* normalize positive fraction until bit 2 is 1 */ - - while ((frac32 & 0x40000000) == 0) { - frac32 = frac32 << 1; - exp32--; - } - - /* adjust exponent bias and check range */ - - exp32 += (1023-128) - 1; - - /* pack into an IEEE DPFP, losing the leading 1 bit in the process */ - - frac64 = (long long)sign | ((long long)exp32 << 52) | (((long long)frac32 << 22) & 0xfffffffffffffLL); - return *(double *)&frac64; -} - - /* conversion from IEEE back to Prime. Prime exponents are larger, so - this conversion cannot overflow/underflow, but precision is lost */ + this conversion cannot overflow/underflow, but precision may be + lost */ double ieeepr8(double d) { long long frac64; @@ -263,9 +236,10 @@ double fltl (int int32) { dfcm (void *dp) { long long frac64; - int exp32; + int exp32, oflow; CLEARC; + oflow = 0; getdp(dp, &frac64, &exp32); if (frac64 != 0) { /* can't normalize zero */ if (frac64 == 0x8000000000000000LL) { /* overflow case? */ @@ -278,11 +252,12 @@ dfcm (void *dp) { exp32--; } } - if (exp32 > 32767 || exp32 < -32768) - mathexception('f', FC_DFP_OFLOW, 0); - else - putdp(dp, frac64, exp32); - } + putdp(dp, frac64, exp32); + oflow = exp32 > 32767 || exp32 < -32768; + } else + *(double *)dp = 0.0;; /* DFCM is documented to clean up dirty zeroes */ + if (oflow) + mathexception('f', FC_DFP_OFLOW, 0); } @@ -311,35 +286,132 @@ void norm(void *dp) { /* double->single floating point round (FRN) instruction. Passed a pointer to a Prime double precision variable, one of the - FACC's, and updates it in place. May also take an arithmetic fault - if overflow occurs. For faults, the ea is zero because FACC's - don't have an effective address. */ + FACC's, and updates it in place. + + NOTE: this routine is coded strangely because I ran into compiler + bugs (gcc 4.0.1) */ void frn(void *dp) { long long frac64; int exp32; - int origsign, newsign; + int doround1, doround2; - CLEARC; getdp(dp, &frac64, &exp32); if (frac64 == 0) *(long long *)dp = 0; else { - origsign = (frac64 < 0); - if ((frac64 & 0x18000000000LL) - | ((frac64 & 0x8000000000LL) && (frac64 & 0x7FFFFF0000LL))) { - frac64 += 0x10000000000LL; + doround1 = ((frac64 & 0x18000000000LL) != 0); + doround2 = ((frac64 & 0x8000000000LL) != 0) && ((frac64 & 0x7FFFFF0000LL) != 0); + if (doround1 || doround2) { frac64 &= 0xFFFFFF0000000000LL; + if (frac64 != 0x7FFFFF0000000000LL) + frac64 += 0x10000000000LL; + else { + frac64 = 0x4000000000000000LL; + exp32++; + } + frac64 |= (exp32 & 0xFFFF); norm(&frac64); *(long long *)dp = frac64; - newsign = (frac64 < 0); - if (newsign != origsign) - /* XXX: is this fault code right? */ - mathexception('f', FC_DFP_OFLOW, 0); } } } + +/* SPFP comparison, for both FCS (SRV-mode) and FC (I-mode) + For I-mode FC instruction, condition codes are used. + For SRV-mode FCS instruction, return value is the amount + RPL should be advanced. +*/ + +int fcs (unsigned int *fac, int fop) { + int templ; + short fopexp, facexp; + + CLEARCC; + templ = fac[0] & 0xffffff00; /* FAC SP mantissa */ + if (templ == 0) /* fix dirty zero */ + facexp = 0; + else + facexp = fac[1] & 0xffff; /* FAC exponent */ + fopexp = fop & 0xff; + fop = fop & 0xffffff00; + if (fop == 0) /* fix dirty zero */ + fopexp = 0; + if ((templ & 0x80000000) == (fop & 0x80000000)) { /* compare signs */ + if (facexp == fopexp) /* compare exponents */ + if (templ == fop) { /* compare fractions */ + SETEQ; + return 1; + } else if (templ < fop) { /* compare fractions */ + SETLT; /* FAC < operand */ + return 2; + } else + return 0; /* FAC > operand */ + else if (facexp < fopexp) { /* compare exponents */ + SETLT; /* FAC < operand */ + return 2; + } else + return 0; + } else if (templ & 0x80000000) { + SETLT; /* FAC < operand */ + return 2; + } else + return 0; /* FAC > operand */ +} + + +/* DPFP comparison, for both DFCS (SRV-mode) and DFC (I-mode) + For I-mode DFC instruction, condition codes are used. + For SRV-mode DFCS instruction, return value is the amount + RPL should be advanced. + + NOTE: This code doesn't pass Prime diagnostics for higher model + CPU's, I'm guessing because comparison is implemented as subtract, + and we can't do that because numbers with huge exponents (and + Prime ASCII characters in the DAC) won't convert to IEEE. +*/ + +int dfcs (unsigned int *fac, long long fop) { + long long templl; + short fopexp, facexp; + + CLEARCC; + templl = *(long long *)fac; + facexp = templl & 0xffff; /* FAC exponent */ + templl = templl & 0xffffffffffff0000LL; /* FAC SP mantissa */ + if (templl == 0) /* fix dirty zero */ + facexp = 0; + fopexp = fop & 0xffff; + fop = fop & 0xffffffffffff0000LL; + if (fop == 0) /* fix dirty zero */ + fopexp = 0; +#if 0 + printf("dfcs: FAC: %016llx %04x; op: %016llx %04x\n", templl, facexp, fop, fopexp); +#endif + if ((templl & 0x8000000000000000LL) == (fop & 0x8000000000000000LL)) { /* compare signs */ + if (facexp == fopexp) /* compare exponents */ + if (templl == fop) { /* compare fractions */ + SETEQ; + return 1; + } else if (templl < fop) { /* compare fractions */ + SETLT; /* FAC < operand */ + return 2; + } else + return 0; /* FAC > operand */ + else if (facexp < fopexp) { /* compare exponents */ + SETLT; /* FAC < operand */ + return 2; + } else + return 0; + } else if (templl & 0x8000000000000000LL) { + SETLT; /* FAC < operand */ + return 2; + } else + return 0; /* FAC > operand */ +} + + #if 0 /* Prime DPFP multiply */