--- /dev/null
+*.o
+/multics_sim
--- /dev/null
+CFLAGS=-g -O3 -std=c99 -U__STRICT_ANSI__ -IdecNumber -Idps8 -D_GNU_SOURCE -DUSE_READER_THREAD -DUSE_INT64 -Wno-unused-result
+
+multics_sim: \
+multics_sim.o \
+decNumber/decContext.o \
+decNumber/decDouble.o \
+decNumber/decimal128.o \
+decNumber/decimal32.o \
+decNumber/decimal64.o \
+decNumber/decNumber.o \
+decNumber/decPacked.o \
+decNumber/decQuad.o \
+decNumber/decSingle.o \
+dps8/dps8_addrmods.o \
+dps8/dps8_append.o \
+dps8/dps8_cpu.o \
+dps8/dps8_decimal.o \
+dps8/dps8_eis.o \
+dps8/dps8_faults.o \
+dps8/dps8_iefp.o \
+dps8/dps8_ins.o \
+dps8/dps8_math128.o \
+dps8/dps8_math.o \
+dps8/dps8_opcodetable.o \
+dps8/dps8_sys.o \
+dps8/dps8_utils.o \
+dps8/hdbg.o
+ ${CC} ${CFLAGS} -o $@ $^ -lm
+
+multics_sim.o: multics_sim.c definition.h object_map.h rassert.h
+decNumber/decContext.o: decNumber/decContext.c
+decNumber/decDouble.o: decNumber/decDouble.c
+decNumber/decimal128.o: decNumber/decimal128.c
+decNumber/decimal32.o: decNumber/decimal32.c
+decNumber/decimal64.o: decNumber/decimal64.c
+decNumber/decNumber.o: decNumber/decNumber.c
+decNumber/decPacked.o: decNumber/decPacked.c
+decNumber/decQuad.o: decNumber/decQuad.c
+decNumber/decSingle.o: decNumber/decSingle.c
+dps8/dps8_addrmods.o: dps8/dps8_addrmods.c
+dps8/dps8_append.o: dps8/dps8_append.c
+dps8/dps8_cpu.o: dps8/dps8_cpu.c
+dps8/dps8_decimal.o: dps8/dps8_decimal.c
+dps8/dps8_eis.o: dps8/dps8_eis.c
+dps8/dps8_faults.o: dps8/dps8_faults.c
+dps8/dps8_iefp.o: dps8/dps8_iefp.c
+dps8/dps8_ins.o: dps8/dps8_ins.c
+dps8/dps8_math128.o: dps8/dps8_math128.c
+dps8/dps8_math.o: dps8/dps8_math.c
+dps8/dps8_opcodetable.o: dps8/dps8_opcodetable.c
+dps8/dps8_sys.o: dps8/dps8_sys.c
+dps8/dps8_utils.o: dps8/dps8_utils.c
+dps8/hdbg.o: dps8/hdbg.c
--- /dev/null
+/* ------------------------------------------------------------------ */\r
+/* decBasic.c -- common base code for Basic decimal types */\r
+/* ------------------------------------------------------------------ */\r
+/* Copyright (c) IBM Corporation, 2000, 2010. All rights reserved. */\r
+/* */\r
+/* This software is made available under the terms of the */\r
+/* ICU License -- ICU 1.8.1 and later. */\r
+/* */\r
+/* The description and User's Guide ("The decNumber C Library") for */\r
+/* this software is included in the package as decNumber.pdf. This */\r
+/* document is also available in HTML, together with specifications, */\r
+/* testcases, and Web links, on the General Decimal Arithmetic page. */\r
+/* */\r
+/* Please send comments, suggestions, and corrections to the author: */\r
+/* mfc@uk.ibm.com */\r
+/* Mike Cowlishaw, IBM Fellow */\r
+/* IBM UK, PO Box 31, Birmingham Road, Warwick CV34 5JL, UK */\r
+/* ------------------------------------------------------------------ */\r
+/* This module comprises code that is shared between decDouble and */\r
+/* decQuad (but not decSingle). The main arithmetic operations are */\r
+/* here (Add, Subtract, Multiply, FMA, and Division operators). */\r
+/* */\r
+/* Unlike decNumber, parameterization takes place at compile time */\r
+/* rather than at runtime. The parameters are set in the decDouble.c */\r
+/* (etc.) files, which then include this one to produce the compiled */\r
+/* code. The functions here, therefore, are code shared between */\r
+/* multiple formats. */\r
+/* */\r
+/* This must be included after decCommon.inc. */\r
+/* ------------------------------------------------------------------ */\r
+// Names here refer to decFloat rather than to decDouble, etc., and\r
+// the functions are in strict alphabetical order.\r
+\r
+// The compile-time flags SINGLE, DOUBLE, and QUAD are set up in\r
+// decCommon.inc\r
+#if !defined(QUAD)\r
+ #error decBasic.c must be included after decCommon.inc\r
+#endif\r
+#if SINGLE\r
+ #error Routines in decBasic.c are for decDouble and decQuad only\r
+#endif\r
+\r
+/* Private constants */\r
+#define DIVIDE 0x80000000 // Divide operations [as flags]\r
+#define REMAINDER 0x40000000 // ..\r
+#define DIVIDEINT 0x20000000 // ..\r
+#define REMNEAR 0x10000000 // ..\r
+\r
+/* Private functions (local, used only by routines in this module) */\r
+static decFloat *decDivide(decFloat *, const decFloat *,\r
+ const decFloat *, decContext *, uInt);\r
+static decFloat *decCanonical(decFloat *, const decFloat *);\r
+static void decFiniteMultiply(bcdnum *, uByte *, const decFloat *,\r
+ const decFloat *);\r
+static decFloat *decInfinity(decFloat *, const decFloat *);\r
+static decFloat *decInvalid(decFloat *, decContext *);\r
+static decFloat *decNaNs(decFloat *, const decFloat *, const decFloat *,\r
+ decContext *);\r
+static Int decNumCompare(const decFloat *, const decFloat *, Flag);\r
+static decFloat *decToIntegral(decFloat *, const decFloat *, decContext *,\r
+ enum rounding, Flag);\r
+static uInt decToInt32(const decFloat *, decContext *, enum rounding,\r
+ Flag, Flag);\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decCanonical -- copy a decFloat, making canonical */\r
+/* */\r
+/* result gets the canonicalized df */\r
+/* df is the decFloat to copy and make canonical */\r
+/* returns result */\r
+/* */\r
+/* This is exposed via decFloatCanonical for Double and Quad only. */\r
+/* This works on specials, too; no error or exception is possible. */\r
+/* ------------------------------------------------------------------ */\r
+static decFloat * decCanonical(decFloat *result, const decFloat *df) {\r
+ uInt encode, precode, dpd; // work\r
+ uInt inword, uoff, canon; // ..\r
+ Int n; // counter (down)\r
+ if (df!=result) *result=*df; // effect copy if needed\r
+ if (DFISSPECIAL(result)) {\r
+ if (DFISINF(result)) return decInfinity(result, df); // clean Infinity\r
+ // is a NaN\r
+ DFWORD(result, 0)&=~ECONNANMASK; // clear ECON except selector\r
+ if (DFISCCZERO(df)) return result; // coefficient continuation is 0\r
+ // drop through to check payload\r
+ }\r
+ // return quickly if the coefficient continuation is canonical\r
+ { // declare block\r
+ #if DOUBLE\r
+ uInt sourhi=DFWORD(df, 0);\r
+ uInt sourlo=DFWORD(df, 1);\r
+ if (CANONDPDOFF(sourhi, 8)\r
+ && CANONDPDTWO(sourhi, sourlo, 30)\r
+ && CANONDPDOFF(sourlo, 20)\r
+ && CANONDPDOFF(sourlo, 10)\r
+ && CANONDPDOFF(sourlo, 0)) return result;\r
+ #elif QUAD\r
+ uInt sourhi=DFWORD(df, 0);\r
+ uInt sourmh=DFWORD(df, 1);\r
+ uInt sourml=DFWORD(df, 2);\r
+ uInt sourlo=DFWORD(df, 3);\r
+ if (CANONDPDOFF(sourhi, 4)\r
+ && CANONDPDTWO(sourhi, sourmh, 26)\r
+ && CANONDPDOFF(sourmh, 16)\r
+ && CANONDPDOFF(sourmh, 6)\r
+ && CANONDPDTWO(sourmh, sourml, 28)\r
+ && CANONDPDOFF(sourml, 18)\r
+ && CANONDPDOFF(sourml, 8)\r
+ && CANONDPDTWO(sourml, sourlo, 30)\r
+ && CANONDPDOFF(sourlo, 20)\r
+ && CANONDPDOFF(sourlo, 10)\r
+ && CANONDPDOFF(sourlo, 0)) return result;\r
+ #endif\r
+ } // block\r
+\r
+ // Loop to repair a non-canonical coefficent, as needed\r
+ inword=DECWORDS-1; // current input word\r
+ uoff=0; // bit offset of declet\r
+ encode=DFWORD(result, inword);\r
+ for (n=DECLETS-1; n>=0; n--) { // count down declets of 10 bits\r
+ dpd=encode>>uoff;\r
+ uoff+=10;\r
+ if (uoff>32) { // crossed uInt boundary\r
+ inword--;\r
+ encode=DFWORD(result, inword);\r
+ uoff-=32;\r
+ dpd|=encode<<(10-uoff); // get pending bits\r
+ }\r
+ dpd&=0x3ff; // clear uninteresting bits\r
+ if (dpd<0x16e) continue; // must be canonical\r
+ canon=BIN2DPD[DPD2BIN[dpd]]; // determine canonical declet\r
+ if (canon==dpd) continue; // have canonical declet\r
+ // need to replace declet\r
+ if (uoff>=10) { // all within current word\r
+ encode&=~(0x3ff<<(uoff-10)); // clear the 10 bits ready for replace\r
+ encode|=canon<<(uoff-10); // insert the canonical form\r
+ DFWORD(result, inword)=encode; // .. and save\r
+ continue;\r
+ }\r
+ // straddled words\r
+ precode=DFWORD(result, inword+1); // get previous\r
+ precode&=0xffffffff>>(10-uoff); // clear top bits\r
+ DFWORD(result, inword+1)=precode|(canon<<(32-(10-uoff)));\r
+ encode&=0xffffffff<<uoff; // clear bottom bits\r
+ encode|=canon>>(10-uoff); // insert canonical\r
+ DFWORD(result, inword)=encode; // .. and save\r
+ } // n\r
+ return result;\r
+ } // decCanonical\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decDivide -- divide operations */\r
+/* */\r
+/* result gets the result of dividing dfl by dfr: */\r
+/* dfl is the first decFloat (lhs) */\r
+/* dfr is the second decFloat (rhs) */\r
+/* set is the context */\r
+/* op is the operation selector */\r
+/* returns result */\r
+/* */\r
+/* op is one of DIVIDE, REMAINDER, DIVIDEINT, or REMNEAR. */\r
+/* ------------------------------------------------------------------ */\r
+#define DIVCOUNT 0 // 1 to instrument subtractions counter\r
+#define DIVBASE ((uInt)BILLION) // the base used for divide\r
+#define DIVOPLEN DECPMAX9 // operand length ('digits' base 10**9)\r
+#define DIVACCLEN (DIVOPLEN*3) // accumulator length (ditto)\r
+static decFloat * decDivide(decFloat *result, const decFloat *dfl,\r
+ const decFloat *dfr, decContext *set, uInt op) {\r
+ decFloat quotient; // for remainders\r
+ bcdnum num; // for final conversion\r
+ uInt acc[DIVACCLEN]; // coefficent in base-billion ..\r
+ uInt div[DIVOPLEN]; // divisor in base-billion ..\r
+ uInt quo[DIVOPLEN+1]; // quotient in base-billion ..\r
+ uByte bcdacc[(DIVOPLEN+1)*9+2]; // for quotient in BCD, +1, +1\r
+ uInt *msua, *msud, *msuq; // -> msu of acc, div, and quo\r
+ Int divunits, accunits; // lengths\r
+ Int quodigits; // digits in quotient\r
+ uInt *lsua, *lsuq; // -> current acc and quo lsus\r
+ Int length, multiplier; // work\r
+ uInt carry, sign; // ..\r
+ uInt *ua, *ud, *uq; // ..\r
+ uByte *ub; // ..\r
+ uInt uiwork; // for macros\r
+ uInt divtop; // top unit of div adjusted for estimating\r
+ #if DIVCOUNT\r
+ static uInt maxcount=0; // worst-seen subtractions count\r
+ uInt divcount=0; // subtractions count [this divide]\r
+ #endif\r
+\r
+ // calculate sign\r
+ num.sign=(DFWORD(dfl, 0)^DFWORD(dfr, 0)) & DECFLOAT_Sign;\r
+\r
+ if (DFISSPECIAL(dfl) || DFISSPECIAL(dfr)) { // either is special?\r
+ // NaNs are handled as usual\r
+ if (DFISNAN(dfl) || DFISNAN(dfr)) return decNaNs(result, dfl, dfr, set);\r
+ // one or two infinities\r
+ if (DFISINF(dfl)) {\r
+ if (DFISINF(dfr)) return decInvalid(result, set); // Two infinities bad\r
+ if (op&(REMAINDER|REMNEAR)) return decInvalid(result, set); // as is rem\r
+ // Infinity/x is infinite and quiet, even if x=0\r
+ DFWORD(result, 0)=num.sign;\r
+ return decInfinity(result, result);\r
+ }\r
+ // must be x/Infinity -- remainders are lhs\r
+ if (op&(REMAINDER|REMNEAR)) return decCanonical(result, dfl);\r
+ // divides: return zero with correct sign and exponent depending\r
+ // on op (Etiny for divide, 0 for divideInt)\r
+ decFloatZero(result);\r
+ if (op==DIVIDEINT) DFWORD(result, 0)|=num.sign; // add sign\r
+ else DFWORD(result, 0)=num.sign; // zeros the exponent, too\r
+ return result;\r
+ }\r
+ // next, handle zero operands (x/0 and 0/x)\r
+ if (DFISZERO(dfr)) { // x/0\r
+ if (DFISZERO(dfl)) { // 0/0 is undefined\r
+ decFloatZero(result);\r
+ DFWORD(result, 0)=DECFLOAT_qNaN;\r
+ set->status|=DEC_Division_undefined;\r
+ return result;\r
+ }\r
+ if (op&(REMAINDER|REMNEAR)) return decInvalid(result, set); // bad rem\r
+ set->status|=DEC_Division_by_zero;\r
+ DFWORD(result, 0)=num.sign;\r
+ return decInfinity(result, result); // x/0 -> signed Infinity\r
+ }\r
+ num.exponent=GETEXPUN(dfl)-GETEXPUN(dfr); // ideal exponent\r
+ if (DFISZERO(dfl)) { // 0/x (x!=0)\r
+ // if divide, result is 0 with ideal exponent; divideInt has\r
+ // exponent=0, remainders give zero with lower exponent\r
+ if (op&DIVIDEINT) {\r
+ decFloatZero(result);\r
+ DFWORD(result, 0)|=num.sign; // add sign\r
+ return result;\r
+ }\r
+ if (!(op&DIVIDE)) { // a remainder\r
+ // exponent is the minimum of the operands\r
+ num.exponent=MINI(GETEXPUN(dfl), GETEXPUN(dfr));\r
+ // if the result is zero the sign shall be sign of dfl\r
+ num.sign=DFWORD(dfl, 0)&DECFLOAT_Sign;\r
+ }\r
+ bcdacc[0]=0;\r
+ num.msd=bcdacc; // -> 0\r
+ num.lsd=bcdacc; // ..\r
+ return decFinalize(result, &num, set); // [divide may clamp exponent]\r
+ } // 0/x\r
+ // [here, both operands are known to be finite and non-zero]\r
+\r
+ // extract the operand coefficents into 'units' which are\r
+ // base-billion; the lhs is high-aligned in acc and the msu of both\r
+ // acc and div is at the right-hand end of array (offset length-1);\r
+ // the quotient can need one more unit than the operands as digits\r
+ // in it are not necessarily aligned neatly; further, the quotient\r
+ // may not start accumulating until after the end of the initial\r
+ // operand in acc if that is small (e.g., 1) so the accumulator\r
+ // must have at least that number of units extra (at the ls end)\r
+ GETCOEFFBILL(dfl, acc+DIVACCLEN-DIVOPLEN);\r
+ GETCOEFFBILL(dfr, div);\r
+ // zero the low uInts of acc\r
+ acc[0]=0;\r
+ acc[1]=0;\r
+ acc[2]=0;\r
+ acc[3]=0;\r
+ #if DOUBLE\r
+ #if DIVOPLEN!=2\r
+ #error Unexpected Double DIVOPLEN\r
+ #endif\r
+ #elif QUAD\r
+ acc[4]=0;\r
+ acc[5]=0;\r
+ acc[6]=0;\r
+ acc[7]=0;\r
+ #if DIVOPLEN!=4\r
+ #error Unexpected Quad DIVOPLEN\r
+ #endif\r
+ #endif\r
+\r
+ // set msu and lsu pointers\r
+ msua=acc+DIVACCLEN-1; // [leading zeros removed below]\r
+ msuq=quo+DIVOPLEN;\r
+ //[loop for div will terminate because operands are non-zero]\r
+ for (msud=div+DIVOPLEN-1; *msud==0;) msud--;\r
+ // the initial least-significant unit of acc is set so acc appears\r
+ // to have the same length as div.\r
+ // This moves one position towards the least possible for each\r
+ // iteration\r
+ divunits=(Int)(msud-div+1); // precalculate\r
+ lsua=msua-divunits+1; // initial working lsu of acc\r
+ lsuq=msuq; // and of quo\r
+\r
+ // set up the estimator for the multiplier; this is the msu of div,\r
+ // plus two bits from the unit below (if any) rounded up by one if\r
+ // there are any non-zero bits or units below that [the extra two\r
+ // bits makes for a much better estimate when the top unit is small]\r
+ divtop=*msud<<2;\r
+ if (divunits>1) {\r
+ uInt *um=msud-1;\r
+ uInt d=*um;\r
+ if (d>=750000000) {divtop+=3; d-=750000000;}\r
+ else if (d>=500000000) {divtop+=2; d-=500000000;}\r
+ else if (d>=250000000) {divtop++; d-=250000000;}\r
+ if (d) divtop++;\r
+ else for (um--; um>=div; um--) if (*um) {\r
+ divtop++;\r
+ break;\r
+ }\r
+ } // >1 unit\r
+\r
+ #if DECTRACE\r
+ {Int i;\r
+ printf("----- div=");\r
+ for (i=divunits-1; i>=0; i--) printf("%09ld ", (LI)div[i]);\r
+ printf("\n");}\r
+ #endif\r
+\r
+ // now collect up to DECPMAX+1 digits in the quotient (this may\r
+ // need OPLEN+1 uInts if unaligned)\r
+ quodigits=0; // no digits yet\r
+ for (;; lsua--) { // outer loop -- each input position\r
+ #if DECCHECK\r
+ if (lsua<acc) {\r
+ printf("Acc underrun...\n");\r
+ break;\r
+ }\r
+ #endif\r
+ #if DECTRACE\r
+ printf("Outer: quodigits=%ld acc=", (LI)quodigits);\r
+ for (ua=msua; ua>=lsua; ua--) printf("%09ld ", (LI)*ua);\r
+ printf("\n");\r
+ #endif\r
+ *lsuq=0; // default unit result is 0\r
+ for (;;) { // inner loop -- calculate quotient unit\r
+ // strip leading zero units from acc (either there initially or\r
+ // from subtraction below); this may strip all if exactly 0\r
+ for (; *msua==0 && msua>=lsua;) msua--;\r
+ accunits=(Int)(msua-lsua+1); // [maybe 0]\r
+ // subtraction is only necessary and possible if there are as\r
+ // least as many units remaining in acc for this iteration as\r
+ // there are in div\r
+ if (accunits<divunits) {\r
+ if (accunits==0) msua++; // restore\r
+ break;\r
+ }\r
+\r
+ // If acc is longer than div then subtraction is definitely\r
+ // possible (as msu of both is non-zero), but if they are the\r
+ // same length a comparison is needed.\r
+ // If a subtraction is needed then a good estimate of the\r
+ // multiplier for the subtraction is also needed in order to\r
+ // minimise the iterations of this inner loop because the\r
+ // subtractions needed dominate division performance.\r
+ if (accunits==divunits) {\r
+ // compare the high divunits of acc and div:\r
+ // acc<div: this quotient unit is unchanged; subtraction\r
+ // will be possible on the next iteration\r
+ // acc==div: quotient gains 1, set acc=0\r
+ // acc>div: subtraction necessary at this position\r
+ for (ud=msud, ua=msua; ud>div; ud--, ua--) if (*ud!=*ua) break;\r
+ // [now at first mismatch or lsu]\r
+ if (*ud>*ua) break; // next time...\r
+ if (*ud==*ua) { // all compared equal\r
+ *lsuq+=1; // increment result\r
+ msua=lsua; // collapse acc units\r
+ *msua=0; // .. to a zero\r
+ break;\r
+ }\r
+\r
+ // subtraction necessary; estimate multiplier [see above]\r
+ // if both *msud and *msua are small it is cost-effective to\r
+ // bring in part of the following units (if any) to get a\r
+ // better estimate (assume some other non-zero in div)\r
+ #define DIVLO 1000000U\r
+ #define DIVHI (DIVBASE/DIVLO)\r
+ #if DECUSE64\r
+ if (divunits>1) {\r
+ // there cannot be a *(msud-2) for DECDOUBLE so next is\r
+ // an exact calculation unless DECQUAD (which needs to\r
+ // assume bits out there if divunits>2)\r
+ uLong mul=(uLong)*msua * DIVBASE + *(msua-1);\r
+ uLong div=(uLong)*msud * DIVBASE + *(msud-1);\r
+ #if QUAD\r
+ if (divunits>2) div++;\r
+ #endif\r
+ mul/=div;\r
+ multiplier=(Int)mul;\r
+ }\r
+ else multiplier=*msua/(*msud);\r
+ #else\r
+ if (divunits>1 && *msua<DIVLO && *msud<DIVLO) {\r
+ multiplier=(*msua*DIVHI + *(msua-1)/DIVLO)\r
+ /(*msud*DIVHI + *(msud-1)/DIVLO +1);\r
+ }\r
+ else multiplier=(*msua<<2)/divtop;\r
+ #endif\r
+ }\r
+ else { // accunits>divunits\r
+ // msud is one unit 'lower' than msua, so estimate differently\r
+ #if DECUSE64\r
+ uLong mul;\r
+ // as before, bring in extra digits if possible\r
+ if (divunits>1 && *msua<DIVLO && *msud<DIVLO) {\r
+ mul=((uLong)*msua * DIVHI * DIVBASE) + *(msua-1) * DIVHI\r
+ + *(msua-2)/DIVLO;\r
+ mul/=(*msud*DIVHI + *(msud-1)/DIVLO +1);\r
+ }\r
+ else if (divunits==1) {\r
+ mul=(uLong)*msua * DIVBASE + *(msua-1);\r
+ mul/=*msud; // no more to the right\r
+ }\r
+ else {\r
+ mul=(uLong)(*msua) * (uInt)(DIVBASE<<2)\r
+ + (*(msua-1)<<2);\r
+ mul/=divtop; // [divtop already allows for sticky bits]\r
+ }\r
+ multiplier=(Int)mul;\r
+ #else\r
+ multiplier=*msua * ((DIVBASE<<2)/divtop);\r
+ #endif\r
+ }\r
+ if (multiplier==0) multiplier=1; // marginal case\r
+ *lsuq+=multiplier;\r
+\r
+ #if DIVCOUNT\r
+ // printf("Multiplier: %ld\n", (LI)multiplier);\r
+ divcount++;\r
+ #endif\r
+\r
+ // Carry out the subtraction acc-(div*multiplier); for each\r
+ // unit in div, do the multiply, split to units (see\r
+ // decFloatMultiply for the algorithm), and subtract from acc\r
+ #define DIVMAGIC 2305843009U // 2**61/10**9\r
+ #define DIVSHIFTA 29\r
+ #define DIVSHIFTB 32\r
+ carry=0;\r
+ for (ud=div, ua=lsua; ud<=msud; ud++, ua++) {\r
+ uInt lo, hop;\r
+ #if DECUSE64\r
+ uLong sub=(uLong)multiplier*(*ud)+carry;\r
+ if (sub<DIVBASE) {\r
+ carry=0;\r
+ lo=(uInt)sub;\r
+ }\r
+ else {\r
+ hop=(uInt)(sub>>DIVSHIFTA);\r
+ carry=(uInt)(((uLong)hop*DIVMAGIC)>>DIVSHIFTB);\r
+ // the estimate is now in hi; now calculate sub-hi*10**9\r
+ // to get the remainder (which will be <DIVBASE))\r
+ lo=(uInt)sub;\r
+ lo-=carry*DIVBASE; // low word of result\r
+ if (lo>=DIVBASE) {\r
+ lo-=DIVBASE; // correct by +1\r
+ carry++;\r
+ }\r
+ }\r
+ #else // 32-bit\r
+ uInt hi;\r
+ // calculate multiplier*(*ud) into hi and lo\r
+ LONGMUL32HI(hi, *ud, multiplier); // get the high word\r
+ lo=multiplier*(*ud); // .. and the low\r
+ lo+=carry; // add the old hi\r
+ carry=hi+(lo<carry); // .. with any carry\r
+ if (carry || lo>=DIVBASE) { // split is needed\r
+ hop=(carry<<3)+(lo>>DIVSHIFTA); // hi:lo/2**29\r
+ LONGMUL32HI(carry, hop, DIVMAGIC); // only need the high word\r
+ // [DIVSHIFTB is 32, so carry can be used directly]\r
+ // the estimate is now in carry; now calculate hi:lo-est*10**9;\r
+ // happily the top word of the result is irrelevant because it\r
+ // will always be zero so this needs only one multiplication\r
+ lo-=(carry*DIVBASE);\r
+ // the correction here will be at most +1; do it\r
+ if (lo>=DIVBASE) {\r
+ lo-=DIVBASE;\r
+ carry++;\r
+ }\r
+ }\r
+ #endif\r
+ if (lo>*ua) { // borrow needed\r
+ *ua+=DIVBASE;\r
+ carry++;\r
+ }\r
+ *ua-=lo;\r
+ } // ud loop\r
+ if (carry) *ua-=carry; // accdigits>divdigits [cannot borrow]\r
+ } // inner loop\r
+\r
+ // the outer loop terminates when there is either an exact result\r
+ // or enough digits; first update the quotient digit count and\r
+ // pointer (if any significant digits)\r
+ #if DECTRACE\r
+ if (*lsuq || quodigits) printf("*lsuq=%09ld\n", (LI)*lsuq);\r
+ #endif\r
+ if (quodigits) {\r
+ quodigits+=9; // had leading unit earlier\r
+ lsuq--;\r
+ if (quodigits>DECPMAX+1) break; // have enough\r
+ }\r
+ else if (*lsuq) { // first quotient digits\r
+ const uInt *pow;\r
+ for (pow=DECPOWERS; *lsuq>=*pow; pow++) quodigits++;\r
+ lsuq--;\r
+ // [cannot have >DECPMAX+1 on first unit]\r
+ }\r
+\r
+ if (*msua!=0) continue; // not an exact result\r
+ // acc is zero iff used all of original units and zero down to lsua\r
+ // (must also continue to original lsu for correct quotient length)\r
+ if (lsua>acc+DIVACCLEN-DIVOPLEN) continue;\r
+ for (; msua>lsua && *msua==0;) msua--;\r
+ if (*msua==0 && msua==lsua) break;\r
+ } // outer loop\r
+\r
+ // all of the original operand in acc has been covered at this point\r
+ // quotient now has at least DECPMAX+2 digits\r
+ // *msua is now non-0 if inexact and sticky bits\r
+ // lsuq is one below the last uint of the quotient\r
+ lsuq++; // set -> true lsu of quo\r
+ if (*msua) *lsuq|=1; // apply sticky bit\r
+\r
+ // quo now holds the (unrounded) quotient in base-billion; one\r
+ // base-billion 'digit' per uInt.\r
+ #if DECTRACE\r
+ printf("DivQuo:");\r
+ for (uq=msuq; uq>=lsuq; uq--) printf(" %09ld", (LI)*uq);\r
+ printf("\n");\r
+ #endif\r
+\r
+ // Now convert to BCD for rounding and cleanup, starting from the\r
+ // most significant end [offset by one into bcdacc to leave room\r
+ // for a possible carry digit if rounding for REMNEAR is needed]\r
+ for (uq=msuq, ub=bcdacc+1; uq>=lsuq; uq--, ub+=9) {\r
+ uInt top, mid, rem; // work\r
+ if (*uq==0) { // no split needed\r
+ UBFROMUI(ub, 0); // clear 9 BCD8s\r
+ UBFROMUI(ub+4, 0); // ..\r
+ *(ub+8)=0; // ..\r
+ continue;\r
+ }\r
+ // *uq is non-zero -- split the base-billion digit into\r
+ // hi, mid, and low three-digits\r
+ #define divsplit9 1000000 // divisor\r
+ #define divsplit6 1000 // divisor\r
+ // The splitting is done by simple divides and remainders,\r
+ // assuming the compiler will optimize these [GCC does]\r
+ top=*uq/divsplit9;\r
+ rem=*uq%divsplit9;\r
+ mid=rem/divsplit6;\r
+ rem=rem%divsplit6;\r
+ // lay out the nine BCD digits (plus one unwanted byte)\r
+ UBFROMUI(ub, UBTOUI(&BIN2BCD8[top*4]));\r
+ UBFROMUI(ub+3, UBTOUI(&BIN2BCD8[mid*4]));\r
+ UBFROMUI(ub+6, UBTOUI(&BIN2BCD8[rem*4]));\r
+ } // BCD conversion loop\r
+ ub--; // -> lsu\r
+\r
+ // complete the bcdnum; quodigits is correct, so the position of\r
+ // the first non-zero is known\r
+ num.msd=bcdacc+1+(msuq-lsuq+1)*9-quodigits;\r
+ num.lsd=ub;\r
+\r
+ // make exponent adjustments, etc\r
+ if (lsua<acc+DIVACCLEN-DIVOPLEN) { // used extra digits\r
+ num.exponent-=(Int)((acc+DIVACCLEN-DIVOPLEN-lsua)*9);\r
+ // if the result was exact then there may be up to 8 extra\r
+ // trailing zeros in the overflowed quotient final unit\r
+ if (*msua==0) {\r
+ for (; *ub==0;) ub--; // drop zeros\r
+ num.exponent+=(Int)(num.lsd-ub); // and adjust exponent\r
+ num.lsd=ub;\r
+ }\r
+ } // adjustment needed\r
+\r
+ #if DIVCOUNT\r
+ if (divcount>maxcount) { // new high-water nark\r
+ maxcount=divcount;\r
+ printf("DivNewMaxCount: %ld\n", (LI)maxcount);\r
+ }\r
+ #endif\r
+\r
+ if (op&DIVIDE) return decFinalize(result, &num, set); // all done\r
+\r
+ // Is DIVIDEINT or a remainder; there is more to do -- first form\r
+ // the integer (this is done 'after the fact', unlike as in\r
+ // decNumber, so as not to tax DIVIDE)\r
+\r
+ // The first non-zero digit will be in the first 9 digits, known\r
+ // from quodigits and num.msd, so there is always space for DECPMAX\r
+ // digits\r
+\r
+ length=(Int)(num.lsd-num.msd+1);\r
+ //printf("Length exp: %ld %ld\n", (LI)length, (LI)num.exponent);\r
+\r
+ if (length+num.exponent>DECPMAX) { // cannot fit\r
+ decFloatZero(result);\r
+ DFWORD(result, 0)=DECFLOAT_qNaN;\r
+ set->status|=DEC_Division_impossible;\r
+ return result;\r
+ }\r
+\r
+ if (num.exponent>=0) { // already an int, or need pad zeros\r
+ for (ub=num.lsd+1; ub<=num.lsd+num.exponent; ub++) *ub=0;\r
+ num.lsd+=num.exponent;\r
+ }\r
+ else { // too long: round or truncate needed\r
+ Int drop=-num.exponent;\r
+ if (!(op&REMNEAR)) { // simple truncate\r
+ num.lsd-=drop;\r
+ if (num.lsd<num.msd) { // truncated all\r
+ num.lsd=num.msd; // make 0\r
+ *num.lsd=0; // .. [sign still relevant]\r
+ }\r
+ }\r
+ else { // round to nearest even [sigh]\r
+ // round-to-nearest, in-place; msd is at or to right of bcdacc+1\r
+ // (this is a special case of Quantize -- q.v. for commentary)\r
+ uByte *roundat; // -> re-round digit\r
+ uByte reround; // reround value\r
+ *(num.msd-1)=0; // in case of left carry, or make 0\r
+ if (drop<length) roundat=num.lsd-drop+1;\r
+ else if (drop==length) roundat=num.msd;\r
+ else roundat=num.msd-1; // [-> 0]\r
+ reround=*roundat;\r
+ for (ub=roundat+1; ub<=num.lsd; ub++) {\r
+ if (*ub!=0) {\r
+ reround=DECSTICKYTAB[reround];\r
+ break;\r
+ }\r
+ } // check stickies\r
+ if (roundat>num.msd) num.lsd=roundat-1;\r
+ else {\r
+ num.msd--; // use the 0 ..\r
+ num.lsd=num.msd; // .. at the new MSD place\r
+ }\r
+ if (reround!=0) { // discarding non-zero\r
+ uInt bump=0;\r
+ // rounding is DEC_ROUND_HALF_EVEN always\r
+ if (reround>5) bump=1; // >0.5 goes up\r
+ else if (reround==5) // exactly 0.5000 ..\r
+ bump=*(num.lsd) & 0x01; // .. up iff [new] lsd is odd\r
+ if (bump!=0) { // need increment\r
+ // increment the coefficient; this might end up with 1000...\r
+ ub=num.lsd;\r
+ for (; UBTOUI(ub-3)==0x09090909; ub-=4) UBFROMUI(ub-3, 0);\r
+ for (; *ub==9; ub--) *ub=0; // at most 3 more\r
+ *ub+=1;\r
+ if (ub<num.msd) num.msd--; // carried\r
+ } // bump needed\r
+ } // reround!=0\r
+ } // remnear\r
+ } // round or truncate needed\r
+ num.exponent=0; // all paths\r
+ //decShowNum(&num, "int");\r
+\r
+ if (op&DIVIDEINT) return decFinalize(result, &num, set); // all done\r
+\r
+ // Have a remainder to calculate\r
+ decFinalize("ient, &num, set); // lay out the integer so far\r
+ DFWORD("ient, 0)^=DECFLOAT_Sign; // negate it\r
+ sign=DFWORD(dfl, 0); // save sign of dfl\r
+ decFloatFMA(result, "ient, dfr, dfl, set);\r
+ if (!DFISZERO(result)) return result;\r
+ // if the result is zero the sign shall be sign of dfl\r
+ DFWORD("ient, 0)=sign; // construct decFloat of sign\r
+ return decFloatCopySign(result, result, "ient);\r
+ } // decDivide\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decFiniteMultiply -- multiply two finite decFloats */\r
+/* */\r
+/* num gets the result of multiplying dfl and dfr */\r
+/* bcdacc .. with the coefficient in this array */\r
+/* dfl is the first decFloat (lhs) */\r
+/* dfr is the second decFloat (rhs) */\r
+/* */\r
+/* This effects the multiplication of two decFloats, both known to be */\r
+/* finite, leaving the result in a bcdnum ready for decFinalize (for */\r
+/* use in Multiply) or in a following addition (FMA). */\r
+/* */\r
+/* bcdacc must have space for at least DECPMAX9*18+1 bytes. */\r
+/* No error is possible and no status is set. */\r
+/* ------------------------------------------------------------------ */\r
+// This routine has two separate implementations of the core\r
+// multiplication; both using base-billion. One uses only 32-bit\r
+// variables (Ints and uInts) or smaller; the other uses uLongs (for\r
+// multiplication and addition only). Both implementations cover\r
+// both arithmetic sizes (DOUBLE and QUAD) in order to allow timing\r
+// comparisons. In any one compilation only one implementation for\r
+// each size can be used, and if DECUSE64 is 0 then use of the 32-bit\r
+// version is forced.\r
+//\r
+// Historical note: an earlier version of this code also supported the\r
+// 256-bit format and has been preserved. That is somewhat trickier\r
+// during lazy carry splitting because the initial quotient estimate\r
+// (est) can exceed 32 bits.\r
+\r
+#define MULTBASE ((uInt)BILLION) // the base used for multiply\r
+#define MULOPLEN DECPMAX9 // operand length ('digits' base 10**9)\r
+#define MULACCLEN (MULOPLEN*2) // accumulator length (ditto)\r
+#define LEADZEROS (MULACCLEN*9 - DECPMAX*2) // leading zeros always\r
+\r
+// Assertions: exponent not too large and MULACCLEN is a multiple of 4\r
+#if DECEMAXD>9\r
+ #error Exponent may overflow when doubled for Multiply\r
+#endif\r
+#if MULACCLEN!=(MULACCLEN/4)*4\r
+ // This assumption is used below only for initialization\r
+ #error MULACCLEN is not a multiple of 4\r
+#endif\r
+\r
+static void decFiniteMultiply(bcdnum *num, uByte *bcdacc,\r
+ const decFloat *dfl, const decFloat *dfr) {\r
+ uInt bufl[MULOPLEN]; // left coefficient (base-billion)\r
+ uInt bufr[MULOPLEN]; // right coefficient (base-billion)\r
+ uInt *ui, *uj; // work\r
+ uByte *ub; // ..\r
+ uInt uiwork; // for macros\r
+\r
+ #if DECUSE64\r
+ uLong accl[MULACCLEN]; // lazy accumulator (base-billion+)\r
+ uLong *pl; // work -> lazy accumulator\r
+ uInt acc[MULACCLEN]; // coefficent in base-billion ..\r
+ #else\r
+ uInt acc[MULACCLEN*2]; // accumulator in base-billion ..\r
+ #endif\r
+ uInt *pa; // work -> accumulator\r
+ //printf("Base10**9: OpLen=%d MulAcclen=%d\n", OPLEN, MULACCLEN);\r
+\r
+ /* Calculate sign and exponent */\r
+ num->sign=(DFWORD(dfl, 0)^DFWORD(dfr, 0)) & DECFLOAT_Sign;\r
+ num->exponent=GETEXPUN(dfl)+GETEXPUN(dfr); // [see assertion above]\r
+\r
+ /* Extract the coefficients and prepare the accumulator */\r
+ // the coefficients of the operands are decoded into base-billion\r
+ // numbers in uInt arrays (bufl and bufr, LSD at offset 0) of the\r
+ // appropriate size.\r
+ GETCOEFFBILL(dfl, bufl);\r
+ GETCOEFFBILL(dfr, bufr);\r
+ #if DECTRACE && 0\r
+ printf("CoeffbL:");\r
+ for (ui=bufl+MULOPLEN-1; ui>=bufl; ui--) printf(" %08lx", (LI)*ui);\r
+ printf("\n");\r
+ printf("CoeffbR:");\r
+ for (uj=bufr+MULOPLEN-1; uj>=bufr; uj--) printf(" %08lx", (LI)*uj);\r
+ printf("\n");\r
+ #endif\r
+\r
+ // start the 64-bit/32-bit differing paths...\r
+#if DECUSE64\r
+\r
+ // zero the accumulator\r
+ #if MULACCLEN==4\r
+ accl[0]=0; accl[1]=0; accl[2]=0; accl[3]=0;\r
+ #else // use a loop\r
+ // MULACCLEN is a multiple of four, asserted above\r
+ for (pl=accl; pl<accl+MULACCLEN; pl+=4) {\r
+ *pl=0; *(pl+1)=0; *(pl+2)=0; *(pl+3)=0;// [reduce overhead]\r
+ } // pl\r
+ #endif\r
+\r
+ /* Effect the multiplication */\r
+ // The multiplcation proceeds using MFC's lazy-carry resolution\r
+ // algorithm from decNumber. First, the multiplication is\r
+ // effected, allowing accumulation of the partial products (which\r
+ // are in base-billion at each column position) into 64 bits\r
+ // without resolving back to base=billion after each addition.\r
+ // These 64-bit numbers (which may contain up to 19 decimal digits)\r
+ // are then split using the Clark & Cowlishaw algorithm (see below).\r
+ // [Testing for 0 in the inner loop is not really a 'win']\r
+ for (ui=bufr; ui<bufr+MULOPLEN; ui++) { // over each item in rhs\r
+ if (*ui==0) continue; // product cannot affect result\r
+ pl=accl+(ui-bufr); // where to add the lhs\r
+ for (uj=bufl; uj<bufl+MULOPLEN; uj++, pl++) { // over each item in lhs\r
+ // if (*uj==0) continue; // product cannot affect result\r
+ *pl+=((uLong)*ui)*(*uj);\r
+ } // uj\r
+ } // ui\r
+\r
+ // The 64-bit carries must now be resolved; this means that a\r
+ // quotient/remainder has to be calculated for base-billion (1E+9).\r
+ // For this, Clark & Cowlishaw's quotient estimation approach (also\r
+ // used in decNumber) is needed, because 64-bit divide is generally\r
+ // extremely slow on 32-bit machines, and may be slower than this\r
+ // approach even on 64-bit machines. This algorithm splits X\r
+ // using:\r
+ //\r
+ // magic=2**(A+B)/1E+9; // 'magic number'\r
+ // hop=X/2**A; // high order part of X (by shift)\r
+ // est=magic*hop/2**B // quotient estimate (may be low by 1)\r
+ //\r
+ // A and B are quite constrained; hop and magic must fit in 32 bits,\r
+ // and 2**(A+B) must be as large as possible (which is 2**61 if\r
+ // magic is to fit). Further, maxX increases with the length of\r
+ // the operands (and hence the number of partial products\r
+ // accumulated); maxX is OPLEN*(10**18), which is up to 19 digits.\r
+ //\r
+ // It can be shown that when OPLEN is 2 then the maximum error in\r
+ // the estimated quotient is <1, but for larger maximum x the\r
+ // maximum error is above 1 so a correction that is >1 may be\r
+ // needed. Values of A and B are chosen to satisfy the constraints\r
+ // just mentioned while minimizing the maximum error (and hence the\r
+ // maximum correction), as shown in the following table:\r
+ //\r
+ // Type OPLEN A B maxX maxError maxCorrection\r
+ // ---------------------------------------------------------\r
+ // DOUBLE 2 29 32 <2*10**18 0.63 1\r
+ // QUAD 4 30 31 <4*10**18 1.17 2\r
+ //\r
+ // In the OPLEN==2 case there is most choice, but the value for B\r
+ // of 32 has a big advantage as then the calculation of the\r
+ // estimate requires no shifting; the compiler can extract the high\r
+ // word directly after multiplying magic*hop.\r
+ #define MULMAGIC 2305843009U // 2**61/10**9 [both cases]\r
+ #if DOUBLE\r
+ #define MULSHIFTA 29\r
+ #define MULSHIFTB 32\r
+ #elif QUAD\r
+ #define MULSHIFTA 30\r
+ #define MULSHIFTB 31\r
+ #else\r
+ #error Unexpected type\r
+ #endif\r
+\r
+ #if DECTRACE\r
+ printf("MulAccl:");\r
+ for (pl=accl+MULACCLEN-1; pl>=accl; pl--)\r
+ printf(" %08lx:%08lx", (LI)(*pl>>32), (LI)(*pl&0xffffffff));\r
+ printf("\n");\r
+ #endif\r
+\r
+ for (pl=accl, pa=acc; pl<accl+MULACCLEN; pl++, pa++) { // each column position\r
+ uInt lo, hop; // work\r
+ uInt est; // cannot exceed 4E+9\r
+ if (*pl>=MULTBASE) {\r
+ // *pl holds a binary number which needs to be split\r
+ hop=(uInt)(*pl>>MULSHIFTA);\r
+ est=(uInt)(((uLong)hop*MULMAGIC)>>MULSHIFTB);\r
+ // the estimate is now in est; now calculate hi:lo-est*10**9;\r
+ // happily the top word of the result is irrelevant because it\r
+ // will always be zero so this needs only one multiplication\r
+ lo=(uInt)(*pl-((uLong)est*MULTBASE)); // low word of result\r
+ // If QUAD, the correction here could be +2\r
+ if (lo>=MULTBASE) {\r
+ lo-=MULTBASE; // correct by +1\r
+ est++;\r
+ #if QUAD\r
+ // may need to correct by +2\r
+ if (lo>=MULTBASE) {\r
+ lo-=MULTBASE;\r
+ est++;\r
+ }\r
+ #endif\r
+ }\r
+ // finally place lo as the new coefficient 'digit' and add est to\r
+ // the next place up [this is safe because this path is never\r
+ // taken on the final iteration as *pl will fit]\r
+ *pa=lo;\r
+ *(pl+1)+=est;\r
+ } // *pl needed split\r
+ else { // *pl<MULTBASE\r
+ *pa=(uInt)*pl; // just copy across\r
+ }\r
+ } // pl loop\r
+\r
+#else // 32-bit\r
+ for (pa=acc;; pa+=4) { // zero the accumulator\r
+ *pa=0; *(pa+1)=0; *(pa+2)=0; *(pa+3)=0; // [reduce overhead]\r
+ if (pa==acc+MULACCLEN*2-4) break; // multiple of 4 asserted\r
+ } // pa\r
+\r
+ /* Effect the multiplication */\r
+ // uLongs are not available (and in particular, there is no uLong\r
+ // divide) but it is still possible to use MFC's lazy-carry\r
+ // resolution algorithm from decNumber. First, the multiplication\r
+ // is effected, allowing accumulation of the partial products\r
+ // (which are in base-billion at each column position) into 64 bits\r
+ // [with the high-order 32 bits in each position being held at\r
+ // offset +ACCLEN from the low-order 32 bits in the accumulator].\r
+ // These 64-bit numbers (which may contain up to 19 decimal digits)\r
+ // are then split using the Clark & Cowlishaw algorithm (see\r
+ // below).\r
+ for (ui=bufr;; ui++) { // over each item in rhs\r
+ uInt hi, lo; // words of exact multiply result\r
+ pa=acc+(ui-bufr); // where to add the lhs\r
+ for (uj=bufl;; uj++, pa++) { // over each item in lhs\r
+ LONGMUL32HI(hi, *ui, *uj); // calculate product of digits\r
+ lo=(*ui)*(*uj); // ..\r
+ *pa+=lo; // accumulate low bits and ..\r
+ *(pa+MULACCLEN)+=hi+(*pa<lo); // .. high bits with any carry\r
+ if (uj==bufl+MULOPLEN-1) break;\r
+ }\r
+ if (ui==bufr+MULOPLEN-1) break;\r
+ }\r
+\r
+ // The 64-bit carries must now be resolved; this means that a\r
+ // quotient/remainder has to be calculated for base-billion (1E+9).\r
+ // For this, Clark & Cowlishaw's quotient estimation approach (also\r
+ // used in decNumber) is needed, because 64-bit divide is generally\r
+ // extremely slow on 32-bit machines. This algorithm splits X\r
+ // using:\r
+ //\r
+ // magic=2**(A+B)/1E+9; // 'magic number'\r
+ // hop=X/2**A; // high order part of X (by shift)\r
+ // est=magic*hop/2**B // quotient estimate (may be low by 1)\r
+ //\r
+ // A and B are quite constrained; hop and magic must fit in 32 bits,\r
+ // and 2**(A+B) must be as large as possible (which is 2**61 if\r
+ // magic is to fit). Further, maxX increases with the length of\r
+ // the operands (and hence the number of partial products\r
+ // accumulated); maxX is OPLEN*(10**18), which is up to 19 digits.\r
+ //\r
+ // It can be shown that when OPLEN is 2 then the maximum error in\r
+ // the estimated quotient is <1, but for larger maximum x the\r
+ // maximum error is above 1 so a correction that is >1 may be\r
+ // needed. Values of A and B are chosen to satisfy the constraints\r
+ // just mentioned while minimizing the maximum error (and hence the\r
+ // maximum correction), as shown in the following table:\r
+ //\r
+ // Type OPLEN A B maxX maxError maxCorrection\r
+ // ---------------------------------------------------------\r
+ // DOUBLE 2 29 32 <2*10**18 0.63 1\r
+ // QUAD 4 30 31 <4*10**18 1.17 2\r
+ //\r
+ // In the OPLEN==2 case there is most choice, but the value for B\r
+ // of 32 has a big advantage as then the calculation of the\r
+ // estimate requires no shifting; the high word is simply\r
+ // calculated from multiplying magic*hop.\r
+ #define MULMAGIC 2305843009U // 2**61/10**9 [both cases]\r
+ #if DOUBLE\r
+ #define MULSHIFTA 29\r
+ #define MULSHIFTB 32\r
+ #elif QUAD\r
+ #define MULSHIFTA 30\r
+ #define MULSHIFTB 31\r
+ #else\r
+ #error Unexpected type\r
+ #endif\r
+\r
+ #if DECTRACE\r
+ printf("MulHiLo:");\r
+ for (pa=acc+MULACCLEN-1; pa>=acc; pa--)\r
+ printf(" %08lx:%08lx", (LI)*(pa+MULACCLEN), (LI)*pa);\r
+ printf("\n");\r
+ #endif\r
+\r
+ for (pa=acc;; pa++) { // each low uInt\r
+ uInt hi, lo; // words of exact multiply result\r
+ uInt hop, estlo; // work\r
+ #if QUAD\r
+ uInt esthi; // ..\r
+ #endif\r
+\r
+ lo=*pa;\r
+ hi=*(pa+MULACCLEN); // top 32 bits\r
+ // hi and lo now hold a binary number which needs to be split\r
+\r
+ #if DOUBLE\r
+ hop=(hi<<3)+(lo>>MULSHIFTA); // hi:lo/2**29\r
+ LONGMUL32HI(estlo, hop, MULMAGIC);// only need the high word\r
+ // [MULSHIFTB is 32, so estlo can be used directly]\r
+ // the estimate is now in estlo; now calculate hi:lo-est*10**9;\r
+ // happily the top word of the result is irrelevant because it\r
+ // will always be zero so this needs only one multiplication\r
+ lo-=(estlo*MULTBASE);\r
+ // esthi=0; // high word is ignored below\r
+ // the correction here will be at most +1; do it\r
+ if (lo>=MULTBASE) {\r
+ lo-=MULTBASE;\r
+ estlo++;\r
+ }\r
+ #elif QUAD\r
+ hop=(hi<<2)+(lo>>MULSHIFTA); // hi:lo/2**30\r
+ LONGMUL32HI(esthi, hop, MULMAGIC);// shift will be 31 ..\r
+ estlo=hop*MULMAGIC; // .. so low word needed\r
+ estlo=(esthi<<1)+(estlo>>MULSHIFTB); // [just the top bit]\r
+ // esthi=0; // high word is ignored below\r
+ lo-=(estlo*MULTBASE); // as above\r
+ // the correction here could be +1 or +2\r
+ if (lo>=MULTBASE) {\r
+ lo-=MULTBASE;\r
+ estlo++;\r
+ }\r
+ if (lo>=MULTBASE) {\r
+ lo-=MULTBASE;\r
+ estlo++;\r
+ }\r
+ #else\r
+ #error Unexpected type\r
+ #endif\r
+\r
+ // finally place lo as the new accumulator digit and add est to\r
+ // the next place up; this latter add could cause a carry of 1\r
+ // to the high word of the next place\r
+ *pa=lo;\r
+ *(pa+1)+=estlo;\r
+ // esthi is always 0 for DOUBLE and QUAD so this is skipped\r
+ // *(pa+1+MULACCLEN)+=esthi;\r
+ if (*(pa+1)<estlo) *(pa+1+MULACCLEN)+=1; // carry\r
+ if (pa==acc+MULACCLEN-2) break; // [MULACCLEN-1 will never need split]\r
+ } // pa loop\r
+#endif\r
+\r
+ // At this point, whether using the 64-bit or the 32-bit paths, the\r
+ // accumulator now holds the (unrounded) result in base-billion;\r
+ // one base-billion 'digit' per uInt.\r
+ #if DECTRACE\r
+ printf("MultAcc:");\r
+ for (pa=acc+MULACCLEN-1; pa>=acc; pa--) printf(" %09ld", (LI)*pa);\r
+ printf("\n");\r
+ #endif\r
+\r
+ // Now convert to BCD for rounding and cleanup, starting from the\r
+ // most significant end\r
+ pa=acc+MULACCLEN-1;\r
+ if (*pa!=0) num->msd=bcdacc+LEADZEROS;// drop known lead zeros\r
+ else { // >=1 word of leading zeros\r
+ num->msd=bcdacc; // known leading zeros are gone\r
+ pa--; // skip first word ..\r
+ for (; *pa==0; pa--) if (pa==acc) break; // .. and any more leading 0s\r
+ }\r
+ for (ub=bcdacc;; pa--, ub+=9) {\r
+ if (*pa!=0) { // split(s) needed\r
+ uInt top, mid, rem; // work\r
+ // *pa is non-zero -- split the base-billion acc digit into\r
+ // hi, mid, and low three-digits\r
+ #define mulsplit9 1000000 // divisor\r
+ #define mulsplit6 1000 // divisor\r
+ // The splitting is done by simple divides and remainders,\r
+ // assuming the compiler will optimize these where useful\r
+ // [GCC does]\r
+ top=*pa/mulsplit9;\r
+ rem=*pa%mulsplit9;\r
+ mid=rem/mulsplit6;\r
+ rem=rem%mulsplit6;\r
+ // lay out the nine BCD digits (plus one unwanted byte)\r
+ UBFROMUI(ub, UBTOUI(&BIN2BCD8[top*4]));\r
+ UBFROMUI(ub+3, UBTOUI(&BIN2BCD8[mid*4]));\r
+ UBFROMUI(ub+6, UBTOUI(&BIN2BCD8[rem*4]));\r
+ }\r
+ else { // *pa==0\r
+ UBFROMUI(ub, 0); // clear 9 BCD8s\r
+ UBFROMUI(ub+4, 0); // ..\r
+ *(ub+8)=0; // ..\r
+ }\r
+ if (pa==acc) break;\r
+ } // BCD conversion loop\r
+\r
+ num->lsd=ub+8; // complete the bcdnum ..\r
+\r
+ #if DECTRACE\r
+ decShowNum(num, "postmult");\r
+ decFloatShow(dfl, "dfl");\r
+ decFloatShow(dfr, "dfr");\r
+ #endif\r
+ return;\r
+ } // decFiniteMultiply\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decFloatAbs -- absolute value, heeding NaNs, etc. */\r
+/* */\r
+/* result gets the canonicalized df with sign 0 */\r
+/* df is the decFloat to abs */\r
+/* set is the context */\r
+/* returns result */\r
+/* */\r
+/* This has the same effect as decFloatPlus unless df is negative, */\r
+/* in which case it has the same effect as decFloatMinus. The */\r
+/* effect is also the same as decFloatCopyAbs except that NaNs are */\r
+/* handled normally (the sign of a NaN is not affected, and an sNaN */\r
+/* will signal) and the result will be canonical. */\r
+/* ------------------------------------------------------------------ */\r
+decFloat * decFloatAbs(decFloat *result, const decFloat *df,\r
+ decContext *set) {\r
+ if (DFISNAN(df)) return decNaNs(result, df, NULL, set);\r
+ decCanonical(result, df); // copy and check\r
+ DFBYTE(result, 0)&=~0x80; // zero sign bit\r
+ return result;\r
+ } // decFloatAbs\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decFloatAdd -- add two decFloats */\r
+/* */\r
+/* result gets the result of adding dfl and dfr: */\r
+/* dfl is the first decFloat (lhs) */\r
+/* dfr is the second decFloat (rhs) */\r
+/* set is the context */\r
+/* returns result */\r
+/* */\r
+/* ------------------------------------------------------------------ */\r
+#if QUAD\r
+// Table for testing MSDs for fastpath elimination; returns the MSD of\r
+// a decDouble or decQuad (top 6 bits tested) ignoring the sign.\r
+// Infinities return -32 and NaNs return -128 so that summing the two\r
+// MSDs also allows rapid tests for the Specials (see code below).\r
+const Int DECTESTMSD[64]={\r
+ 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7,\r
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 8, 9, 8, 9, -32, -128,\r
+ 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7,\r
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 8, 9, 8, 9, -32, -128};\r
+#else\r
+// The table for testing MSDs is shared between the modules\r
+extern const Int DECTESTMSD[64];\r
+#endif\r
+\r
+decFloat * decFloatAdd(decFloat *result,\r
+ const decFloat *dfl, const decFloat *dfr,\r
+ decContext *set) {\r
+ bcdnum num; // for final conversion\r
+ Int bexpl, bexpr; // left and right biased exponents\r
+ uByte *ub, *us, *ut; // work\r
+ uInt uiwork; // for macros\r
+ #if QUAD\r
+ uShort uswork; // ..\r
+ #endif\r
+\r
+ uInt sourhil, sourhir; // top words from source decFloats\r
+ // [valid only through end of\r
+ // fastpath code -- before swap]\r
+ uInt diffsign; // non-zero if signs differ\r
+ uInt carry; // carry: 0 or 1 before add loop\r
+ Int overlap; // coefficient overlap (if full)\r
+ Int summ; // sum of the MSDs\r
+ // the following buffers hold coefficients with various alignments\r
+ // (see commentary and diagrams below)\r
+ uByte acc[4+2+DECPMAX*3+8];\r
+ uByte buf[4+2+DECPMAX*2];\r
+ uByte *umsd, *ulsd; // local MSD and LSD pointers\r
+\r
+ #if DECLITEND\r
+ #define CARRYPAT 0x01000000 // carry=1 pattern\r
+ #else\r
+ #define CARRYPAT 0x00000001 // carry=1 pattern\r
+ #endif\r
+\r
+ /* Start decoding the arguments */\r
+ // The initial exponents are placed into the opposite Ints to\r
+ // that which might be expected; there are two sets of data to\r
+ // keep track of (each decFloat and the corresponding exponent),\r
+ // and this scheme means that at the swap point (after comparing\r
+ // exponents) only one pair of words needs to be swapped\r
+ // whichever path is taken (thereby minimising worst-case path).\r
+ // The calculated exponents will be nonsense when the arguments are\r
+ // Special, but are not used in that path\r
+ sourhil=DFWORD(dfl, 0); // LHS top word\r
+ summ=DECTESTMSD[sourhil>>26]; // get first MSD for testing\r
+ bexpr=DECCOMBEXP[sourhil>>26]; // get exponent high bits (in place)\r
+ bexpr+=GETECON(dfl); // .. + continuation\r
+\r
+ sourhir=DFWORD(dfr, 0); // RHS top word\r
+ summ+=DECTESTMSD[sourhir>>26]; // sum MSDs for testing\r
+ bexpl=DECCOMBEXP[sourhir>>26];\r
+ bexpl+=GETECON(dfr);\r
+\r
+ // here bexpr has biased exponent from lhs, and vice versa\r
+\r
+ diffsign=(sourhil^sourhir)&DECFLOAT_Sign;\r
+\r
+ // now determine whether to take a fast path or the full-function\r
+ // slow path. The slow path must be taken when:\r
+ // -- both numbers are finite, and:\r
+ // the exponents are different, or\r
+ // the signs are different, or\r
+ // the sum of the MSDs is >8 (hence might overflow)\r
+ // specialness and the sum of the MSDs can be tested at once using\r
+ // the summ value just calculated, so the test for specials is no\r
+ // longer on the worst-case path (as of 3.60)\r
+\r
+ if (summ<=8) { // MSD+MSD is good, or there is a special\r
+ if (summ<0) { // there is a special\r
+ // Inf+Inf would give -64; Inf+finite is -32 or higher\r
+ if (summ<-64) return decNaNs(result, dfl, dfr, set); // one or two NaNs\r
+ // two infinities with different signs is invalid\r
+ if (summ==-64 && diffsign) return decInvalid(result, set);\r
+ if (DFISINF(dfl)) return decInfinity(result, dfl); // LHS is infinite\r
+ return decInfinity(result, dfr); // RHS must be Inf\r
+ }\r
+ // Here when both arguments are finite; fast path is possible\r
+ // (currently only for aligned and same-sign)\r
+ if (bexpr==bexpl && !diffsign) {\r
+ uInt tac[DECLETS+1]; // base-1000 coefficient\r
+ uInt encode; // work\r
+\r
+ // Get one coefficient as base-1000 and add the other\r
+ GETCOEFFTHOU(dfl, tac); // least-significant goes to [0]\r
+ ADDCOEFFTHOU(dfr, tac);\r
+ // here the sum of the MSDs (plus any carry) will be <10 due to\r
+ // the fastpath test earlier\r
+\r
+ // construct the result; low word is the same for both formats\r
+ encode =BIN2DPD[tac[0]];\r
+ encode|=BIN2DPD[tac[1]]<<10;\r
+ encode|=BIN2DPD[tac[2]]<<20;\r
+ encode|=BIN2DPD[tac[3]]<<30;\r
+ DFWORD(result, (DECBYTES/4)-1)=encode;\r
+\r
+ // collect next two declets (all that remains, for Double)\r
+ encode =BIN2DPD[tac[3]]>>2;\r
+ encode|=BIN2DPD[tac[4]]<<8;\r
+\r
+ #if QUAD\r
+ // complete and lay out middling words\r
+ encode|=BIN2DPD[tac[5]]<<18;\r
+ encode|=BIN2DPD[tac[6]]<<28;\r
+ DFWORD(result, 2)=encode;\r
+\r
+ encode =BIN2DPD[tac[6]]>>4;\r
+ encode|=BIN2DPD[tac[7]]<<6;\r
+ encode|=BIN2DPD[tac[8]]<<16;\r
+ encode|=BIN2DPD[tac[9]]<<26;\r
+ DFWORD(result, 1)=encode;\r
+\r
+ // and final two declets\r
+ encode =BIN2DPD[tac[9]]>>6;\r
+ encode|=BIN2DPD[tac[10]]<<4;\r
+ #endif\r
+\r
+ // add exponent continuation and sign (from either argument)\r
+ encode|=sourhil & (ECONMASK | DECFLOAT_Sign);\r
+\r
+ // create lookup index = MSD + top two bits of biased exponent <<4\r
+ tac[DECLETS]|=(bexpl>>DECECONL)<<4;\r
+ encode|=DECCOMBFROM[tac[DECLETS]]; // add constructed combination field\r
+ DFWORD(result, 0)=encode; // complete\r
+\r
+ // decFloatShow(result, ">");\r
+ return result;\r
+ } // fast path OK\r
+ // drop through to slow path\r
+ } // low sum or Special(s)\r
+\r
+ /* Slow path required -- arguments are finite and might overflow, */\r
+ /* or require alignment, or might have different signs */\r
+\r
+ // now swap either exponents or argument pointers\r
+ if (bexpl<=bexpr) {\r
+ // original left is bigger\r
+ Int bexpswap=bexpl;\r
+ bexpl=bexpr;\r
+ bexpr=bexpswap;\r
+ // printf("left bigger\n");\r
+ }\r
+ else {\r
+ const decFloat *dfswap=dfl;\r
+ dfl=dfr;\r
+ dfr=dfswap;\r
+ // printf("right bigger\n");\r
+ }\r
+ // [here dfl and bexpl refer to the datum with the larger exponent,\r
+ // of if the exponents are equal then the original LHS argument]\r
+\r
+ // if lhs is zero then result will be the rhs (now known to have\r
+ // the smaller exponent), which also may need to be tested for zero\r
+ // for the weird IEEE 754 sign rules\r
+ if (DFISZERO(dfl)) {\r
+ decCanonical(result, dfr); // clean copy\r
+ // "When the sum of two operands with opposite signs is\r
+ // exactly zero, the sign of that sum shall be '+' in all\r
+ // rounding modes except round toward -Infinity, in which\r
+ // mode that sign shall be '-'."\r
+ if (diffsign && DFISZERO(result)) {\r
+ DFWORD(result, 0)&=~DECFLOAT_Sign; // assume sign 0\r
+ if (set->round==DEC_ROUND_FLOOR) DFWORD(result, 0)|=DECFLOAT_Sign;\r
+ }\r
+ return result;\r
+ } // numfl is zero\r
+ // [here, LHS is non-zero; code below assumes that]\r
+\r
+ // Coefficients layout during the calculations to follow:\r
+ //\r
+ // Overlap case:\r
+ // +------------------------------------------------+\r
+ // acc: |0000| coeffa | tail B | |\r
+ // +------------------------------------------------+\r
+ // buf: |0000| pad0s | coeffb | |\r
+ // +------------------------------------------------+\r
+ //\r
+ // Touching coefficients or gap:\r
+ // +------------------------------------------------+\r
+ // acc: |0000| coeffa | gap | coeffb |\r
+ // +------------------------------------------------+\r
+ // [buf not used or needed; gap clamped to Pmax]\r
+\r
+ // lay out lhs coefficient into accumulator; this starts at acc+4\r
+ // for decDouble or acc+6 for decQuad so the LSD is word-\r
+ // aligned; the top word gap is there only in case a carry digit\r
+ // is prefixed after the add -- it does not need to be zeroed\r
+ #if DOUBLE\r
+ #define COFF 4 // offset into acc\r
+ #elif QUAD\r
+ UBFROMUS(acc+4, 0); // prefix 00\r
+ #define COFF 6 // offset into acc\r
+ #endif\r
+\r
+ GETCOEFF(dfl, acc+COFF); // decode from decFloat\r
+ ulsd=acc+COFF+DECPMAX-1;\r
+ umsd=acc+4; // [having this here avoids\r
+\r
+ #if DECTRACE\r
+ {bcdnum tum;\r
+ tum.msd=umsd;\r
+ tum.lsd=ulsd;\r
+ tum.exponent=bexpl-DECBIAS;\r
+ tum.sign=DFWORD(dfl, 0) & DECFLOAT_Sign;\r
+ decShowNum(&tum, "dflx");}\r
+ #endif\r
+\r
+ // if signs differ, take ten's complement of lhs (here the\r
+ // coefficient is subtracted from all-nines; the 1 is added during\r
+ // the later add cycle -- zeros to the right do not matter because\r
+ // the complement of zero is zero); these are fixed-length inverts\r
+ // where the lsd is known to be at a 4-byte boundary (so no borrow\r
+ // possible)\r
+ carry=0; // assume no carry\r
+ if (diffsign) {\r
+ carry=CARRYPAT; // for +1 during add\r
+ UBFROMUI(acc+ 4, 0x09090909-UBTOUI(acc+ 4));\r
+ UBFROMUI(acc+ 8, 0x09090909-UBTOUI(acc+ 8));\r
+ UBFROMUI(acc+12, 0x09090909-UBTOUI(acc+12));\r
+ UBFROMUI(acc+16, 0x09090909-UBTOUI(acc+16));\r
+ #if QUAD\r
+ UBFROMUI(acc+20, 0x09090909-UBTOUI(acc+20));\r
+ UBFROMUI(acc+24, 0x09090909-UBTOUI(acc+24));\r
+ UBFROMUI(acc+28, 0x09090909-UBTOUI(acc+28));\r
+ UBFROMUI(acc+32, 0x09090909-UBTOUI(acc+32));\r
+ UBFROMUI(acc+36, 0x09090909-UBTOUI(acc+36));\r
+ #endif\r
+ } // diffsign\r
+\r
+ // now process the rhs coefficient; if it cannot overlap lhs then\r
+ // it can be put straight into acc (with an appropriate gap, if\r
+ // needed) because no actual addition will be needed (except\r
+ // possibly to complete ten's complement)\r
+ overlap=DECPMAX-(bexpl-bexpr);\r
+ #if DECTRACE\r
+ printf("exps: %ld %ld\n", (LI)(bexpl-DECBIAS), (LI)(bexpr-DECBIAS));\r
+ printf("Overlap=%ld carry=%08lx\n", (LI)overlap, (LI)carry);\r
+ #endif\r
+\r
+ if (overlap<=0) { // no overlap possible\r
+ uInt gap; // local work\r
+ // since a full addition is not needed, a ten's complement\r
+ // calculation started above may need to be completed\r
+ if (carry) {\r
+ for (ub=ulsd; *ub==9; ub--) *ub=0;\r
+ *ub+=1;\r
+ carry=0; // taken care of\r
+ }\r
+ // up to DECPMAX-1 digits of the final result can extend down\r
+ // below the LSD of the lhs, so if the gap is >DECPMAX then the\r
+ // rhs will be simply sticky bits. In this case the gap is\r
+ // clamped to DECPMAX and the exponent adjusted to suit [this is\r
+ // safe because the lhs is non-zero].\r
+ gap=-overlap;\r
+ if (gap>DECPMAX) {\r
+ bexpr+=gap-1;\r
+ gap=DECPMAX;\r
+ }\r
+ ub=ulsd+gap+1; // where MSD will go\r
+ // Fill the gap with 0s; note that there is no addition to do\r
+ ut=acc+COFF+DECPMAX; // start of gap\r
+ for (; ut<ub; ut+=4) UBFROMUI(ut, 0); // mind the gap\r
+ if (overlap<-DECPMAX) { // gap was > DECPMAX\r
+ *ub=(uByte)(!DFISZERO(dfr)); // make sticky digit\r
+ }\r
+ else { // need full coefficient\r
+ GETCOEFF(dfr, ub); // decode from decFloat\r
+ ub+=DECPMAX-1; // new LSD...\r
+ }\r
+ ulsd=ub; // save new LSD\r
+ } // no overlap possible\r
+\r
+ else { // overlap>0\r
+ // coefficients overlap (perhaps completely, although also\r
+ // perhaps only where zeros)\r
+ if (overlap==DECPMAX) { // aligned\r
+ ub=buf+COFF; // where msd will go\r
+ #if QUAD\r
+ UBFROMUS(buf+4, 0); // clear quad's 00\r
+ #endif\r
+ GETCOEFF(dfr, ub); // decode from decFloat\r
+ }\r
+ else { // unaligned\r
+ ub=buf+COFF+DECPMAX-overlap; // where MSD will go\r
+ // Fill the prefix gap with 0s; 8 will cover most common\r
+ // unalignments, so start with direct assignments (a loop is\r
+ // then used for any remaining -- the loop (and the one in a\r
+ // moment) is not then on the critical path because the number\r
+ // of additions is reduced by (at least) two in this case)\r
+ UBFROMUI(buf+4, 0); // [clears decQuad 00 too]\r
+ UBFROMUI(buf+8, 0);\r
+ if (ub>buf+12) {\r
+ ut=buf+12; // start any remaining\r
+ for (; ut<ub; ut+=4) UBFROMUI(ut, 0); // fill them\r
+ }\r
+ GETCOEFF(dfr, ub); // decode from decFloat\r
+\r
+ // now move tail of rhs across to main acc; again use direct\r
+ // copies for 8 digits-worth\r
+ UBFROMUI(acc+COFF+DECPMAX, UBTOUI(buf+COFF+DECPMAX));\r
+ UBFROMUI(acc+COFF+DECPMAX+4, UBTOUI(buf+COFF+DECPMAX+4));\r
+ if (buf+COFF+DECPMAX+8<ub+DECPMAX) {\r
+ us=buf+COFF+DECPMAX+8; // source\r
+ ut=acc+COFF+DECPMAX+8; // target\r
+ for (; us<ub+DECPMAX; us+=4, ut+=4) UBFROMUI(ut, UBTOUI(us));\r
+ }\r
+ } // unaligned\r
+\r
+ ulsd=acc+(ub-buf+DECPMAX-1); // update LSD pointer\r
+\r
+ // Now do the add of the non-tail; this is all nicely aligned,\r
+ // and is over a multiple of four digits (because for Quad two\r
+ // zero digits were added on the left); words in both acc and\r
+ // buf (buf especially) will often be zero\r
+ // [byte-by-byte add, here, is about 15% slower total effect than\r
+ // the by-fours]\r
+\r
+ // Now effect the add; this is harder on a little-endian\r
+ // machine as the inter-digit carry cannot use the usual BCD\r
+ // addition trick because the bytes are loaded in the wrong order\r
+ // [this loop could be unrolled, but probably scarcely worth it]\r
+\r
+ ut=acc+COFF+DECPMAX-4; // target LSW (acc)\r
+ us=buf+COFF+DECPMAX-4; // source LSW (buf, to add to acc)\r
+\r
+ #if !DECLITEND\r
+ for (; ut>=acc+4; ut-=4, us-=4) { // big-endian add loop\r
+ // bcd8 add\r
+ carry+=UBTOUI(us); // rhs + carry\r
+ if (carry==0) continue; // no-op\r
+ carry+=UBTOUI(ut); // lhs\r
+ // Big-endian BCD adjust (uses internal carry)\r
+ carry+=0x76f6f6f6; // note top nibble not all bits\r
+ // apply BCD adjust and save\r
+ UBFROMUI(ut, (carry & 0x0f0f0f0f) - ((carry & 0x60606060)>>4));\r
+ carry>>=31; // true carry was at far left\r
+ } // add loop\r
+ #else\r
+ for (; ut>=acc+4; ut-=4, us-=4) { // little-endian add loop\r
+ // bcd8 add\r
+ carry+=UBTOUI(us); // rhs + carry\r
+ if (carry==0) continue; // no-op [common if unaligned]\r
+ carry+=UBTOUI(ut); // lhs\r
+ // Little-endian BCD adjust; inter-digit carry must be manual\r
+ // because the lsb from the array will be in the most-significant\r
+ // byte of carry\r
+ carry+=0x76767676; // note no inter-byte carries\r
+ carry+=(carry & 0x80000000)>>15;\r
+ carry+=(carry & 0x00800000)>>15;\r
+ carry+=(carry & 0x00008000)>>15;\r
+ carry-=(carry & 0x60606060)>>4; // BCD adjust back\r
+ UBFROMUI(ut, carry & 0x0f0f0f0f); // clear debris and save\r
+ // here, final carry-out bit is at 0x00000080; move it ready\r
+ // for next word-add (i.e., to 0x01000000)\r
+ carry=(carry & 0x00000080)<<17;\r
+ } // add loop\r
+ #endif\r
+\r
+ #if DECTRACE\r
+ {bcdnum tum;\r
+ printf("Add done, carry=%08lx, diffsign=%ld\n", (LI)carry, (LI)diffsign);\r
+ tum.msd=umsd; // acc+4;\r
+ tum.lsd=ulsd;\r
+ tum.exponent=0;\r
+ tum.sign=0;\r
+ decShowNum(&tum, "dfadd");}\r
+ #endif\r
+ } // overlap possible\r
+\r
+ // ordering here is a little strange in order to have slowest path\r
+ // first in GCC asm listing\r
+ if (diffsign) { // subtraction\r
+ if (!carry) { // no carry out means RHS<LHS\r
+ // borrowed -- take ten's complement\r
+ // sign is lhs sign\r
+ num.sign=DFWORD(dfl, 0) & DECFLOAT_Sign;\r
+\r
+ // invert the coefficient first by fours, then add one; space\r
+ // at the end of the buffer ensures the by-fours is always\r
+ // safe, but lsd+1 must be cleared to prevent a borrow\r
+ // if big-endian\r
+ #if !DECLITEND\r
+ *(ulsd+1)=0;\r
+ #endif\r
+ // there are always at least four coefficient words\r
+ UBFROMUI(umsd, 0x09090909-UBTOUI(umsd));\r
+ UBFROMUI(umsd+4, 0x09090909-UBTOUI(umsd+4));\r
+ UBFROMUI(umsd+8, 0x09090909-UBTOUI(umsd+8));\r
+ UBFROMUI(umsd+12, 0x09090909-UBTOUI(umsd+12));\r
+ #if DOUBLE\r
+ #define BNEXT 16\r
+ #elif QUAD\r
+ UBFROMUI(umsd+16, 0x09090909-UBTOUI(umsd+16));\r
+ UBFROMUI(umsd+20, 0x09090909-UBTOUI(umsd+20));\r
+ UBFROMUI(umsd+24, 0x09090909-UBTOUI(umsd+24));\r
+ UBFROMUI(umsd+28, 0x09090909-UBTOUI(umsd+28));\r
+ UBFROMUI(umsd+32, 0x09090909-UBTOUI(umsd+32));\r
+ #define BNEXT 36\r
+ #endif\r
+ if (ulsd>=umsd+BNEXT) { // unaligned\r
+ // eight will handle most unaligments for Double; 16 for Quad\r
+ UBFROMUI(umsd+BNEXT, 0x09090909-UBTOUI(umsd+BNEXT));\r
+ UBFROMUI(umsd+BNEXT+4, 0x09090909-UBTOUI(umsd+BNEXT+4));\r
+ #if DOUBLE\r
+ #define BNEXTY (BNEXT+8)\r
+ #elif QUAD\r
+ UBFROMUI(umsd+BNEXT+8, 0x09090909-UBTOUI(umsd+BNEXT+8));\r
+ UBFROMUI(umsd+BNEXT+12, 0x09090909-UBTOUI(umsd+BNEXT+12));\r
+ #define BNEXTY (BNEXT+16)\r
+ #endif\r
+ if (ulsd>=umsd+BNEXTY) { // very unaligned\r
+ ut=umsd+BNEXTY; // -> continue\r
+ for (;;ut+=4) {\r
+ UBFROMUI(ut, 0x09090909-UBTOUI(ut)); // invert four digits\r
+ if (ut>=ulsd-3) break; // all done\r
+ }\r
+ }\r
+ }\r
+ // complete the ten's complement by adding 1\r
+ for (ub=ulsd; *ub==9; ub--) *ub=0;\r
+ *ub+=1;\r
+ } // borrowed\r
+\r
+ else { // carry out means RHS>=LHS\r
+ num.sign=DFWORD(dfr, 0) & DECFLOAT_Sign;\r
+ // all done except for the special IEEE 754 exact-zero-result\r
+ // rule (see above); while testing for zero, strip leading\r
+ // zeros (which will save decFinalize doing it) (this is in\r
+ // diffsign path, so carry impossible and true umsd is\r
+ // acc+COFF)\r
+\r
+ // Check the initial coefficient area using the fast macro;\r
+ // this will often be all that needs to be done (as on the\r
+ // worst-case path when the subtraction was aligned and\r
+ // full-length)\r
+ if (ISCOEFFZERO(acc+COFF)) {\r
+ umsd=acc+COFF+DECPMAX-1; // so far, so zero\r
+ if (ulsd>umsd) { // more to check\r
+ umsd++; // to align after checked area\r
+ for (; UBTOUI(umsd)==0 && umsd+3<ulsd;) umsd+=4;\r
+ for (; *umsd==0 && umsd<ulsd;) umsd++;\r
+ }\r
+ if (*umsd==0) { // must be true zero (and diffsign)\r
+ num.sign=0; // assume +\r
+ if (set->round==DEC_ROUND_FLOOR) num.sign=DECFLOAT_Sign;\r
+ }\r
+ }\r
+ // [else was not zero, might still have leading zeros]\r
+ } // subtraction gave positive result\r
+ } // diffsign\r
+\r
+ else { // same-sign addition\r
+ num.sign=DFWORD(dfl, 0)&DECFLOAT_Sign;\r
+ #if DOUBLE\r
+ if (carry) { // only possible with decDouble\r
+ *(acc+3)=1; // [Quad has leading 00]\r
+ umsd=acc+3;\r
+ }\r
+ #endif\r
+ } // same sign\r
+\r
+ num.msd=umsd; // set MSD ..\r
+ num.lsd=ulsd; // .. and LSD\r
+ num.exponent=bexpr-DECBIAS; // set exponent to smaller, unbiassed\r
+\r
+ #if DECTRACE\r
+ decFloatShow(dfl, "dfl");\r
+ decFloatShow(dfr, "dfr");\r
+ decShowNum(&num, "postadd");\r
+ #endif\r
+ return decFinalize(result, &num, set); // round, check, and lay out\r
+ } // decFloatAdd\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decFloatAnd -- logical digitwise AND of two decFloats */\r
+/* */\r
+/* result gets the result of ANDing dfl and dfr */\r
+/* dfl is the first decFloat (lhs) */\r
+/* dfr is the second decFloat (rhs) */\r
+/* set is the context */\r
+/* returns result, which will be canonical with sign=0 */\r
+/* */\r
+/* The operands must be positive, finite with exponent q=0, and */\r
+/* comprise just zeros and ones; if not, Invalid operation results. */\r
+/* ------------------------------------------------------------------ */\r
+decFloat * decFloatAnd(decFloat *result,\r
+ const decFloat *dfl, const decFloat *dfr,\r
+ decContext *set) {\r
+ if (!DFISUINT01(dfl) || !DFISUINT01(dfr)\r
+ || !DFISCC01(dfl) || !DFISCC01(dfr)) return decInvalid(result, set);\r
+ // the operands are positive finite integers (q=0) with just 0s and 1s\r
+ #if DOUBLE\r
+ DFWORD(result, 0)=ZEROWORD\r
+ |((DFWORD(dfl, 0) & DFWORD(dfr, 0))&0x04009124);\r
+ DFWORD(result, 1)=(DFWORD(dfl, 1) & DFWORD(dfr, 1))&0x49124491;\r
+ #elif QUAD\r
+ DFWORD(result, 0)=ZEROWORD\r
+ |((DFWORD(dfl, 0) & DFWORD(dfr, 0))&0x04000912);\r
+ DFWORD(result, 1)=(DFWORD(dfl, 1) & DFWORD(dfr, 1))&0x44912449;\r
+ DFWORD(result, 2)=(DFWORD(dfl, 2) & DFWORD(dfr, 2))&0x12449124;\r
+ DFWORD(result, 3)=(DFWORD(dfl, 3) & DFWORD(dfr, 3))&0x49124491;\r
+ #endif\r
+ return result;\r
+ } // decFloatAnd\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decFloatCanonical -- copy a decFloat, making canonical */\r
+/* */\r
+/* result gets the canonicalized df */\r
+/* df is the decFloat to copy and make canonical */\r
+/* returns result */\r
+/* */\r
+/* This works on specials, too; no error or exception is possible. */\r
+/* ------------------------------------------------------------------ */\r
+decFloat * decFloatCanonical(decFloat *result, const decFloat *df) {\r
+ return decCanonical(result, df);\r
+ } // decFloatCanonical\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decFloatClass -- return the class of a decFloat */\r
+/* */\r
+/* df is the decFloat to test */\r
+/* returns the decClass that df falls into */\r
+/* ------------------------------------------------------------------ */\r
+enum decClass decFloatClass(const decFloat *df) {\r
+ Int exp; // exponent\r
+ if (DFISSPECIAL(df)) {\r
+ if (DFISQNAN(df)) return DEC_CLASS_QNAN;\r
+ if (DFISSNAN(df)) return DEC_CLASS_SNAN;\r
+ // must be an infinity\r
+ if (DFISSIGNED(df)) return DEC_CLASS_NEG_INF;\r
+ return DEC_CLASS_POS_INF;\r
+ }\r
+ if (DFISZERO(df)) { // quite common\r
+ if (DFISSIGNED(df)) return DEC_CLASS_NEG_ZERO;\r
+ return DEC_CLASS_POS_ZERO;\r
+ }\r
+ // is finite and non-zero; similar code to decFloatIsNormal, here\r
+ // [this could be speeded up slightly by in-lining decFloatDigits]\r
+ exp=GETEXPUN(df) // get unbiased exponent ..\r
+ +decFloatDigits(df)-1; // .. and make adjusted exponent\r
+ if (exp>=DECEMIN) { // is normal\r
+ if (DFISSIGNED(df)) return DEC_CLASS_NEG_NORMAL;\r
+ return DEC_CLASS_POS_NORMAL;\r
+ }\r
+ // is subnormal\r
+ if (DFISSIGNED(df)) return DEC_CLASS_NEG_SUBNORMAL;\r
+ return DEC_CLASS_POS_SUBNORMAL;\r
+ } // decFloatClass\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decFloatClassString -- return the class of a decFloat as a string */\r
+/* */\r
+/* df is the decFloat to test */\r
+/* returns a constant string describing the class df falls into */\r
+/* ------------------------------------------------------------------ */\r
+const char *decFloatClassString(const decFloat *df) {\r
+ enum decClass eclass=decFloatClass(df);\r
+ if (eclass==DEC_CLASS_POS_NORMAL) return DEC_ClassString_PN;\r
+ if (eclass==DEC_CLASS_NEG_NORMAL) return DEC_ClassString_NN;\r
+ if (eclass==DEC_CLASS_POS_ZERO) return DEC_ClassString_PZ;\r
+ if (eclass==DEC_CLASS_NEG_ZERO) return DEC_ClassString_NZ;\r
+ if (eclass==DEC_CLASS_POS_SUBNORMAL) return DEC_ClassString_PS;\r
+ if (eclass==DEC_CLASS_NEG_SUBNORMAL) return DEC_ClassString_NS;\r
+ if (eclass==DEC_CLASS_POS_INF) return DEC_ClassString_PI;\r
+ if (eclass==DEC_CLASS_NEG_INF) return DEC_ClassString_NI;\r
+ if (eclass==DEC_CLASS_QNAN) return DEC_ClassString_QN;\r
+ if (eclass==DEC_CLASS_SNAN) return DEC_ClassString_SN;\r
+ return DEC_ClassString_UN; // Unknown\r
+ } // decFloatClassString\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decFloatCompare -- compare two decFloats; quiet NaNs allowed */\r
+/* */\r
+/* result gets the result of comparing dfl and dfr */\r
+/* dfl is the first decFloat (lhs) */\r
+/* dfr is the second decFloat (rhs) */\r
+/* set is the context */\r
+/* returns result, which may be -1, 0, 1, or NaN (Unordered) */\r
+/* ------------------------------------------------------------------ */\r
+decFloat * decFloatCompare(decFloat *result,\r
+ const decFloat *dfl, const decFloat *dfr,\r
+ decContext *set) {\r
+ Int comp; // work\r
+ // NaNs are handled as usual\r
+ if (DFISNAN(dfl) || DFISNAN(dfr)) return decNaNs(result, dfl, dfr, set);\r
+ // numeric comparison needed\r
+ comp=decNumCompare(dfl, dfr, 0);\r
+ decFloatZero(result);\r
+ if (comp==0) return result;\r
+ DFBYTE(result, DECBYTES-1)=0x01; // LSD=1\r
+ if (comp<0) DFBYTE(result, 0)|=0x80; // set sign bit\r
+ return result;\r
+ } // decFloatCompare\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decFloatCompareSignal -- compare two decFloats; all NaNs signal */\r
+/* */\r
+/* result gets the result of comparing dfl and dfr */\r
+/* dfl is the first decFloat (lhs) */\r
+/* dfr is the second decFloat (rhs) */\r
+/* set is the context */\r
+/* returns result, which may be -1, 0, 1, or NaN (Unordered) */\r
+/* ------------------------------------------------------------------ */\r
+decFloat * decFloatCompareSignal(decFloat *result,\r
+ const decFloat *dfl, const decFloat *dfr,\r
+ decContext *set) {\r
+ Int comp; // work\r
+ // NaNs are handled as usual, except that all NaNs signal\r
+ if (DFISNAN(dfl) || DFISNAN(dfr)) {\r
+ set->status|=DEC_Invalid_operation;\r
+ return decNaNs(result, dfl, dfr, set);\r
+ }\r
+ // numeric comparison needed\r
+ comp=decNumCompare(dfl, dfr, 0);\r
+ decFloatZero(result);\r
+ if (comp==0) return result;\r
+ DFBYTE(result, DECBYTES-1)=0x01; // LSD=1\r
+ if (comp<0) DFBYTE(result, 0)|=0x80; // set sign bit\r
+ return result;\r
+ } // decFloatCompareSignal\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decFloatCompareTotal -- compare two decFloats with total ordering */\r
+/* */\r
+/* result gets the result of comparing dfl and dfr */\r
+/* dfl is the first decFloat (lhs) */\r
+/* dfr is the second decFloat (rhs) */\r
+/* returns result, which may be -1, 0, or 1 */\r
+/* ------------------------------------------------------------------ */\r
+decFloat * decFloatCompareTotal(decFloat *result,\r
+ const decFloat *dfl, const decFloat *dfr) {\r
+ Int comp; // work\r
+ uInt uiwork; // for macros\r
+ #if QUAD\r
+ uShort uswork; // ..\r
+ #endif\r
+ if (DFISNAN(dfl) || DFISNAN(dfr)) {\r
+ Int nanl, nanr; // work\r
+ // morph NaNs to +/- 1 or 2, leave numbers as 0\r
+ nanl=DFISSNAN(dfl)+DFISQNAN(dfl)*2; // quiet > signalling\r
+ if (DFISSIGNED(dfl)) nanl=-nanl;\r
+ nanr=DFISSNAN(dfr)+DFISQNAN(dfr)*2;\r
+ if (DFISSIGNED(dfr)) nanr=-nanr;\r
+ if (nanl>nanr) comp=+1;\r
+ else if (nanl<nanr) comp=-1;\r
+ else { // NaNs are the same type and sign .. must compare payload\r
+ // buffers need +2 for QUAD\r
+ uByte bufl[DECPMAX+4]; // for LHS coefficient + foot\r
+ uByte bufr[DECPMAX+4]; // for RHS coefficient + foot\r
+ uByte *ub, *uc; // work\r
+ Int sigl; // signum of LHS\r
+ sigl=(DFISSIGNED(dfl) ? -1 : +1);\r
+\r
+ // decode the coefficients\r
+ // (shift both right two if Quad to make a multiple of four)\r
+ #if QUAD\r
+ UBFROMUS(bufl, 0);\r
+ UBFROMUS(bufr, 0);\r
+ #endif\r
+ GETCOEFF(dfl, bufl+QUAD*2); // decode from decFloat\r
+ GETCOEFF(dfr, bufr+QUAD*2); // ..\r
+ // all multiples of four, here\r
+ comp=0; // assume equal\r
+ for (ub=bufl, uc=bufr; ub<bufl+DECPMAX+QUAD*2; ub+=4, uc+=4) {\r
+ uInt ui=UBTOUI(ub);\r
+ if (ui==UBTOUI(uc)) continue; // so far so same\r
+ // about to find a winner; go by bytes in case little-endian\r
+ for (;; ub++, uc++) {\r
+ if (*ub==*uc) continue;\r
+ if (*ub>*uc) comp=sigl; // difference found\r
+ else comp=-sigl; // ..\r
+ break;\r
+ }\r
+ }\r
+ } // same NaN type and sign\r
+ }\r
+ else {\r
+ // numeric comparison needed\r
+ comp=decNumCompare(dfl, dfr, 1); // total ordering\r
+ }\r
+ decFloatZero(result);\r
+ if (comp==0) return result;\r
+ DFBYTE(result, DECBYTES-1)=0x01; // LSD=1\r
+ if (comp<0) DFBYTE(result, 0)|=0x80; // set sign bit\r
+ return result;\r
+ } // decFloatCompareTotal\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decFloatCompareTotalMag -- compare magnitudes with total ordering */\r
+/* */\r
+/* result gets the result of comparing abs(dfl) and abs(dfr) */\r
+/* dfl is the first decFloat (lhs) */\r
+/* dfr is the second decFloat (rhs) */\r
+/* returns result, which may be -1, 0, or 1 */\r
+/* ------------------------------------------------------------------ */\r
+decFloat * decFloatCompareTotalMag(decFloat *result,\r
+ const decFloat *dfl, const decFloat *dfr) {\r
+ decFloat a, b; // for copy if needed\r
+ // copy and redirect signed operand(s)\r
+ if (DFISSIGNED(dfl)) {\r
+ decFloatCopyAbs(&a, dfl);\r
+ dfl=&a;\r
+ }\r
+ if (DFISSIGNED(dfr)) {\r
+ decFloatCopyAbs(&b, dfr);\r
+ dfr=&b;\r
+ }\r
+ return decFloatCompareTotal(result, dfl, dfr);\r
+ } // decFloatCompareTotalMag\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decFloatCopy -- copy a decFloat as-is */\r
+/* */\r
+/* result gets the copy of dfl */\r
+/* dfl is the decFloat to copy */\r
+/* returns result */\r
+/* */\r
+/* This is a bitwise operation; no errors or exceptions are possible. */\r
+/* ------------------------------------------------------------------ */\r
+decFloat * decFloatCopy(decFloat *result, const decFloat *dfl) {\r
+ if (dfl!=result) *result=*dfl; // copy needed\r
+ return result;\r
+ } // decFloatCopy\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decFloatCopyAbs -- copy a decFloat as-is and set sign bit to 0 */\r
+/* */\r
+/* result gets the copy of dfl with sign bit 0 */\r
+/* dfl is the decFloat to copy */\r
+/* returns result */\r
+/* */\r
+/* This is a bitwise operation; no errors or exceptions are possible. */\r
+/* ------------------------------------------------------------------ */\r
+decFloat * decFloatCopyAbs(decFloat *result, const decFloat *dfl) {\r
+ if (dfl!=result) *result=*dfl; // copy needed\r
+ DFBYTE(result, 0)&=~0x80; // zero sign bit\r
+ return result;\r
+ } // decFloatCopyAbs\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decFloatCopyNegate -- copy a decFloat as-is with inverted sign bit */\r
+/* */\r
+/* result gets the copy of dfl with sign bit inverted */\r
+/* dfl is the decFloat to copy */\r
+/* returns result */\r
+/* */\r
+/* This is a bitwise operation; no errors or exceptions are possible. */\r
+/* ------------------------------------------------------------------ */\r
+decFloat * decFloatCopyNegate(decFloat *result, const decFloat *dfl) {\r
+ if (dfl!=result) *result=*dfl; // copy needed\r
+ DFBYTE(result, 0)^=0x80; // invert sign bit\r
+ return result;\r
+ } // decFloatCopyNegate\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decFloatCopySign -- copy a decFloat with the sign of another */\r
+/* */\r
+/* result gets the result of copying dfl with the sign of dfr */\r
+/* dfl is the first decFloat (lhs) */\r
+/* dfr is the second decFloat (rhs) */\r
+/* returns result */\r
+/* */\r
+/* This is a bitwise operation; no errors or exceptions are possible. */\r
+/* ------------------------------------------------------------------ */\r
+decFloat * decFloatCopySign(decFloat *result,\r
+ const decFloat *dfl, const decFloat *dfr) {\r
+ uByte sign=(uByte)(DFBYTE(dfr, 0)&0x80); // save sign bit\r
+ if (dfl!=result) *result=*dfl; // copy needed\r
+ DFBYTE(result, 0)&=~0x80; // clear sign ..\r
+ DFBYTE(result, 0)=(uByte)(DFBYTE(result, 0)|sign); // .. and set saved\r
+ return result;\r
+ } // decFloatCopySign\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decFloatDigits -- return the number of digits in a decFloat */\r
+/* */\r
+/* df is the decFloat to investigate */\r
+/* returns the number of significant digits in the decFloat; a */\r
+/* zero coefficient returns 1 as does an infinity (a NaN returns */\r
+/* the number of digits in the payload) */\r
+/* ------------------------------------------------------------------ */\r
+// private macro to extract a declet according to provided formula\r
+// (form), and if it is non-zero then return the calculated digits\r
+// depending on the declet number (n), where n=0 for the most\r
+// significant declet; uses uInt dpd for work\r
+#define dpdlenchk(n, form) dpd=(form)&0x3ff; \\r
+ if (dpd) return (DECPMAX-1-3*(n))-(3-DPD2BCD8[dpd*4+3])\r
+// next one is used when it is known that the declet must be\r
+// non-zero, or is the final zero declet\r
+#define dpdlendun(n, form) dpd=(form)&0x3ff; \\r
+ if (dpd==0) return 1; \\r
+ return (DECPMAX-1-3*(n))-(3-DPD2BCD8[dpd*4+3])\r
+\r
+uInt decFloatDigits(const decFloat *df) {\r
+ uInt dpd; // work\r
+ uInt sourhi=DFWORD(df, 0); // top word from source decFloat\r
+ #if QUAD\r
+ uInt sourmh, sourml;\r
+ #endif\r
+ uInt sourlo;\r
+\r
+ if (DFISINF(df)) return 1;\r
+ // A NaN effectively has an MSD of 0; otherwise if non-zero MSD\r
+ // then the coefficient is full-length\r
+ if (!DFISNAN(df) && DECCOMBMSD[sourhi>>26]) return DECPMAX;\r
+\r
+ #if DOUBLE\r
+ if (sourhi&0x0003ffff) { // ends in first\r
+ dpdlenchk(0, sourhi>>8);\r
+ sourlo=DFWORD(df, 1);\r
+ dpdlendun(1, (sourhi<<2) | (sourlo>>30));\r
+ } // [cannot drop through]\r
+ sourlo=DFWORD(df, 1); // sourhi not involved now\r
+ if (sourlo&0xfff00000) { // in one of first two\r
+ dpdlenchk(1, sourlo>>30); // very rare\r
+ dpdlendun(2, sourlo>>20);\r
+ } // [cannot drop through]\r
+ dpdlenchk(3, sourlo>>10);\r
+ dpdlendun(4, sourlo);\r
+ // [cannot drop through]\r
+\r
+ #elif QUAD\r
+ if (sourhi&0x00003fff) { // ends in first\r
+ dpdlenchk(0, sourhi>>4);\r
+ sourmh=DFWORD(df, 1);\r
+ dpdlendun(1, ((sourhi)<<6) | (sourmh>>26));\r
+ } // [cannot drop through]\r
+ sourmh=DFWORD(df, 1);\r
+ if (sourmh) {\r
+ dpdlenchk(1, sourmh>>26);\r
+ dpdlenchk(2, sourmh>>16);\r
+ dpdlenchk(3, sourmh>>6);\r
+ sourml=DFWORD(df, 2);\r
+ dpdlendun(4, ((sourmh)<<4) | (sourml>>28));\r
+ } // [cannot drop through]\r
+ sourml=DFWORD(df, 2);\r
+ if (sourml) {\r
+ dpdlenchk(4, sourml>>28);\r
+ dpdlenchk(5, sourml>>18);\r
+ dpdlenchk(6, sourml>>8);\r
+ sourlo=DFWORD(df, 3);\r
+ dpdlendun(7, ((sourml)<<2) | (sourlo>>30));\r
+ } // [cannot drop through]\r
+ sourlo=DFWORD(df, 3);\r
+ if (sourlo&0xfff00000) { // in one of first two\r
+ dpdlenchk(7, sourlo>>30); // very rare\r
+ dpdlendun(8, sourlo>>20);\r
+ } // [cannot drop through]\r
+ dpdlenchk(9, sourlo>>10);\r
+ dpdlendun(10, sourlo);\r
+ // [cannot drop through]\r
+ #endif\r
+ } // decFloatDigits\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decFloatDivide -- divide a decFloat by another */\r
+/* */\r
+/* result gets the result of dividing dfl by dfr: */\r
+/* dfl is the first decFloat (lhs) */\r
+/* dfr is the second decFloat (rhs) */\r
+/* set is the context */\r
+/* returns result */\r
+/* */\r
+/* ------------------------------------------------------------------ */\r
+// This is just a wrapper.\r
+decFloat * decFloatDivide(decFloat *result,\r
+ const decFloat *dfl, const decFloat *dfr,\r
+ decContext *set) {\r
+ return decDivide(result, dfl, dfr, set, DIVIDE);\r
+ } // decFloatDivide\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decFloatDivideInteger -- integer divide a decFloat by another */\r
+/* */\r
+/* result gets the result of dividing dfl by dfr: */\r
+/* dfl is the first decFloat (lhs) */\r
+/* dfr is the second decFloat (rhs) */\r
+/* set is the context */\r
+/* returns result */\r
+/* */\r
+/* ------------------------------------------------------------------ */\r
+decFloat * decFloatDivideInteger(decFloat *result,\r
+ const decFloat *dfl, const decFloat *dfr,\r
+ decContext *set) {\r
+ return decDivide(result, dfl, dfr, set, DIVIDEINT);\r
+ } // decFloatDivideInteger\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decFloatFMA -- multiply and add three decFloats, fused */\r
+/* */\r
+/* result gets the result of (dfl*dfr)+dff with a single rounding */\r
+/* dfl is the first decFloat (lhs) */\r
+/* dfr is the second decFloat (rhs) */\r
+/* dff is the final decFloat (fhs) */\r
+/* set is the context */\r
+/* returns result */\r
+/* */\r
+/* ------------------------------------------------------------------ */\r
+decFloat * decFloatFMA(decFloat *result, const decFloat *dfl,\r
+ const decFloat *dfr, const decFloat *dff,\r
+ decContext *set) {\r
+\r
+ // The accumulator has the bytes needed for FiniteMultiply, plus\r
+ // one byte to the left in case of carry, plus DECPMAX+2 to the\r
+ // right for the final addition (up to full fhs + round & sticky)\r
+ #define FMALEN (ROUNDUP4(1+ (DECPMAX9*18+1) +DECPMAX+2))\r
+ uByte acc[FMALEN]; // for multiplied coefficient in BCD\r
+ // .. and for final result\r
+ bcdnum mul; // for multiplication result\r
+ bcdnum fin; // for final operand, expanded\r
+ uByte coe[ROUNDUP4(DECPMAX)]; // dff coefficient in BCD\r
+ bcdnum *hi, *lo; // bcdnum with higher/lower exponent\r
+ uInt diffsign; // non-zero if signs differ\r
+ uInt hipad; // pad digit for hi if needed\r
+ Int padding; // excess exponent\r
+ uInt carry; // +1 for ten's complement and during add\r
+ uByte *ub, *uh, *ul; // work\r
+ uInt uiwork; // for macros\r
+\r
+ // handle all the special values [any special operand leads to a\r
+ // special result]\r
+ if (DFISSPECIAL(dfl) || DFISSPECIAL(dfr) || DFISSPECIAL(dff)) {\r
+ decFloat proxy; // multiplication result proxy\r
+ // NaNs are handled as usual, giving priority to sNaNs\r
+ if (DFISSNAN(dfl) || DFISSNAN(dfr)) return decNaNs(result, dfl, dfr, set);\r
+ if (DFISSNAN(dff)) return decNaNs(result, dff, NULL, set);\r
+ if (DFISNAN(dfl) || DFISNAN(dfr)) return decNaNs(result, dfl, dfr, set);\r
+ if (DFISNAN(dff)) return decNaNs(result, dff, NULL, set);\r
+ // One or more of the three is infinite\r
+ // infinity times zero is bad\r
+ decFloatZero(&proxy);\r
+ if (DFISINF(dfl)) {\r
+ if (DFISZERO(dfr)) return decInvalid(result, set);\r
+ decInfinity(&proxy, &proxy);\r
+ }\r
+ else if (DFISINF(dfr)) {\r
+ if (DFISZERO(dfl)) return decInvalid(result, set);\r
+ decInfinity(&proxy, &proxy);\r
+ }\r
+ // compute sign of multiplication and place in proxy\r
+ DFWORD(&proxy, 0)|=(DFWORD(dfl, 0)^DFWORD(dfr, 0))&DECFLOAT_Sign;\r
+ if (!DFISINF(dff)) return decFloatCopy(result, &proxy);\r
+ // dff is Infinite\r
+ if (!DFISINF(&proxy)) return decInfinity(result, dff);\r
+ // both sides of addition are infinite; different sign is bad\r
+ if ((DFWORD(dff, 0)&DECFLOAT_Sign)!=(DFWORD(&proxy, 0)&DECFLOAT_Sign))\r
+ return decInvalid(result, set);\r
+ return decFloatCopy(result, &proxy);\r
+ }\r
+\r
+ /* Here when all operands are finite */\r
+\r
+ // First multiply dfl*dfr\r
+ decFiniteMultiply(&mul, acc+1, dfl, dfr);\r
+ // The multiply is complete, exact and unbounded, and described in\r
+ // mul with the coefficient held in acc[1...]\r
+\r
+ // now add in dff; the algorithm is essentially the same as\r
+ // decFloatAdd, but the code is different because the code there\r
+ // is highly optimized for adding two numbers of the same size\r
+ fin.exponent=GETEXPUN(dff); // get dff exponent and sign\r
+ fin.sign=DFWORD(dff, 0)&DECFLOAT_Sign;\r
+ diffsign=mul.sign^fin.sign; // note if signs differ\r
+ fin.msd=coe;\r
+ fin.lsd=coe+DECPMAX-1;\r
+ GETCOEFF(dff, coe); // extract the coefficient\r
+\r
+ // now set hi and lo so that hi points to whichever of mul and fin\r
+ // has the higher exponent and lo points to the other [don't care,\r
+ // if the same]. One coefficient will be in acc, the other in coe.\r
+ if (mul.exponent>=fin.exponent) {\r
+ hi=&mul;\r
+ lo=&fin;\r
+ }\r
+ else {\r
+ hi=&fin;\r
+ lo=&mul;\r
+ }\r
+\r
+ // remove leading zeros on both operands; this will save time later\r
+ // and make testing for zero trivial (tests are safe because acc\r
+ // and coe are rounded up to uInts)\r
+ for (; UBTOUI(hi->msd)==0 && hi->msd+3<hi->lsd;) hi->msd+=4;\r
+ for (; *hi->msd==0 && hi->msd<hi->lsd;) hi->msd++;\r
+ for (; UBTOUI(lo->msd)==0 && lo->msd+3<lo->lsd;) lo->msd+=4;\r
+ for (; *lo->msd==0 && lo->msd<lo->lsd;) lo->msd++;\r
+\r
+ // if hi is zero then result will be lo (which has the smaller\r
+ // exponent), which also may need to be tested for zero for the\r
+ // weird IEEE 754 sign rules\r
+ if (*hi->msd==0) { // hi is zero\r
+ // "When the sum of two operands with opposite signs is\r
+ // exactly zero, the sign of that sum shall be '+' in all\r
+ // rounding modes except round toward -Infinity, in which\r
+ // mode that sign shall be '-'."\r
+ if (diffsign) {\r
+ if (*lo->msd==0) { // lo is zero\r
+ lo->sign=0;\r
+ if (set->round==DEC_ROUND_FLOOR) lo->sign=DECFLOAT_Sign;\r
+ } // diffsign && lo=0\r
+ } // diffsign\r
+ return decFinalize(result, lo, set); // may need clamping\r
+ } // numfl is zero\r
+ // [here, both are minimal length and hi is non-zero]\r
+ // (if lo is zero then padding with zeros may be needed, below)\r
+\r
+ // if signs differ, take the ten's complement of hi (zeros to the\r
+ // right do not matter because the complement of zero is zero); the\r
+ // +1 is done later, as part of the addition, inserted at the\r
+ // correct digit\r
+ hipad=0;\r
+ carry=0;\r
+ if (diffsign) {\r
+ hipad=9;\r
+ carry=1;\r
+ // exactly the correct number of digits must be inverted\r
+ for (uh=hi->msd; uh<hi->lsd-3; uh+=4) UBFROMUI(uh, 0x09090909-UBTOUI(uh));\r
+ for (; uh<=hi->lsd; uh++) *uh=(uByte)(0x09-*uh);\r
+ }\r
+\r
+ // ready to add; note that hi has no leading zeros so gap\r
+ // calculation does not have to be as pessimistic as in decFloatAdd\r
+ // (this is much more like the arbitrary-precision algorithm in\r
+ // Rexx and decNumber)\r
+\r
+ // padding is the number of zeros that would need to be added to hi\r
+ // for its lsd to be aligned with the lsd of lo\r
+ padding=hi->exponent-lo->exponent;\r
+ // printf("FMA pad %ld\n", (LI)padding);\r
+\r
+ // the result of the addition will be built into the accumulator,\r
+ // starting from the far right; this could be either hi or lo, and\r
+ // will be aligned\r
+ ub=acc+FMALEN-1; // where lsd of result will go\r
+ ul=lo->lsd; // lsd of rhs\r
+\r
+ if (padding!=0) { // unaligned\r
+ // if the msd of lo is more than DECPMAX+2 digits to the right of\r
+ // the original msd of hi then it can be reduced to a single\r
+ // digit at the right place, as it stays clear of hi digits\r
+ // [it must be DECPMAX+2 because during a subtraction the msd\r
+ // could become 0 after a borrow from 1.000 to 0.9999...]\r
+\r
+ Int hilen=(Int)(hi->lsd-hi->msd+1); // length of hi\r
+ Int lolen=(Int)(lo->lsd-lo->msd+1); // and of lo\r
+\r
+ if (hilen+padding-lolen > DECPMAX+2) { // can reduce lo to single\r
+ // make sure it is virtually at least DECPMAX from hi->msd, at\r
+ // least to right of hi->lsd (in case of destructive subtract),\r
+ // and separated by at least two digits from either of those\r
+ // (the tricky DOUBLE case is when hi is a 1 that will become a\r
+ // 0.9999... by subtraction:\r
+ // hi: 1 E+16\r
+ // lo: .................1000000000000000 E-16\r
+ // which for the addition pads to:\r
+ // hi: 1000000000000000000 E-16\r
+ // lo: .................1000000000000000 E-16\r
+ Int newexp=MINI(hi->exponent, hi->exponent+hilen-DECPMAX)-3;\r
+\r
+ // printf("FMA reduce: %ld\n", (LI)reduce);\r
+ lo->lsd=lo->msd; // to single digit [maybe 0]\r
+ lo->exponent=newexp; // new lowest exponent\r
+ padding=hi->exponent-lo->exponent; // recalculate\r
+ ul=lo->lsd; // .. and repoint\r
+ }\r
+\r
+ // padding is still > 0, but will fit in acc (less leading carry slot)\r
+ #if DECCHECK\r
+ if (padding<=0) printf("FMA low padding: %ld\n", (LI)padding);\r
+ if (hilen+padding+1>FMALEN)\r
+ printf("FMA excess hilen+padding: %ld+%ld \n", (LI)hilen, (LI)padding);\r
+ // printf("FMA padding: %ld\n", (LI)padding);\r
+ #endif\r
+\r
+ // padding digits can now be set in the result; one or more of\r
+ // these will come from lo; others will be zeros in the gap\r
+ for (; ul-3>=lo->msd && padding>3; padding-=4, ul-=4, ub-=4) {\r
+ UBFROMUI(ub-3, UBTOUI(ul-3)); // [cannot overlap]\r
+ }\r
+ for (; ul>=lo->msd && padding>0; padding--, ul--, ub--) *ub=*ul;\r
+ for (;padding>0; padding--, ub--) *ub=0; // mind the gap\r
+ }\r
+\r
+ // addition now complete to the right of the rightmost digit of hi\r
+ uh=hi->lsd;\r
+\r
+ // dow do the add from hi->lsd to the left\r
+ // [bytewise, because either operand can run out at any time]\r
+ // carry was set up depending on ten's complement above\r
+ // first assume both operands have some digits\r
+ for (;; ub--) {\r
+ if (uh<hi->msd || ul<lo->msd) break;\r
+ *ub=(uByte)(carry+(*uh--)+(*ul--));\r
+ carry=0;\r
+ if (*ub<10) continue;\r
+ *ub-=10;\r
+ carry=1;\r
+ } // both loop\r
+\r
+ if (ul<lo->msd) { // to left of lo\r
+ for (;; ub--) {\r
+ if (uh<hi->msd) break;\r
+ *ub=(uByte)(carry+(*uh--)); // [+0]\r
+ carry=0;\r
+ if (*ub<10) continue;\r
+ *ub-=10;\r
+ carry=1;\r
+ } // hi loop\r
+ }\r
+ else { // to left of hi\r
+ for (;; ub--) {\r
+ if (ul<lo->msd) break;\r
+ *ub=(uByte)(carry+hipad+(*ul--));\r
+ carry=0;\r
+ if (*ub<10) continue;\r
+ *ub-=10;\r
+ carry=1;\r
+ } // lo loop\r
+ }\r
+\r
+ // addition complete -- now handle carry, borrow, etc.\r
+ // use lo to set up the num (its exponent is already correct, and\r
+ // sign usually is)\r
+ lo->msd=ub+1;\r
+ lo->lsd=acc+FMALEN-1;\r
+ // decShowNum(lo, "lo");\r
+ if (!diffsign) { // same-sign addition\r
+ if (carry) { // carry out\r
+ *ub=1; // place the 1 ..\r
+ lo->msd--; // .. and update\r
+ }\r
+ } // same sign\r
+ else { // signs differed (subtraction)\r
+ if (!carry) { // no carry out means hi<lo\r
+ // borrowed -- take ten's complement of the right digits\r
+ lo->sign=hi->sign; // sign is lhs sign\r
+ for (ul=lo->msd; ul<lo->lsd-3; ul+=4) UBFROMUI(ul, 0x09090909-UBTOUI(ul));\r
+ for (; ul<=lo->lsd; ul++) *ul=(uByte)(0x09-*ul); // [leaves ul at lsd+1]\r
+ // complete the ten's complement by adding 1 [cannot overrun]\r
+ for (ul--; *ul==9; ul--) *ul=0;\r
+ *ul+=1;\r
+ } // borrowed\r
+ else { // carry out means hi>=lo\r
+ // sign to use is lo->sign\r
+ // all done except for the special IEEE 754 exact-zero-result\r
+ // rule (see above); while testing for zero, strip leading\r
+ // zeros (which will save decFinalize doing it)\r
+ for (; UBTOUI(lo->msd)==0 && lo->msd+3<lo->lsd;) lo->msd+=4;\r
+ for (; *lo->msd==0 && lo->msd<lo->lsd;) lo->msd++;\r
+ if (*lo->msd==0) { // must be true zero (and diffsign)\r
+ lo->sign=0; // assume +\r
+ if (set->round==DEC_ROUND_FLOOR) lo->sign=DECFLOAT_Sign;\r
+ }\r
+ // [else was not zero, might still have leading zeros]\r
+ } // subtraction gave positive result\r
+ } // diffsign\r
+\r
+ #if DECCHECK\r
+ // assert no left underrun\r
+ if (lo->msd<acc) {\r
+ printf("FMA underrun by %ld \n", (LI)(acc-lo->msd));\r
+ }\r
+ #endif\r
+\r
+ return decFinalize(result, lo, set); // round, check, and lay out\r
+ } // decFloatFMA\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decFloatFromInt -- initialise a decFloat from an Int */\r
+/* */\r
+/* result gets the converted Int */\r
+/* n is the Int to convert */\r
+/* returns result */\r
+/* */\r
+/* The result is Exact; no errors or exceptions are possible. */\r
+/* ------------------------------------------------------------------ */\r
+decFloat * decFloatFromInt32(decFloat *result, Int n) {\r
+ uInt u=(uInt)n; // copy as bits\r
+ uInt encode; // work\r
+ DFWORD(result, 0)=ZEROWORD; // always\r
+ #if QUAD\r
+ DFWORD(result, 1)=0;\r
+ DFWORD(result, 2)=0;\r
+ #endif\r
+ if (n<0) { // handle -n with care\r
+ // [This can be done without the test, but is then slightly slower]\r
+ u=(~u)+1;\r
+ DFWORD(result, 0)|=DECFLOAT_Sign;\r
+ }\r
+ // Since the maximum value of u now is 2**31, only the low word of\r
+ // result is affected\r
+ encode=BIN2DPD[u%1000];\r
+ u/=1000;\r
+ encode|=BIN2DPD[u%1000]<<10;\r
+ u/=1000;\r
+ encode|=BIN2DPD[u%1000]<<20;\r
+ u/=1000; // now 0, 1, or 2\r
+ encode|=u<<30;\r
+ DFWORD(result, DECWORDS-1)=encode;\r
+ return result;\r
+ } // decFloatFromInt32\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decFloatFromUInt -- initialise a decFloat from a uInt */\r
+/* */\r
+/* result gets the converted uInt */\r
+/* n is the uInt to convert */\r
+/* returns result */\r
+/* */\r
+/* The result is Exact; no errors or exceptions are possible. */\r
+/* ------------------------------------------------------------------ */\r
+decFloat * decFloatFromUInt32(decFloat *result, uInt u) {\r
+ uInt encode; // work\r
+ DFWORD(result, 0)=ZEROWORD; // always\r
+ #if QUAD\r
+ DFWORD(result, 1)=0;\r
+ DFWORD(result, 2)=0;\r
+ #endif\r
+ encode=BIN2DPD[u%1000];\r
+ u/=1000;\r
+ encode|=BIN2DPD[u%1000]<<10;\r
+ u/=1000;\r
+ encode|=BIN2DPD[u%1000]<<20;\r
+ u/=1000; // now 0 -> 4\r
+ encode|=u<<30;\r
+ DFWORD(result, DECWORDS-1)=encode;\r
+ DFWORD(result, DECWORDS-2)|=u>>2; // rarely non-zero\r
+ return result;\r
+ } // decFloatFromUInt32\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decFloatInvert -- logical digitwise INVERT of a decFloat */\r
+/* */\r
+/* result gets the result of INVERTing df */\r
+/* df is the decFloat to invert */\r
+/* set is the context */\r
+/* returns result, which will be canonical with sign=0 */\r
+/* */\r
+/* The operand must be positive, finite with exponent q=0, and */\r
+/* comprise just zeros and ones; if not, Invalid operation results. */\r
+/* ------------------------------------------------------------------ */\r
+decFloat * decFloatInvert(decFloat *result, const decFloat *df,\r
+ decContext *set) {\r
+ uInt sourhi=DFWORD(df, 0); // top word of dfs\r
+\r
+ if (!DFISUINT01(df) || !DFISCC01(df)) return decInvalid(result, set);\r
+ // the operand is a finite integer (q=0)\r
+ #if DOUBLE\r
+ DFWORD(result, 0)=ZEROWORD|((~sourhi)&0x04009124);\r
+ DFWORD(result, 1)=(~DFWORD(df, 1)) &0x49124491;\r
+ #elif QUAD\r
+ DFWORD(result, 0)=ZEROWORD|((~sourhi)&0x04000912);\r
+ DFWORD(result, 1)=(~DFWORD(df, 1)) &0x44912449;\r
+ DFWORD(result, 2)=(~DFWORD(df, 2)) &0x12449124;\r
+ DFWORD(result, 3)=(~DFWORD(df, 3)) &0x49124491;\r
+ #endif\r
+ return result;\r
+ } // decFloatInvert\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decFloatIs -- decFloat tests (IsSigned, etc.) */\r
+/* */\r
+/* df is the decFloat to test */\r
+/* returns 0 or 1 in a uInt */\r
+/* */\r
+/* Many of these could be macros, but having them as real functions */\r
+/* is a little cleaner (and they can be referred to here by the */\r
+/* generic names) */\r
+/* ------------------------------------------------------------------ */\r
+uInt decFloatIsCanonical(const decFloat *df) {\r
+ if (DFISSPECIAL(df)) {\r
+ if (DFISINF(df)) {\r
+ if (DFWORD(df, 0)&ECONMASK) return 0; // exponent continuation\r
+ if (!DFISCCZERO(df)) return 0; // coefficient continuation\r
+ return 1;\r
+ }\r
+ // is a NaN\r
+ if (DFWORD(df, 0)&ECONNANMASK) return 0; // exponent continuation\r
+ if (DFISCCZERO(df)) return 1; // coefficient continuation\r
+ // drop through to check payload\r
+ }\r
+ { // declare block\r
+ #if DOUBLE\r
+ uInt sourhi=DFWORD(df, 0);\r
+ uInt sourlo=DFWORD(df, 1);\r
+ if (CANONDPDOFF(sourhi, 8)\r
+ && CANONDPDTWO(sourhi, sourlo, 30)\r
+ && CANONDPDOFF(sourlo, 20)\r
+ && CANONDPDOFF(sourlo, 10)\r
+ && CANONDPDOFF(sourlo, 0)) return 1;\r
+ #elif QUAD\r
+ uInt sourhi=DFWORD(df, 0);\r
+ uInt sourmh=DFWORD(df, 1);\r
+ uInt sourml=DFWORD(df, 2);\r
+ uInt sourlo=DFWORD(df, 3);\r
+ if (CANONDPDOFF(sourhi, 4)\r
+ && CANONDPDTWO(sourhi, sourmh, 26)\r
+ && CANONDPDOFF(sourmh, 16)\r
+ && CANONDPDOFF(sourmh, 6)\r
+ && CANONDPDTWO(sourmh, sourml, 28)\r
+ && CANONDPDOFF(sourml, 18)\r
+ && CANONDPDOFF(sourml, 8)\r
+ && CANONDPDTWO(sourml, sourlo, 30)\r
+ && CANONDPDOFF(sourlo, 20)\r
+ && CANONDPDOFF(sourlo, 10)\r
+ && CANONDPDOFF(sourlo, 0)) return 1;\r
+ #endif\r
+ } // block\r
+ return 0; // a declet is non-canonical\r
+ }\r
+\r
+uInt decFloatIsFinite(const decFloat *df) {\r
+ return !DFISSPECIAL(df);\r
+ }\r
+uInt decFloatIsInfinite(const decFloat *df) {\r
+ return DFISINF(df);\r
+ }\r
+uInt decFloatIsInteger(const decFloat *df) {\r
+ return DFISINT(df);\r
+ }\r
+uInt decFloatIsLogical(const decFloat *df) {\r
+ return DFISUINT01(df) & DFISCC01(df);\r
+ }\r
+uInt decFloatIsNaN(const decFloat *df) {\r
+ return DFISNAN(df);\r
+ }\r
+uInt decFloatIsNegative(const decFloat *df) {\r
+ return DFISSIGNED(df) && !DFISZERO(df) && !DFISNAN(df);\r
+ }\r
+uInt decFloatIsNormal(const decFloat *df) {\r
+ Int exp; // exponent\r
+ if (DFISSPECIAL(df)) return 0;\r
+ if (DFISZERO(df)) return 0;\r
+ // is finite and non-zero\r
+ exp=GETEXPUN(df) // get unbiased exponent ..\r
+ +decFloatDigits(df)-1; // .. and make adjusted exponent\r
+ return (exp>=DECEMIN); // < DECEMIN is subnormal\r
+ }\r
+uInt decFloatIsPositive(const decFloat *df) {\r
+ return !DFISSIGNED(df) && !DFISZERO(df) && !DFISNAN(df);\r
+ }\r
+uInt decFloatIsSignaling(const decFloat *df) {\r
+ return DFISSNAN(df);\r
+ }\r
+uInt decFloatIsSignalling(const decFloat *df) {\r
+ return DFISSNAN(df);\r
+ }\r
+uInt decFloatIsSigned(const decFloat *df) {\r
+ return DFISSIGNED(df);\r
+ }\r
+uInt decFloatIsSubnormal(const decFloat *df) {\r
+ if (DFISSPECIAL(df)) return 0;\r
+ // is finite\r
+ if (decFloatIsNormal(df)) return 0;\r
+ // it is <Nmin, but could be zero\r
+ if (DFISZERO(df)) return 0;\r
+ return 1; // is subnormal\r
+ }\r
+uInt decFloatIsZero(const decFloat *df) {\r
+ return DFISZERO(df);\r
+ } // decFloatIs...\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decFloatLogB -- return adjusted exponent, by 754 rules */\r
+/* */\r
+/* result gets the adjusted exponent as an integer, or a NaN etc. */\r
+/* df is the decFloat to be examined */\r
+/* set is the context */\r
+/* returns result */\r
+/* */\r
+/* Notable cases: */\r
+/* A<0 -> Use |A| */\r
+/* A=0 -> -Infinity (Division by zero) */\r
+/* A=Infinite -> +Infinity (Exact) */\r
+/* A=1 exactly -> 0 (Exact) */\r
+/* NaNs are propagated as usual */\r
+/* ------------------------------------------------------------------ */\r
+decFloat * decFloatLogB(decFloat *result, const decFloat *df,\r
+ decContext *set) {\r
+ Int ae; // adjusted exponent\r
+ if (DFISNAN(df)) return decNaNs(result, df, NULL, set);\r
+ if (DFISINF(df)) {\r
+ DFWORD(result, 0)=0; // need +ve\r
+ return decInfinity(result, result); // canonical +Infinity\r
+ }\r
+ if (DFISZERO(df)) {\r
+ set->status|=DEC_Division_by_zero; // as per 754\r
+ DFWORD(result, 0)=DECFLOAT_Sign; // make negative\r
+ return decInfinity(result, result); // canonical -Infinity\r
+ }\r
+ ae=GETEXPUN(df) // get unbiased exponent ..\r
+ +decFloatDigits(df)-1; // .. and make adjusted exponent\r
+ // ae has limited range (3 digits for DOUBLE and 4 for QUAD), so\r
+ // it is worth using a special case of decFloatFromInt32\r
+ DFWORD(result, 0)=ZEROWORD; // always\r
+ if (ae<0) {\r
+ DFWORD(result, 0)|=DECFLOAT_Sign; // -0 so far\r
+ ae=-ae;\r
+ }\r
+ #if DOUBLE\r
+ DFWORD(result, 1)=BIN2DPD[ae]; // a single declet\r
+ #elif QUAD\r
+ DFWORD(result, 1)=0;\r
+ DFWORD(result, 2)=0;\r
+ DFWORD(result, 3)=(ae/1000)<<10; // is <10, so need no DPD encode\r
+ DFWORD(result, 3)|=BIN2DPD[ae%1000];\r
+ #endif\r
+ return result;\r
+ } // decFloatLogB\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decFloatMax -- return maxnum of two operands */\r
+/* */\r
+/* result gets the chosen decFloat */\r
+/* dfl is the first decFloat (lhs) */\r
+/* dfr is the second decFloat (rhs) */\r
+/* set is the context */\r
+/* returns result */\r
+/* */\r
+/* If just one operand is a quiet NaN it is ignored. */\r
+/* ------------------------------------------------------------------ */\r
+decFloat * decFloatMax(decFloat *result,\r
+ const decFloat *dfl, const decFloat *dfr,\r
+ decContext *set) {\r
+ Int comp;\r
+ if (DFISNAN(dfl)) {\r
+ // sNaN or both NaNs leads to normal NaN processing\r
+ if (DFISNAN(dfr) || DFISSNAN(dfl)) return decNaNs(result, dfl, dfr, set);\r
+ return decCanonical(result, dfr); // RHS is numeric\r
+ }\r
+ if (DFISNAN(dfr)) {\r
+ // sNaN leads to normal NaN processing (both NaN handled above)\r
+ if (DFISSNAN(dfr)) return decNaNs(result, dfl, dfr, set);\r
+ return decCanonical(result, dfl); // LHS is numeric\r
+ }\r
+ // Both operands are numeric; numeric comparison needed -- use\r
+ // total order for a well-defined choice (and +0 > -0)\r
+ comp=decNumCompare(dfl, dfr, 1);\r
+ if (comp>=0) return decCanonical(result, dfl);\r
+ return decCanonical(result, dfr);\r
+ } // decFloatMax\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decFloatMaxMag -- return maxnummag of two operands */\r
+/* */\r
+/* result gets the chosen decFloat */\r
+/* dfl is the first decFloat (lhs) */\r
+/* dfr is the second decFloat (rhs) */\r
+/* set is the context */\r
+/* returns result */\r
+/* */\r
+/* Returns according to the magnitude comparisons if both numeric and */\r
+/* unequal, otherwise returns maxnum */\r
+/* ------------------------------------------------------------------ */\r
+decFloat * decFloatMaxMag(decFloat *result,\r
+ const decFloat *dfl, const decFloat *dfr,\r
+ decContext *set) {\r
+ Int comp;\r
+ decFloat absl, absr;\r
+ if (DFISNAN(dfl) || DFISNAN(dfr)) return decFloatMax(result, dfl, dfr, set);\r
+\r
+ decFloatCopyAbs(&absl, dfl);\r
+ decFloatCopyAbs(&absr, dfr);\r
+ comp=decNumCompare(&absl, &absr, 0);\r
+ if (comp>0) return decCanonical(result, dfl);\r
+ if (comp<0) return decCanonical(result, dfr);\r
+ return decFloatMax(result, dfl, dfr, set);\r
+ } // decFloatMaxMag\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decFloatMin -- return minnum of two operands */\r
+/* */\r
+/* result gets the chosen decFloat */\r
+/* dfl is the first decFloat (lhs) */\r
+/* dfr is the second decFloat (rhs) */\r
+/* set is the context */\r
+/* returns result */\r
+/* */\r
+/* If just one operand is a quiet NaN it is ignored. */\r
+/* ------------------------------------------------------------------ */\r
+decFloat * decFloatMin(decFloat *result,\r
+ const decFloat *dfl, const decFloat *dfr,\r
+ decContext *set) {\r
+ Int comp;\r
+ if (DFISNAN(dfl)) {\r
+ // sNaN or both NaNs leads to normal NaN processing\r
+ if (DFISNAN(dfr) || DFISSNAN(dfl)) return decNaNs(result, dfl, dfr, set);\r
+ return decCanonical(result, dfr); // RHS is numeric\r
+ }\r
+ if (DFISNAN(dfr)) {\r
+ // sNaN leads to normal NaN processing (both NaN handled above)\r
+ if (DFISSNAN(dfr)) return decNaNs(result, dfl, dfr, set);\r
+ return decCanonical(result, dfl); // LHS is numeric\r
+ }\r
+ // Both operands are numeric; numeric comparison needed -- use\r
+ // total order for a well-defined choice (and +0 > -0)\r
+ comp=decNumCompare(dfl, dfr, 1);\r
+ if (comp<=0) return decCanonical(result, dfl);\r
+ return decCanonical(result, dfr);\r
+ } // decFloatMin\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decFloatMinMag -- return minnummag of two operands */\r
+/* */\r
+/* result gets the chosen decFloat */\r
+/* dfl is the first decFloat (lhs) */\r
+/* dfr is the second decFloat (rhs) */\r
+/* set is the context */\r
+/* returns result */\r
+/* */\r
+/* Returns according to the magnitude comparisons if both numeric and */\r
+/* unequal, otherwise returns minnum */\r
+/* ------------------------------------------------------------------ */\r
+decFloat * decFloatMinMag(decFloat *result,\r
+ const decFloat *dfl, const decFloat *dfr,\r
+ decContext *set) {\r
+ Int comp;\r
+ decFloat absl, absr;\r
+ if (DFISNAN(dfl) || DFISNAN(dfr)) return decFloatMin(result, dfl, dfr, set);\r
+\r
+ decFloatCopyAbs(&absl, dfl);\r
+ decFloatCopyAbs(&absr, dfr);\r
+ comp=decNumCompare(&absl, &absr, 0);\r
+ if (comp<0) return decCanonical(result, dfl);\r
+ if (comp>0) return decCanonical(result, dfr);\r
+ return decFloatMin(result, dfl, dfr, set);\r
+ } // decFloatMinMag\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decFloatMinus -- negate value, heeding NaNs, etc. */\r
+/* */\r
+/* result gets the canonicalized 0-df */\r
+/* df is the decFloat to minus */\r
+/* set is the context */\r
+/* returns result */\r
+/* */\r
+/* This has the same effect as 0-df where the exponent of the zero is */\r
+/* the same as that of df (if df is finite). */\r
+/* The effect is also the same as decFloatCopyNegate except that NaNs */\r
+/* are handled normally (the sign of a NaN is not affected, and an */\r
+/* sNaN will signal), the result is canonical, and zero gets sign 0. */\r
+/* ------------------------------------------------------------------ */\r
+decFloat * decFloatMinus(decFloat *result, const decFloat *df,\r
+ decContext *set) {\r
+ if (DFISNAN(df)) return decNaNs(result, df, NULL, set);\r
+ decCanonical(result, df); // copy and check\r
+ if (DFISZERO(df)) DFBYTE(result, 0)&=~0x80; // turn off sign bit\r
+ else DFBYTE(result, 0)^=0x80; // flip sign bit\r
+ return result;\r
+ } // decFloatMinus\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decFloatMultiply -- multiply two decFloats */\r
+/* */\r
+/* result gets the result of multiplying dfl and dfr: */\r
+/* dfl is the first decFloat (lhs) */\r
+/* dfr is the second decFloat (rhs) */\r
+/* set is the context */\r
+/* returns result */\r
+/* */\r
+/* ------------------------------------------------------------------ */\r
+decFloat * decFloatMultiply(decFloat *result,\r
+ const decFloat *dfl, const decFloat *dfr,\r
+ decContext *set) {\r
+ bcdnum num; // for final conversion\r
+ uByte bcdacc[DECPMAX9*18+1]; // for coefficent in BCD\r
+\r
+ if (DFISSPECIAL(dfl) || DFISSPECIAL(dfr)) { // either is special?\r
+ // NaNs are handled as usual\r
+ if (DFISNAN(dfl) || DFISNAN(dfr)) return decNaNs(result, dfl, dfr, set);\r
+ // infinity times zero is bad\r
+ if (DFISINF(dfl) && DFISZERO(dfr)) return decInvalid(result, set);\r
+ if (DFISINF(dfr) && DFISZERO(dfl)) return decInvalid(result, set);\r
+ // both infinite; return canonical infinity with computed sign\r
+ DFWORD(result, 0)=DFWORD(dfl, 0)^DFWORD(dfr, 0); // compute sign\r
+ return decInfinity(result, result);\r
+ }\r
+\r
+ /* Here when both operands are finite */\r
+ decFiniteMultiply(&num, bcdacc, dfl, dfr);\r
+ return decFinalize(result, &num, set); // round, check, and lay out\r
+ } // decFloatMultiply\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decFloatNextMinus -- next towards -Infinity */\r
+/* */\r
+/* result gets the next lesser decFloat */\r
+/* dfl is the decFloat to start with */\r
+/* set is the context */\r
+/* returns result */\r
+/* */\r
+/* This is 754 nextdown; Invalid is the only status possible (from */\r
+/* an sNaN). */\r
+/* ------------------------------------------------------------------ */\r
+decFloat * decFloatNextMinus(decFloat *result, const decFloat *dfl,\r
+ decContext *set) {\r
+ decFloat delta; // tiny increment\r
+ uInt savestat; // saves status\r
+ enum rounding saveround; // .. and mode\r
+\r
+ // +Infinity is the special case\r
+ if (DFISINF(dfl) && !DFISSIGNED(dfl)) {\r
+ DFSETNMAX(result);\r
+ return result; // [no status to set]\r
+ }\r
+ // other cases are effected by sutracting a tiny delta -- this\r
+ // should be done in a wider format as the delta is unrepresentable\r
+ // here (but can be done with normal add if the sign of zero is\r
+ // treated carefully, because no Inexactitude is interesting);\r
+ // rounding to -Infinity then pushes the result to next below\r
+ decFloatZero(&delta); // set up tiny delta\r
+ DFWORD(&delta, DECWORDS-1)=1; // coefficient=1\r
+ DFWORD(&delta, 0)=DECFLOAT_Sign; // Sign=1 + biased exponent=0\r
+ // set up for the directional round\r
+ saveround=set->round; // save mode\r
+ set->round=DEC_ROUND_FLOOR; // .. round towards -Infinity\r
+ savestat=set->status; // save status\r
+ decFloatAdd(result, dfl, &delta, set);\r
+ // Add rules mess up the sign when going from +Ntiny to 0\r
+ if (DFISZERO(result)) DFWORD(result, 0)^=DECFLOAT_Sign; // correct\r
+ set->status&=DEC_Invalid_operation; // preserve only sNaN status\r
+ set->status|=savestat; // restore pending flags\r
+ set->round=saveround; // .. and mode\r
+ return result;\r
+ } // decFloatNextMinus\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decFloatNextPlus -- next towards +Infinity */\r
+/* */\r
+/* result gets the next larger decFloat */\r
+/* dfl is the decFloat to start with */\r
+/* set is the context */\r
+/* returns result */\r
+/* */\r
+/* This is 754 nextup; Invalid is the only status possible (from */\r
+/* an sNaN). */\r
+/* ------------------------------------------------------------------ */\r
+decFloat * decFloatNextPlus(decFloat *result, const decFloat *dfl,\r
+ decContext *set) {\r
+ uInt savestat; // saves status\r
+ enum rounding saveround; // .. and mode\r
+ decFloat delta; // tiny increment\r
+\r
+ // -Infinity is the special case\r
+ if (DFISINF(dfl) && DFISSIGNED(dfl)) {\r
+ DFSETNMAX(result);\r
+ DFWORD(result, 0)|=DECFLOAT_Sign; // make negative\r
+ return result; // [no status to set]\r
+ }\r
+ // other cases are effected by sutracting a tiny delta -- this\r
+ // should be done in a wider format as the delta is unrepresentable\r
+ // here (but can be done with normal add if the sign of zero is\r
+ // treated carefully, because no Inexactitude is interesting);\r
+ // rounding to +Infinity then pushes the result to next above\r
+ decFloatZero(&delta); // set up tiny delta\r
+ DFWORD(&delta, DECWORDS-1)=1; // coefficient=1\r
+ DFWORD(&delta, 0)=0; // Sign=0 + biased exponent=0\r
+ // set up for the directional round\r
+ saveround=set->round; // save mode\r
+ set->round=DEC_ROUND_CEILING; // .. round towards +Infinity\r
+ savestat=set->status; // save status\r
+ decFloatAdd(result, dfl, &delta, set);\r
+ // Add rules mess up the sign when going from -Ntiny to -0\r
+ if (DFISZERO(result)) DFWORD(result, 0)^=DECFLOAT_Sign; // correct\r
+ set->status&=DEC_Invalid_operation; // preserve only sNaN status\r
+ set->status|=savestat; // restore pending flags\r
+ set->round=saveround; // .. and mode\r
+ return result;\r
+ } // decFloatNextPlus\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decFloatNextToward -- next towards a decFloat */\r
+/* */\r
+/* result gets the next decFloat */\r
+/* dfl is the decFloat to start with */\r
+/* dfr is the decFloat to move toward */\r
+/* set is the context */\r
+/* returns result */\r
+/* */\r
+/* This is 754-1985 nextafter, as modified during revision (dropped */\r
+/* from 754-2008); status may be set unless the result is a normal */\r
+/* number. */\r
+/* ------------------------------------------------------------------ */\r
+decFloat * decFloatNextToward(decFloat *result,\r
+ const decFloat *dfl, const decFloat *dfr,\r
+ decContext *set) {\r
+ decFloat delta; // tiny increment or decrement\r
+ decFloat pointone; // 1e-1\r
+ uInt savestat; // saves status\r
+ enum rounding saveround; // .. and mode\r
+ uInt deltatop; // top word for delta\r
+ Int comp; // work\r
+\r
+ if (DFISNAN(dfl) || DFISNAN(dfr)) return decNaNs(result, dfl, dfr, set);\r
+ // Both are numeric, so Invalid no longer a possibility\r
+ comp=decNumCompare(dfl, dfr, 0);\r
+ if (comp==0) return decFloatCopySign(result, dfl, dfr); // equal\r
+ // unequal; do NextPlus or NextMinus but with different status rules\r
+\r
+ if (comp<0) { // lhs<rhs, do NextPlus, see above for commentary\r
+ if (DFISINF(dfl) && DFISSIGNED(dfl)) { // -Infinity special case\r
+ DFSETNMAX(result);\r
+ DFWORD(result, 0)|=DECFLOAT_Sign;\r
+ return result;\r
+ }\r
+ saveround=set->round; // save mode\r
+ set->round=DEC_ROUND_CEILING; // .. round towards +Infinity\r
+ deltatop=0; // positive delta\r
+ }\r
+ else { // lhs>rhs, do NextMinus, see above for commentary\r
+ if (DFISINF(dfl) && !DFISSIGNED(dfl)) { // +Infinity special case\r
+ DFSETNMAX(result);\r
+ return result;\r
+ }\r
+ saveround=set->round; // save mode\r
+ set->round=DEC_ROUND_FLOOR; // .. round towards -Infinity\r
+ deltatop=DECFLOAT_Sign; // negative delta\r
+ }\r
+ savestat=set->status; // save status\r
+ // Here, Inexact is needed where appropriate (and hence Underflow,\r
+ // etc.). Therefore the tiny delta which is otherwise\r
+ // unrepresentable (see NextPlus and NextMinus) is constructed\r
+ // using the multiplication of FMA.\r
+ decFloatZero(&delta); // set up tiny delta\r
+ DFWORD(&delta, DECWORDS-1)=1; // coefficient=1\r
+ DFWORD(&delta, 0)=deltatop; // Sign + biased exponent=0\r
+ decFloatFromString(&pointone, "1E-1", set); // set up multiplier\r
+ decFloatFMA(result, &delta, &pointone, dfl, set);\r
+ // [Delta is truly tiny, so no need to correct sign of zero]\r
+ // use new status unless the result is normal\r
+ if (decFloatIsNormal(result)) set->status=savestat; // else goes forward\r
+ set->round=saveround; // restore mode\r
+ return result;\r
+ } // decFloatNextToward\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decFloatOr -- logical digitwise OR of two decFloats */\r
+/* */\r
+/* result gets the result of ORing dfl and dfr */\r
+/* dfl is the first decFloat (lhs) */\r
+/* dfr is the second decFloat (rhs) */\r
+/* set is the context */\r
+/* returns result, which will be canonical with sign=0 */\r
+/* */\r
+/* The operands must be positive, finite with exponent q=0, and */\r
+/* comprise just zeros and ones; if not, Invalid operation results. */\r
+/* ------------------------------------------------------------------ */\r
+decFloat * decFloatOr(decFloat *result,\r
+ const decFloat *dfl, const decFloat *dfr,\r
+ decContext *set) {\r
+ if (!DFISUINT01(dfl) || !DFISUINT01(dfr)\r
+ || !DFISCC01(dfl) || !DFISCC01(dfr)) return decInvalid(result, set);\r
+ // the operands are positive finite integers (q=0) with just 0s and 1s\r
+ #if DOUBLE\r
+ DFWORD(result, 0)=ZEROWORD\r
+ |((DFWORD(dfl, 0) | DFWORD(dfr, 0))&0x04009124);\r
+ DFWORD(result, 1)=(DFWORD(dfl, 1) | DFWORD(dfr, 1))&0x49124491;\r
+ #elif QUAD\r
+ DFWORD(result, 0)=ZEROWORD\r
+ |((DFWORD(dfl, 0) | DFWORD(dfr, 0))&0x04000912);\r
+ DFWORD(result, 1)=(DFWORD(dfl, 1) | DFWORD(dfr, 1))&0x44912449;\r
+ DFWORD(result, 2)=(DFWORD(dfl, 2) | DFWORD(dfr, 2))&0x12449124;\r
+ DFWORD(result, 3)=(DFWORD(dfl, 3) | DFWORD(dfr, 3))&0x49124491;\r
+ #endif\r
+ return result;\r
+ } // decFloatOr\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decFloatPlus -- add value to 0, heeding NaNs, etc. */\r
+/* */\r
+/* result gets the canonicalized 0+df */\r
+/* df is the decFloat to plus */\r
+/* set is the context */\r
+/* returns result */\r
+/* */\r
+/* This has the same effect as 0+df where the exponent of the zero is */\r
+/* the same as that of df (if df is finite). */\r
+/* The effect is also the same as decFloatCopy except that NaNs */\r
+/* are handled normally (the sign of a NaN is not affected, and an */\r
+/* sNaN will signal), the result is canonical, and zero gets sign 0. */\r
+/* ------------------------------------------------------------------ */\r
+decFloat * decFloatPlus(decFloat *result, const decFloat *df,\r
+ decContext *set) {\r
+ if (DFISNAN(df)) return decNaNs(result, df, NULL, set);\r
+ decCanonical(result, df); // copy and check\r
+ if (DFISZERO(df)) DFBYTE(result, 0)&=~0x80; // turn off sign bit\r
+ return result;\r
+ } // decFloatPlus\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decFloatQuantize -- quantize a decFloat */\r
+/* */\r
+/* result gets the result of quantizing dfl to match dfr */\r
+/* dfl is the first decFloat (lhs) */\r
+/* dfr is the second decFloat (rhs), which sets the exponent */\r
+/* set is the context */\r
+/* returns result */\r
+/* */\r
+/* Unless there is an error or the result is infinite, the exponent */\r
+/* of result is guaranteed to be the same as that of dfr. */\r
+/* ------------------------------------------------------------------ */\r
+decFloat * decFloatQuantize(decFloat *result,\r
+ const decFloat *dfl, const decFloat *dfr,\r
+ decContext *set) {\r
+ Int explb, exprb; // left and right biased exponents\r
+ uByte *ulsd; // local LSD pointer\r
+ uByte *ub, *uc; // work\r
+ Int drop; // ..\r
+ uInt dpd; // ..\r
+ uInt encode; // encoding accumulator\r
+ uInt sourhil, sourhir; // top words from source decFloats\r
+ uInt uiwork; // for macros\r
+ #if QUAD\r
+ uShort uswork; // ..\r
+ #endif\r
+ // the following buffer holds the coefficient for manipulation\r
+ uByte buf[4+DECPMAX*3+2*QUAD]; // + space for zeros to left or right\r
+ #if DECTRACE\r
+ bcdnum num; // for trace displays\r
+ #endif\r
+\r
+ /* Start decoding the arguments */\r
+ sourhil=DFWORD(dfl, 0); // LHS top word\r
+ explb=DECCOMBEXP[sourhil>>26]; // get exponent high bits (in place)\r
+ sourhir=DFWORD(dfr, 0); // RHS top word\r
+ exprb=DECCOMBEXP[sourhir>>26];\r
+\r
+ if (EXPISSPECIAL(explb | exprb)) { // either is special?\r
+ // NaNs are handled as usual\r
+ if (DFISNAN(dfl) || DFISNAN(dfr)) return decNaNs(result, dfl, dfr, set);\r
+ // one infinity but not both is bad\r
+ if (DFISINF(dfl)!=DFISINF(dfr)) return decInvalid(result, set);\r
+ // both infinite; return canonical infinity with sign of LHS\r
+ return decInfinity(result, dfl);\r
+ }\r
+\r
+ /* Here when both arguments are finite */\r
+ // complete extraction of the exponents [no need to unbias]\r
+ explb+=GETECON(dfl); // + continuation\r
+ exprb+=GETECON(dfr); // ..\r
+\r
+ // calculate the number of digits to drop from the coefficient\r
+ drop=exprb-explb; // 0 if nothing to do\r
+ if (drop==0) return decCanonical(result, dfl); // return canonical\r
+\r
+ // the coefficient is needed; lay it out into buf, offset so zeros\r
+ // can be added before or after as needed -- an extra heading is\r
+ // added so can safely pad Quad DECPMAX-1 zeros to the left by\r
+ // fours\r
+ #define BUFOFF (buf+4+DECPMAX)\r
+ GETCOEFF(dfl, BUFOFF); // decode from decFloat\r
+ // [now the msd is at BUFOFF and the lsd is at BUFOFF+DECPMAX-1]\r
+\r
+ #if DECTRACE\r
+ num.msd=BUFOFF;\r
+ num.lsd=BUFOFF+DECPMAX-1;\r
+ num.exponent=explb-DECBIAS;\r
+ num.sign=sourhil & DECFLOAT_Sign;\r
+ decShowNum(&num, "dfl");\r
+ #endif\r
+\r
+ if (drop>0) { // [most common case]\r
+ // (this code is very similar to that in decFloatFinalize, but\r
+ // has many differences so is duplicated here -- so any changes\r
+ // may need to be made there, too)\r
+ uByte *roundat; // -> re-round digit\r
+ uByte reround; // reround value\r
+ // printf("Rounding; drop=%ld\n", (LI)drop);\r
+\r
+ // there is at least one zero needed to the left, in all but one\r
+ // exceptional (all-nines) case, so place four zeros now; this is\r
+ // needed almost always and makes rounding all-nines by fours safe\r
+ UBFROMUI(BUFOFF-4, 0);\r
+\r
+ // Three cases here:\r
+ // 1. new LSD is in coefficient (almost always)\r
+ // 2. new LSD is digit to left of coefficient (so MSD is\r
+ // round-for-reround digit)\r
+ // 3. new LSD is to left of case 2 (whole coefficient is sticky)\r
+ // Note that leading zeros can safely be treated as useful digits\r
+\r
+ // [duplicate check-stickies code to save a test]\r
+ // [by-digit check for stickies as runs of zeros are rare]\r
+ if (drop<DECPMAX) { // NB lengths not addresses\r
+ roundat=BUFOFF+DECPMAX-drop;\r
+ reround=*roundat;\r
+ for (ub=roundat+1; ub<BUFOFF+DECPMAX; ub++) {\r
+ if (*ub!=0) { // non-zero to be discarded\r
+ reround=DECSTICKYTAB[reround]; // apply sticky bit\r
+ break; // [remainder don't-care]\r
+ }\r
+ } // check stickies\r
+ ulsd=roundat-1; // set LSD\r
+ }\r
+ else { // edge case\r
+ if (drop==DECPMAX) {\r
+ roundat=BUFOFF;\r
+ reround=*roundat;\r
+ }\r
+ else {\r
+ roundat=BUFOFF-1;\r
+ reround=0;\r
+ }\r
+ for (ub=roundat+1; ub<BUFOFF+DECPMAX; ub++) {\r
+ if (*ub!=0) { // non-zero to be discarded\r
+ reround=DECSTICKYTAB[reround]; // apply sticky bit\r
+ break; // [remainder don't-care]\r
+ }\r
+ } // check stickies\r
+ *BUFOFF=0; // make a coefficient of 0\r
+ ulsd=BUFOFF; // .. at the MSD place\r
+ }\r
+\r
+ if (reround!=0) { // discarding non-zero\r
+ uInt bump=0;\r
+ set->status|=DEC_Inexact;\r
+\r
+ // next decide whether to increment the coefficient\r
+ if (set->round==DEC_ROUND_HALF_EVEN) { // fastpath slowest case\r
+ if (reround>5) bump=1; // >0.5 goes up\r
+ else if (reround==5) // exactly 0.5000 ..\r
+ bump=*ulsd & 0x01; // .. up iff [new] lsd is odd\r
+ } // r-h-e\r
+ else switch (set->round) {\r
+ case DEC_ROUND_DOWN: {\r
+ // no change\r
+ break;} // r-d\r
+ case DEC_ROUND_HALF_DOWN: {\r
+ if (reround>5) bump=1;\r
+ break;} // r-h-d\r
+ case DEC_ROUND_HALF_UP: {\r
+ if (reround>=5) bump=1;\r
+ break;} // r-h-u\r
+ case DEC_ROUND_UP: {\r
+ if (reround>0) bump=1;\r
+ break;} // r-u\r
+ case DEC_ROUND_CEILING: {\r
+ // same as _UP for positive numbers, and as _DOWN for negatives\r
+ if (!(sourhil&DECFLOAT_Sign) && reround>0) bump=1;\r
+ break;} // r-c\r
+ case DEC_ROUND_FLOOR: {\r
+ // same as _UP for negative numbers, and as _DOWN for positive\r
+ // [negative reround cannot occur on 0]\r
+ if (sourhil&DECFLOAT_Sign && reround>0) bump=1;\r
+ break;} // r-f\r
+ case DEC_ROUND_05UP: {\r
+ if (reround>0) { // anything out there is 'sticky'\r
+ // bump iff lsd=0 or 5; this cannot carry so it could be\r
+ // effected immediately with no bump -- but the code\r
+ // is clearer if this is done the same way as the others\r
+ if (*ulsd==0 || *ulsd==5) bump=1;\r
+ }\r
+ break;} // r-r\r
+ default: { // e.g., DEC_ROUND_MAX\r
+ set->status|=DEC_Invalid_context;\r
+ #if DECCHECK\r
+ printf("Unknown rounding mode: %ld\n", (LI)set->round);\r
+ #endif\r
+ break;}\r
+ } // switch (not r-h-e)\r
+ // printf("ReRound: %ld bump: %ld\n", (LI)reround, (LI)bump);\r
+\r
+ if (bump!=0) { // need increment\r
+ // increment the coefficient; this could give 1000... (after\r
+ // the all nines case)\r
+ ub=ulsd;\r
+ for (; UBTOUI(ub-3)==0x09090909; ub-=4) UBFROMUI(ub-3, 0);\r
+ // now at most 3 digits left to non-9 (usually just the one)\r
+ for (; *ub==9; ub--) *ub=0;\r
+ *ub+=1;\r
+ // [the all-nines case will have carried one digit to the\r
+ // left of the original MSD -- just where it is needed]\r
+ } // bump needed\r
+ } // inexact rounding\r
+\r
+ // now clear zeros to the left so exactly DECPMAX digits will be\r
+ // available in the coefficent -- the first word to the left was\r
+ // cleared earlier for safe carry; now add any more needed\r
+ if (drop>4) {\r
+ UBFROMUI(BUFOFF-8, 0); // must be at least 5\r
+ for (uc=BUFOFF-12; uc>ulsd-DECPMAX-3; uc-=4) UBFROMUI(uc, 0);\r
+ }\r
+ } // need round (drop>0)\r
+\r
+ else { // drop<0; padding with -drop digits is needed\r
+ // This is the case where an error can occur if the padded\r
+ // coefficient will not fit; checking for this can be done in the\r
+ // same loop as padding for zeros if the no-hope and zero cases\r
+ // are checked first\r
+ if (-drop>DECPMAX-1) { // cannot fit unless 0\r
+ if (!ISCOEFFZERO(BUFOFF)) return decInvalid(result, set);\r
+ // a zero can have any exponent; just drop through and use it\r
+ ulsd=BUFOFF+DECPMAX-1;\r
+ }\r
+ else { // padding will fit (but may still be too long)\r
+ // final-word mask depends on endianess\r
+ #if DECLITEND\r
+ static const uInt dmask[]={0, 0x000000ff, 0x0000ffff, 0x00ffffff};\r
+ #else\r
+ static const uInt dmask[]={0, 0xff000000, 0xffff0000, 0xffffff00};\r
+ #endif\r
+ // note that here zeros to the right are added by fours, so in\r
+ // the Quad case this could write 36 zeros if the coefficient has\r
+ // fewer than three significant digits (hence the +2*QUAD for buf)\r
+ for (uc=BUFOFF+DECPMAX;; uc+=4) {\r
+ UBFROMUI(uc, 0);\r
+ if (UBTOUI(uc-DECPMAX)!=0) { // could be bad\r
+ // if all four digits should be zero, definitely bad\r
+ if (uc<=BUFOFF+DECPMAX+(-drop)-4)\r
+ return decInvalid(result, set);\r
+ // must be a 1- to 3-digit sequence; check more carefully\r
+ if ((UBTOUI(uc-DECPMAX)&dmask[(-drop)%4])!=0)\r
+ return decInvalid(result, set);\r
+ break; // no need for loop end test\r
+ }\r
+ if (uc>=BUFOFF+DECPMAX+(-drop)-4) break; // done\r
+ }\r
+ ulsd=BUFOFF+DECPMAX+(-drop)-1;\r
+ } // pad and check leading zeros\r
+ } // drop<0\r
+\r
+ #if DECTRACE\r
+ num.msd=ulsd-DECPMAX+1;\r
+ num.lsd=ulsd;\r
+ num.exponent=explb-DECBIAS;\r
+ num.sign=sourhil & DECFLOAT_Sign;\r
+ decShowNum(&num, "res");\r
+ #endif\r
+\r
+ /*------------------------------------------------------------------*/\r
+ /* At this point the result is DECPMAX digits, ending at ulsd, so */\r
+ /* fits the encoding exactly; there is no possibility of error */\r
+ /*------------------------------------------------------------------*/\r
+ encode=((exprb>>DECECONL)<<4) + *(ulsd-DECPMAX+1); // make index\r
+ encode=DECCOMBFROM[encode]; // indexed by (0-2)*16+msd\r
+ // the exponent continuation can be extracted from the original RHS\r
+ encode|=sourhir & ECONMASK;\r
+ encode|=sourhil&DECFLOAT_Sign; // add the sign from LHS\r
+\r
+ // finally encode the coefficient\r
+ // private macro to encode a declet; this version can be used\r
+ // because all coefficient digits exist\r
+ #define getDPD3q(dpd, n) ub=ulsd-(3*(n))-2; \\r
+ dpd=BCD2DPD[(*ub*256)+(*(ub+1)*16)+*(ub+2)];\r
+\r
+ #if DOUBLE\r
+ getDPD3q(dpd, 4); encode|=dpd<<8;\r
+ getDPD3q(dpd, 3); encode|=dpd>>2;\r
+ DFWORD(result, 0)=encode;\r
+ encode=dpd<<30;\r
+ getDPD3q(dpd, 2); encode|=dpd<<20;\r
+ getDPD3q(dpd, 1); encode|=dpd<<10;\r
+ getDPD3q(dpd, 0); encode|=dpd;\r
+ DFWORD(result, 1)=encode;\r
+\r
+ #elif QUAD\r
+ getDPD3q(dpd,10); encode|=dpd<<4;\r
+ getDPD3q(dpd, 9); encode|=dpd>>6;\r
+ DFWORD(result, 0)=encode;\r
+ encode=dpd<<26;\r
+ getDPD3q(dpd, 8); encode|=dpd<<16;\r
+ getDPD3q(dpd, 7); encode|=dpd<<6;\r
+ getDPD3q(dpd, 6); encode|=dpd>>4;\r
+ DFWORD(result, 1)=encode;\r
+ encode=dpd<<28;\r
+ getDPD3q(dpd, 5); encode|=dpd<<18;\r
+ getDPD3q(dpd, 4); encode|=dpd<<8;\r
+ getDPD3q(dpd, 3); encode|=dpd>>2;\r
+ DFWORD(result, 2)=encode;\r
+ encode=dpd<<30;\r
+ getDPD3q(dpd, 2); encode|=dpd<<20;\r
+ getDPD3q(dpd, 1); encode|=dpd<<10;\r
+ getDPD3q(dpd, 0); encode|=dpd;\r
+ DFWORD(result, 3)=encode;\r
+ #endif\r
+ return result;\r
+ } // decFloatQuantize\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decFloatReduce -- reduce finite coefficient to minimum length */\r
+/* */\r
+/* result gets the reduced decFloat */\r
+/* df is the source decFloat */\r
+/* set is the context */\r
+/* returns result, which will be canonical */\r
+/* */\r
+/* This removes all possible trailing zeros from the coefficient; */\r
+/* some may remain when the number is very close to Nmax. */\r
+/* Special values are unchanged and no status is set unless df=sNaN. */\r
+/* Reduced zero has an exponent q=0. */\r
+/* ------------------------------------------------------------------ */\r
+decFloat * decFloatReduce(decFloat *result, const decFloat *df,\r
+ decContext *set) {\r
+ bcdnum num; // work\r
+ uByte buf[DECPMAX], *ub; // coefficient and pointer\r
+ if (df!=result) *result=*df; // copy, if needed\r
+ if (DFISNAN(df)) return decNaNs(result, df, NULL, set); // sNaN\r
+ // zeros and infinites propagate too\r
+ if (DFISINF(df)) return decInfinity(result, df); // canonical\r
+ if (DFISZERO(df)) {\r
+ uInt sign=DFWORD(df, 0)&DECFLOAT_Sign;\r
+ decFloatZero(result);\r
+ DFWORD(result, 0)|=sign;\r
+ return result; // exponent dropped, sign OK\r
+ }\r
+ // non-zero finite\r
+ GETCOEFF(df, buf);\r
+ ub=buf+DECPMAX-1; // -> lsd\r
+ if (*ub) return result; // no trailing zeros\r
+ for (ub--; *ub==0;) ub--; // terminates because non-zero\r
+ // *ub is the first non-zero from the right\r
+ num.sign=DFWORD(df, 0)&DECFLOAT_Sign; // set up number...\r
+ num.exponent=GETEXPUN(df)+(Int)(buf+DECPMAX-1-ub); // adjusted exponent\r
+ num.msd=buf;\r
+ num.lsd=ub;\r
+ return decFinalize(result, &num, set);\r
+ } // decFloatReduce\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decFloatRemainder -- integer divide and return remainder */\r
+/* */\r
+/* result gets the remainder of dividing dfl by dfr: */\r
+/* dfl is the first decFloat (lhs) */\r
+/* dfr is the second decFloat (rhs) */\r
+/* set is the context */\r
+/* returns result */\r
+/* */\r
+/* ------------------------------------------------------------------ */\r
+decFloat * decFloatRemainder(decFloat *result,\r
+ const decFloat *dfl, const decFloat *dfr,\r
+ decContext *set) {\r
+ return decDivide(result, dfl, dfr, set, REMAINDER);\r
+ } // decFloatRemainder\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decFloatRemainderNear -- integer divide to nearest and remainder */\r
+/* */\r
+/* result gets the remainder of dividing dfl by dfr: */\r
+/* dfl is the first decFloat (lhs) */\r
+/* dfr is the second decFloat (rhs) */\r
+/* set is the context */\r
+/* returns result */\r
+/* */\r
+/* This is the IEEE remainder, where the nearest integer is used. */\r
+/* ------------------------------------------------------------------ */\r
+decFloat * decFloatRemainderNear(decFloat *result,\r
+ const decFloat *dfl, const decFloat *dfr,\r
+ decContext *set) {\r
+ return decDivide(result, dfl, dfr, set, REMNEAR);\r
+ } // decFloatRemainderNear\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decFloatRotate -- rotate the coefficient of a decFloat left/right */\r
+/* */\r
+/* result gets the result of rotating dfl */\r
+/* dfl is the source decFloat to rotate */\r
+/* dfr is the count of digits to rotate, an integer (with q=0) */\r
+/* set is the context */\r
+/* returns result */\r
+/* */\r
+/* The digits of the coefficient of dfl are rotated to the left (if */\r
+/* dfr is positive) or to the right (if dfr is negative) without */\r
+/* adjusting the exponent or the sign of dfl. */\r
+/* */\r
+/* dfr must be in the range -DECPMAX through +DECPMAX. */\r
+/* NaNs are propagated as usual. An infinite dfl is unaffected (but */\r
+/* dfr must be valid). No status is set unless dfr is invalid or an */\r
+/* operand is an sNaN. The result is canonical. */\r
+/* ------------------------------------------------------------------ */\r
+#define PHALF (ROUNDUP(DECPMAX/2, 4)) // half length, rounded up\r
+decFloat * decFloatRotate(decFloat *result,\r
+ const decFloat *dfl, const decFloat *dfr,\r
+ decContext *set) {\r
+ Int rotate; // dfr as an Int\r
+ uByte buf[DECPMAX+PHALF]; // coefficient + half\r
+ uInt digits, savestat; // work\r
+ bcdnum num; // ..\r
+ uByte *ub; // ..\r
+\r
+ if (DFISNAN(dfl)||DFISNAN(dfr)) return decNaNs(result, dfl, dfr, set);\r
+ if (!DFISINT(dfr)) return decInvalid(result, set);\r
+ digits=decFloatDigits(dfr); // calculate digits\r
+ if (digits>2) return decInvalid(result, set); // definitely out of range\r
+ rotate=DPD2BIN[DFWORD(dfr, DECWORDS-1)&0x3ff]; // is in bottom declet\r
+ if (rotate>DECPMAX) return decInvalid(result, set); // too big\r
+ // [from here on no error or status change is possible]\r
+ if (DFISINF(dfl)) return decInfinity(result, dfl); // canonical\r
+ // handle no-rotate cases\r
+ if (rotate==0 || rotate==DECPMAX) return decCanonical(result, dfl);\r
+ // a real rotate is needed: 0 < rotate < DECPMAX\r
+ // reduce the rotation to no more than half to reduce copying later\r
+ // (for QUAD in fact half + 2 digits)\r
+ if (DFISSIGNED(dfr)) rotate=-rotate;\r
+ if (abs(rotate)>PHALF) {\r
+ if (rotate<0) rotate=DECPMAX+rotate;\r
+ else rotate=rotate-DECPMAX;\r
+ }\r
+ // now lay out the coefficient, leaving room to the right or the\r
+ // left depending on the direction of rotation\r
+ ub=buf;\r
+ if (rotate<0) ub+=PHALF; // rotate right, so space to left\r
+ GETCOEFF(dfl, ub);\r
+ // copy half the digits to left or right, and set num.msd\r
+ if (rotate<0) {\r
+ memcpy(buf, buf+DECPMAX, PHALF);\r
+ num.msd=buf+PHALF+rotate;\r
+ }\r
+ else {\r
+ memcpy(buf+DECPMAX, buf, PHALF);\r
+ num.msd=buf+rotate;\r
+ }\r
+ // fill in rest of num\r
+ num.lsd=num.msd+DECPMAX-1;\r
+ num.sign=DFWORD(dfl, 0)&DECFLOAT_Sign;\r
+ num.exponent=GETEXPUN(dfl);\r
+ savestat=set->status; // record\r
+ decFinalize(result, &num, set);\r
+ set->status=savestat; // restore\r
+ return result;\r
+ } // decFloatRotate\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decFloatSameQuantum -- test decFloats for same quantum */\r
+/* */\r
+/* dfl is the first decFloat (lhs) */\r
+/* dfr is the second decFloat (rhs) */\r
+/* returns 1 if the operands have the same quantum, 0 otherwise */\r
+/* */\r
+/* No error is possible and no status results. */\r
+/* ------------------------------------------------------------------ */\r
+uInt decFloatSameQuantum(const decFloat *dfl, const decFloat *dfr) {\r
+ if (DFISSPECIAL(dfl) || DFISSPECIAL(dfr)) {\r
+ if (DFISNAN(dfl) && DFISNAN(dfr)) return 1;\r
+ if (DFISINF(dfl) && DFISINF(dfr)) return 1;\r
+ return 0; // any other special mixture gives false\r
+ }\r
+ if (GETEXP(dfl)==GETEXP(dfr)) return 1; // biased exponents match\r
+ return 0;\r
+ } // decFloatSameQuantum\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decFloatScaleB -- multiply by a power of 10, as per 754 */\r
+/* */\r
+/* result gets the result of the operation */\r
+/* dfl is the first decFloat (lhs) */\r
+/* dfr is the second decFloat (rhs), am integer (with q=0) */\r
+/* set is the context */\r
+/* returns result */\r
+/* */\r
+/* This computes result=dfl x 10**dfr where dfr is an integer in the */\r
+/* range +/-2*(emax+pmax), typically resulting from LogB. */\r
+/* Underflow and Overflow (with Inexact) may occur. NaNs propagate */\r
+/* as usual. */\r
+/* ------------------------------------------------------------------ */\r
+#define SCALEBMAX 2*(DECEMAX+DECPMAX) // D=800, Q=12356\r
+decFloat * decFloatScaleB(decFloat *result,\r
+ const decFloat *dfl, const decFloat *dfr,\r
+ decContext *set) {\r
+ uInt digits; // work\r
+ Int expr; // dfr as an Int\r
+\r
+ if (DFISNAN(dfl)||DFISNAN(dfr)) return decNaNs(result, dfl, dfr, set);\r
+ if (!DFISINT(dfr)) return decInvalid(result, set);\r
+ digits=decFloatDigits(dfr); // calculate digits\r
+\r
+ #if DOUBLE\r
+ if (digits>3) return decInvalid(result, set); // definitely out of range\r
+ expr=DPD2BIN[DFWORD(dfr, 1)&0x3ff]; // must be in bottom declet\r
+ #elif QUAD\r
+ if (digits>5) return decInvalid(result, set); // definitely out of range\r
+ expr=DPD2BIN[DFWORD(dfr, 3)&0x3ff] // in bottom 2 declets ..\r
+ +DPD2BIN[(DFWORD(dfr, 3)>>10)&0x3ff]*1000; // ..\r
+ #endif\r
+ if (expr>SCALEBMAX) return decInvalid(result, set); // oops\r
+ // [from now on no error possible]\r
+ if (DFISINF(dfl)) return decInfinity(result, dfl); // canonical\r
+ if (DFISSIGNED(dfr)) expr=-expr;\r
+ // dfl is finite and expr is valid\r
+ *result=*dfl; // copy to target\r
+ return decFloatSetExponent(result, set, GETEXPUN(result)+expr);\r
+ } // decFloatScaleB\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decFloatShift -- shift the coefficient of a decFloat left or right */\r
+/* */\r
+/* result gets the result of shifting dfl */\r
+/* dfl is the source decFloat to shift */\r
+/* dfr is the count of digits to shift, an integer (with q=0) */\r
+/* set is the context */\r
+/* returns result */\r
+/* */\r
+/* The digits of the coefficient of dfl are shifted to the left (if */\r
+/* dfr is positive) or to the right (if dfr is negative) without */\r
+/* adjusting the exponent or the sign of dfl. */\r
+/* */\r
+/* dfr must be in the range -DECPMAX through +DECPMAX. */\r
+/* NaNs are propagated as usual. An infinite dfl is unaffected (but */\r
+/* dfr must be valid). No status is set unless dfr is invalid or an */\r
+/* operand is an sNaN. The result is canonical. */\r
+/* ------------------------------------------------------------------ */\r
+decFloat * decFloatShift(decFloat *result,\r
+ const decFloat *dfl, const decFloat *dfr,\r
+ decContext *set) {\r
+ Int shift; // dfr as an Int\r
+ uByte buf[DECPMAX*2]; // coefficient + padding\r
+ uInt digits, savestat; // work\r
+ bcdnum num; // ..\r
+ uInt uiwork; // for macros\r
+\r
+ if (DFISNAN(dfl)||DFISNAN(dfr)) return decNaNs(result, dfl, dfr, set);\r
+ if (!DFISINT(dfr)) return decInvalid(result, set);\r
+ digits=decFloatDigits(dfr); // calculate digits\r
+ if (digits>2) return decInvalid(result, set); // definitely out of range\r
+ shift=DPD2BIN[DFWORD(dfr, DECWORDS-1)&0x3ff]; // is in bottom declet\r
+ if (shift>DECPMAX) return decInvalid(result, set); // too big\r
+ // [from here on no error or status change is possible]\r
+\r
+ if (DFISINF(dfl)) return decInfinity(result, dfl); // canonical\r
+ // handle no-shift and all-shift (clear to zero) cases\r
+ if (shift==0) return decCanonical(result, dfl);\r
+ if (shift==DECPMAX) { // zero with sign\r
+ uByte sign=(uByte)(DFBYTE(dfl, 0)&0x80); // save sign bit\r
+ decFloatZero(result); // make +0\r
+ DFBYTE(result, 0)=(uByte)(DFBYTE(result, 0)|sign); // and set sign\r
+ // [cannot safely use CopySign]\r
+ return result;\r
+ }\r
+ // a real shift is needed: 0 < shift < DECPMAX\r
+ num.sign=DFWORD(dfl, 0)&DECFLOAT_Sign;\r
+ num.exponent=GETEXPUN(dfl);\r
+ num.msd=buf;\r
+ GETCOEFF(dfl, buf);\r
+ if (DFISSIGNED(dfr)) { // shift right\r
+ // edge cases are taken care of, so this is easy\r
+ num.lsd=buf+DECPMAX-shift-1;\r
+ }\r
+ else { // shift left -- zero padding needed to right\r
+ UBFROMUI(buf+DECPMAX, 0); // 8 will handle most cases\r
+ UBFROMUI(buf+DECPMAX+4, 0); // ..\r
+ if (shift>8) memset(buf+DECPMAX+8, 0, 8+QUAD*18); // all other cases\r
+ num.msd+=shift;\r
+ num.lsd=num.msd+DECPMAX-1;\r
+ }\r
+ savestat=set->status; // record\r
+ decFinalize(result, &num, set);\r
+ set->status=savestat; // restore\r
+ return result;\r
+ } // decFloatShift\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decFloatSubtract -- subtract a decFloat from another */\r
+/* */\r
+/* result gets the result of subtracting dfr from dfl: */\r
+/* dfl is the first decFloat (lhs) */\r
+/* dfr is the second decFloat (rhs) */\r
+/* set is the context */\r
+/* returns result */\r
+/* */\r
+/* ------------------------------------------------------------------ */\r
+decFloat * decFloatSubtract(decFloat *result,\r
+ const decFloat *dfl, const decFloat *dfr,\r
+ decContext *set) {\r
+ decFloat temp;\r
+ // NaNs must propagate without sign change\r
+ if (DFISNAN(dfr)) return decFloatAdd(result, dfl, dfr, set);\r
+ temp=*dfr; // make a copy\r
+ DFBYTE(&temp, 0)^=0x80; // flip sign\r
+ return decFloatAdd(result, dfl, &temp, set); // and add to the lhs\r
+ } // decFloatSubtract\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decFloatToInt -- round to 32-bit binary integer (4 flavours) */\r
+/* */\r
+/* df is the decFloat to round */\r
+/* set is the context */\r
+/* round is the rounding mode to use */\r
+/* returns a uInt or an Int, rounded according to the name */\r
+/* */\r
+/* Invalid will always be signaled if df is a NaN, is Infinite, or is */\r
+/* outside the range of the target; Inexact will not be signaled for */\r
+/* simple rounding unless 'Exact' appears in the name. */\r
+/* ------------------------------------------------------------------ */\r
+uInt decFloatToUInt32(const decFloat *df, decContext *set,\r
+ enum rounding round) {\r
+ return decToInt32(df, set, round, 0, 1);}\r
+\r
+uInt decFloatToUInt32Exact(const decFloat *df, decContext *set,\r
+ enum rounding round) {\r
+ return decToInt32(df, set, round, 1, 1);}\r
+\r
+Int decFloatToInt32(const decFloat *df, decContext *set,\r
+ enum rounding round) {\r
+ return (Int)decToInt32(df, set, round, 0, 0);}\r
+\r
+Int decFloatToInt32Exact(const decFloat *df, decContext *set,\r
+ enum rounding round) {\r
+ return (Int)decToInt32(df, set, round, 1, 0);}\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decFloatToIntegral -- round to integral value (two flavours) */\r
+/* */\r
+/* result gets the result */\r
+/* df is the decFloat to round */\r
+/* set is the context */\r
+/* round is the rounding mode to use */\r
+/* returns result */\r
+/* */\r
+/* No exceptions, even Inexact, are raised except for sNaN input, or */\r
+/* if 'Exact' appears in the name. */\r
+/* ------------------------------------------------------------------ */\r
+decFloat * decFloatToIntegralValue(decFloat *result, const decFloat *df,\r
+ decContext *set, enum rounding round) {\r
+ return decToIntegral(result, df, set, round, 0);}\r
+\r
+decFloat * decFloatToIntegralExact(decFloat *result, const decFloat *df,\r
+ decContext *set) {\r
+ return decToIntegral(result, df, set, set->round, 1);}\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decFloatXor -- logical digitwise XOR of two decFloats */\r
+/* */\r
+/* result gets the result of XORing dfl and dfr */\r
+/* dfl is the first decFloat (lhs) */\r
+/* dfr is the second decFloat (rhs) */\r
+/* set is the context */\r
+/* returns result, which will be canonical with sign=0 */\r
+/* */\r
+/* The operands must be positive, finite with exponent q=0, and */\r
+/* comprise just zeros and ones; if not, Invalid operation results. */\r
+/* ------------------------------------------------------------------ */\r
+decFloat * decFloatXor(decFloat *result,\r
+ const decFloat *dfl, const decFloat *dfr,\r
+ decContext *set) {\r
+ if (!DFISUINT01(dfl) || !DFISUINT01(dfr)\r
+ || !DFISCC01(dfl) || !DFISCC01(dfr)) return decInvalid(result, set);\r
+ // the operands are positive finite integers (q=0) with just 0s and 1s\r
+ #if DOUBLE\r
+ DFWORD(result, 0)=ZEROWORD\r
+ |((DFWORD(dfl, 0) ^ DFWORD(dfr, 0))&0x04009124);\r
+ DFWORD(result, 1)=(DFWORD(dfl, 1) ^ DFWORD(dfr, 1))&0x49124491;\r
+ #elif QUAD\r
+ DFWORD(result, 0)=ZEROWORD\r
+ |((DFWORD(dfl, 0) ^ DFWORD(dfr, 0))&0x04000912);\r
+ DFWORD(result, 1)=(DFWORD(dfl, 1) ^ DFWORD(dfr, 1))&0x44912449;\r
+ DFWORD(result, 2)=(DFWORD(dfl, 2) ^ DFWORD(dfr, 2))&0x12449124;\r
+ DFWORD(result, 3)=(DFWORD(dfl, 3) ^ DFWORD(dfr, 3))&0x49124491;\r
+ #endif\r
+ return result;\r
+ } // decFloatXor\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decInvalid -- set Invalid_operation result */\r
+/* */\r
+/* result gets a canonical NaN */\r
+/* set is the context */\r
+/* returns result */\r
+/* */\r
+/* status has Invalid_operation added */\r
+/* ------------------------------------------------------------------ */\r
+static decFloat *decInvalid(decFloat *result, decContext *set) {\r
+ decFloatZero(result);\r
+ DFWORD(result, 0)=DECFLOAT_qNaN;\r
+ set->status|=DEC_Invalid_operation;\r
+ return result;\r
+ } // decInvalid\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decInfinity -- set canonical Infinity with sign from a decFloat */\r
+/* */\r
+/* result gets a canonical Infinity */\r
+/* df is source decFloat (only the sign is used) */\r
+/* returns result */\r
+/* */\r
+/* df may be the same as result */\r
+/* ------------------------------------------------------------------ */\r
+static decFloat *decInfinity(decFloat *result, const decFloat *df) {\r
+ uInt sign=DFWORD(df, 0); // save source signword\r
+ decFloatZero(result); // clear everything\r
+ DFWORD(result, 0)=DECFLOAT_Inf | (sign & DECFLOAT_Sign);\r
+ return result;\r
+ } // decInfinity\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decNaNs -- handle NaN argument(s) */\r
+/* */\r
+/* result gets the result of handling dfl and dfr, one or both of */\r
+/* which is a NaN */\r
+/* dfl is the first decFloat (lhs) */\r
+/* dfr is the second decFloat (rhs) -- may be NULL for a single- */\r
+/* operand operation */\r
+/* set is the context */\r
+/* returns result */\r
+/* */\r
+/* Called when one or both operands is a NaN, and propagates the */\r
+/* appropriate result to res. When an sNaN is found, it is changed */\r
+/* to a qNaN and Invalid operation is set. */\r
+/* ------------------------------------------------------------------ */\r
+static decFloat *decNaNs(decFloat *result,\r
+ const decFloat *dfl, const decFloat *dfr,\r
+ decContext *set) {\r
+ // handle sNaNs first\r
+ if (dfr!=NULL && DFISSNAN(dfr) && !DFISSNAN(dfl)) dfl=dfr; // use RHS\r
+ if (DFISSNAN(dfl)) {\r
+ decCanonical(result, dfl); // propagate canonical sNaN\r
+ DFWORD(result, 0)&=~(DECFLOAT_qNaN ^ DECFLOAT_sNaN); // quiet\r
+ set->status|=DEC_Invalid_operation;\r
+ return result;\r
+ }\r
+ // one or both is a quiet NaN\r
+ if (!DFISNAN(dfl)) dfl=dfr; // RHS must be NaN, use it\r
+ return decCanonical(result, dfl); // propagate canonical qNaN\r
+ } // decNaNs\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decNumCompare -- numeric comparison of two decFloats */\r
+/* */\r
+/* dfl is the left-hand decFloat, which is not a NaN */\r
+/* dfr is the right-hand decFloat, which is not a NaN */\r
+/* tot is 1 for total order compare, 0 for simple numeric */\r
+/* returns -1, 0, or +1 for dfl<dfr, dfl=dfr, dfl>dfr */\r
+/* */\r
+/* No error is possible; status and mode are unchanged. */\r
+/* ------------------------------------------------------------------ */\r
+static Int decNumCompare(const decFloat *dfl, const decFloat *dfr, Flag tot) {\r
+ Int sigl, sigr; // LHS and RHS non-0 signums\r
+ Int shift; // shift needed to align operands\r
+ uByte *ub, *uc; // work\r
+ uInt uiwork; // for macros\r
+ // buffers +2 if Quad (36 digits), need double plus 4 for safe padding\r
+ uByte bufl[DECPMAX*2+QUAD*2+4]; // for LHS coefficient + padding\r
+ uByte bufr[DECPMAX*2+QUAD*2+4]; // for RHS coefficient + padding\r
+\r
+ sigl=1;\r
+ if (DFISSIGNED(dfl)) {\r
+ if (!DFISSIGNED(dfr)) { // -LHS +RHS\r
+ if (DFISZERO(dfl) && DFISZERO(dfr) && !tot) return 0;\r
+ return -1; // RHS wins\r
+ }\r
+ sigl=-1;\r
+ }\r
+ if (DFISSIGNED(dfr)) {\r
+ if (!DFISSIGNED(dfl)) { // +LHS -RHS\r
+ if (DFISZERO(dfl) && DFISZERO(dfr) && !tot) return 0;\r
+ return +1; // LHS wins\r
+ }\r
+ }\r
+\r
+ // signs are the same; operand(s) could be zero\r
+ sigr=-sigl; // sign to return if abs(RHS) wins\r
+\r
+ if (DFISINF(dfl)) {\r
+ if (DFISINF(dfr)) return 0; // both infinite & same sign\r
+ return sigl; // inf > n\r
+ }\r
+ if (DFISINF(dfr)) return sigr; // n < inf [dfl is finite]\r
+\r
+ // here, both are same sign and finite; calculate their offset\r
+ shift=GETEXP(dfl)-GETEXP(dfr); // [0 means aligned]\r
+ // [bias can be ignored -- the absolute exponent is not relevant]\r
+\r
+ if (DFISZERO(dfl)) {\r
+ if (!DFISZERO(dfr)) return sigr; // LHS=0, RHS!=0\r
+ // both are zero, return 0 if both same exponent or numeric compare\r
+ if (shift==0 || !tot) return 0;\r
+ if (shift>0) return sigl;\r
+ return sigr; // [shift<0]\r
+ }\r
+ else { // LHS!=0\r
+ if (DFISZERO(dfr)) return sigl; // LHS!=0, RHS=0\r
+ }\r
+ // both are known to be non-zero at this point\r
+\r
+ // if the exponents are so different that the coefficients do not\r
+ // overlap (by even one digit) then a full comparison is not needed\r
+ if (abs(shift)>=DECPMAX) { // no overlap\r
+ // coefficients are known to be non-zero\r
+ if (shift>0) return sigl;\r
+ return sigr; // [shift<0]\r
+ }\r
+\r
+ // decode the coefficients\r
+ // (shift both right two if Quad to make a multiple of four)\r
+ #if QUAD\r
+ UBFROMUI(bufl, 0);\r
+ UBFROMUI(bufr, 0);\r
+ #endif\r
+ GETCOEFF(dfl, bufl+QUAD*2); // decode from decFloat\r
+ GETCOEFF(dfr, bufr+QUAD*2); // ..\r
+ if (shift==0) { // aligned; common and easy\r
+ // all multiples of four, here\r
+ for (ub=bufl, uc=bufr; ub<bufl+DECPMAX+QUAD*2; ub+=4, uc+=4) {\r
+ uInt ui=UBTOUI(ub);\r
+ if (ui==UBTOUI(uc)) continue; // so far so same\r
+ // about to find a winner; go by bytes in case little-endian\r
+ for (;; ub++, uc++) {\r
+ if (*ub>*uc) return sigl; // difference found\r
+ if (*ub<*uc) return sigr; // ..\r
+ }\r
+ }\r
+ } // aligned\r
+ else if (shift>0) { // lhs to left\r
+ ub=bufl; // RHS pointer\r
+ // pad bufl so right-aligned; most shifts will fit in 8\r
+ UBFROMUI(bufl+DECPMAX+QUAD*2, 0); // add eight zeros\r
+ UBFROMUI(bufl+DECPMAX+QUAD*2+4, 0); // ..\r
+ if (shift>8) {\r
+ // more than eight; fill the rest, and also worth doing the\r
+ // lead-in by fours\r
+ uByte *up; // work\r
+ uByte *upend=bufl+DECPMAX+QUAD*2+shift;\r
+ for (up=bufl+DECPMAX+QUAD*2+8; up<upend; up+=4) UBFROMUI(up, 0);\r
+ // [pads up to 36 in all for Quad]\r
+ for (;; ub+=4) {\r
+ if (UBTOUI(ub)!=0) return sigl;\r
+ if (ub+4>bufl+shift-4) break;\r
+ }\r
+ }\r
+ // check remaining leading digits\r
+ for (; ub<bufl+shift; ub++) if (*ub!=0) return sigl;\r
+ // now start the overlapped part; bufl has been padded, so the\r
+ // comparison can go for the full length of bufr, which is a\r
+ // multiple of 4 bytes\r
+ for (uc=bufr; ; uc+=4, ub+=4) {\r
+ uInt ui=UBTOUI(ub);\r
+ if (ui!=UBTOUI(uc)) { // mismatch found\r
+ for (;; uc++, ub++) { // check from left [little-endian?]\r
+ if (*ub>*uc) return sigl; // difference found\r
+ if (*ub<*uc) return sigr; // ..\r
+ }\r
+ } // mismatch\r
+ if (uc==bufr+QUAD*2+DECPMAX-4) break; // all checked\r
+ }\r
+ } // shift>0\r
+\r
+ else { // shift<0) .. RHS is to left of LHS; mirror shift>0\r
+ uc=bufr; // RHS pointer\r
+ // pad bufr so right-aligned; most shifts will fit in 8\r
+ UBFROMUI(bufr+DECPMAX+QUAD*2, 0); // add eight zeros\r
+ UBFROMUI(bufr+DECPMAX+QUAD*2+4, 0); // ..\r
+ if (shift<-8) {\r
+ // more than eight; fill the rest, and also worth doing the\r
+ // lead-in by fours\r
+ uByte *up; // work\r
+ uByte *upend=bufr+DECPMAX+QUAD*2-shift;\r
+ for (up=bufr+DECPMAX+QUAD*2+8; up<upend; up+=4) UBFROMUI(up, 0);\r
+ // [pads up to 36 in all for Quad]\r
+ for (;; uc+=4) {\r
+ if (UBTOUI(uc)!=0) return sigr;\r
+ if (uc+4>bufr-shift-4) break;\r
+ }\r
+ }\r
+ // check remaining leading digits\r
+ for (; uc<bufr-shift; uc++) if (*uc!=0) return sigr;\r
+ // now start the overlapped part; bufr has been padded, so the\r
+ // comparison can go for the full length of bufl, which is a\r
+ // multiple of 4 bytes\r
+ for (ub=bufl; ; ub+=4, uc+=4) {\r
+ uInt ui=UBTOUI(ub);\r
+ if (ui!=UBTOUI(uc)) { // mismatch found\r
+ for (;; ub++, uc++) { // check from left [little-endian?]\r
+ if (*ub>*uc) return sigl; // difference found\r
+ if (*ub<*uc) return sigr; // ..\r
+ }\r
+ } // mismatch\r
+ if (ub==bufl+QUAD*2+DECPMAX-4) break; // all checked\r
+ }\r
+ } // shift<0\r
+\r
+ // Here when compare equal\r
+ if (!tot) return 0; // numerically equal\r
+ // total ordering .. exponent matters\r
+ if (shift>0) return sigl; // total order by exponent\r
+ if (shift<0) return sigr; // ..\r
+ return 0;\r
+ } // decNumCompare\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decToInt32 -- local routine to effect ToInteger conversions */\r
+/* */\r
+/* df is the decFloat to convert */\r
+/* set is the context */\r
+/* rmode is the rounding mode to use */\r
+/* exact is 1 if Inexact should be signalled */\r
+/* unsign is 1 if the result a uInt, 0 if an Int (cast to uInt) */\r
+/* returns 32-bit result as a uInt */\r
+/* */\r
+/* Invalid is set is df is a NaN, is infinite, or is out-of-range; in */\r
+/* these cases 0 is returned. */\r
+/* ------------------------------------------------------------------ */\r
+static uInt decToInt32(const decFloat *df, decContext *set,\r
+ enum rounding rmode, Flag exact, Flag unsign) {\r
+ Int exp; // exponent\r
+ uInt sourhi, sourpen, sourlo; // top word from source decFloat ..\r
+ uInt hi, lo; // .. penultimate, least, etc.\r
+ decFloat zero, result; // work\r
+ Int i; // ..\r
+\r
+ /* Start decoding the argument */\r
+ sourhi=DFWORD(df, 0); // top word\r
+ exp=DECCOMBEXP[sourhi>>26]; // get exponent high bits (in place)\r
+ if (EXPISSPECIAL(exp)) { // is special?\r
+ set->status|=DEC_Invalid_operation; // signal\r
+ return 0;\r
+ }\r
+\r
+ /* Here when the argument is finite */\r
+ if (GETEXPUN(df)==0) result=*df; // already a true integer\r
+ else { // need to round to integer\r
+ enum rounding saveround; // saver\r
+ uInt savestatus; // ..\r
+ saveround=set->round; // save rounding mode ..\r
+ savestatus=set->status; // .. and status\r
+ set->round=rmode; // set mode\r
+ decFloatZero(&zero); // make 0E+0\r
+ set->status=0; // clear\r
+ decFloatQuantize(&result, df, &zero, set); // [this may fail]\r
+ set->round=saveround; // restore rounding mode ..\r
+ if (exact) set->status|=savestatus; // include Inexact\r
+ else set->status=savestatus; // .. or just original status\r
+ }\r
+\r
+ // only the last four declets of the coefficient can contain\r
+ // non-zero; check for others (and also NaN or Infinity from the\r
+ // Quantize) first (see DFISZERO for explanation):\r
+ // decFloatShow(&result, "sofar");\r
+ #if DOUBLE\r
+ if ((DFWORD(&result, 0)&0x1c03ff00)!=0\r
+ || (DFWORD(&result, 0)&0x60000000)==0x60000000) {\r
+ #elif QUAD\r
+ if ((DFWORD(&result, 2)&0xffffff00)!=0\r
+ || DFWORD(&result, 1)!=0\r
+ || (DFWORD(&result, 0)&0x1c003fff)!=0\r
+ || (DFWORD(&result, 0)&0x60000000)==0x60000000) {\r
+ #endif\r
+ set->status|=DEC_Invalid_operation; // Invalid or out of range\r
+ return 0;\r
+ }\r
+ // get last twelve digits of the coefficent into hi & ho, base\r
+ // 10**9 (see GETCOEFFBILL):\r
+ sourlo=DFWORD(&result, DECWORDS-1);\r
+ lo=DPD2BIN0[sourlo&0x3ff]\r
+ +DPD2BINK[(sourlo>>10)&0x3ff]\r
+ +DPD2BINM[(sourlo>>20)&0x3ff];\r
+ sourpen=DFWORD(&result, DECWORDS-2);\r
+ hi=DPD2BIN0[((sourpen<<2) | (sourlo>>30))&0x3ff];\r
+\r
+ // according to request, check range carefully\r
+ if (unsign) {\r
+ if (hi>4 || (hi==4 && lo>294967295) || (hi+lo!=0 && DFISSIGNED(&result))) {\r
+ set->status|=DEC_Invalid_operation; // out of range\r
+ return 0;\r
+ }\r
+ return hi*BILLION+lo;\r
+ }\r
+ // signed\r
+ if (hi>2 || (hi==2 && lo>147483647)) {\r
+ // handle the usual edge case\r
+ if (lo==147483648 && hi==2 && DFISSIGNED(&result)) return 0x80000000;\r
+ set->status|=DEC_Invalid_operation; // truly out of range\r
+ return 0;\r
+ }\r
+ i=hi*BILLION+lo;\r
+ if (DFISSIGNED(&result)) i=-i;\r
+ return (uInt)i;\r
+ } // decToInt32\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decToIntegral -- local routine to effect ToIntegral value */\r
+/* */\r
+/* result gets the result */\r
+/* df is the decFloat to round */\r
+/* set is the context */\r
+/* rmode is the rounding mode to use */\r
+/* exact is 1 if Inexact should be signalled */\r
+/* returns result */\r
+/* ------------------------------------------------------------------ */\r
+static decFloat * decToIntegral(decFloat *result, const decFloat *df,\r
+ decContext *set, enum rounding rmode,\r
+ Flag exact) {\r
+ Int exp; // exponent\r
+ uInt sourhi; // top word from source decFloat\r
+ enum rounding saveround; // saver\r
+ uInt savestatus; // ..\r
+ decFloat zero; // work\r
+\r
+ /* Start decoding the argument */\r
+ sourhi=DFWORD(df, 0); // top word\r
+ exp=DECCOMBEXP[sourhi>>26]; // get exponent high bits (in place)\r
+\r
+ if (EXPISSPECIAL(exp)) { // is special?\r
+ // NaNs are handled as usual\r
+ if (DFISNAN(df)) return decNaNs(result, df, NULL, set);\r
+ // must be infinite; return canonical infinity with sign of df\r
+ return decInfinity(result, df);\r
+ }\r
+\r
+ /* Here when the argument is finite */\r
+ // complete extraction of the exponent\r
+ exp+=GETECON(df)-DECBIAS; // .. + continuation and unbias\r
+\r
+ if (exp>=0) return decCanonical(result, df); // already integral\r
+\r
+ saveround=set->round; // save rounding mode ..\r
+ savestatus=set->status; // .. and status\r
+ set->round=rmode; // set mode\r
+ decFloatZero(&zero); // make 0E+0\r
+ decFloatQuantize(result, df, &zero, set); // 'integrate'; cannot fail\r
+ set->round=saveround; // restore rounding mode ..\r
+ if (!exact) set->status=savestatus; // .. and status, unless exact\r
+ return result;\r
+ } // decToIntegral\r
--- /dev/null
+/* ------------------------------------------------------------------ */\r
+/* decCommon.c -- common code for all three fixed-size types */\r
+/* ------------------------------------------------------------------ */\r
+/* Copyright (c) IBM Corporation, 2000, 2010. All rights reserved. */\r
+/* */\r
+/* This software is made available under the terms of the */\r
+/* ICU License -- ICU 1.8.1 and later. */\r
+/* */\r
+/* The description and User's Guide ("The decNumber C Library") for */\r
+/* this software is included in the package as decNumber.pdf. This */\r
+/* document is also available in HTML, together with specifications, */\r
+/* testcases, and Web links, on the General Decimal Arithmetic page. */\r
+/* */\r
+/* Please send comments, suggestions, and corrections to the author: */\r
+/* mfc@uk.ibm.com */\r
+/* Mike Cowlishaw, IBM Fellow */\r
+/* IBM UK, PO Box 31, Birmingham Road, Warwick CV34 5JL, UK */\r
+/* ------------------------------------------------------------------ */\r
+/* This module comprises code that is shared between all the formats */\r
+/* (decSingle, decDouble, and decQuad); it includes set and extract */\r
+/* of format components, widening, narrowing, and string conversions. */\r
+/* */\r
+/* Unlike decNumber, parameterization takes place at compile time */\r
+/* rather than at runtime. The parameters are set in the decDouble.c */\r
+/* (etc.) files, which then include this one to produce the compiled */\r
+/* code. The functions here, therefore, are code shared between */\r
+/* multiple formats. */\r
+/* ------------------------------------------------------------------ */\r
+// Names here refer to decFloat rather than to decDouble, etc., and\r
+// the functions are in strict alphabetical order.\r
+// Constants, tables, and debug function(s) are included only for QUAD\r
+// (which will always be compiled if DOUBLE or SINGLE are used).\r
+//\r
+// Whenever a decContext is used, only the status may be set (using\r
+// OR) or the rounding mode read; all other fields are ignored and\r
+// untouched.\r
+\r
+// names for simpler testing and default context\r
+#if DECPMAX==7\r
+ #define SINGLE 1\r
+ #define DOUBLE 0\r
+ #define QUAD 0\r
+ #define DEFCONTEXT DEC_INIT_DECIMAL32\r
+#elif DECPMAX==16\r
+ #define SINGLE 0\r
+ #define DOUBLE 1\r
+ #define QUAD 0\r
+ #define DEFCONTEXT DEC_INIT_DECIMAL64\r
+#elif DECPMAX==34\r
+ #define SINGLE 0\r
+ #define DOUBLE 0\r
+ #define QUAD 1\r
+ #define DEFCONTEXT DEC_INIT_DECIMAL128\r
+#else\r
+ #error Unexpected DECPMAX value\r
+#endif\r
+\r
+/* Assertions */\r
+\r
+#if DECPMAX!=7 && DECPMAX!=16 && DECPMAX!=34\r
+ #error Unexpected Pmax (DECPMAX) value for this module\r
+#endif\r
+\r
+// Assert facts about digit characters, etc.\r
+#if ('9'&0x0f)!=9\r
+ #error This module assumes characters are of the form 0b....nnnn\r
+ // where .... are don't care 4 bits and nnnn is 0000 through 1001\r
+#endif\r
+#if ('9'&0xf0)==('.'&0xf0)\r
+ #error This module assumes '.' has a different mask than a digit\r
+#endif\r
+\r
+// Assert ToString lay-out conditions\r
+#if DECSTRING<DECPMAX+9\r
+ #error ToString needs at least 8 characters for lead-in and dot\r
+#endif\r
+#if DECPMAX+DECEMAXD+5 > DECSTRING\r
+ #error Exponent form can be too long for ToString to lay out safely\r
+#endif\r
+#if DECEMAXD > 4\r
+ #error Exponent form is too long for ToString to lay out\r
+ // Note: code for up to 9 digits exists in archives [decOct]\r
+#endif\r
+\r
+/* Private functions used here and possibly in decBasic.c, etc. */\r
+static decFloat * decFinalize(decFloat *, bcdnum *, decContext *);\r
+static Flag decBiStr(const char *, const char *, const char *);\r
+\r
+/* Macros and private tables; those which are not format-dependent */\r
+/* are only included if decQuad is being built. */\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* Combination field lookup tables (uInts to save measurable work) */\r
+/* */\r
+/* DECCOMBEXP - 2 most-significant-bits of exponent (00, 01, or */\r
+/* 10), shifted left for format, or DECFLOAT_Inf/NaN */\r
+/* DECCOMBWEXP - The same, for the next-wider format (unless QUAD) */\r
+/* DECCOMBMSD - 4-bit most-significant-digit */\r
+/* [0 if the index is a special (Infinity or NaN)] */\r
+/* DECCOMBFROM - 5-bit combination field from EXP top bits and MSD */\r
+/* (placed in uInt so no shift is needed) */\r
+/* */\r
+/* DECCOMBEXP, DECCOMBWEXP, and DECCOMBMSD are indexed by the sign */\r
+/* and 5-bit combination field (0-63, the second half of the table */\r
+/* identical to the first half) */\r
+/* DECCOMBFROM is indexed by expTopTwoBits*16 + msd */\r
+/* */\r
+/* DECCOMBMSD and DECCOMBFROM are not format-dependent and so are */\r
+/* only included once, when QUAD is being built */\r
+/* ------------------------------------------------------------------ */\r
+static const uInt DECCOMBEXP[64]={\r
+ 0, 0, 0, 0, 0, 0, 0, 0,\r
+ 1<<DECECONL, 1<<DECECONL, 1<<DECECONL, 1<<DECECONL,\r
+ 1<<DECECONL, 1<<DECECONL, 1<<DECECONL, 1<<DECECONL,\r
+ 2<<DECECONL, 2<<DECECONL, 2<<DECECONL, 2<<DECECONL,\r
+ 2<<DECECONL, 2<<DECECONL, 2<<DECECONL, 2<<DECECONL,\r
+ 0, 0, 1<<DECECONL, 1<<DECECONL,\r
+ 2<<DECECONL, 2<<DECECONL, DECFLOAT_Inf, DECFLOAT_NaN,\r
+ 0, 0, 0, 0, 0, 0, 0, 0,\r
+ 1<<DECECONL, 1<<DECECONL, 1<<DECECONL, 1<<DECECONL,\r
+ 1<<DECECONL, 1<<DECECONL, 1<<DECECONL, 1<<DECECONL,\r
+ 2<<DECECONL, 2<<DECECONL, 2<<DECECONL, 2<<DECECONL,\r
+ 2<<DECECONL, 2<<DECECONL, 2<<DECECONL, 2<<DECECONL,\r
+ 0, 0, 1<<DECECONL, 1<<DECECONL,\r
+ 2<<DECECONL, 2<<DECECONL, DECFLOAT_Inf, DECFLOAT_NaN};\r
+#if !QUAD\r
+static const uInt DECCOMBWEXP[64]={\r
+ 0, 0, 0, 0, 0, 0, 0, 0,\r
+ 1<<DECWECONL, 1<<DECWECONL, 1<<DECWECONL, 1<<DECWECONL,\r
+ 1<<DECWECONL, 1<<DECWECONL, 1<<DECWECONL, 1<<DECWECONL,\r
+ 2<<DECWECONL, 2<<DECWECONL, 2<<DECWECONL, 2<<DECWECONL,\r
+ 2<<DECWECONL, 2<<DECWECONL, 2<<DECWECONL, 2<<DECWECONL,\r
+ 0, 0, 1<<DECWECONL, 1<<DECWECONL,\r
+ 2<<DECWECONL, 2<<DECWECONL, DECFLOAT_Inf, DECFLOAT_NaN,\r
+ 0, 0, 0, 0, 0, 0, 0, 0,\r
+ 1<<DECWECONL, 1<<DECWECONL, 1<<DECWECONL, 1<<DECWECONL,\r
+ 1<<DECWECONL, 1<<DECWECONL, 1<<DECWECONL, 1<<DECWECONL,\r
+ 2<<DECWECONL, 2<<DECWECONL, 2<<DECWECONL, 2<<DECWECONL,\r
+ 2<<DECWECONL, 2<<DECWECONL, 2<<DECWECONL, 2<<DECWECONL,\r
+ 0, 0, 1<<DECWECONL, 1<<DECWECONL,\r
+ 2<<DECWECONL, 2<<DECWECONL, DECFLOAT_Inf, DECFLOAT_NaN};\r
+#endif\r
+\r
+#if QUAD\r
+const uInt DECCOMBMSD[64]={\r
+ 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7,\r
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 8, 9, 8, 9, 0, 0,\r
+ 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7,\r
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 8, 9, 8, 9, 0, 0};\r
+\r
+const uInt DECCOMBFROM[48]={\r
+ 0x00000000, 0x04000000, 0x08000000, 0x0C000000, 0x10000000, 0x14000000,\r
+ 0x18000000, 0x1C000000, 0x60000000, 0x64000000, 0x00000000, 0x00000000,\r
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x20000000, 0x24000000,\r
+ 0x28000000, 0x2C000000, 0x30000000, 0x34000000, 0x38000000, 0x3C000000,\r
+ 0x68000000, 0x6C000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,\r
+ 0x00000000, 0x00000000, 0x40000000, 0x44000000, 0x48000000, 0x4C000000,\r
+ 0x50000000, 0x54000000, 0x58000000, 0x5C000000, 0x70000000, 0x74000000,\r
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000};\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* Request and include the tables to use for conversions */\r
+/* ------------------------------------------------------------------ */\r
+#define DEC_BCD2DPD 1 // 0-0x999 -> DPD\r
+#define DEC_BIN2DPD 1 // 0-999 -> DPD\r
+#define DEC_BIN2BCD8 1 // 0-999 -> ddd, len\r
+#define DEC_DPD2BCD8 1 // DPD -> ddd, len\r
+#define DEC_DPD2BIN 1 // DPD -> 0-999\r
+#define DEC_DPD2BINK 1 // DPD -> 0-999000\r
+#define DEC_DPD2BINM 1 // DPD -> 0-999000000\r
+#include "decDPD.h" // source of the lookup tables\r
+\r
+#endif\r
+\r
+/* ----------------------------------------------------------------- */\r
+/* decBiStr -- compare string with pairwise options */\r
+/* */\r
+/* targ is the string to compare */\r
+/* str1 is one of the strings to compare against (length may be 0) */\r
+/* str2 is the other; it must be the same length as str1 */\r
+/* */\r
+/* returns 1 if strings compare equal, (that is, targ is the same */\r
+/* length as str1 and str2, and each character of targ is in one */\r
+/* of str1 or str2 in the corresponding position), or 0 otherwise */\r
+/* */\r
+/* This is used for generic caseless compare, including the awkward */\r
+/* case of the Turkish dotted and dotless Is. Use as (for example): */\r
+/* if (decBiStr(test, "mike", "MIKE")) ... */\r
+/* ----------------------------------------------------------------- */\r
+static Flag decBiStr(const char *targ, const char *str1, const char *str2) {\r
+ for (;;targ++, str1++, str2++) {\r
+ if (*targ!=*str1 && *targ!=*str2) return 0;\r
+ // *targ has a match in one (or both, if terminator)\r
+ if (*targ=='\0') break;\r
+ } // forever\r
+ return 1;\r
+ } // decBiStr\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decFinalize -- adjust and store a final result */\r
+/* */\r
+/* df is the decFloat format number which gets the final result */\r
+/* num is the descriptor of the number to be checked and encoded */\r
+/* [its values, including the coefficient, may be modified] */\r
+/* set is the context to use */\r
+/* returns df */\r
+/* */\r
+/* The num descriptor may point to a bcd8 string of any length; this */\r
+/* string may have leading insignificant zeros. If it has more than */\r
+/* DECPMAX digits then the final digit can be a round-for-reround */\r
+/* digit (i.e., it may include a sticky bit residue). */\r
+/* */\r
+/* The exponent (q) may be one of the codes for a special value and */\r
+/* can be up to 999999999 for conversion from string. */\r
+/* */\r
+/* No error is possible, but Inexact, Underflow, and/or Overflow may */\r
+/* be set. */\r
+/* ------------------------------------------------------------------ */\r
+// Constant whose size varies with format; also the check for surprises\r
+static uByte allnines[DECPMAX]=\r
+#if SINGLE\r
+ {9, 9, 9, 9, 9, 9, 9};\r
+#elif DOUBLE\r
+ {9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9};\r
+#elif QUAD\r
+ {9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,\r
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9};\r
+#endif\r
+\r
+static decFloat * decFinalize(decFloat *df, bcdnum *num,\r
+ decContext *set) {\r
+ uByte *ub; // work\r
+ uInt dpd; // ..\r
+ uInt uiwork; // for macros\r
+ uByte *umsd=num->msd; // local copy\r
+ uByte *ulsd=num->lsd; // ..\r
+ uInt encode; // encoding accumulator\r
+ Int length; // coefficient length\r
+\r
+ #if DECCHECK\r
+ Int clen=ulsd-umsd+1;\r
+ #if QUAD\r
+ #define COEXTRA 2 // extra-long coefficent\r
+ #else\r
+ #define COEXTRA 0\r
+ #endif\r
+ if (clen<1 || clen>DECPMAX*3+2+COEXTRA)\r
+ printf("decFinalize: suspect coefficient [length=%ld]\n", (LI)clen);\r
+ if (num->sign!=0 && num->sign!=DECFLOAT_Sign)\r
+ printf("decFinalize: bad sign [%08lx]\n", (LI)num->sign);\r
+ if (!EXPISSPECIAL(num->exponent)\r
+ && (num->exponent>1999999999 || num->exponent<-1999999999))\r
+ printf("decFinalize: improbable exponent [%ld]\n", (LI)num->exponent);\r
+ // decShowNum(num, "final");\r
+ #endif\r
+\r
+ // A special will have an 'exponent' which is very positive and a\r
+ // coefficient < DECPMAX\r
+ length=(uInt)(ulsd-umsd+1); // coefficient length\r
+\r
+ if (!NUMISSPECIAL(num)) {\r
+ Int drop; // digits to be dropped\r
+ // skip leading insignificant zeros to calculate an exact length\r
+ // [this is quite expensive]\r
+ if (*umsd==0) {\r
+ for (; umsd+3<ulsd && UBTOUI(umsd)==0;) umsd+=4;\r
+ for (; *umsd==0 && umsd<ulsd;) umsd++;\r
+ length=ulsd-umsd+1; // recalculate\r
+ }\r
+ drop=MAXI(length-DECPMAX, DECQTINY-num->exponent);\r
+ // drop can now be > digits for bottom-clamp (subnormal) cases\r
+ if (drop>0) { // rounding needed\r
+ // (decFloatQuantize has very similar code to this, so any\r
+ // changes may need to be made there, too)\r
+ uByte *roundat; // -> re-round digit\r
+ uByte reround; // reround value\r
+ // printf("Rounding; drop=%ld\n", (LI)drop);\r
+\r
+ num->exponent+=drop; // always update exponent\r
+\r
+ // Three cases here:\r
+ // 1. new LSD is in coefficient (almost always)\r
+ // 2. new LSD is digit to left of coefficient (so MSD is\r
+ // round-for-reround digit)\r
+ // 3. new LSD is to left of case 2 (whole coefficient is sticky)\r
+ // [duplicate check-stickies code to save a test]\r
+ // [by-digit check for stickies as runs of zeros are rare]\r
+ if (drop<length) { // NB lengths not addresses\r
+ roundat=umsd+length-drop;\r
+ reround=*roundat;\r
+ for (ub=roundat+1; ub<=ulsd; ub++) {\r
+ if (*ub!=0) { // non-zero to be discarded\r
+ reround=DECSTICKYTAB[reround]; // apply sticky bit\r
+ break; // [remainder don't-care]\r
+ }\r
+ } // check stickies\r
+ ulsd=roundat-1; // new LSD\r
+ }\r
+ else { // edge case\r
+ if (drop==length) {\r
+ roundat=umsd;\r
+ reround=*roundat;\r
+ }\r
+ else {\r
+ roundat=umsd-1;\r
+ reround=0;\r
+ }\r
+ for (ub=roundat+1; ub<=ulsd; ub++) {\r
+ if (*ub!=0) { // non-zero to be discarded\r
+ reround=DECSTICKYTAB[reround]; // apply sticky bit\r
+ break; // [remainder don't-care]\r
+ }\r
+ } // check stickies\r
+ *umsd=0; // coefficient is a 0\r
+ ulsd=umsd; // ..\r
+ }\r
+\r
+ if (reround!=0) { // discarding non-zero\r
+ uInt bump=0;\r
+ set->status|=DEC_Inexact;\r
+ // if adjusted exponent [exp+digits-1] is < EMIN then num is\r
+ // subnormal -- so raise Underflow\r
+ if (num->exponent<DECEMIN && (num->exponent+(ulsd-umsd+1)-1)<DECEMIN)\r
+ set->status|=DEC_Underflow;\r
+\r
+ // next decide whether increment of the coefficient is needed\r
+ if (set->round==DEC_ROUND_HALF_EVEN) { // fastpath slowest case\r
+ if (reround>5) bump=1; // >0.5 goes up\r
+ else if (reround==5) // exactly 0.5000 ..\r
+ bump=*ulsd & 0x01; // .. up iff [new] lsd is odd\r
+ } // r-h-e\r
+ else switch (set->round) {\r
+ case DEC_ROUND_DOWN: {\r
+ // no change\r
+ break;} // r-d\r
+ case DEC_ROUND_HALF_DOWN: {\r
+ if (reround>5) bump=1;\r
+ break;} // r-h-d\r
+ case DEC_ROUND_HALF_UP: {\r
+ if (reround>=5) bump=1;\r
+ break;} // r-h-u\r
+ case DEC_ROUND_UP: {\r
+ if (reround>0) bump=1;\r
+ break;} // r-u\r
+ case DEC_ROUND_CEILING: {\r
+ // same as _UP for positive numbers, and as _DOWN for negatives\r
+ if (!num->sign && reround>0) bump=1;\r
+ break;} // r-c\r
+ case DEC_ROUND_FLOOR: {\r
+ // same as _UP for negative numbers, and as _DOWN for positive\r
+ // [negative reround cannot occur on 0]\r
+ if (num->sign && reround>0) bump=1;\r
+ break;} // r-f\r
+ case DEC_ROUND_05UP: {\r
+ if (reround>0) { // anything out there is 'sticky'\r
+ // bump iff lsd=0 or 5; this cannot carry so it could be\r
+ // effected immediately with no bump -- but the code\r
+ // is clearer if this is done the same way as the others\r
+ if (*ulsd==0 || *ulsd==5) bump=1;\r
+ }\r
+ break;} // r-r\r
+ default: { // e.g., DEC_ROUND_MAX\r
+ set->status|=DEC_Invalid_context;\r
+ #if DECCHECK\r
+ printf("Unknown rounding mode: %ld\n", (LI)set->round);\r
+ #endif\r
+ break;}\r
+ } // switch (not r-h-e)\r
+ // printf("ReRound: %ld bump: %ld\n", (LI)reround, (LI)bump);\r
+\r
+ if (bump!=0) { // need increment\r
+ // increment the coefficient; this might end up with 1000...\r
+ // (after the all nines case)\r
+ ub=ulsd;\r
+ for(; ub-3>=umsd && UBTOUI(ub-3)==0x09090909; ub-=4) {\r
+ UBFROMUI(ub-3, 0); // to 00000000\r
+ }\r
+ // [note ub could now be to left of msd, and it is not safe\r
+ // to write to the the left of the msd]\r
+ // now at most 3 digits left to non-9 (usually just the one)\r
+ for (; ub>=umsd; *ub=0, ub--) {\r
+ if (*ub==9) continue; // carry\r
+ *ub+=1;\r
+ break;\r
+ }\r
+ if (ub<umsd) { // had all-nines\r
+ *umsd=1; // coefficient to 1000...\r
+ // usually the 1000... coefficient can be used as-is\r
+ if ((ulsd-umsd+1)==DECPMAX) {\r
+ num->exponent++;\r
+ }\r
+ else {\r
+ // if coefficient is shorter than Pmax then num is\r
+ // subnormal, so extend it; this is safe as drop>0\r
+ // (or, if the coefficient was supplied above, it could\r
+ // not be 9); this may make the result normal.\r
+ ulsd++;\r
+ *ulsd=0;\r
+ // [exponent unchanged]\r
+ #if DECCHECK\r
+ if (num->exponent!=DECQTINY) // sanity check\r
+ printf("decFinalize: bad all-nines extend [^%ld, %ld]\n",\r
+ (LI)num->exponent, (LI)(ulsd-umsd+1));\r
+ #endif\r
+ } // subnormal extend\r
+ } // had all-nines\r
+ } // bump needed\r
+ } // inexact rounding\r
+\r
+ length=ulsd-umsd+1; // recalculate (may be <DECPMAX)\r
+ } // need round (drop>0)\r
+\r
+ // The coefficient will now fit and has final length unless overflow\r
+ // decShowNum(num, "rounded");\r
+\r
+ // if exponent is >=emax may have to clamp, overflow, or fold-down\r
+ if (num->exponent>DECEMAX-(DECPMAX-1)) { // is edge case\r
+ // printf("overflow checks...\n");\r
+ if (*ulsd==0 && ulsd==umsd) { // have zero\r
+ num->exponent=DECEMAX-(DECPMAX-1); // clamp to max\r
+ }\r
+ else if ((num->exponent+length-1)>DECEMAX) { // > Nmax\r
+ // Overflow -- these could go straight to encoding, here, but\r
+ // instead num is adjusted to keep the code cleaner\r
+ Flag needmax=0; // 1 for finite result\r
+ set->status|=(DEC_Overflow | DEC_Inexact);\r
+ switch (set->round) {\r
+ case DEC_ROUND_DOWN: {\r
+ needmax=1; // never Infinity\r
+ break;} // r-d\r
+ case DEC_ROUND_05UP: {\r
+ needmax=1; // never Infinity\r
+ break;} // r-05\r
+ case DEC_ROUND_CEILING: {\r
+ if (num->sign) needmax=1; // Infinity iff non-negative\r
+ break;} // r-c\r
+ case DEC_ROUND_FLOOR: {\r
+ if (!num->sign) needmax=1; // Infinity iff negative\r
+ break;} // r-f\r
+ default: break; // Infinity in all other cases\r
+ }\r
+ if (!needmax) { // easy .. set Infinity\r
+ num->exponent=DECFLOAT_Inf;\r
+ *umsd=0; // be clean: coefficient to 0\r
+ ulsd=umsd; // ..\r
+ }\r
+ else { // return Nmax\r
+ umsd=allnines; // use constant array\r
+ ulsd=allnines+DECPMAX-1;\r
+ num->exponent=DECEMAX-(DECPMAX-1);\r
+ }\r
+ }\r
+ else { // no overflow but non-zero and may have to fold-down\r
+ Int shift=num->exponent-(DECEMAX-(DECPMAX-1));\r
+ if (shift>0) { // fold-down needed\r
+ // fold down needed; must copy to buffer in order to pad\r
+ // with zeros safely; fortunately this is not the worst case\r
+ // path because cannot have had a round\r
+ uByte buffer[ROUNDUP(DECPMAX+3, 4)]; // [+3 allows uInt padding]\r
+ uByte *s=umsd; // source\r
+ uByte *t=buffer; // safe target\r
+ uByte *tlsd=buffer+(ulsd-umsd)+shift; // target LSD\r
+ // printf("folddown shift=%ld\n", (LI)shift);\r
+ for (; s<=ulsd; s+=4, t+=4) UBFROMUI(t, UBTOUI(s));\r
+ for (t=tlsd-shift+1; t<=tlsd; t+=4) UBFROMUI(t, 0); // pad 0s\r
+ num->exponent-=shift;\r
+ umsd=buffer;\r
+ ulsd=tlsd;\r
+ }\r
+ } // fold-down?\r
+ length=ulsd-umsd+1; // recalculate length\r
+ } // high-end edge case\r
+ } // finite number\r
+\r
+ /*------------------------------------------------------------------*/\r
+ /* At this point the result will properly fit the decFloat */\r
+ /* encoding, and it can be encoded with no possibility of error */\r
+ /*------------------------------------------------------------------*/\r
+ // Following code does not alter coefficient (could be allnines array)\r
+\r
+ // fast path possible when DECPMAX digits\r
+ if (length==DECPMAX) {\r
+ return decFloatFromBCD(df, num->exponent, umsd, num->sign);\r
+ } // full-length\r
+\r
+ // slower path when not a full-length number; must care about length\r
+ // [coefficient length here will be < DECPMAX]\r
+ if (!NUMISSPECIAL(num)) { // is still finite\r
+ // encode the combination field and exponent continuation\r
+ uInt uexp=(uInt)(num->exponent+DECBIAS); // biased exponent\r
+ uInt code=(uexp>>DECECONL)<<4; // top two bits of exp\r
+ // [msd==0]\r
+ // look up the combination field and make high word\r
+ encode=DECCOMBFROM[code]; // indexed by (0-2)*16+msd\r
+ encode|=(uexp<<(32-6-DECECONL)) & 0x03ffffff; // exponent continuation\r
+ }\r
+ else encode=num->exponent; // special [already in word]\r
+ encode|=num->sign; // add sign\r
+\r
+ // private macro to extract a declet, n (where 0<=n<DECLETS and 0\r
+ // refers to the declet from the least significant three digits)\r
+ // and put the corresponding DPD code into dpd. Access to umsd and\r
+ // ulsd (pointers to the most and least significant digit of the\r
+ // variable-length coefficient) is assumed, along with use of a\r
+ // working pointer, uInt *ub.\r
+ // As not full-length then chances are there are many leading zeros\r
+ // [and there may be a partial triad]\r
+ #define getDPDt(dpd, n) ub=ulsd-(3*(n))-2; \\r
+ if (ub<umsd-2) dpd=0; \\r
+ else if (ub>=umsd) dpd=BCD2DPD[(*ub*256)+(*(ub+1)*16)+*(ub+2)]; \\r
+ else {dpd=*(ub+2); if (ub+1==umsd) dpd+=*(ub+1)*16; dpd=BCD2DPD[dpd];}\r
+\r
+ // place the declets in the encoding words and copy to result (df),\r
+ // according to endianness; in all cases complete the sign word\r
+ // first\r
+ #if DECPMAX==7\r
+ getDPDt(dpd, 1);\r
+ encode|=dpd<<10;\r
+ getDPDt(dpd, 0);\r
+ encode|=dpd;\r
+ DFWORD(df, 0)=encode; // just the one word\r
+\r
+ #elif DECPMAX==16\r
+ getDPDt(dpd, 4); encode|=dpd<<8;\r
+ getDPDt(dpd, 3); encode|=dpd>>2;\r
+ DFWORD(df, 0)=encode;\r
+ encode=dpd<<30;\r
+ getDPDt(dpd, 2); encode|=dpd<<20;\r
+ getDPDt(dpd, 1); encode|=dpd<<10;\r
+ getDPDt(dpd, 0); encode|=dpd;\r
+ DFWORD(df, 1)=encode;\r
+\r
+ #elif DECPMAX==34\r
+ getDPDt(dpd,10); encode|=dpd<<4;\r
+ getDPDt(dpd, 9); encode|=dpd>>6;\r
+ DFWORD(df, 0)=encode;\r
+\r
+ encode=dpd<<26;\r
+ getDPDt(dpd, 8); encode|=dpd<<16;\r
+ getDPDt(dpd, 7); encode|=dpd<<6;\r
+ getDPDt(dpd, 6); encode|=dpd>>4;\r
+ DFWORD(df, 1)=encode;\r
+\r
+ encode=dpd<<28;\r
+ getDPDt(dpd, 5); encode|=dpd<<18;\r
+ getDPDt(dpd, 4); encode|=dpd<<8;\r
+ getDPDt(dpd, 3); encode|=dpd>>2;\r
+ DFWORD(df, 2)=encode;\r
+\r
+ encode=dpd<<30;\r
+ getDPDt(dpd, 2); encode|=dpd<<20;\r
+ getDPDt(dpd, 1); encode|=dpd<<10;\r
+ getDPDt(dpd, 0); encode|=dpd;\r
+ DFWORD(df, 3)=encode;\r
+ #endif\r
+\r
+ // printf("Status: %08lx\n", (LI)set->status);\r
+ // decFloatShow(df, "final2");\r
+ return df;\r
+ } // decFinalize\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decFloatFromBCD -- set decFloat from exponent, BCD8, and sign */\r
+/* */\r
+/* df is the target decFloat */\r
+/* exp is the in-range unbiased exponent, q, or a special value in */\r
+/* the form returned by decFloatGetExponent */\r
+/* bcdar holds DECPMAX digits to set the coefficient from, one */\r
+/* digit in each byte (BCD8 encoding); the first (MSD) is ignored */\r
+/* if df is a NaN; all are ignored if df is infinite. */\r
+/* All bytes must be in 0-9; results are undefined otherwise. */\r
+/* sig is DECFLOAT_Sign to set the sign bit, 0 otherwise */\r
+/* returns df, which will be canonical */\r
+/* */\r
+/* No error is possible, and no status will be set. */\r
+/* ------------------------------------------------------------------ */\r
+decFloat * decFloatFromBCD(decFloat *df, Int exp, const uByte *bcdar,\r
+ Int sig) {\r
+ uInt encode, dpd; // work\r
+ const uByte *ub; // ..\r
+\r
+ if (EXPISSPECIAL(exp)) encode=exp|sig;// specials already encoded\r
+ else { // is finite\r
+ // encode the combination field and exponent continuation\r
+ uInt uexp=(uInt)(exp+DECBIAS); // biased exponent\r
+ uInt code=(uexp>>DECECONL)<<4; // top two bits of exp\r
+ code+=bcdar[0]; // add msd\r
+ // look up the combination field and make high word\r
+ encode=DECCOMBFROM[code]|sig; // indexed by (0-2)*16+msd\r
+ encode|=(uexp<<(32-6-DECECONL)) & 0x03ffffff; // exponent continuation\r
+ }\r
+\r
+ // private macro to extract a declet, n (where 0<=n<DECLETS and 0\r
+ // refers to the declet from the least significant three digits)\r
+ // and put the corresponding DPD code into dpd.\r
+ // Use of a working pointer, uInt *ub, is assumed.\r
+\r
+ #define getDPDb(dpd, n) ub=bcdar+DECPMAX-1-(3*(n))-2; \\r
+ dpd=BCD2DPD[(*ub*256)+(*(ub+1)*16)+*(ub+2)];\r
+\r
+ // place the declets in the encoding words and copy to result (df),\r
+ // according to endianness; in all cases complete the sign word\r
+ // first\r
+ #if DECPMAX==7\r
+ getDPDb(dpd, 1);\r
+ encode|=dpd<<10;\r
+ getDPDb(dpd, 0);\r
+ encode|=dpd;\r
+ DFWORD(df, 0)=encode; // just the one word\r
+\r
+ #elif DECPMAX==16\r
+ getDPDb(dpd, 4); encode|=dpd<<8;\r
+ getDPDb(dpd, 3); encode|=dpd>>2;\r
+ DFWORD(df, 0)=encode;\r
+ encode=dpd<<30;\r
+ getDPDb(dpd, 2); encode|=dpd<<20;\r
+ getDPDb(dpd, 1); encode|=dpd<<10;\r
+ getDPDb(dpd, 0); encode|=dpd;\r
+ DFWORD(df, 1)=encode;\r
+\r
+ #elif DECPMAX==34\r
+ getDPDb(dpd,10); encode|=dpd<<4;\r
+ getDPDb(dpd, 9); encode|=dpd>>6;\r
+ DFWORD(df, 0)=encode;\r
+\r
+ encode=dpd<<26;\r
+ getDPDb(dpd, 8); encode|=dpd<<16;\r
+ getDPDb(dpd, 7); encode|=dpd<<6;\r
+ getDPDb(dpd, 6); encode|=dpd>>4;\r
+ DFWORD(df, 1)=encode;\r
+\r
+ encode=dpd<<28;\r
+ getDPDb(dpd, 5); encode|=dpd<<18;\r
+ getDPDb(dpd, 4); encode|=dpd<<8;\r
+ getDPDb(dpd, 3); encode|=dpd>>2;\r
+ DFWORD(df, 2)=encode;\r
+\r
+ encode=dpd<<30;\r
+ getDPDb(dpd, 2); encode|=dpd<<20;\r
+ getDPDb(dpd, 1); encode|=dpd<<10;\r
+ getDPDb(dpd, 0); encode|=dpd;\r
+ DFWORD(df, 3)=encode;\r
+ #endif\r
+ // decFloatShow(df, "fromB");\r
+ return df;\r
+ } // decFloatFromBCD\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decFloatFromPacked -- set decFloat from exponent and packed BCD */\r
+/* */\r
+/* df is the target decFloat */\r
+/* exp is the in-range unbiased exponent, q, or a special value in */\r
+/* the form returned by decFloatGetExponent */\r
+/* packed holds DECPMAX packed decimal digits plus a sign nibble */\r
+/* (all 6 codes are OK); the first (MSD) is ignored if df is a NaN */\r
+/* and all except sign are ignored if df is infinite. For DOUBLE */\r
+/* and QUAD the first (pad) nibble is also ignored in all cases. */\r
+/* All coefficient nibbles must be in 0-9 and sign in A-F; results */\r
+/* are undefined otherwise. */\r
+/* returns df, which will be canonical */\r
+/* */\r
+/* No error is possible, and no status will be set. */\r
+/* ------------------------------------------------------------------ */\r
+decFloat * decFloatFromPacked(decFloat *df, Int exp, const uByte *packed) {\r
+ uByte bcdar[DECPMAX+2]; // work [+1 for pad, +1 for sign]\r
+ const uByte *ip; // ..\r
+ uByte *op; // ..\r
+ Int sig=0; // sign\r
+\r
+ // expand coefficient and sign to BCDAR\r
+ #if SINGLE\r
+ op=bcdar+1; // no pad digit\r
+ #else\r
+ op=bcdar; // first (pad) digit ignored\r
+ #endif\r
+ for (ip=packed; ip<packed+((DECPMAX+2)/2); ip++) {\r
+ *op++=*ip>>4;\r
+ *op++=(uByte)(*ip&0x0f); // [final nibble is sign]\r
+ }\r
+ op--; // -> sign byte\r
+ if (*op==DECPMINUS || *op==DECPMINUSALT) sig=DECFLOAT_Sign;\r
+\r
+ if (EXPISSPECIAL(exp)) { // Infinity or NaN\r
+ if (!EXPISINF(exp)) bcdar[1]=0; // a NaN: ignore MSD\r
+ else memset(bcdar+1, 0, DECPMAX); // Infinite: coefficient to 0\r
+ }\r
+ return decFloatFromBCD(df, exp, bcdar+1, sig);\r
+ } // decFloatFromPacked\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decFloatFromPackedChecked -- set from exponent and packed; checked */\r
+/* */\r
+/* df is the target decFloat */\r
+/* exp is the in-range unbiased exponent, q, or a special value in */\r
+/* the form returned by decFloatGetExponent */\r
+/* packed holds DECPMAX packed decimal digits plus a sign nibble */\r
+/* (all 6 codes are OK); the first (MSD) must be 0 if df is a NaN */\r
+/* and all digits must be 0 if df is infinite. For DOUBLE and */\r
+/* QUAD the first (pad) nibble must be 0. */\r
+/* All coefficient nibbles must be in 0-9 and sign in A-F. */\r
+/* returns df, which will be canonical or NULL if any of the */\r
+/* requirements are not met (if this case df is unchanged); that */\r
+/* is, the input data must be as returned by decFloatToPacked, */\r
+/* except that all six sign codes are acccepted. */\r
+/* */\r
+/* No status will be set. */\r
+/* ------------------------------------------------------------------ */\r
+decFloat * decFloatFromPackedChecked(decFloat *df, Int exp,\r
+ const uByte *packed) {\r
+ uByte bcdar[DECPMAX+2]; // work [+1 for pad, +1 for sign]\r
+ const uByte *ip; // ..\r
+ uByte *op; // ..\r
+ Int sig=0; // sign\r
+\r
+ // expand coefficient and sign to BCDAR\r
+ #if SINGLE\r
+ op=bcdar+1; // no pad digit\r
+ #else\r
+ op=bcdar; // first (pad) digit here\r
+ #endif\r
+ for (ip=packed; ip<packed+((DECPMAX+2)/2); ip++) {\r
+ *op=*ip>>4;\r
+ if (*op>9) return NULL;\r
+ op++;\r
+ *op=(uByte)(*ip&0x0f); // [final nibble is sign]\r
+ if (*op>9 && ip<packed+((DECPMAX+2)/2)-1) return NULL;\r
+ op++;\r
+ }\r
+ op--; // -> sign byte\r
+ if (*op<=9) return NULL; // bad sign\r
+ if (*op==DECPMINUS || *op==DECPMINUSALT) sig=DECFLOAT_Sign;\r
+\r
+ #if !SINGLE\r
+ if (bcdar[0]!=0) return NULL; // bad pad nibble\r
+ #endif\r
+\r
+ if (EXPISNAN(exp)) { // a NaN\r
+ if (bcdar[1]!=0) return NULL; // bad msd\r
+ } // NaN\r
+ else if (EXPISINF(exp)) { // is infinite\r
+ Int i;\r
+ for (i=0; i<DECPMAX; i++) {\r
+ if (bcdar[i+1]!=0) return NULL; // should be all zeros\r
+ }\r
+ } // infinity\r
+ else { // finite\r
+ // check the exponent is in range\r
+ if (exp>DECEMAX-DECPMAX+1) return NULL;\r
+ if (exp<DECEMIN-DECPMAX+1) return NULL;\r
+ }\r
+ return decFloatFromBCD(df, exp, bcdar+1, sig);\r
+ } // decFloatFromPacked\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decFloatFromString -- conversion from numeric string */\r
+/* */\r
+/* result is the decFloat format number which gets the result of */\r
+/* the conversion */\r
+/* *string is the character string which should contain a valid */\r
+/* number (which may be a special value), \0-terminated */\r
+/* If there are too many significant digits in the */\r
+/* coefficient it will be rounded. */\r
+/* set is the context */\r
+/* returns result */\r
+/* */\r
+/* The length of the coefficient and the size of the exponent are */\r
+/* checked by this routine, so the correct error (Underflow or */\r
+/* Overflow) can be reported or rounding applied, as necessary. */\r
+/* */\r
+/* There is no limit to the coefficient length for finite inputs; */\r
+/* NaN payloads must be integers with no more than DECPMAX-1 digits. */\r
+/* Exponents may have up to nine significant digits. */\r
+/* */\r
+/* If bad syntax is detected, the result will be a quiet NaN. */\r
+/* ------------------------------------------------------------------ */\r
+decFloat * decFloatFromString(decFloat *result, const char *string,\r
+ decContext *set) {\r
+ Int digits; // count of digits in coefficient\r
+ const char *dotchar=NULL; // where dot was found [NULL if none]\r
+ const char *cfirst=string; // -> first character of decimal part\r
+ const char *c; // work\r
+ uByte *ub; // ..\r
+ uInt uiwork; // for macros\r
+ bcdnum num; // collects data for finishing\r
+ uInt error=DEC_Conversion_syntax; // assume the worst\r
+ uByte buffer[ROUNDUP(DECSTRING+11, 8)]; // room for most coefficents,\r
+ // some common rounding, +3, & pad\r
+ #if DECTRACE\r
+ // printf("FromString %s ...\n", string);\r
+ #endif\r
+\r
+ for(;;) { // once-only 'loop'\r
+ num.sign=0; // assume non-negative\r
+ num.msd=buffer; // MSD is here always\r
+\r
+ // detect and validate the coefficient, including any leading,\r
+ // trailing, or embedded '.'\r
+ // [could test four-at-a-time here (saving 10% for decQuads),\r
+ // but that risks storage violation because the position of the\r
+ // terminator is unknown]\r
+ for (c=string;; c++) { // -> input character\r
+ if (((unsigned)(*c-'0'))<=9) continue; // '0' through '9' is good\r
+ if (*c=='\0') break; // most common non-digit\r
+ if (*c=='.') {\r
+ if (dotchar!=NULL) break; // not first '.'\r
+ dotchar=c; // record offset into decimal part\r
+ continue;}\r
+ if (c==string) { // first in string...\r
+ if (*c=='-') { // valid - sign\r
+ cfirst++;\r
+ num.sign=DECFLOAT_Sign;\r
+ continue;}\r
+ if (*c=='+') { // valid + sign\r
+ cfirst++;\r
+ continue;}\r
+ }\r
+ // *c is not a digit, terminator, or a valid +, -, or '.'\r
+ break;\r
+ } // c loop\r
+\r
+ digits=(uInt)(c-cfirst); // digits (+1 if a dot)\r
+\r
+ if (digits>0) { // had digits and/or dot\r
+ const char *clast=c-1; // note last coefficient char position\r
+ Int exp=0; // exponent accumulator\r
+ if (*c!='\0') { // something follows the coefficient\r
+ uInt edig; // unsigned work\r
+ // had some digits and more to come; expect E[+|-]nnn now\r
+ const char *firstexp; // exponent first non-zero\r
+ if (*c!='E' && *c!='e') break;\r
+ c++; // to (optional) sign\r
+ if (*c=='-' || *c=='+') c++; // step over sign (c=clast+2)\r
+ if (*c=='\0') break; // no digits! (e.g., '1.2E')\r
+ for (; *c=='0';) c++; // skip leading zeros [even last]\r
+ firstexp=c; // remember start [maybe '\0']\r
+ // gather exponent digits\r
+ edig=(uInt)*c-(uInt)'0';\r
+ if (edig<=9) { // [check not bad or terminator]\r
+ exp+=edig; // avoid initial X10\r
+ c++;\r
+ for (;; c++) {\r
+ edig=(uInt)*c-(uInt)'0';\r
+ if (edig>9) break;\r
+ exp=exp*10+edig;\r
+ }\r
+ }\r
+ // if not now on the '\0', *c must not be a digit\r
+ if (*c!='\0') break;\r
+\r
+ // (this next test must be after the syntax checks)\r
+ // if definitely more than the possible digits for format then\r
+ // the exponent may have wrapped, so simply set it to a certain\r
+ // over/underflow value\r
+ if (c>firstexp+DECEMAXD) exp=DECEMAX*2;\r
+ if (*(clast+2)=='-') exp=-exp; // was negative\r
+ } // exponent part\r
+\r
+ if (dotchar!=NULL) { // had a '.'\r
+ digits--; // remove from digits count\r
+ if (digits==0) break; // was dot alone: bad syntax\r
+ exp-=(Int)(clast-dotchar); // adjust exponent\r
+ // [the '.' can now be ignored]\r
+ }\r
+ num.exponent=exp; // exponent is good; store it\r
+\r
+ // Here when whole string has been inspected and syntax is good\r
+ // cfirst->first digit or dot, clast->last digit or dot\r
+ error=0; // no error possible now\r
+\r
+ // if the number of digits in the coefficient will fit in buffer\r
+ // then it can simply be converted to bcd8 and copied -- decFinalize\r
+ // will take care of leading zeros and rounding; the buffer is big\r
+ // enough for all canonical coefficients, including 0.00000nn...\r
+ ub=buffer;\r
+ if (digits<=(Int)(sizeof(buffer)-3)) { // [-3 allows by-4s copy]\r
+ c=cfirst;\r
+ if (dotchar!=NULL) { // a dot to worry about\r
+ if (*(c+1)=='.') { // common canonical case\r
+ *ub++=(uByte)(*c-'0'); // copy leading digit\r
+ c+=2; // prepare to handle rest\r
+ }\r
+ else for (; c<=clast;) { // '.' could be anywhere\r
+ // as usual, go by fours when safe; NB it has been asserted\r
+ // that a '.' does not have the same mask as a digit\r
+ if (c<=clast-3 // safe for four\r
+ && (UBTOUI(c)&0xf0f0f0f0)==CHARMASK) { // test four\r
+ UBFROMUI(ub, UBTOUI(c)&0x0f0f0f0f); // to BCD8\r
+ ub+=4;\r
+ c+=4;\r
+ continue;\r
+ }\r
+ if (*c=='.') { // found the dot\r
+ c++; // step over it ..\r
+ break; // .. and handle the rest\r
+ }\r
+ *ub++=(uByte)(*c++-'0');\r
+ }\r
+ } // had dot\r
+ // Now no dot; do this by fours (where safe)\r
+ for (; c<=clast-3; c+=4, ub+=4) UBFROMUI(ub, UBTOUI(c)&0x0f0f0f0f);\r
+ for (; c<=clast; c++, ub++) *ub=(uByte)(*c-'0');\r
+ num.lsd=buffer+digits-1; // record new LSD\r
+ } // fits\r
+\r
+ else { // too long for buffer\r
+ // [This is a rare and unusual case; arbitrary-length input]\r
+ // strip leading zeros [but leave final 0 if all 0's]\r
+ if (*cfirst=='.') cfirst++; // step past dot at start\r
+ if (*cfirst=='0') { // [cfirst always -> digit]\r
+ for (; cfirst<clast; cfirst++) {\r
+ if (*cfirst!='0') { // non-zero found\r
+ if (*cfirst=='.') continue; // [ignore]\r
+ break; // done\r
+ }\r
+ digits--; // 0 stripped\r
+ } // cfirst\r
+ } // at least one leading 0\r
+\r
+ // the coefficient is now as short as possible, but may still\r
+ // be too long; copy up to Pmax+1 digits to the buffer, then\r
+ // just record any non-zeros (set round-for-reround digit)\r
+ for (c=cfirst; c<=clast && ub<=buffer+DECPMAX; c++) {\r
+ // (see commentary just above)\r
+ if (c<=clast-3 // safe for four\r
+ && (UBTOUI(c)&0xf0f0f0f0)==CHARMASK) { // four digits\r
+ UBFROMUI(ub, UBTOUI(c)&0x0f0f0f0f); // to BCD8\r
+ ub+=4;\r
+ c+=3; // [will become 4]\r
+ continue;\r
+ }\r
+ if (*c=='.') continue; // [ignore]\r
+ *ub++=(uByte)(*c-'0');\r
+ }\r
+ ub--; // -> LSD\r
+ for (; c<=clast; c++) { // inspect remaining chars\r
+ if (*c!='0') { // sticky bit needed\r
+ if (*c=='.') continue; // [ignore]\r
+ *ub=DECSTICKYTAB[*ub]; // update round-for-reround\r
+ break; // no need to look at more\r
+ }\r
+ }\r
+ num.lsd=ub; // record LSD\r
+ // adjust exponent for dropped digits\r
+ num.exponent+=digits-(Int)(ub-buffer+1);\r
+ } // too long for buffer\r
+ } // digits and/or dot\r
+\r
+ else { // no digits or dot were found\r
+ // only Infinities and NaNs are allowed, here\r
+ if (*c=='\0') break; // nothing there is bad\r
+ buffer[0]=0; // default a coefficient of 0\r
+ num.lsd=buffer; // ..\r
+ if (decBiStr(c, "infinity", "INFINITY")\r
+ || decBiStr(c, "inf", "INF")) num.exponent=DECFLOAT_Inf;\r
+ else { // should be a NaN\r
+ num.exponent=DECFLOAT_qNaN; // assume quiet NaN\r
+ if (*c=='s' || *c=='S') { // probably an sNaN\r
+ num.exponent=DECFLOAT_sNaN; // effect the 's'\r
+ c++; // and step over it\r
+ }\r
+ if (*c!='N' && *c!='n') break; // check caseless "NaN"\r
+ c++;\r
+ if (*c!='a' && *c!='A') break; // ..\r
+ c++;\r
+ if (*c!='N' && *c!='n') break; // ..\r
+ c++;\r
+ // now either nothing, or nnnn payload (no dots), expected\r
+ // -> start of integer, and skip leading 0s [including plain 0]\r
+ for (cfirst=c; *cfirst=='0';) cfirst++;\r
+ if (*cfirst!='\0') { // not empty or all-0, payload\r
+ // payload found; check all valid digits and copy to buffer as bcd8\r
+ ub=buffer;\r
+ for (c=cfirst;; c++, ub++) {\r
+ if ((unsigned)(*c-'0')>9) break; // quit if not 0-9\r
+ if (c-cfirst==DECPMAX-1) break; // too many digits\r
+ *ub=(uByte)(*c-'0'); // good bcd8\r
+ }\r
+ if (*c!='\0') break; // not all digits, or too many\r
+ num.lsd=ub-1; // record new LSD\r
+ }\r
+ } // NaN or sNaN\r
+ error=0; // syntax is OK\r
+ } // digits=0 (special expected)\r
+ break; // drop out\r
+ } // [for(;;) once-loop]\r
+\r
+ // decShowNum(&num, "fromStr");\r
+\r
+ if (error!=0) {\r
+ set->status|=error;\r
+ num.exponent=DECFLOAT_qNaN; // set up quiet NaN\r
+ num.sign=0; // .. with 0 sign\r
+ buffer[0]=0; // .. and coefficient\r
+ num.lsd=buffer; // ..\r
+ // decShowNum(&num, "oops");\r
+ }\r
+\r
+ // decShowNum(&num, "dffs");\r
+ decFinalize(result, &num, set); // round, check, and lay out\r
+ // decFloatShow(result, "fromString");\r
+ return result;\r
+ } // decFloatFromString\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decFloatFromWider -- conversion from next-wider format */\r
+/* */\r
+/* result is the decFloat format number which gets the result of */\r
+/* the conversion */\r
+/* wider is the decFloatWider format number which will be narrowed */\r
+/* set is the context */\r
+/* returns result */\r
+/* */\r
+/* Narrowing can cause rounding, overflow, etc., but not Invalid */\r
+/* operation (sNaNs are copied and do not signal). */\r
+/* ------------------------------------------------------------------ */\r
+// narrow-to is not possible for decQuad format numbers; simply omit\r
+#if !QUAD\r
+decFloat * decFloatFromWider(decFloat *result, const decFloatWider *wider,\r
+ decContext *set) {\r
+ bcdnum num; // collects data for finishing\r
+ uByte bcdar[DECWPMAX]; // room for wider coefficient\r
+ uInt widerhi=DFWWORD(wider, 0); // top word\r
+ Int exp;\r
+\r
+ GETWCOEFF(wider, bcdar);\r
+\r
+ num.msd=bcdar; // MSD is here always\r
+ num.lsd=bcdar+DECWPMAX-1; // LSD is here always\r
+ num.sign=widerhi&0x80000000; // extract sign [DECFLOAT_Sign=Neg]\r
+\r
+ // decode the wider combination field to exponent\r
+ exp=DECCOMBWEXP[widerhi>>26]; // decode from wider combination field\r
+ // if it is a special there's nothing to do unless sNaN; if it's\r
+ // finite then add the (wider) exponent continuation and unbias\r
+ if (EXPISSPECIAL(exp)) exp=widerhi&0x7e000000; // include sNaN selector\r
+ else exp+=GETWECON(wider)-DECWBIAS;\r
+ num.exponent=exp;\r
+\r
+ // decShowNum(&num, "dffw");\r
+ return decFinalize(result, &num, set);// round, check, and lay out\r
+ } // decFloatFromWider\r
+#endif\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decFloatGetCoefficient -- get coefficient as BCD8 */\r
+/* */\r
+/* df is the decFloat from which to extract the coefficient */\r
+/* bcdar is where DECPMAX bytes will be written, one BCD digit in */\r
+/* each byte (BCD8 encoding); if df is a NaN the first byte will */\r
+/* be zero, and if it is infinite they will all be zero */\r
+/* returns the sign of the coefficient (DECFLOAT_Sign if negative, */\r
+/* 0 otherwise) */\r
+/* */\r
+/* No error is possible, and no status will be set. If df is a */\r
+/* special value the array is set to zeros (for Infinity) or to the */\r
+/* payload of a qNaN or sNaN. */\r
+/* ------------------------------------------------------------------ */\r
+Int decFloatGetCoefficient(const decFloat *df, uByte *bcdar) {\r
+ if (DFISINF(df)) memset(bcdar, 0, DECPMAX);\r
+ else {\r
+ GETCOEFF(df, bcdar); // use macro\r
+ if (DFISNAN(df)) bcdar[0]=0; // MSD needs correcting\r
+ }\r
+ return GETSIGN(df);\r
+ } // decFloatGetCoefficient\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decFloatGetExponent -- get unbiased exponent */\r
+/* */\r
+/* df is the decFloat from which to extract the exponent */\r
+/* returns the exponent, q. */\r
+/* */\r
+/* No error is possible, and no status will be set. If df is a */\r
+/* special value the first seven bits of the decFloat are returned, */\r
+/* left adjusted and with the first (sign) bit set to 0 (followed by */\r
+/* 25 0 bits). e.g., -sNaN would return 0x7e000000 (DECFLOAT_sNaN). */\r
+/* ------------------------------------------------------------------ */\r
+Int decFloatGetExponent(const decFloat *df) {\r
+ if (DFISSPECIAL(df)) return DFWORD(df, 0)&0x7e000000;\r
+ return GETEXPUN(df);\r
+ } // decFloatGetExponent\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decFloatSetCoefficient -- set coefficient from BCD8 */\r
+/* */\r
+/* df is the target decFloat (and source of exponent/special value) */\r
+/* bcdar holds DECPMAX digits to set the coefficient from, one */\r
+/* digit in each byte (BCD8 encoding); the first (MSD) is ignored */\r
+/* if df is a NaN; all are ignored if df is infinite. */\r
+/* sig is DECFLOAT_Sign to set the sign bit, 0 otherwise */\r
+/* returns df, which will be canonical */\r
+/* */\r
+/* No error is possible, and no status will be set. */\r
+/* ------------------------------------------------------------------ */\r
+decFloat * decFloatSetCoefficient(decFloat *df, const uByte *bcdar,\r
+ Int sig) {\r
+ uInt exp; // for exponent\r
+ uByte bcdzero[DECPMAX]; // for infinities\r
+\r
+ // Exponent/special code is extracted from df\r
+ if (DFISSPECIAL(df)) {\r
+ exp=DFWORD(df, 0)&0x7e000000;\r
+ if (DFISINF(df)) {\r
+ memset(bcdzero, 0, DECPMAX);\r
+ return decFloatFromBCD(df, exp, bcdzero, sig);\r
+ }\r
+ }\r
+ else exp=GETEXPUN(df);\r
+ return decFloatFromBCD(df, exp, bcdar, sig);\r
+ } // decFloatSetCoefficient\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decFloatSetExponent -- set exponent or special value */\r
+/* */\r
+/* df is the target decFloat (and source of coefficient/payload) */\r
+/* set is the context for reporting status */\r
+/* exp is the unbiased exponent, q, or a special value in the form */\r
+/* returned by decFloatGetExponent */\r
+/* returns df, which will be canonical */\r
+/* */\r
+/* No error is possible, but Overflow or Underflow might occur. */\r
+/* ------------------------------------------------------------------ */\r
+decFloat * decFloatSetExponent(decFloat *df, decContext *set, Int exp) {\r
+ uByte bcdcopy[DECPMAX]; // for coefficient\r
+ bcdnum num; // work\r
+ num.exponent=exp;\r
+ num.sign=decFloatGetCoefficient(df, bcdcopy); // extract coefficient\r
+ if (DFISSPECIAL(df)) { // MSD or more needs correcting\r
+ if (DFISINF(df)) memset(bcdcopy, 0, DECPMAX);\r
+ bcdcopy[0]=0;\r
+ }\r
+ num.msd=bcdcopy;\r
+ num.lsd=bcdcopy+DECPMAX-1;\r
+ return decFinalize(df, &num, set);\r
+ } // decFloatSetExponent\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decFloatRadix -- returns the base (10) */\r
+/* */\r
+/* df is any decFloat of this format */\r
+/* ------------------------------------------------------------------ */\r
+uInt decFloatRadix(const decFloat *df) {\r
+ if (df) return 10; // to placate compiler\r
+ return 10;\r
+ } // decFloatRadix\r
+\r
+/* The following function is not available if DECPRINT=0 */\r
+#if DECPRINT\r
+/* ------------------------------------------------------------------ */\r
+/* decFloatShow -- printf a decFloat in hexadecimal and decimal */\r
+/* df is the decFloat to show */\r
+/* tag is a tag string displayed with the number */\r
+/* */\r
+/* This is a debug aid; the precise format of the string may change. */\r
+/* ------------------------------------------------------------------ */\r
+void decFloatShow(const decFloat *df, const char *tag) {\r
+ char hexbuf[DECBYTES*2+DECBYTES/4+1]; // NB blank after every fourth\r
+ char buff[DECSTRING]; // for value in decimal\r
+ Int i, j=0;\r
+\r
+ for (i=0; i<DECBYTES; i++) {\r
+ #if DECLITEND\r
+ sprintf(&hexbuf[j], "%02x", df->bytes[DECBYTES-1-i]);\r
+ #else\r
+ sprintf(&hexbuf[j], "%02x", df->bytes[i]);\r
+ #endif\r
+ j+=2;\r
+ // the next line adds blank (and terminator) after final pair, too\r
+ if ((i+1)%4==0) {strcpy(&hexbuf[j], " "); j++;}\r
+ }\r
+ decFloatToString(df, buff);\r
+ printf(">%s> %s [big-endian] %s\n", tag, hexbuf, buff);\r
+ return;\r
+ } // decFloatShow\r
+#endif\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decFloatToBCD -- get sign, exponent, and BCD8 from a decFloat */\r
+/* */\r
+/* df is the source decFloat */\r
+/* exp will be set to the unbiased exponent, q, or to a special */\r
+/* value in the form returned by decFloatGetExponent */\r
+/* bcdar is where DECPMAX bytes will be written, one BCD digit in */\r
+/* each byte (BCD8 encoding); if df is a NaN the first byte will */\r
+/* be zero, and if it is infinite they will all be zero */\r
+/* returns the sign of the coefficient (DECFLOAT_Sign if negative, */\r
+/* 0 otherwise) */\r
+/* */\r
+/* No error is possible, and no status will be set. */\r
+/* ------------------------------------------------------------------ */\r
+Int decFloatToBCD(const decFloat *df, Int *exp, uByte *bcdar) {\r
+ if (DFISINF(df)) {\r
+ memset(bcdar, 0, DECPMAX);\r
+ *exp=DFWORD(df, 0)&0x7e000000;\r
+ }\r
+ else {\r
+ GETCOEFF(df, bcdar); // use macro\r
+ if (DFISNAN(df)) {\r
+ bcdar[0]=0; // MSD needs correcting\r
+ *exp=DFWORD(df, 0)&0x7e000000;\r
+ }\r
+ else { // finite\r
+ *exp=GETEXPUN(df);\r
+ }\r
+ }\r
+ return GETSIGN(df);\r
+ } // decFloatToBCD\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decFloatToEngString -- conversion to numeric string, engineering */\r
+/* */\r
+/* df is the decFloat format number to convert */\r
+/* string is the string where the result will be laid out */\r
+/* */\r
+/* string must be at least DECPMAX+9 characters (the worst case is */\r
+/* "-0.00000nnn...nnn\0", which is as long as the exponent form when */\r
+/* DECEMAXD<=4); this condition is asserted above */\r
+/* */\r
+/* No error is possible, and no status will be set */\r
+/* ------------------------------------------------------------------ */\r
+char * decFloatToEngString(const decFloat *df, char *string){\r
+ uInt msd; // coefficient MSD\r
+ Int exp; // exponent top two bits or full\r
+ uInt comb; // combination field\r
+ char *cstart; // coefficient start\r
+ char *c; // output pointer in string\r
+ char *s, *t; // .. (source, target)\r
+ Int pre, e; // work\r
+ const uByte *u; // ..\r
+ uInt uiwork; // for macros [one compiler needs\r
+ // volatile here to avoid bug, but\r
+ // that doubles execution time]\r
+\r
+ // Source words; macro handles endianness\r
+ uInt sourhi=DFWORD(df, 0); // word with sign\r
+ #if DECPMAX==16\r
+ uInt sourlo=DFWORD(df, 1);\r
+ #elif DECPMAX==34\r
+ uInt sourmh=DFWORD(df, 1);\r
+ uInt sourml=DFWORD(df, 2);\r
+ uInt sourlo=DFWORD(df, 3);\r
+ #endif\r
+\r
+ c=string; // where result will go\r
+ if (((Int)sourhi)<0) *c++='-'; // handle sign\r
+ comb=sourhi>>26; // sign+combination field\r
+ msd=DECCOMBMSD[comb]; // decode the combination field\r
+ exp=DECCOMBEXP[comb]; // ..\r
+\r
+ if (EXPISSPECIAL(exp)) { // special\r
+ if (exp==DECFLOAT_Inf) { // infinity\r
+ strcpy(c, "Inf");\r
+ strcpy(c+3, "inity");\r
+ return string; // easy\r
+ }\r
+ if (sourhi&0x02000000) *c++='s'; // sNaN\r
+ strcpy(c, "NaN"); // complete word\r
+ c+=3; // step past\r
+ // quick exit if the payload is zero\r
+ #if DECPMAX==7\r
+ if ((sourhi&0x000fffff)==0) return string;\r
+ #elif DECPMAX==16\r
+ if (sourlo==0 && (sourhi&0x0003ffff)==0) return string;\r
+ #elif DECPMAX==34\r
+ if (sourlo==0 && sourml==0 && sourmh==0\r
+ && (sourhi&0x00003fff)==0) return string;\r
+ #endif\r
+ // otherwise drop through to add integer; set correct exp etc.\r
+ exp=0; msd=0; // setup for following code\r
+ }\r
+ else { // complete exponent; top two bits are in place\r
+ exp+=GETECON(df)-DECBIAS; // .. + continuation and unbias\r
+ }\r
+\r
+ /* convert the digits of the significand to characters */\r
+ cstart=c; // save start of coefficient\r
+ if (msd) *c++=(char)('0'+(char)msd); // non-zero most significant digit\r
+\r
+ // Decode the declets. After extracting each declet, it is\r
+ // decoded to a 4-uByte sequence by table lookup; the four uBytes\r
+ // are the three encoded BCD8 digits followed by a 1-byte length\r
+ // (significant digits, except that 000 has length 0). This allows\r
+ // us to left-align the first declet with non-zero content, then\r
+ // the remaining ones are full 3-char length. Fixed-length copies\r
+ // are used because variable-length memcpy causes a subroutine call\r
+ // in at least two compilers. (The copies are length 4 for speed\r
+ // and are safe because the last item in the array is of length\r
+ // three and has the length byte following.)\r
+ #define dpd2char(dpdin) u=&DPD2BCD8[((dpdin)&0x3ff)*4]; \\r
+ if (c!=cstart) {UBFROMUI(c, UBTOUI(u)|CHARMASK); c+=3;} \\r
+ else if (*(u+3)) { \\r
+ UBFROMUI(c, UBTOUI(u+3-*(u+3))|CHARMASK); c+=*(u+3);}\r
+\r
+ #if DECPMAX==7\r
+ dpd2char(sourhi>>10); // declet 1\r
+ dpd2char(sourhi); // declet 2\r
+\r
+ #elif DECPMAX==16\r
+ dpd2char(sourhi>>8); // declet 1\r
+ dpd2char((sourhi<<2) | (sourlo>>30)); // declet 2\r
+ dpd2char(sourlo>>20); // declet 3\r
+ dpd2char(sourlo>>10); // declet 4\r
+ dpd2char(sourlo); // declet 5\r
+\r
+ #elif DECPMAX==34\r
+ dpd2char(sourhi>>4); // declet 1\r
+ dpd2char((sourhi<<6) | (sourmh>>26)); // declet 2\r
+ dpd2char(sourmh>>16); // declet 3\r
+ dpd2char(sourmh>>6); // declet 4\r
+ dpd2char((sourmh<<4) | (sourml>>28)); // declet 5\r
+ dpd2char(sourml>>18); // declet 6\r
+ dpd2char(sourml>>8); // declet 7\r
+ dpd2char((sourml<<2) | (sourlo>>30)); // declet 8\r
+ dpd2char(sourlo>>20); // declet 9\r
+ dpd2char(sourlo>>10); // declet 10\r
+ dpd2char(sourlo); // declet 11\r
+ #endif\r
+\r
+ if (c==cstart) *c++='0'; // all zeros, empty -- make "0"\r
+\r
+ if (exp==0) { // integer or NaN case -- easy\r
+ *c='\0'; // terminate\r
+ return string;\r
+ }\r
+ /* non-0 exponent */\r
+\r
+ e=0; // assume no E\r
+ pre=(Int)(c-cstart)+exp; // length+exp [c->LSD+1]\r
+ // [here, pre-exp is the digits count (==1 for zero)]\r
+\r
+ if (exp>0 || pre<-5) { // need exponential form\r
+ e=pre-1; // calculate E value\r
+ pre=1; // assume one digit before '.'\r
+ if (e!=0) { // engineering: may need to adjust\r
+ Int adj; // adjustment\r
+ // The C remainder operator is undefined for negative numbers, so\r
+ // a positive remainder calculation must be used here\r
+ if (e<0) {\r
+ adj=(-e)%3;\r
+ if (adj!=0) adj=3-adj;\r
+ }\r
+ else { // e>0\r
+ adj=e%3;\r
+ }\r
+ e=e-adj;\r
+ // if dealing with zero still produce an exponent which is a\r
+ // multiple of three, as expected, but there will only be the\r
+ // one zero before the E, still. Otherwise note the padding.\r
+ if (!DFISZERO(df)) pre+=adj;\r
+ else { // is zero\r
+ if (adj!=0) { // 0.00Esnn needed\r
+ e=e+3;\r
+ pre=-(2-adj);\r
+ }\r
+ } // zero\r
+ } // engineering adjustment\r
+ } // exponential form\r
+ // printf("e=%ld pre=%ld exp=%ld\n", (LI)e, (LI)pre, (LI)exp);\r
+\r
+ /* modify the coefficient, adding 0s, '.', and E+nn as needed */\r
+ if (pre>0) { // ddd.ddd (plain), perhaps with E\r
+ // or dd00 padding for engineering\r
+ char *dotat=cstart+pre;\r
+ if (dotat<c) { // if embedded dot needed...\r
+ // move by fours; there must be space for junk at the end\r
+ // because there is still space for exponent\r
+ s=dotat+ROUNDDOWN4(c-dotat); // source\r
+ t=s+1; // target\r
+ // open the gap [cannot use memcpy]\r
+ for (; s>=dotat; s-=4, t-=4) UBFROMUI(t, UBTOUI(s));\r
+ *dotat='.';\r
+ c++; // length increased by one\r
+ } // need dot?\r
+ else for (; c<dotat; c++) *c='0'; // pad for engineering\r
+ } // pre>0\r
+ else {\r
+ /* -5<=pre<=0: here for plain 0.ddd or 0.000ddd forms (may have\r
+ E, but only for 0.00E+3 kind of case -- with plenty of spare\r
+ space in this case */\r
+ pre=-pre+2; // gap width, including "0."\r
+ t=cstart+ROUNDDOWN4(c-cstart)+pre; // preferred first target point\r
+ // backoff if too far to the right\r
+ if (t>string+DECSTRING-5) t=string+DECSTRING-5; // adjust to fit\r
+ // now shift the entire coefficient to the right, being careful not\r
+ // to access to the left of string [cannot use memcpy]\r
+ for (s=t-pre; s>=string; s-=4, t-=4) UBFROMUI(t, UBTOUI(s));\r
+ // for Quads and Singles there may be a character or two left...\r
+ s+=3; // where next would come from\r
+ for(; s>=cstart; s--, t--) *(t+3)=*(s);\r
+ // now have fill 0. through 0.00000; use overlaps to avoid tests\r
+ if (pre>=4) {\r
+ memcpy(cstart+pre-4, "0000", 4);\r
+ memcpy(cstart, "0.00", 4);\r
+ }\r
+ else { // 2 or 3\r
+ *(cstart+pre-1)='0';\r
+ memcpy(cstart, "0.", 2);\r
+ }\r
+ c+=pre; // to end\r
+ }\r
+\r
+ // finally add the E-part, if needed; it will never be 0, and has\r
+ // a maximum length of 3 or 4 digits (asserted above)\r
+ if (e!=0) {\r
+ memcpy(c, "E+", 2); // starts with E, assume +\r
+ c++;\r
+ if (e<0) {\r
+ *c='-'; // oops, need '-'\r
+ e=-e; // uInt, please\r
+ }\r
+ c++;\r
+ // Three-character exponents are easy; 4-character a little trickier\r
+ #if DECEMAXD<=3\r
+ u=&BIN2BCD8[e*4]; // -> 3 digits + length byte\r
+ // copy fixed 4 characters [is safe], starting at non-zero\r
+ // and with character mask to convert BCD to char\r
+ UBFROMUI(c, UBTOUI(u+3-*(u+3))|CHARMASK);\r
+ c+=*(u+3); // bump pointer appropriately\r
+ #elif DECEMAXD==4\r
+ if (e<1000) { // 3 (or fewer) digits case\r
+ u=&BIN2BCD8[e*4]; // -> 3 digits + length byte\r
+ UBFROMUI(c, UBTOUI(u+3-*(u+3))|CHARMASK); // [as above]\r
+ c+=*(u+3); // bump pointer appropriately\r
+ }\r
+ else { // 4-digits\r
+ Int thou=((e>>3)*1049)>>17; // e/1000\r
+ Int rem=e-(1000*thou); // e%1000\r
+ *c++=(char)('0'+(char)thou); // the thousands digit\r
+ u=&BIN2BCD8[rem*4]; // -> 3 digits + length byte\r
+ UBFROMUI(c, UBTOUI(u)|CHARMASK);// copy fixed 3+1 characters [is safe]\r
+ c+=3; // bump pointer, always 3 digits\r
+ }\r
+ #endif\r
+ }\r
+ *c='\0'; // terminate\r
+ //printf("res %s\n", string);\r
+ return string;\r
+ } // decFloatToEngString\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decFloatToPacked -- convert decFloat to Packed decimal + exponent */\r
+/* */\r
+/* df is the source decFloat */\r
+/* exp will be set to the unbiased exponent, q, or to a special */\r
+/* value in the form returned by decFloatGetExponent */\r
+/* packed is where DECPMAX nibbles will be written with the sign as */\r
+/* final nibble (0x0c for +, 0x0d for -); a NaN has a first nibble */\r
+/* of zero, and an infinity is all zeros. decDouble and decQuad */\r
+/* have a additional leading zero nibble, leading to result */\r
+/* lengths of 4, 9, and 18 bytes. */\r
+/* returns the sign of the coefficient (DECFLOAT_Sign if negative, */\r
+/* 0 otherwise) */\r
+/* */\r
+/* No error is possible, and no status will be set. */\r
+/* ------------------------------------------------------------------ */\r
+Int decFloatToPacked(const decFloat *df, Int *exp, uByte *packed) {\r
+ uByte bcdar[DECPMAX+2]; // work buffer\r
+ uByte *ip=bcdar, *op=packed; // work pointers\r
+ if (DFISINF(df)) {\r
+ memset(bcdar, 0, DECPMAX+2);\r
+ *exp=DECFLOAT_Inf;\r
+ }\r
+ else {\r
+ GETCOEFF(df, bcdar+1); // use macro\r
+ if (DFISNAN(df)) {\r
+ bcdar[1]=0; // MSD needs clearing\r
+ *exp=DFWORD(df, 0)&0x7e000000;\r
+ }\r
+ else { // finite\r
+ *exp=GETEXPUN(df);\r
+ }\r
+ }\r
+ // now pack; coefficient currently at bcdar+1\r
+ #if SINGLE\r
+ ip++; // ignore first byte\r
+ #else\r
+ *ip=0; // need leading zero\r
+ #endif\r
+ // set final byte to Packed BCD sign value\r
+ bcdar[DECPMAX+1]=(DFISSIGNED(df) ? DECPMINUS : DECPPLUS);\r
+ // pack an even number of bytes...\r
+ for (; op<packed+((DECPMAX+2)/2); op++, ip+=2) {\r
+ *op=(uByte)((*ip<<4)+*(ip+1));\r
+ }\r
+ return (bcdar[DECPMAX+1]==DECPMINUS ? DECFLOAT_Sign : 0);\r
+ } // decFloatToPacked\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decFloatToString -- conversion to numeric string */\r
+/* */\r
+/* df is the decFloat format number to convert */\r
+/* string is the string where the result will be laid out */\r
+/* */\r
+/* string must be at least DECPMAX+9 characters (the worst case is */\r
+/* "-0.00000nnn...nnn\0", which is as long as the exponent form when */\r
+/* DECEMAXD<=4); this condition is asserted above */\r
+/* */\r
+/* No error is possible, and no status will be set */\r
+/* ------------------------------------------------------------------ */\r
+char * decFloatToString(const decFloat *df, char *string){\r
+ uInt msd; // coefficient MSD\r
+ Int exp; // exponent top two bits or full\r
+ uInt comb; // combination field\r
+ char *cstart; // coefficient start\r
+ char *c; // output pointer in string\r
+ char *s, *t; // .. (source, target)\r
+ Int pre, e; // work\r
+ const uByte *u; // ..\r
+ uInt uiwork; // for macros [one compiler needs\r
+ // volatile here to avoid bug, but\r
+ // that doubles execution time]\r
+\r
+ // Source words; macro handles endianness\r
+ uInt sourhi=DFWORD(df, 0); // word with sign\r
+ #if DECPMAX==16\r
+ uInt sourlo=DFWORD(df, 1);\r
+ #elif DECPMAX==34\r
+ uInt sourmh=DFWORD(df, 1);\r
+ uInt sourml=DFWORD(df, 2);\r
+ uInt sourlo=DFWORD(df, 3);\r
+ #endif\r
+\r
+ c=string; // where result will go\r
+ if (((Int)sourhi)<0) *c++='-'; // handle sign\r
+ comb=sourhi>>26; // sign+combination field\r
+ msd=DECCOMBMSD[comb]; // decode the combination field\r
+ exp=DECCOMBEXP[comb]; // ..\r
+\r
+ if (!EXPISSPECIAL(exp)) { // finite\r
+ // complete exponent; top two bits are in place\r
+ exp+=GETECON(df)-DECBIAS; // .. + continuation and unbias\r
+ }\r
+ else { // IS special\r
+ if (exp==DECFLOAT_Inf) { // infinity\r
+ strcpy(c, "Infinity");\r
+ return string; // easy\r
+ }\r
+ if (sourhi&0x02000000) *c++='s'; // sNaN\r
+ strcpy(c, "NaN"); // complete word\r
+ c+=3; // step past\r
+ // quick exit if the payload is zero\r
+ #if DECPMAX==7\r
+ if ((sourhi&0x000fffff)==0) return string;\r
+ #elif DECPMAX==16\r
+ if (sourlo==0 && (sourhi&0x0003ffff)==0) return string;\r
+ #elif DECPMAX==34\r
+ if (sourlo==0 && sourml==0 && sourmh==0\r
+ && (sourhi&0x00003fff)==0) return string;\r
+ #endif\r
+ // otherwise drop through to add integer; set correct exp etc.\r
+ exp=0; msd=0; // setup for following code\r
+ }\r
+\r
+ /* convert the digits of the significand to characters */\r
+ cstart=c; // save start of coefficient\r
+ if (msd) *c++=(char)('0'+(char)msd); // non-zero most significant digit\r
+\r
+ // Decode the declets. After extracting each declet, it is\r
+ // decoded to a 4-uByte sequence by table lookup; the four uBytes\r
+ // are the three encoded BCD8 digits followed by a 1-byte length\r
+ // (significant digits, except that 000 has length 0). This allows\r
+ // us to left-align the first declet with non-zero content, then\r
+ // the remaining ones are full 3-char length. Fixed-length copies\r
+ // are used because variable-length memcpy causes a subroutine call\r
+ // in at least two compilers. (The copies are length 4 for speed\r
+ // and are safe because the last item in the array is of length\r
+ // three and has the length byte following.)\r
+ #define dpd2char(dpdin) u=&DPD2BCD8[((dpdin)&0x3ff)*4]; \\r
+ if (c!=cstart) {UBFROMUI(c, UBTOUI(u)|CHARMASK); c+=3;} \\r
+ else if (*(u+3)) { \\r
+ UBFROMUI(c, UBTOUI(u+3-*(u+3))|CHARMASK); c+=*(u+3);}\r
+\r
+ #if DECPMAX==7\r
+ dpd2char(sourhi>>10); // declet 1\r
+ dpd2char(sourhi); // declet 2\r
+\r
+ #elif DECPMAX==16\r
+ dpd2char(sourhi>>8); // declet 1\r
+ dpd2char((sourhi<<2) | (sourlo>>30)); // declet 2\r
+ dpd2char(sourlo>>20); // declet 3\r
+ dpd2char(sourlo>>10); // declet 4\r
+ dpd2char(sourlo); // declet 5\r
+\r
+ #elif DECPMAX==34\r
+ dpd2char(sourhi>>4); // declet 1\r
+ dpd2char((sourhi<<6) | (sourmh>>26)); // declet 2\r
+ dpd2char(sourmh>>16); // declet 3\r
+ dpd2char(sourmh>>6); // declet 4\r
+ dpd2char((sourmh<<4) | (sourml>>28)); // declet 5\r
+ dpd2char(sourml>>18); // declet 6\r
+ dpd2char(sourml>>8); // declet 7\r
+ dpd2char((sourml<<2) | (sourlo>>30)); // declet 8\r
+ dpd2char(sourlo>>20); // declet 9\r
+ dpd2char(sourlo>>10); // declet 10\r
+ dpd2char(sourlo); // declet 11\r
+ #endif\r
+\r
+ if (c==cstart) *c++='0'; // all zeros, empty -- make "0"\r
+\r
+ //[This fast path is valid but adds 3-5 cycles to worst case length]\r
+ //if (exp==0) { // integer or NaN case -- easy\r
+ // *c='\0'; // terminate\r
+ // return string;\r
+ // }\r
+\r
+ e=0; // assume no E\r
+ pre=(Int)(c-cstart)+exp; // length+exp [c->LSD+1]\r
+ // [here, pre-exp is the digits count (==1 for zero)]\r
+\r
+ if (exp>0 || pre<-5) { // need exponential form\r
+ e=pre-1; // calculate E value\r
+ pre=1; // assume one digit before '.'\r
+ } // exponential form\r
+\r
+ /* modify the coefficient, adding 0s, '.', and E+nn as needed */\r
+ if (pre>0) { // ddd.ddd (plain), perhaps with E\r
+ char *dotat=cstart+pre;\r
+ if (dotat<c) { // if embedded dot needed...\r
+ // [memmove is a disaster, here]\r
+ // move by fours; there must be space for junk at the end\r
+ // because exponent is still possible\r
+ s=dotat+ROUNDDOWN4(c-dotat); // source\r
+ t=s+1; // target\r
+ // open the gap [cannot use memcpy]\r
+ for (; s>=dotat; s-=4, t-=4) UBFROMUI(t, UBTOUI(s));\r
+ *dotat='.';\r
+ c++; // length increased by one\r
+ } // need dot?\r
+\r
+ // finally add the E-part, if needed; it will never be 0, and has\r
+ // a maximum length of 3 or 4 digits (asserted above)\r
+ if (e!=0) {\r
+ memcpy(c, "E+", 2); // starts with E, assume +\r
+ c++;\r
+ if (e<0) {\r
+ *c='-'; // oops, need '-'\r
+ e=-e; // uInt, please\r
+ }\r
+ c++;\r
+ // Three-character exponents are easy; 4-character a little trickier\r
+ #if DECEMAXD<=3\r
+ u=&BIN2BCD8[e*4]; // -> 3 digits + length byte\r
+ // copy fixed 4 characters [is safe], starting at non-zero\r
+ // and with character mask to convert BCD to char\r
+ UBFROMUI(c, UBTOUI(u+3-*(u+3))|CHARMASK);\r
+ c+=*(u+3); // bump pointer appropriately\r
+ #elif DECEMAXD==4\r
+ if (e<1000) { // 3 (or fewer) digits case\r
+ u=&BIN2BCD8[e*4]; // -> 3 digits + length byte\r
+ UBFROMUI(c, UBTOUI(u+3-*(u+3))|CHARMASK); // [as above]\r
+ c+=*(u+3); // bump pointer appropriately\r
+ }\r
+ else { // 4-digits\r
+ Int thou=((e>>3)*1049)>>17; // e/1000\r
+ Int rem=e-(1000*thou); // e%1000\r
+ *c++=(char)('0'+(char)thou); // the thousands digit\r
+ u=&BIN2BCD8[rem*4]; // -> 3 digits + length byte\r
+ UBFROMUI(c, UBTOUI(u)|CHARMASK); // copy fixed 3+1 characters [is safe]\r
+ c+=3; // bump pointer, always 3 digits\r
+ }\r
+ #endif\r
+ }\r
+ *c='\0'; // add terminator\r
+ //printf("res %s\n", string);\r
+ return string;\r
+ } // pre>0\r
+\r
+ /* -5<=pre<=0: here for plain 0.ddd or 0.000ddd forms (can never have E) */\r
+ // Surprisingly, this is close to being the worst-case path, so the\r
+ // shift is done by fours; this is a little tricky because the\r
+ // rightmost character to be written must not be beyond where the\r
+ // rightmost terminator could be -- so backoff to not touch\r
+ // terminator position if need be (this can make exact alignments\r
+ // for full Doubles, but in some cases needs care not to access too\r
+ // far to the left)\r
+\r
+ pre=-pre+2; // gap width, including "0."\r
+ t=cstart+ROUNDDOWN4(c-cstart)+pre; // preferred first target point\r
+ // backoff if too far to the right\r
+ if (t>string+DECSTRING-5) t=string+DECSTRING-5; // adjust to fit\r
+ // now shift the entire coefficient to the right, being careful not\r
+ // to access to the left of string [cannot use memcpy]\r
+ for (s=t-pre; s>=string; s-=4, t-=4) UBFROMUI(t, UBTOUI(s));\r
+ // for Quads and Singles there may be a character or two left...\r
+ s+=3; // where next would come from\r
+ for(; s>=cstart; s--, t--) *(t+3)=*(s);\r
+ // now have fill 0. through 0.00000; use overlaps to avoid tests\r
+ if (pre>=4) {\r
+ memcpy(cstart+pre-4, "0000", 4);\r
+ memcpy(cstart, "0.00", 4);\r
+ }\r
+ else { // 2 or 3\r
+ *(cstart+pre-1)='0';\r
+ memcpy(cstart, "0.", 2);\r
+ }\r
+ *(c+pre)='\0'; // terminate\r
+ return string;\r
+ } // decFloatToString\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decFloatToWider -- conversion to next-wider format */\r
+/* */\r
+/* source is the decFloat format number which gets the result of */\r
+/* the conversion */\r
+/* wider is the decFloatWider format number which will be narrowed */\r
+/* returns wider */\r
+/* */\r
+/* Widening is always exact; no status is set (sNaNs are copied and */\r
+/* do not signal). The result will be canonical if the source is, */\r
+/* and may or may not be if the source is not. */\r
+/* ------------------------------------------------------------------ */\r
+// widening is not possible for decQuad format numbers; simply omit\r
+#if !QUAD\r
+decFloatWider * decFloatToWider(const decFloat *source, decFloatWider *wider) {\r
+ uInt msd;\r
+\r
+ /* Construct and copy the sign word */\r
+ if (DFISSPECIAL(source)) {\r
+ // copy sign, combination, and first bit of exponent (sNaN selector)\r
+ DFWWORD(wider, 0)=DFWORD(source, 0)&0xfe000000;\r
+ msd=0;\r
+ }\r
+ else { // is finite number\r
+ uInt exp=GETEXPUN(source)+DECWBIAS; // get unbiased exponent and rebias\r
+ uInt code=(exp>>DECWECONL)<<29; // set two bits of exp [msd=0]\r
+ code|=(exp<<(32-6-DECWECONL)) & 0x03ffffff; // add exponent continuation\r
+ code|=DFWORD(source, 0)&0x80000000; // add sign\r
+ DFWWORD(wider, 0)=code; // .. and place top word in wider\r
+ msd=GETMSD(source); // get source coefficient MSD [0-9]\r
+ }\r
+ /* Copy the coefficient and clear any 'unused' words to left */\r
+ #if SINGLE\r
+ DFWWORD(wider, 1)=(DFWORD(source, 0)&0x000fffff)|(msd<<20);\r
+ #elif DOUBLE\r
+ DFWWORD(wider, 2)=(DFWORD(source, 0)&0x0003ffff)|(msd<<18);\r
+ DFWWORD(wider, 3)=DFWORD(source, 1);\r
+ DFWWORD(wider, 1)=0;\r
+ #endif\r
+ return wider;\r
+ } // decFloatToWider\r
+#endif\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decFloatVersion -- return package version string */\r
+/* */\r
+/* returns a constant string describing this package */\r
+/* ------------------------------------------------------------------ */\r
+const char *decFloatVersion(void) {\r
+ return DECVERSION;\r
+ } // decFloatVersion\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decFloatZero -- set to canonical (integer) zero */\r
+/* */\r
+/* df is the decFloat format number to integer +0 (q=0, c=+0) */\r
+/* returns df */\r
+/* */\r
+/* No error is possible, and no status can be set. */\r
+/* ------------------------------------------------------------------ */\r
+decFloat * decFloatZero(decFloat *df){\r
+ DFWORD(df, 0)=ZEROWORD; // set appropriate top word\r
+ #if DOUBLE || QUAD\r
+ DFWORD(df, 1)=0;\r
+ #if QUAD\r
+ DFWORD(df, 2)=0;\r
+ DFWORD(df, 3)=0;\r
+ #endif\r
+ #endif\r
+ // decFloatShow(df, "zero");\r
+ return df;\r
+ } // decFloatZero\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* Private generic function (not format-specific) for development use */\r
+/* ------------------------------------------------------------------ */\r
+// This is included once only, for all to use\r
+#if QUAD && (DECCHECK || DECTRACE)\r
+ /* ---------------------------------------------------------------- */\r
+ /* decShowNum -- display bcd8 number in debug form */\r
+ /* */\r
+ /* num is the bcdnum to display */\r
+ /* tag is a string to label the display */\r
+ /* ---------------------------------------------------------------- */\r
+ void decShowNum(const bcdnum *num, const char *tag) {\r
+ const char *csign="+"; // sign character\r
+ uByte *ub; // work\r
+ uInt uiwork; // for macros\r
+ if (num->sign==DECFLOAT_Sign) csign="-";\r
+\r
+ printf(">%s> ", tag);\r
+ if (num->exponent==DECFLOAT_Inf) printf("%sInfinity", csign);\r
+ else if (num->exponent==DECFLOAT_qNaN) printf("%sqNaN", csign);\r
+ else if (num->exponent==DECFLOAT_sNaN) printf("%ssNaN", csign);\r
+ else { // finite\r
+ char qbuf[10]; // for right-aligned q\r
+ char *c; // work\r
+ const uByte *u; // ..\r
+ Int e=num->exponent; // .. exponent\r
+ strcpy(qbuf, "q=");\r
+ c=&qbuf[2]; // where exponent will go\r
+ // lay out the exponent\r
+ if (e<0) {\r
+ *c++='-'; // add '-'\r
+ e=-e; // uInt, please\r
+ }\r
+ #if DECEMAXD>4\r
+ #error Exponent form is too long for ShowNum to lay out\r
+ #endif\r
+ if (e==0) *c++='0'; // 0-length case\r
+ else if (e<1000) { // 3 (or fewer) digits case\r
+ u=&BIN2BCD8[e*4]; // -> 3 digits + length byte\r
+ UBFROMUI(c, UBTOUI(u+3-*(u+3))|CHARMASK); // [as above]\r
+ c+=*(u+3); // bump pointer appropriately\r
+ }\r
+ else { // 4-digits\r
+ Int thou=((e>>3)*1049)>>17; // e/1000\r
+ Int rem=e-(1000*thou); // e%1000\r
+ *c++=(char)('0'+(char)thou); // the thousands digit\r
+ u=&BIN2BCD8[rem*4]; // -> 3 digits + length byte\r
+ UBFROMUI(c, UBTOUI(u)|CHARMASK); // copy fixed 3+1 characters [is safe]\r
+ c+=3; // bump pointer, always 3 digits\r
+ }\r
+ *c='\0'; // add terminator\r
+ printf("%7s c=%s", qbuf, csign);\r
+ }\r
+\r
+ if (!EXPISSPECIAL(num->exponent) || num->msd!=num->lsd || *num->lsd!=0) {\r
+ for (ub=num->msd; ub<=num->lsd; ub++) { // coefficient...\r
+ printf("%1x", *ub);\r
+ if ((num->lsd-ub)%3==0 && ub!=num->lsd) printf(" "); // 4-space\r
+ }\r
+ }\r
+ printf("\n");\r
+ } // decShowNum\r
+#endif\r
--- /dev/null
+/* ------------------------------------------------------------------ */\r
+/* Decimal Context module */\r
+/* ------------------------------------------------------------------ */\r
+/* Copyright (c) IBM Corporation, 2000, 2009. All rights reserved. */\r
+/* */\r
+/* This software is made available under the terms of the */\r
+/* ICU License -- ICU 1.8.1 and later. */\r
+/* */\r
+/* The description and User's Guide ("The decNumber C Library") for */\r
+/* this software is called decNumber.pdf. This document is */\r
+/* available, together with arithmetic and format specifications, */\r
+/* testcases, and Web links, on the General Decimal Arithmetic page. */\r
+/* */\r
+/* Please send comments, suggestions, and corrections to the author: */\r
+/* mfc@uk.ibm.com */\r
+/* Mike Cowlishaw, IBM Fellow */\r
+/* IBM UK, PO Box 31, Birmingham Road, Warwick CV34 5JL, UK */\r
+/* ------------------------------------------------------------------ */\r
+/* This module comprises the routines for handling arithmetic */\r
+/* context structures. */\r
+/* ------------------------------------------------------------------ */\r
+\r
+#include <string.h> // for strcmp\r
+#include <stdio.h> // for printf if DECCHECK\r
+#include "decContext.h" // context and base types\r
+#include "decNumberLocal.h" // decNumber local types, etc.\r
+\r
+/* compile-time endian tester [assumes sizeof(Int)>1] */\r
+static const Int mfcone=1; // constant 1\r
+static const Flag *mfctop=(const Flag *)&mfcone; // -> top byte\r
+#define LITEND *mfctop // named flag; 1=little-endian\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* round-for-reround digits */\r
+/* ------------------------------------------------------------------ */\r
+const uByte DECSTICKYTAB[10]={1,1,2,3,4,6,6,7,8,9}; /* used if sticky */\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* Powers of ten (powers[n]==10**n, 0<=n<=9) */\r
+/* ------------------------------------------------------------------ */\r
+const uInt DECPOWERS[10]={1, 10, 100, 1000, 10000, 100000, 1000000,\r
+ 10000000, 100000000, 1000000000};\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decContextClearStatus -- clear bits in current status */\r
+/* */\r
+/* context is the context structure to be queried */\r
+/* mask indicates the bits to be cleared (the status bit that */\r
+/* corresponds to each 1 bit in the mask is cleared) */\r
+/* returns context */\r
+/* */\r
+/* No error is possible. */\r
+/* ------------------------------------------------------------------ */\r
+decContext *decContextClearStatus(decContext *context, uInt mask) {\r
+ context->status&=~mask;\r
+ return context;\r
+ } // decContextClearStatus\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decContextDefault -- initialize a context structure */\r
+/* */\r
+/* context is the structure to be initialized */\r
+/* kind selects the required set of default values, one of: */\r
+/* DEC_INIT_BASE -- select ANSI X3-274 defaults */\r
+/* DEC_INIT_DECIMAL32 -- select IEEE 754 defaults, 32-bit */\r
+/* DEC_INIT_DECIMAL64 -- select IEEE 754 defaults, 64-bit */\r
+/* DEC_INIT_DECIMAL128 -- select IEEE 754 defaults, 128-bit */\r
+/* For any other value a valid context is returned, but with */\r
+/* Invalid_operation set in the status field. */\r
+/* returns a context structure with the appropriate initial values. */\r
+/* ------------------------------------------------------------------ */\r
+decContext * decContextDefault(decContext *context, Int kind) {\r
+ // set defaults...\r
+ context->digits=9; // 9 digits\r
+ context->emax=DEC_MAX_EMAX; // 9-digit exponents\r
+ context->emin=DEC_MIN_EMIN; // .. balanced\r
+ context->round=DEC_ROUND_HALF_UP; // 0.5 rises\r
+ context->traps=DEC_Errors; // all but informational\r
+ context->status=0; // cleared\r
+ context->clamp=0; // no clamping\r
+ #if DECSUBSET\r
+ context->extended=0; // cleared\r
+ #endif\r
+ switch (kind) {\r
+ case DEC_INIT_BASE:\r
+ // [use defaults]\r
+ break;\r
+ case DEC_INIT_DECIMAL32:\r
+ context->digits=7; // digits\r
+ context->emax=96; // Emax\r
+ context->emin=-95; // Emin\r
+ context->round=DEC_ROUND_HALF_EVEN; // 0.5 to nearest even\r
+ context->traps=0; // no traps set\r
+ context->clamp=1; // clamp exponents\r
+ #if DECSUBSET\r
+ context->extended=1; // set\r
+ #endif\r
+ break;\r
+ case DEC_INIT_DECIMAL64:\r
+ context->digits=16; // digits\r
+ context->emax=384; // Emax\r
+ context->emin=-383; // Emin\r
+ context->round=DEC_ROUND_HALF_EVEN; // 0.5 to nearest even\r
+ context->traps=0; // no traps set\r
+ context->clamp=1; // clamp exponents\r
+ #if DECSUBSET\r
+ context->extended=1; // set\r
+ #endif\r
+ break;\r
+ case DEC_INIT_DECIMAL128:\r
+ context->digits=34; // digits\r
+ context->emax=6144; // Emax\r
+ context->emin=-6143; // Emin\r
+ context->round=DEC_ROUND_HALF_EVEN; // 0.5 to nearest even\r
+ context->traps=0; // no traps set\r
+ context->clamp=1; // clamp exponents\r
+ #if DECSUBSET\r
+ context->extended=1; // set\r
+ #endif\r
+ break;\r
+\r
+ default: // invalid Kind\r
+ // use defaults, and ..\r
+ decContextSetStatus(context, DEC_Invalid_operation); // trap\r
+ }\r
+\r
+ return context;} // decContextDefault\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decContextGetRounding -- return current rounding mode */\r
+/* */\r
+/* context is the context structure to be queried */\r
+/* returns the rounding mode */\r
+/* */\r
+/* No error is possible. */\r
+/* ------------------------------------------------------------------ */\r
+enum rounding decContextGetRounding(decContext *context) {\r
+ return context->round;\r
+ } // decContextGetRounding\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decContextGetStatus -- return current status */\r
+/* */\r
+/* context is the context structure to be queried */\r
+/* returns status */\r
+/* */\r
+/* No error is possible. */\r
+/* ------------------------------------------------------------------ */\r
+uInt decContextGetStatus(decContext *context) {\r
+ return context->status;\r
+ } // decContextGetStatus\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decContextRestoreStatus -- restore bits in current status */\r
+/* */\r
+/* context is the context structure to be updated */\r
+/* newstatus is the source for the bits to be restored */\r
+/* mask indicates the bits to be restored (the status bit that */\r
+/* corresponds to each 1 bit in the mask is set to the value of */\r
+/* the correspnding bit in newstatus) */\r
+/* returns context */\r
+/* */\r
+/* No error is possible. */\r
+/* ------------------------------------------------------------------ */\r
+decContext *decContextRestoreStatus(decContext *context,\r
+ uInt newstatus, uInt mask) {\r
+ context->status&=~mask; // clear the selected bits\r
+ context->status|=(mask&newstatus); // or in the new bits\r
+ return context;\r
+ } // decContextRestoreStatus\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decContextSaveStatus -- save bits in current status */\r
+/* */\r
+/* context is the context structure to be queried */\r
+/* mask indicates the bits to be saved (the status bits that */\r
+/* correspond to each 1 bit in the mask are saved) */\r
+/* returns the AND of the mask and the current status */\r
+/* */\r
+/* No error is possible. */\r
+/* ------------------------------------------------------------------ */\r
+uInt decContextSaveStatus(decContext *context, uInt mask) {\r
+ return context->status&mask;\r
+ } // decContextSaveStatus\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decContextSetRounding -- set current rounding mode */\r
+/* */\r
+/* context is the context structure to be updated */\r
+/* newround is the value which will replace the current mode */\r
+/* returns context */\r
+/* */\r
+/* No error is possible. */\r
+/* ------------------------------------------------------------------ */\r
+decContext *decContextSetRounding(decContext *context,\r
+ enum rounding newround) {\r
+ context->round=newround;\r
+ return context;\r
+ } // decContextSetRounding\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decContextSetStatus -- set status and raise trap if appropriate */\r
+/* */\r
+/* context is the context structure to be updated */\r
+/* status is the DEC_ exception code */\r
+/* returns the context structure */\r
+/* */\r
+/* Control may never return from this routine, if there is a signal */\r
+/* handler and it takes a long jump. */\r
+/* ------------------------------------------------------------------ */\r
+decContext * decContextSetStatus(decContext *context, uInt status) {\r
+ context->status|=status;\r
+ if (status & context->traps) raise(SIGFPE);\r
+ return context;} // decContextSetStatus\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decContextSetStatusFromString -- set status from a string + trap */\r
+/* */\r
+/* context is the context structure to be updated */\r
+/* string is a string exactly equal to one that might be returned */\r
+/* by decContextStatusToString */\r
+/* */\r
+/* The status bit corresponding to the string is set, and a trap */\r
+/* is raised if appropriate. */\r
+/* */\r
+/* returns the context structure, unless the string is equal to */\r
+/* DEC_Condition_MU or is not recognized. In these cases NULL is */\r
+/* returned. */\r
+/* ------------------------------------------------------------------ */\r
+decContext * decContextSetStatusFromString(decContext *context,\r
+ const char *string) {\r
+ if (strcmp(string, DEC_Condition_CS)==0)\r
+ return decContextSetStatus(context, DEC_Conversion_syntax);\r
+ if (strcmp(string, DEC_Condition_DZ)==0)\r
+ return decContextSetStatus(context, DEC_Division_by_zero);\r
+ if (strcmp(string, DEC_Condition_DI)==0)\r
+ return decContextSetStatus(context, DEC_Division_impossible);\r
+ if (strcmp(string, DEC_Condition_DU)==0)\r
+ return decContextSetStatus(context, DEC_Division_undefined);\r
+ if (strcmp(string, DEC_Condition_IE)==0)\r
+ return decContextSetStatus(context, DEC_Inexact);\r
+ if (strcmp(string, DEC_Condition_IS)==0)\r
+ return decContextSetStatus(context, DEC_Insufficient_storage);\r
+ if (strcmp(string, DEC_Condition_IC)==0)\r
+ return decContextSetStatus(context, DEC_Invalid_context);\r
+ if (strcmp(string, DEC_Condition_IO)==0)\r
+ return decContextSetStatus(context, DEC_Invalid_operation);\r
+ #if DECSUBSET\r
+ if (strcmp(string, DEC_Condition_LD)==0)\r
+ return decContextSetStatus(context, DEC_Lost_digits);\r
+ #endif\r
+ if (strcmp(string, DEC_Condition_OV)==0)\r
+ return decContextSetStatus(context, DEC_Overflow);\r
+ if (strcmp(string, DEC_Condition_PA)==0)\r
+ return decContextSetStatus(context, DEC_Clamped);\r
+ if (strcmp(string, DEC_Condition_RO)==0)\r
+ return decContextSetStatus(context, DEC_Rounded);\r
+ if (strcmp(string, DEC_Condition_SU)==0)\r
+ return decContextSetStatus(context, DEC_Subnormal);\r
+ if (strcmp(string, DEC_Condition_UN)==0)\r
+ return decContextSetStatus(context, DEC_Underflow);\r
+ if (strcmp(string, DEC_Condition_ZE)==0)\r
+ return context;\r
+ return NULL; // Multiple status, or unknown\r
+ } // decContextSetStatusFromString\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decContextSetStatusFromStringQuiet -- set status from a string */\r
+/* */\r
+/* context is the context structure to be updated */\r
+/* string is a string exactly equal to one that might be returned */\r
+/* by decContextStatusToString */\r
+/* */\r
+/* The status bit corresponding to the string is set; no trap is */\r
+/* raised. */\r
+/* */\r
+/* returns the context structure, unless the string is equal to */\r
+/* DEC_Condition_MU or is not recognized. In these cases NULL is */\r
+/* returned. */\r
+/* ------------------------------------------------------------------ */\r
+decContext * decContextSetStatusFromStringQuiet(decContext *context,\r
+ const char *string) {\r
+ if (strcmp(string, DEC_Condition_CS)==0)\r
+ return decContextSetStatusQuiet(context, DEC_Conversion_syntax);\r
+ if (strcmp(string, DEC_Condition_DZ)==0)\r
+ return decContextSetStatusQuiet(context, DEC_Division_by_zero);\r
+ if (strcmp(string, DEC_Condition_DI)==0)\r
+ return decContextSetStatusQuiet(context, DEC_Division_impossible);\r
+ if (strcmp(string, DEC_Condition_DU)==0)\r
+ return decContextSetStatusQuiet(context, DEC_Division_undefined);\r
+ if (strcmp(string, DEC_Condition_IE)==0)\r
+ return decContextSetStatusQuiet(context, DEC_Inexact);\r
+ if (strcmp(string, DEC_Condition_IS)==0)\r
+ return decContextSetStatusQuiet(context, DEC_Insufficient_storage);\r
+ if (strcmp(string, DEC_Condition_IC)==0)\r
+ return decContextSetStatusQuiet(context, DEC_Invalid_context);\r
+ if (strcmp(string, DEC_Condition_IO)==0)\r
+ return decContextSetStatusQuiet(context, DEC_Invalid_operation);\r
+ #if DECSUBSET\r
+ if (strcmp(string, DEC_Condition_LD)==0)\r
+ return decContextSetStatusQuiet(context, DEC_Lost_digits);\r
+ #endif\r
+ if (strcmp(string, DEC_Condition_OV)==0)\r
+ return decContextSetStatusQuiet(context, DEC_Overflow);\r
+ if (strcmp(string, DEC_Condition_PA)==0)\r
+ return decContextSetStatusQuiet(context, DEC_Clamped);\r
+ if (strcmp(string, DEC_Condition_RO)==0)\r
+ return decContextSetStatusQuiet(context, DEC_Rounded);\r
+ if (strcmp(string, DEC_Condition_SU)==0)\r
+ return decContextSetStatusQuiet(context, DEC_Subnormal);\r
+ if (strcmp(string, DEC_Condition_UN)==0)\r
+ return decContextSetStatusQuiet(context, DEC_Underflow);\r
+ if (strcmp(string, DEC_Condition_ZE)==0)\r
+ return context;\r
+ return NULL; // Multiple status, or unknown\r
+ } // decContextSetStatusFromStringQuiet\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decContextSetStatusQuiet -- set status without trap */\r
+/* */\r
+/* context is the context structure to be updated */\r
+/* status is the DEC_ exception code */\r
+/* returns the context structure */\r
+/* */\r
+/* No error is possible. */\r
+/* ------------------------------------------------------------------ */\r
+decContext * decContextSetStatusQuiet(decContext *context, uInt status) {\r
+ context->status|=status;\r
+ return context;} // decContextSetStatusQuiet\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decContextStatusToString -- convert status flags to a string */\r
+/* */\r
+/* context is a context with valid status field */\r
+/* */\r
+/* returns a constant string describing the condition. If multiple */\r
+/* (or no) flags are set, a generic constant message is returned. */\r
+/* ------------------------------------------------------------------ */\r
+const char *decContextStatusToString(const decContext *context) {\r
+ Int status=context->status;\r
+\r
+ // test the five IEEE first, as some of the others are ambiguous when\r
+ // DECEXTFLAG=0\r
+ if (status==DEC_Invalid_operation ) return DEC_Condition_IO;\r
+ if (status==DEC_Division_by_zero ) return DEC_Condition_DZ;\r
+ if (status==DEC_Overflow ) return DEC_Condition_OV;\r
+ if (status==DEC_Underflow ) return DEC_Condition_UN;\r
+ if (status==DEC_Inexact ) return DEC_Condition_IE;\r
+\r
+ if (status==DEC_Division_impossible ) return DEC_Condition_DI;\r
+ if (status==DEC_Division_undefined ) return DEC_Condition_DU;\r
+ if (status==DEC_Rounded ) return DEC_Condition_RO;\r
+ if (status==DEC_Clamped ) return DEC_Condition_PA;\r
+ if (status==DEC_Subnormal ) return DEC_Condition_SU;\r
+ if (status==DEC_Conversion_syntax ) return DEC_Condition_CS;\r
+ if (status==DEC_Insufficient_storage ) return DEC_Condition_IS;\r
+ if (status==DEC_Invalid_context ) return DEC_Condition_IC;\r
+ #if DECSUBSET\r
+ if (status==DEC_Lost_digits ) return DEC_Condition_LD;\r
+ #endif\r
+ if (status==0 ) return DEC_Condition_ZE;\r
+ return DEC_Condition_MU; // Multiple errors\r
+ } // decContextStatusToString\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decContextTestEndian -- test whether DECLITEND is set correctly */\r
+/* */\r
+/* quiet is 1 to suppress message; 0 otherwise */\r
+/* returns 0 if DECLITEND is correct */\r
+/* 1 if DECLITEND is incorrect and should be 1 */\r
+/* -1 if DECLITEND is incorrect and should be 0 */\r
+/* */\r
+/* A message is displayed if the return value is not 0 and quiet==0. */\r
+/* */\r
+/* No error is possible. */\r
+/* ------------------------------------------------------------------ */\r
+Int decContextTestEndian(Flag quiet) {\r
+ Int res=0; // optimist\r
+ uInt dle=(uInt)DECLITEND; // unsign\r
+ if (dle>1) dle=1; // ensure 0 or 1\r
+\r
+ if (LITEND!=DECLITEND) {\r
+ if (!quiet) { // always refer to this\r
+ #if DECPRINT\r
+ const char *adj;\r
+ if (LITEND) adj="little";\r
+ else adj="big";\r
+ printf("Warning: DECLITEND is set to %d, but this computer appears to be %s-endian\n",\r
+ DECLITEND, adj);\r
+ #endif\r
+ }\r
+ res=(Int)LITEND-dle;\r
+ }\r
+ return res;\r
+ } // decContextTestEndian\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decContextTestSavedStatus -- test bits in saved status */\r
+/* */\r
+/* oldstatus is the status word to be tested */\r
+/* mask indicates the bits to be tested (the oldstatus bits that */\r
+/* correspond to each 1 bit in the mask are tested) */\r
+/* returns 1 if any of the tested bits are 1, or 0 otherwise */\r
+/* */\r
+/* No error is possible. */\r
+/* ------------------------------------------------------------------ */\r
+uInt decContextTestSavedStatus(uInt oldstatus, uInt mask) {\r
+ return (oldstatus&mask)!=0;\r
+ } // decContextTestSavedStatus\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decContextTestStatus -- test bits in current status */\r
+/* */\r
+/* context is the context structure to be updated */\r
+/* mask indicates the bits to be tested (the status bits that */\r
+/* correspond to each 1 bit in the mask are tested) */\r
+/* returns 1 if any of the tested bits are 1, or 0 otherwise */\r
+/* */\r
+/* No error is possible. */\r
+/* ------------------------------------------------------------------ */\r
+uInt decContextTestStatus(decContext *context, uInt mask) {\r
+ return (context->status&mask)!=0;\r
+ } // decContextTestStatus\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decContextZeroStatus -- clear all status bits */\r
+/* */\r
+/* context is the context structure to be updated */\r
+/* returns context */\r
+/* */\r
+/* No error is possible. */\r
+/* ------------------------------------------------------------------ */\r
+decContext *decContextZeroStatus(decContext *context) {\r
+ context->status=0;\r
+ return context;\r
+ } // decContextZeroStatus\r
+\r
--- /dev/null
+/* ------------------------------------------------------------------ */\r
+/* Decimal Context module header */\r
+/* ------------------------------------------------------------------ */\r
+/* Copyright (c) IBM Corporation, 2000, 2010. All rights reserved. */\r
+/* */\r
+/* This software is made available under the terms of the */\r
+/* ICU License -- ICU 1.8.1 and later. */\r
+/* */\r
+/* The description and User's Guide ("The decNumber C Library") for */\r
+/* this software is called decNumber.pdf. This document is */\r
+/* available, together with arithmetic and format specifications, */\r
+/* testcases, and Web links, on the General Decimal Arithmetic page. */\r
+/* */\r
+/* Please send comments, suggestions, and corrections to the author: */\r
+/* mfc@uk.ibm.com */\r
+/* Mike Cowlishaw, IBM Fellow */\r
+/* IBM UK, PO Box 31, Birmingham Road, Warwick CV34 5JL, UK */\r
+/* ------------------------------------------------------------------ */\r
+/* */\r
+/* Context variables must always have valid values: */\r
+/* */\r
+/* status -- [any bits may be cleared, but not set, by user] */\r
+/* round -- must be one of the enumerated rounding modes */\r
+/* */\r
+/* The following variables are implied for fixed size formats (i.e., */\r
+/* they are ignored) but should still be set correctly in case used */\r
+/* with decNumber functions: */\r
+/* */\r
+/* clamp -- must be either 0 or 1 */\r
+/* digits -- must be in the range 1 through 999999999 */\r
+/* emax -- must be in the range 0 through 999999999 */\r
+/* emin -- must be in the range 0 through -999999999 */\r
+/* extended -- must be either 0 or 1 [present only if DECSUBSET] */\r
+/* traps -- only defined bits may be set */\r
+/* */\r
+/* ------------------------------------------------------------------ */\r
+\r
+#if !defined(DECCONTEXT)\r
+ #define DECCONTEXT\r
+ #define DECCNAME "decContext" /* Short name */\r
+ #define DECCFULLNAME "Decimal Context Descriptor" /* Verbose name */\r
+ #define DECCAUTHOR "Mike Cowlishaw" /* Who to blame */\r
+\r
+ #if !defined(int32_t)\r
+ #include <stdint.h> /* C99 standard integers */\r
+ #endif\r
+ #include <stdio.h> /* for printf, etc. */\r
+ #include <signal.h> /* for traps */\r
+\r
+ /* Extended flags setting -- set this to 0 to use only IEEE flags */\r
+ #if !defined(DECEXTFLAG)\r
+ #define DECEXTFLAG 1 /* 1=enable extended flags */\r
+ #endif\r
+\r
+ /* Conditional code flag -- set this to 0 for best performance */\r
+ #if !defined(DECSUBSET)\r
+ #define DECSUBSET 0 /* 1=enable subset arithmetic */\r
+ #endif\r
+\r
+ /* Context for operations, with associated constants */\r
+ enum rounding {\r
+ DEC_ROUND_CEILING, /* round towards +infinity */\r
+ DEC_ROUND_UP, /* round away from 0 */\r
+ DEC_ROUND_HALF_UP, /* 0.5 rounds up */\r
+ DEC_ROUND_HALF_EVEN, /* 0.5 rounds to nearest even */\r
+ DEC_ROUND_HALF_DOWN, /* 0.5 rounds down */\r
+ DEC_ROUND_DOWN, /* round towards 0 (truncate) */\r
+ DEC_ROUND_FLOOR, /* round towards -infinity */\r
+ DEC_ROUND_05UP, /* round for reround */\r
+ DEC_ROUND_MAX /* enum must be less than this */\r
+ };\r
+ #define DEC_ROUND_DEFAULT DEC_ROUND_HALF_EVEN;\r
+\r
+ typedef struct {\r
+ int32_t digits; /* working precision */\r
+ int32_t emax; /* maximum positive exponent */\r
+ int32_t emin; /* minimum negative exponent */\r
+ enum rounding round; /* rounding mode */\r
+ uint32_t traps; /* trap-enabler flags */\r
+ uint32_t status; /* status flags */\r
+ uint8_t clamp; /* flag: apply IEEE exponent clamp */\r
+ #if DECSUBSET\r
+ uint8_t extended; /* flag: special-values allowed */\r
+ #endif\r
+ } decContext;\r
+\r
+ /* Maxima and Minima for context settings */\r
+ #define DEC_MAX_DIGITS 999999999\r
+ #define DEC_MIN_DIGITS 1\r
+ #define DEC_MAX_EMAX 999999999\r
+ #define DEC_MIN_EMAX 0\r
+ #define DEC_MAX_EMIN 0\r
+ #define DEC_MIN_EMIN -999999999\r
+ #define DEC_MAX_MATH 999999 /* max emax, etc., for math funcs. */\r
+\r
+ /* Classifications for decimal numbers, aligned with 754 (note that */\r
+ /* 'normal' and 'subnormal' are meaningful only with a decContext */\r
+ /* or a fixed size format). */\r
+ enum decClass {\r
+ DEC_CLASS_SNAN,\r
+ DEC_CLASS_QNAN,\r
+ DEC_CLASS_NEG_INF,\r
+ DEC_CLASS_NEG_NORMAL,\r
+ DEC_CLASS_NEG_SUBNORMAL,\r
+ DEC_CLASS_NEG_ZERO,\r
+ DEC_CLASS_POS_ZERO,\r
+ DEC_CLASS_POS_SUBNORMAL,\r
+ DEC_CLASS_POS_NORMAL,\r
+ DEC_CLASS_POS_INF\r
+ };\r
+ /* Strings for the decClasses */\r
+ #define DEC_ClassString_SN "sNaN"\r
+ #define DEC_ClassString_QN "NaN"\r
+ #define DEC_ClassString_NI "-Infinity"\r
+ #define DEC_ClassString_NN "-Normal"\r
+ #define DEC_ClassString_NS "-Subnormal"\r
+ #define DEC_ClassString_NZ "-Zero"\r
+ #define DEC_ClassString_PZ "+Zero"\r
+ #define DEC_ClassString_PS "+Subnormal"\r
+ #define DEC_ClassString_PN "+Normal"\r
+ #define DEC_ClassString_PI "+Infinity"\r
+ #define DEC_ClassString_UN "Invalid"\r
+\r
+ /* Trap-enabler and Status flags (exceptional conditions), and */\r
+ /* their names. The top byte is reserved for internal use */\r
+ #if DECEXTFLAG\r
+ /* Extended flags */\r
+ #define DEC_Conversion_syntax 0x00000001\r
+ #define DEC_Division_by_zero 0x00000002\r
+ #define DEC_Division_impossible 0x00000004\r
+ #define DEC_Division_undefined 0x00000008\r
+ #define DEC_Insufficient_storage 0x00000010 /* [when malloc fails] */\r
+ #define DEC_Inexact 0x00000020\r
+ #define DEC_Invalid_context 0x00000040\r
+ #define DEC_Invalid_operation 0x00000080\r
+ #if DECSUBSET\r
+ #define DEC_Lost_digits 0x00000100\r
+ #endif\r
+ #define DEC_Overflow 0x00000200\r
+ #define DEC_Clamped 0x00000400\r
+ #define DEC_Rounded 0x00000800\r
+ #define DEC_Subnormal 0x00001000\r
+ #define DEC_Underflow 0x00002000\r
+ #else\r
+ /* IEEE flags only */\r
+ #define DEC_Conversion_syntax 0x00000010\r
+ #define DEC_Division_by_zero 0x00000002\r
+ #define DEC_Division_impossible 0x00000010\r
+ #define DEC_Division_undefined 0x00000010\r
+ #define DEC_Insufficient_storage 0x00000010 /* [when malloc fails] */\r
+ #define DEC_Inexact 0x00000001\r
+ #define DEC_Invalid_context 0x00000010\r
+ #define DEC_Invalid_operation 0x00000010\r
+ #if DECSUBSET\r
+ #define DEC_Lost_digits 0x00000000\r
+ #endif\r
+ #define DEC_Overflow 0x00000008\r
+ #define DEC_Clamped 0x00000000\r
+ #define DEC_Rounded 0x00000000\r
+ #define DEC_Subnormal 0x00000000\r
+ #define DEC_Underflow 0x00000004\r
+ #endif\r
+\r
+ /* IEEE 754 groupings for the flags */\r
+ /* [DEC_Clamped, DEC_Lost_digits, DEC_Rounded, and DEC_Subnormal */\r
+ /* are not in IEEE 754] */\r
+ #define DEC_IEEE_754_Division_by_zero (DEC_Division_by_zero)\r
+ #if DECSUBSET\r
+ #define DEC_IEEE_754_Inexact (DEC_Inexact | DEC_Lost_digits)\r
+ #else\r
+ #define DEC_IEEE_754_Inexact (DEC_Inexact)\r
+ #endif\r
+ #define DEC_IEEE_754_Invalid_operation (DEC_Conversion_syntax | \\r
+ DEC_Division_impossible | \\r
+ DEC_Division_undefined | \\r
+ DEC_Insufficient_storage | \\r
+ DEC_Invalid_context | \\r
+ DEC_Invalid_operation)\r
+ #define DEC_IEEE_754_Overflow (DEC_Overflow)\r
+ #define DEC_IEEE_754_Underflow (DEC_Underflow)\r
+\r
+ /* flags which are normally errors (result is qNaN, infinite, or 0) */\r
+ #define DEC_Errors (DEC_IEEE_754_Division_by_zero | \\r
+ DEC_IEEE_754_Invalid_operation | \\r
+ DEC_IEEE_754_Overflow | DEC_IEEE_754_Underflow)\r
+ /* flags which cause a result to become qNaN */\r
+ #define DEC_NaNs DEC_IEEE_754_Invalid_operation\r
+\r
+ /* flags which are normally for information only (finite results) */\r
+ #if DECSUBSET\r
+ #define DEC_Information (DEC_Clamped | DEC_Rounded | DEC_Inexact \\r
+ | DEC_Lost_digits)\r
+ #else\r
+ #define DEC_Information (DEC_Clamped | DEC_Rounded | DEC_Inexact)\r
+ #endif\r
+\r
+ /* IEEE 854 names (for compatibility with older decNumber versions) */\r
+ #define DEC_IEEE_854_Division_by_zero DEC_IEEE_754_Division_by_zero\r
+ #define DEC_IEEE_854_Inexact DEC_IEEE_754_Inexact\r
+ #define DEC_IEEE_854_Invalid_operation DEC_IEEE_754_Invalid_operation\r
+ #define DEC_IEEE_854_Overflow DEC_IEEE_754_Overflow\r
+ #define DEC_IEEE_854_Underflow DEC_IEEE_754_Underflow\r
+\r
+ /* Name strings for the exceptional conditions */\r
+ #define DEC_Condition_CS "Conversion syntax"\r
+ #define DEC_Condition_DZ "Division by zero"\r
+ #define DEC_Condition_DI "Division impossible"\r
+ #define DEC_Condition_DU "Division undefined"\r
+ #define DEC_Condition_IE "Inexact"\r
+ #define DEC_Condition_IS "Insufficient storage"\r
+ #define DEC_Condition_IC "Invalid context"\r
+ #define DEC_Condition_IO "Invalid operation"\r
+ #if DECSUBSET\r
+ #define DEC_Condition_LD "Lost digits"\r
+ #endif\r
+ #define DEC_Condition_OV "Overflow"\r
+ #define DEC_Condition_PA "Clamped"\r
+ #define DEC_Condition_RO "Rounded"\r
+ #define DEC_Condition_SU "Subnormal"\r
+ #define DEC_Condition_UN "Underflow"\r
+ #define DEC_Condition_ZE "No status"\r
+ #define DEC_Condition_MU "Multiple status"\r
+ #define DEC_Condition_Length 21 /* length of the longest string, */\r
+ /* including terminator */\r
+\r
+ /* Initialization descriptors, used by decContextDefault */\r
+ #define DEC_INIT_BASE 0\r
+ #define DEC_INIT_DECIMAL32 32\r
+ #define DEC_INIT_DECIMAL64 64\r
+ #define DEC_INIT_DECIMAL128 128\r
+ /* Synonyms */\r
+ #define DEC_INIT_DECSINGLE DEC_INIT_DECIMAL32\r
+ #define DEC_INIT_DECDOUBLE DEC_INIT_DECIMAL64\r
+ #define DEC_INIT_DECQUAD DEC_INIT_DECIMAL128\r
+\r
+ /* decContext routines */\r
+ extern decContext * decContextClearStatus(decContext *, uint32_t);\r
+ extern decContext * decContextDefault(decContext *, int32_t);\r
+ extern enum rounding decContextGetRounding(decContext *);\r
+ extern uint32_t decContextGetStatus(decContext *);\r
+ extern decContext * decContextRestoreStatus(decContext *, uint32_t, uint32_t);\r
+ extern uint32_t decContextSaveStatus(decContext *, uint32_t);\r
+ extern decContext * decContextSetRounding(decContext *, enum rounding);\r
+ extern decContext * decContextSetStatus(decContext *, uint32_t);\r
+ extern decContext * decContextSetStatusFromString(decContext *, const char *);\r
+ extern decContext * decContextSetStatusFromStringQuiet(decContext *, const char *);\r
+ extern decContext * decContextSetStatusQuiet(decContext *, uint32_t);\r
+ extern const char * decContextStatusToString(const decContext *);\r
+ extern int32_t decContextTestEndian(uint8_t);\r
+ extern uint32_t decContextTestSavedStatus(uint32_t, uint32_t);\r
+ extern uint32_t decContextTestStatus(decContext *, uint32_t);\r
+ extern decContext * decContextZeroStatus(decContext *);\r
+\r
+#endif\r
--- /dev/null
+/* ------------------------------------------------------------------------ */\r
+/* Binary Coded Decimal and Densely Packed Decimal conversion lookup tables */\r
+/* [Automatically generated -- do not edit. 2008.06.21] */\r
+/* ------------------------------------------------------------------------ */\r
+/* Copyright (c) IBM Corporation, 2000, 2008. All rights reserved. */\r
+/* ------------------------------------------------------------------------ */\r
+/* For details, see DPDecimal.html on the General Decimal Arithmetic page. */\r
+/* */\r
+/* This include file defines several DPD and BCD conversion tables: */\r
+/* */\r
+/* uint16_t BCD2DPD[2458]; -- BCD -> DPD (0x999 => 2457) */\r
+/* uint16_t BIN2DPD[1000]; -- Bin -> DPD (999 => 2457) */\r
+/* uint8_t BIN2CHAR[4001]; -- Bin -> CHAR (999 => '\3' '9' '9' '9') */\r
+/* uint8_t BIN2BCD8[4000]; -- Bin -> bytes (999 => 9 9 9 3) */\r
+/* uint16_t DPD2BCD[1024]; -- DPD -> BCD (0x3FF => 0x999) */\r
+/* uint16_t DPD2BIN[1024]; -- DPD -> BIN (0x3FF => 999) */\r
+/* uint32_t DPD2BINK[1024]; -- DPD -> BIN * 1000 (0x3FF => 999000) */\r
+/* uint32_t DPD2BINM[1024]; -- DPD -> BIN * 1E+6 (0x3FF => 999000000) */\r
+/* uint8_t DPD2BCD8[4096]; -- DPD -> bytes (x3FF => 9 9 9 3) */\r
+/* */\r
+/* In all cases the result (10 bits or 12 bits, or binary) is right-aligned */\r
+/* in the table entry. BIN2CHAR entries are a single byte length (0 for */\r
+/* value 0) followed by three digit characters; a trailing terminator is */\r
+/* included to allow 4-char moves always. BIN2BCD8 and DPD2BCD8 entries */\r
+/* are similar with the three BCD8 digits followed by a one-byte length */\r
+/* (again, length=0 for value 0). */\r
+/* */\r
+/* To use a table, its name, prefixed with DEC_, must be defined with a */\r
+/* value of 1 before this header file is included. For example: */\r
+/* #define DEC_BCD2DPD 1 */\r
+/* This mechanism allows software to only include tables that are needed. */\r
+/* ------------------------------------------------------------------------ */\r
+ \r
+#if defined(DEC_BCD2DPD) && DEC_BCD2DPD==1 && !defined(DECBCD2DPD)\r
+#define DECBCD2DPD\r
+ \r
+const uint16_t BCD2DPD[2458]={ 0, 1, 2, 3, 4, 5, 6, 7, \r
+ 8, 9, 0, 0, 0, 0, 0, 0, 16, 17, 18, 19, 20, \r
+ 21, 22, 23, 24, 25, 0, 0, 0, 0, 0, 0, 32, 33, \r
+ 34, 35, 36, 37, 38, 39, 40, 41, 0, 0, 0, 0, 0, \r
+ 0, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 0, 0, \r
+ 0, 0, 0, 0, 64, 65, 66, 67, 68, 69, 70, 71, 72, \r
+ 73, 0, 0, 0, 0, 0, 0, 80, 81, 82, 83, 84, 85, \r
+ 86, 87, 88, 89, 0, 0, 0, 0, 0, 0, 96, 97, 98, \r
+ 99, 100, 101, 102, 103, 104, 105, 0, 0, 0, 0, 0, 0, \r
+ 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 0, 0, 0, \r
+ 0, 0, 0, 10, 11, 42, 43, 74, 75, 106, 107, 78, 79, \r
+ 0, 0, 0, 0, 0, 0, 26, 27, 58, 59, 90, 91, 122, \r
+ 123, 94, 95, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \r
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \r
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \r
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \r
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \r
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \r
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \r
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \r
+ 0, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 0, 0, \r
+ 0, 0, 0, 0, 144, 145, 146, 147, 148, 149, 150, 151, 152, \r
+ 153, 0, 0, 0, 0, 0, 0, 160, 161, 162, 163, 164, 165, \r
+ 166, 167, 168, 169, 0, 0, 0, 0, 0, 0, 176, 177, 178, \r
+ 179, 180, 181, 182, 183, 184, 185, 0, 0, 0, 0, 0, 0, \r
+ 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 0, 0, 0, \r
+ 0, 0, 0, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, \r
+ 0, 0, 0, 0, 0, 0, 224, 225, 226, 227, 228, 229, 230, \r
+ 231, 232, 233, 0, 0, 0, 0, 0, 0, 240, 241, 242, 243, \r
+ 244, 245, 246, 247, 248, 249, 0, 0, 0, 0, 0, 0, 138, \r
+ 139, 170, 171, 202, 203, 234, 235, 206, 207, 0, 0, 0, 0, \r
+ 0, 0, 154, 155, 186, 187, 218, 219, 250, 251, 222, 223, 0, \r
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \r
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \r
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \r
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \r
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \r
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \r
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \r
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 256, 257, 258, \r
+ 259, 260, 261, 262, 263, 264, 265, 0, 0, 0, 0, 0, 0, \r
+ 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 0, 0, 0, \r
+ 0, 0, 0, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, \r
+ 0, 0, 0, 0, 0, 0, 304, 305, 306, 307, 308, 309, 310, \r
+ 311, 312, 313, 0, 0, 0, 0, 0, 0, 320, 321, 322, 323, \r
+ 324, 325, 326, 327, 328, 329, 0, 0, 0, 0, 0, 0, 336, \r
+ 337, 338, 339, 340, 341, 342, 343, 344, 345, 0, 0, 0, 0, \r
+ 0, 0, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 0, \r
+ 0, 0, 0, 0, 0, 368, 369, 370, 371, 372, 373, 374, 375, \r
+ 376, 377, 0, 0, 0, 0, 0, 0, 266, 267, 298, 299, 330, \r
+ 331, 362, 363, 334, 335, 0, 0, 0, 0, 0, 0, 282, 283, \r
+ 314, 315, 346, 347, 378, 379, 350, 351, 0, 0, 0, 0, 0, \r
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \r
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \r
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \r
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \r
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \r
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \r
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \r
+ 0, 0, 0, 0, 0, 0, 384, 385, 386, 387, 388, 389, 390, \r
+ 391, 392, 393, 0, 0, 0, 0, 0, 0, 400, 401, 402, 403, \r
+ 404, 405, 406, 407, 408, 409, 0, 0, 0, 0, 0, 0, 416, \r
+ 417, 418, 419, 420, 421, 422, 423, 424, 425, 0, 0, 0, 0, \r
+ 0, 0, 432, 433, 434, 435, 436, 437, 438, 439, 440, 441, 0, \r
+ 0, 0, 0, 0, 0, 448, 449, 450, 451, 452, 453, 454, 455, \r
+ 456, 457, 0, 0, 0, 0, 0, 0, 464, 465, 466, 467, 468, \r
+ 469, 470, 471, 472, 473, 0, 0, 0, 0, 0, 0, 480, 481, \r
+ 482, 483, 484, 485, 486, 487, 488, 489, 0, 0, 0, 0, 0, \r
+ 0, 496, 497, 498, 499, 500, 501, 502, 503, 504, 505, 0, 0, \r
+ 0, 0, 0, 0, 394, 395, 426, 427, 458, 459, 490, 491, 462, \r
+ 463, 0, 0, 0, 0, 0, 0, 410, 411, 442, 443, 474, 475, \r
+ 506, 507, 478, 479, 0, 0, 0, 0, 0, 0, 0, 0, 0, \r
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \r
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \r
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \r
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \r
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \r
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \r
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \r
+ 0, 0, 512, 513, 514, 515, 516, 517, 518, 519, 520, 521, 0, \r
+ 0, 0, 0, 0, 0, 528, 529, 530, 531, 532, 533, 534, 535, \r
+ 536, 537, 0, 0, 0, 0, 0, 0, 544, 545, 546, 547, 548, \r
+ 549, 550, 551, 552, 553, 0, 0, 0, 0, 0, 0, 560, 561, \r
+ 562, 563, 564, 565, 566, 567, 568, 569, 0, 0, 0, 0, 0, \r
+ 0, 576, 577, 578, 579, 580, 581, 582, 583, 584, 585, 0, 0, \r
+ 0, 0, 0, 0, 592, 593, 594, 595, 596, 597, 598, 599, 600, \r
+ 601, 0, 0, 0, 0, 0, 0, 608, 609, 610, 611, 612, 613, \r
+ 614, 615, 616, 617, 0, 0, 0, 0, 0, 0, 624, 625, 626, \r
+ 627, 628, 629, 630, 631, 632, 633, 0, 0, 0, 0, 0, 0, \r
+ 522, 523, 554, 555, 586, 587, 618, 619, 590, 591, 0, 0, 0, \r
+ 0, 0, 0, 538, 539, 570, 571, 602, 603, 634, 635, 606, 607, \r
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \r
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \r
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \r
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \r
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \r
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \r
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \r
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 640, 641, \r
+ 642, 643, 644, 645, 646, 647, 648, 649, 0, 0, 0, 0, 0, \r
+ 0, 656, 657, 658, 659, 660, 661, 662, 663, 664, 665, 0, 0, \r
+ 0, 0, 0, 0, 672, 673, 674, 675, 676, 677, 678, 679, 680, \r
+ 681, 0, 0, 0, 0, 0, 0, 688, 689, 690, 691, 692, 693, \r
+ 694, 695, 696, 697, 0, 0, 0, 0, 0, 0, 704, 705, 706, \r
+ 707, 708, 709, 710, 711, 712, 713, 0, 0, 0, 0, 0, 0, \r
+ 720, 721, 722, 723, 724, 725, 726, 727, 728, 729, 0, 0, 0, \r
+ 0, 0, 0, 736, 737, 738, 739, 740, 741, 742, 743, 744, 745, \r
+ 0, 0, 0, 0, 0, 0, 752, 753, 754, 755, 756, 757, 758, \r
+ 759, 760, 761, 0, 0, 0, 0, 0, 0, 650, 651, 682, 683, \r
+ 714, 715, 746, 747, 718, 719, 0, 0, 0, 0, 0, 0, 666, \r
+ 667, 698, 699, 730, 731, 762, 763, 734, 735, 0, 0, 0, 0, \r
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \r
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \r
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \r
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \r
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \r
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \r
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \r
+ 0, 0, 0, 0, 0, 0, 0, 768, 769, 770, 771, 772, 773, \r
+ 774, 775, 776, 777, 0, 0, 0, 0, 0, 0, 784, 785, 786, \r
+ 787, 788, 789, 790, 791, 792, 793, 0, 0, 0, 0, 0, 0, \r
+ 800, 801, 802, 803, 804, 805, 806, 807, 808, 809, 0, 0, 0, \r
+ 0, 0, 0, 816, 817, 818, 819, 820, 821, 822, 823, 824, 825, \r
+ 0, 0, 0, 0, 0, 0, 832, 833, 834, 835, 836, 837, 838, \r
+ 839, 840, 841, 0, 0, 0, 0, 0, 0, 848, 849, 850, 851, \r
+ 852, 853, 854, 855, 856, 857, 0, 0, 0, 0, 0, 0, 864, \r
+ 865, 866, 867, 868, 869, 870, 871, 872, 873, 0, 0, 0, 0, \r
+ 0, 0, 880, 881, 882, 883, 884, 885, 886, 887, 888, 889, 0, \r
+ 0, 0, 0, 0, 0, 778, 779, 810, 811, 842, 843, 874, 875, \r
+ 846, 847, 0, 0, 0, 0, 0, 0, 794, 795, 826, 827, 858, \r
+ 859, 890, 891, 862, 863, 0, 0, 0, 0, 0, 0, 0, 0, \r
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \r
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \r
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \r
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \r
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \r
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \r
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \r
+ 0, 0, 0, 896, 897, 898, 899, 900, 901, 902, 903, 904, 905, \r
+ 0, 0, 0, 0, 0, 0, 912, 913, 914, 915, 916, 917, 918, \r
+ 919, 920, 921, 0, 0, 0, 0, 0, 0, 928, 929, 930, 931, \r
+ 932, 933, 934, 935, 936, 937, 0, 0, 0, 0, 0, 0, 944, \r
+ 945, 946, 947, 948, 949, 950, 951, 952, 953, 0, 0, 0, 0, \r
+ 0, 0, 960, 961, 962, 963, 964, 965, 966, 967, 968, 969, 0, \r
+ 0, 0, 0, 0, 0, 976, 977, 978, 979, 980, 981, 982, 983, \r
+ 984, 985, 0, 0, 0, 0, 0, 0, 992, 993, 994, 995, 996, \r
+ 997, 998, 999, 1000, 1001, 0, 0, 0, 0, 0, 0, 1008, 1009, \r
+ 1010, 1011, 1012, 1013, 1014, 1015, 1016, 1017, 0, 0, 0, 0, 0, \r
+ 0, 906, 907, 938, 939, 970, 971, 1002, 1003, 974, 975, 0, 0, \r
+ 0, 0, 0, 0, 922, 923, 954, 955, 986, 987, 1018, 1019, 990, \r
+ 991, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \r
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \r
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \r
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \r
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \r
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \r
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \r
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, \r
+ 13, 268, 269, 524, 525, 780, 781, 46, 47, 0, 0, 0, 0, \r
+ 0, 0, 28, 29, 284, 285, 540, 541, 796, 797, 62, 63, 0, \r
+ 0, 0, 0, 0, 0, 44, 45, 300, 301, 556, 557, 812, 813, \r
+ 302, 303, 0, 0, 0, 0, 0, 0, 60, 61, 316, 317, 572, \r
+ 573, 828, 829, 318, 319, 0, 0, 0, 0, 0, 0, 76, 77, \r
+ 332, 333, 588, 589, 844, 845, 558, 559, 0, 0, 0, 0, 0, \r
+ 0, 92, 93, 348, 349, 604, 605, 860, 861, 574, 575, 0, 0, \r
+ 0, 0, 0, 0, 108, 109, 364, 365, 620, 621, 876, 877, 814, \r
+ 815, 0, 0, 0, 0, 0, 0, 124, 125, 380, 381, 636, 637, \r
+ 892, 893, 830, 831, 0, 0, 0, 0, 0, 0, 14, 15, 270, \r
+ 271, 526, 527, 782, 783, 110, 111, 0, 0, 0, 0, 0, 0, \r
+ 30, 31, 286, 287, 542, 543, 798, 799, 126, 127, 0, 0, 0, \r
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \r
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \r
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \r
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \r
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \r
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \r
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \r
+ 0, 0, 0, 0, 0, 0, 0, 0, 140, 141, 396, 397, 652, \r
+ 653, 908, 909, 174, 175, 0, 0, 0, 0, 0, 0, 156, 157, \r
+ 412, 413, 668, 669, 924, 925, 190, 191, 0, 0, 0, 0, 0, \r
+ 0, 172, 173, 428, 429, 684, 685, 940, 941, 430, 431, 0, 0, \r
+ 0, 0, 0, 0, 188, 189, 444, 445, 700, 701, 956, 957, 446, \r
+ 447, 0, 0, 0, 0, 0, 0, 204, 205, 460, 461, 716, 717, \r
+ 972, 973, 686, 687, 0, 0, 0, 0, 0, 0, 220, 221, 476, \r
+ 477, 732, 733, 988, 989, 702, 703, 0, 0, 0, 0, 0, 0, \r
+ 236, 237, 492, 493, 748, 749, 1004, 1005, 942, 943, 0, 0, 0, \r
+ 0, 0, 0, 252, 253, 508, 509, 764, 765, 1020, 1021, 958, 959, \r
+ 0, 0, 0, 0, 0, 0, 142, 143, 398, 399, 654, 655, 910, \r
+ 911, 238, 239, 0, 0, 0, 0, 0, 0, 158, 159, 414, 415, \r
+ 670, 671, 926, 927, 254, 255};\r
+#endif\r
+ \r
+#if defined(DEC_DPD2BCD) && DEC_DPD2BCD==1 && !defined(DECDPD2BCD)\r
+#define DECDPD2BCD\r
+ \r
+const uint16_t DPD2BCD[1024]={ 0, 1, 2, 3, 4, 5, 6, 7, \r
+ 8, 9, 128, 129, 2048, 2049, 2176, 2177, 16, 17, 18, 19, 20, \r
+ 21, 22, 23, 24, 25, 144, 145, 2064, 2065, 2192, 2193, 32, 33, \r
+ 34, 35, 36, 37, 38, 39, 40, 41, 130, 131, 2080, 2081, 2056, \r
+ 2057, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 146, 147, \r
+ 2096, 2097, 2072, 2073, 64, 65, 66, 67, 68, 69, 70, 71, 72, \r
+ 73, 132, 133, 2112, 2113, 136, 137, 80, 81, 82, 83, 84, 85, \r
+ 86, 87, 88, 89, 148, 149, 2128, 2129, 152, 153, 96, 97, 98, \r
+ 99, 100, 101, 102, 103, 104, 105, 134, 135, 2144, 2145, 2184, 2185, \r
+ 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 150, 151, 2160, \r
+ 2161, 2200, 2201, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, \r
+ 384, 385, 2304, 2305, 2432, 2433, 272, 273, 274, 275, 276, 277, 278, \r
+ 279, 280, 281, 400, 401, 2320, 2321, 2448, 2449, 288, 289, 290, 291, \r
+ 292, 293, 294, 295, 296, 297, 386, 387, 2336, 2337, 2312, 2313, 304, \r
+ 305, 306, 307, 308, 309, 310, 311, 312, 313, 402, 403, 2352, 2353, \r
+ 2328, 2329, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 388, \r
+ 389, 2368, 2369, 392, 393, 336, 337, 338, 339, 340, 341, 342, 343, \r
+ 344, 345, 404, 405, 2384, 2385, 408, 409, 352, 353, 354, 355, 356, \r
+ 357, 358, 359, 360, 361, 390, 391, 2400, 2401, 2440, 2441, 368, 369, \r
+ 370, 371, 372, 373, 374, 375, 376, 377, 406, 407, 2416, 2417, 2456, \r
+ 2457, 512, 513, 514, 515, 516, 517, 518, 519, 520, 521, 640, 641, \r
+ 2050, 2051, 2178, 2179, 528, 529, 530, 531, 532, 533, 534, 535, 536, \r
+ 537, 656, 657, 2066, 2067, 2194, 2195, 544, 545, 546, 547, 548, 549, \r
+ 550, 551, 552, 553, 642, 643, 2082, 2083, 2088, 2089, 560, 561, 562, \r
+ 563, 564, 565, 566, 567, 568, 569, 658, 659, 2098, 2099, 2104, 2105, \r
+ 576, 577, 578, 579, 580, 581, 582, 583, 584, 585, 644, 645, 2114, \r
+ 2115, 648, 649, 592, 593, 594, 595, 596, 597, 598, 599, 600, 601, \r
+ 660, 661, 2130, 2131, 664, 665, 608, 609, 610, 611, 612, 613, 614, \r
+ 615, 616, 617, 646, 647, 2146, 2147, 2184, 2185, 624, 625, 626, 627, \r
+ 628, 629, 630, 631, 632, 633, 662, 663, 2162, 2163, 2200, 2201, 768, \r
+ 769, 770, 771, 772, 773, 774, 775, 776, 777, 896, 897, 2306, 2307, \r
+ 2434, 2435, 784, 785, 786, 787, 788, 789, 790, 791, 792, 793, 912, \r
+ 913, 2322, 2323, 2450, 2451, 800, 801, 802, 803, 804, 805, 806, 807, \r
+ 808, 809, 898, 899, 2338, 2339, 2344, 2345, 816, 817, 818, 819, 820, \r
+ 821, 822, 823, 824, 825, 914, 915, 2354, 2355, 2360, 2361, 832, 833, \r
+ 834, 835, 836, 837, 838, 839, 840, 841, 900, 901, 2370, 2371, 904, \r
+ 905, 848, 849, 850, 851, 852, 853, 854, 855, 856, 857, 916, 917, \r
+ 2386, 2387, 920, 921, 864, 865, 866, 867, 868, 869, 870, 871, 872, \r
+ 873, 902, 903, 2402, 2403, 2440, 2441, 880, 881, 882, 883, 884, 885, \r
+ 886, 887, 888, 889, 918, 919, 2418, 2419, 2456, 2457, 1024, 1025, 1026, \r
+ 1027, 1028, 1029, 1030, 1031, 1032, 1033, 1152, 1153, 2052, 2053, 2180, 2181, \r
+ 1040, 1041, 1042, 1043, 1044, 1045, 1046, 1047, 1048, 1049, 1168, 1169, 2068, \r
+ 2069, 2196, 2197, 1056, 1057, 1058, 1059, 1060, 1061, 1062, 1063, 1064, 1065, \r
+ 1154, 1155, 2084, 2085, 2120, 2121, 1072, 1073, 1074, 1075, 1076, 1077, 1078, \r
+ 1079, 1080, 1081, 1170, 1171, 2100, 2101, 2136, 2137, 1088, 1089, 1090, 1091, \r
+ 1092, 1093, 1094, 1095, 1096, 1097, 1156, 1157, 2116, 2117, 1160, 1161, 1104, \r
+ 1105, 1106, 1107, 1108, 1109, 1110, 1111, 1112, 1113, 1172, 1173, 2132, 2133, \r
+ 1176, 1177, 1120, 1121, 1122, 1123, 1124, 1125, 1126, 1127, 1128, 1129, 1158, \r
+ 1159, 2148, 2149, 2184, 2185, 1136, 1137, 1138, 1139, 1140, 1141, 1142, 1143, \r
+ 1144, 1145, 1174, 1175, 2164, 2165, 2200, 2201, 1280, 1281, 1282, 1283, 1284, \r
+ 1285, 1286, 1287, 1288, 1289, 1408, 1409, 2308, 2309, 2436, 2437, 1296, 1297, \r
+ 1298, 1299, 1300, 1301, 1302, 1303, 1304, 1305, 1424, 1425, 2324, 2325, 2452, \r
+ 2453, 1312, 1313, 1314, 1315, 1316, 1317, 1318, 1319, 1320, 1321, 1410, 1411, \r
+ 2340, 2341, 2376, 2377, 1328, 1329, 1330, 1331, 1332, 1333, 1334, 1335, 1336, \r
+ 1337, 1426, 1427, 2356, 2357, 2392, 2393, 1344, 1345, 1346, 1347, 1348, 1349, \r
+ 1350, 1351, 1352, 1353, 1412, 1413, 2372, 2373, 1416, 1417, 1360, 1361, 1362, \r
+ 1363, 1364, 1365, 1366, 1367, 1368, 1369, 1428, 1429, 2388, 2389, 1432, 1433, \r
+ 1376, 1377, 1378, 1379, 1380, 1381, 1382, 1383, 1384, 1385, 1414, 1415, 2404, \r
+ 2405, 2440, 2441, 1392, 1393, 1394, 1395, 1396, 1397, 1398, 1399, 1400, 1401, \r
+ 1430, 1431, 2420, 2421, 2456, 2457, 1536, 1537, 1538, 1539, 1540, 1541, 1542, \r
+ 1543, 1544, 1545, 1664, 1665, 2054, 2055, 2182, 2183, 1552, 1553, 1554, 1555, \r
+ 1556, 1557, 1558, 1559, 1560, 1561, 1680, 1681, 2070, 2071, 2198, 2199, 1568, \r
+ 1569, 1570, 1571, 1572, 1573, 1574, 1575, 1576, 1577, 1666, 1667, 2086, 2087, \r
+ 2152, 2153, 1584, 1585, 1586, 1587, 1588, 1589, 1590, 1591, 1592, 1593, 1682, \r
+ 1683, 2102, 2103, 2168, 2169, 1600, 1601, 1602, 1603, 1604, 1605, 1606, 1607, \r
+ 1608, 1609, 1668, 1669, 2118, 2119, 1672, 1673, 1616, 1617, 1618, 1619, 1620, \r
+ 1621, 1622, 1623, 1624, 1625, 1684, 1685, 2134, 2135, 1688, 1689, 1632, 1633, \r
+ 1634, 1635, 1636, 1637, 1638, 1639, 1640, 1641, 1670, 1671, 2150, 2151, 2184, \r
+ 2185, 1648, 1649, 1650, 1651, 1652, 1653, 1654, 1655, 1656, 1657, 1686, 1687, \r
+ 2166, 2167, 2200, 2201, 1792, 1793, 1794, 1795, 1796, 1797, 1798, 1799, 1800, \r
+ 1801, 1920, 1921, 2310, 2311, 2438, 2439, 1808, 1809, 1810, 1811, 1812, 1813, \r
+ 1814, 1815, 1816, 1817, 1936, 1937, 2326, 2327, 2454, 2455, 1824, 1825, 1826, \r
+ 1827, 1828, 1829, 1830, 1831, 1832, 1833, 1922, 1923, 2342, 2343, 2408, 2409, \r
+ 1840, 1841, 1842, 1843, 1844, 1845, 1846, 1847, 1848, 1849, 1938, 1939, 2358, \r
+ 2359, 2424, 2425, 1856, 1857, 1858, 1859, 1860, 1861, 1862, 1863, 1864, 1865, \r
+ 1924, 1925, 2374, 2375, 1928, 1929, 1872, 1873, 1874, 1875, 1876, 1877, 1878, \r
+ 1879, 1880, 1881, 1940, 1941, 2390, 2391, 1944, 1945, 1888, 1889, 1890, 1891, \r
+ 1892, 1893, 1894, 1895, 1896, 1897, 1926, 1927, 2406, 2407, 2440, 2441, 1904, \r
+ 1905, 1906, 1907, 1908, 1909, 1910, 1911, 1912, 1913, 1942, 1943, 2422, 2423, \r
+ 2456, 2457};\r
+#endif\r
+ \r
+#if defined(DEC_BIN2DPD) && DEC_BIN2DPD==1 && !defined(DECBIN2DPD)\r
+#define DECBIN2DPD\r
+ \r
+const uint16_t BIN2DPD[1000]={ 0, 1, 2, 3, 4, 5, 6, 7, \r
+ 8, 9, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 32, \r
+ 33, 34, 35, 36, 37, 38, 39, 40, 41, 48, 49, 50, 51, \r
+ 52, 53, 54, 55, 56, 57, 64, 65, 66, 67, 68, 69, 70, \r
+ 71, 72, 73, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, \r
+ 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 112, 113, 114, \r
+ 115, 116, 117, 118, 119, 120, 121, 10, 11, 42, 43, 74, 75, \r
+ 106, 107, 78, 79, 26, 27, 58, 59, 90, 91, 122, 123, 94, \r
+ 95, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 144, 145, \r
+ 146, 147, 148, 149, 150, 151, 152, 153, 160, 161, 162, 163, 164, \r
+ 165, 166, 167, 168, 169, 176, 177, 178, 179, 180, 181, 182, 183, \r
+ 184, 185, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 208, \r
+ 209, 210, 211, 212, 213, 214, 215, 216, 217, 224, 225, 226, 227, \r
+ 228, 229, 230, 231, 232, 233, 240, 241, 242, 243, 244, 245, 246, \r
+ 247, 248, 249, 138, 139, 170, 171, 202, 203, 234, 235, 206, 207, \r
+ 154, 155, 186, 187, 218, 219, 250, 251, 222, 223, 256, 257, 258, \r
+ 259, 260, 261, 262, 263, 264, 265, 272, 273, 274, 275, 276, 277, \r
+ 278, 279, 280, 281, 288, 289, 290, 291, 292, 293, 294, 295, 296, \r
+ 297, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 320, 321, \r
+ 322, 323, 324, 325, 326, 327, 328, 329, 336, 337, 338, 339, 340, \r
+ 341, 342, 343, 344, 345, 352, 353, 354, 355, 356, 357, 358, 359, \r
+ 360, 361, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 266, \r
+ 267, 298, 299, 330, 331, 362, 363, 334, 335, 282, 283, 314, 315, \r
+ 346, 347, 378, 379, 350, 351, 384, 385, 386, 387, 388, 389, 390, \r
+ 391, 392, 393, 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, \r
+ 416, 417, 418, 419, 420, 421, 422, 423, 424, 425, 432, 433, 434, \r
+ 435, 436, 437, 438, 439, 440, 441, 448, 449, 450, 451, 452, 453, \r
+ 454, 455, 456, 457, 464, 465, 466, 467, 468, 469, 470, 471, 472, \r
+ 473, 480, 481, 482, 483, 484, 485, 486, 487, 488, 489, 496, 497, \r
+ 498, 499, 500, 501, 502, 503, 504, 505, 394, 395, 426, 427, 458, \r
+ 459, 490, 491, 462, 463, 410, 411, 442, 443, 474, 475, 506, 507, \r
+ 478, 479, 512, 513, 514, 515, 516, 517, 518, 519, 520, 521, 528, \r
+ 529, 530, 531, 532, 533, 534, 535, 536, 537, 544, 545, 546, 547, \r
+ 548, 549, 550, 551, 552, 553, 560, 561, 562, 563, 564, 565, 566, \r
+ 567, 568, 569, 576, 577, 578, 579, 580, 581, 582, 583, 584, 585, \r
+ 592, 593, 594, 595, 596, 597, 598, 599, 600, 601, 608, 609, 610, \r
+ 611, 612, 613, 614, 615, 616, 617, 624, 625, 626, 627, 628, 629, \r
+ 630, 631, 632, 633, 522, 523, 554, 555, 586, 587, 618, 619, 590, \r
+ 591, 538, 539, 570, 571, 602, 603, 634, 635, 606, 607, 640, 641, \r
+ 642, 643, 644, 645, 646, 647, 648, 649, 656, 657, 658, 659, 660, \r
+ 661, 662, 663, 664, 665, 672, 673, 674, 675, 676, 677, 678, 679, \r
+ 680, 681, 688, 689, 690, 691, 692, 693, 694, 695, 696, 697, 704, \r
+ 705, 706, 707, 708, 709, 710, 711, 712, 713, 720, 721, 722, 723, \r
+ 724, 725, 726, 727, 728, 729, 736, 737, 738, 739, 740, 741, 742, \r
+ 743, 744, 745, 752, 753, 754, 755, 756, 757, 758, 759, 760, 761, \r
+ 650, 651, 682, 683, 714, 715, 746, 747, 718, 719, 666, 667, 698, \r
+ 699, 730, 731, 762, 763, 734, 735, 768, 769, 770, 771, 772, 773, \r
+ 774, 775, 776, 777, 784, 785, 786, 787, 788, 789, 790, 791, 792, \r
+ 793, 800, 801, 802, 803, 804, 805, 806, 807, 808, 809, 816, 817, \r
+ 818, 819, 820, 821, 822, 823, 824, 825, 832, 833, 834, 835, 836, \r
+ 837, 838, 839, 840, 841, 848, 849, 850, 851, 852, 853, 854, 855, \r
+ 856, 857, 864, 865, 866, 867, 868, 869, 870, 871, 872, 873, 880, \r
+ 881, 882, 883, 884, 885, 886, 887, 888, 889, 778, 779, 810, 811, \r
+ 842, 843, 874, 875, 846, 847, 794, 795, 826, 827, 858, 859, 890, \r
+ 891, 862, 863, 896, 897, 898, 899, 900, 901, 902, 903, 904, 905, \r
+ 912, 913, 914, 915, 916, 917, 918, 919, 920, 921, 928, 929, 930, \r
+ 931, 932, 933, 934, 935, 936, 937, 944, 945, 946, 947, 948, 949, \r
+ 950, 951, 952, 953, 960, 961, 962, 963, 964, 965, 966, 967, 968, \r
+ 969, 976, 977, 978, 979, 980, 981, 982, 983, 984, 985, 992, 993, \r
+ 994, 995, 996, 997, 998, 999, 1000, 1001, 1008, 1009, 1010, 1011, 1012, \r
+ 1013, 1014, 1015, 1016, 1017, 906, 907, 938, 939, 970, 971, 1002, 1003, \r
+ 974, 975, 922, 923, 954, 955, 986, 987, 1018, 1019, 990, 991, 12, \r
+ 13, 268, 269, 524, 525, 780, 781, 46, 47, 28, 29, 284, 285, \r
+ 540, 541, 796, 797, 62, 63, 44, 45, 300, 301, 556, 557, 812, \r
+ 813, 302, 303, 60, 61, 316, 317, 572, 573, 828, 829, 318, 319, \r
+ 76, 77, 332, 333, 588, 589, 844, 845, 558, 559, 92, 93, 348, \r
+ 349, 604, 605, 860, 861, 574, 575, 108, 109, 364, 365, 620, 621, \r
+ 876, 877, 814, 815, 124, 125, 380, 381, 636, 637, 892, 893, 830, \r
+ 831, 14, 15, 270, 271, 526, 527, 782, 783, 110, 111, 30, 31, \r
+ 286, 287, 542, 543, 798, 799, 126, 127, 140, 141, 396, 397, 652, \r
+ 653, 908, 909, 174, 175, 156, 157, 412, 413, 668, 669, 924, 925, \r
+ 190, 191, 172, 173, 428, 429, 684, 685, 940, 941, 430, 431, 188, \r
+ 189, 444, 445, 700, 701, 956, 957, 446, 447, 204, 205, 460, 461, \r
+ 716, 717, 972, 973, 686, 687, 220, 221, 476, 477, 732, 733, 988, \r
+ 989, 702, 703, 236, 237, 492, 493, 748, 749, 1004, 1005, 942, 943, \r
+ 252, 253, 508, 509, 764, 765, 1020, 1021, 958, 959, 142, 143, 398, \r
+ 399, 654, 655, 910, 911, 238, 239, 158, 159, 414, 415, 670, 671, \r
+ 926, 927, 254, 255};\r
+#endif \r
+ \r
+#if defined(DEC_DPD2BIN) && DEC_DPD2BIN==1 && !defined(DECDPD2BIN)\r
+#define DECDPD2BIN\r
+ \r
+const uint16_t DPD2BIN[1024]={ 0, 1, 2, 3, 4, 5, 6, 7, \r
+ 8, 9, 80, 81, 800, 801, 880, 881, 10, 11, 12, 13, 14, \r
+ 15, 16, 17, 18, 19, 90, 91, 810, 811, 890, 891, 20, 21, \r
+ 22, 23, 24, 25, 26, 27, 28, 29, 82, 83, 820, 821, 808, \r
+ 809, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 92, 93, \r
+ 830, 831, 818, 819, 40, 41, 42, 43, 44, 45, 46, 47, 48, \r
+ 49, 84, 85, 840, 841, 88, 89, 50, 51, 52, 53, 54, 55, \r
+ 56, 57, 58, 59, 94, 95, 850, 851, 98, 99, 60, 61, 62, \r
+ 63, 64, 65, 66, 67, 68, 69, 86, 87, 860, 861, 888, 889, \r
+ 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 96, 97, 870, \r
+ 871, 898, 899, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, \r
+ 180, 181, 900, 901, 980, 981, 110, 111, 112, 113, 114, 115, 116, \r
+ 117, 118, 119, 190, 191, 910, 911, 990, 991, 120, 121, 122, 123, \r
+ 124, 125, 126, 127, 128, 129, 182, 183, 920, 921, 908, 909, 130, \r
+ 131, 132, 133, 134, 135, 136, 137, 138, 139, 192, 193, 930, 931, \r
+ 918, 919, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 184, \r
+ 185, 940, 941, 188, 189, 150, 151, 152, 153, 154, 155, 156, 157, \r
+ 158, 159, 194, 195, 950, 951, 198, 199, 160, 161, 162, 163, 164, \r
+ 165, 166, 167, 168, 169, 186, 187, 960, 961, 988, 989, 170, 171, \r
+ 172, 173, 174, 175, 176, 177, 178, 179, 196, 197, 970, 971, 998, \r
+ 999, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 280, 281, \r
+ 802, 803, 882, 883, 210, 211, 212, 213, 214, 215, 216, 217, 218, \r
+ 219, 290, 291, 812, 813, 892, 893, 220, 221, 222, 223, 224, 225, \r
+ 226, 227, 228, 229, 282, 283, 822, 823, 828, 829, 230, 231, 232, \r
+ 233, 234, 235, 236, 237, 238, 239, 292, 293, 832, 833, 838, 839, \r
+ 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 284, 285, 842, \r
+ 843, 288, 289, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, \r
+ 294, 295, 852, 853, 298, 299, 260, 261, 262, 263, 264, 265, 266, \r
+ 267, 268, 269, 286, 287, 862, 863, 888, 889, 270, 271, 272, 273, \r
+ 274, 275, 276, 277, 278, 279, 296, 297, 872, 873, 898, 899, 300, \r
+ 301, 302, 303, 304, 305, 306, 307, 308, 309, 380, 381, 902, 903, \r
+ 982, 983, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 390, \r
+ 391, 912, 913, 992, 993, 320, 321, 322, 323, 324, 325, 326, 327, \r
+ 328, 329, 382, 383, 922, 923, 928, 929, 330, 331, 332, 333, 334, \r
+ 335, 336, 337, 338, 339, 392, 393, 932, 933, 938, 939, 340, 341, \r
+ 342, 343, 344, 345, 346, 347, 348, 349, 384, 385, 942, 943, 388, \r
+ 389, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 394, 395, \r
+ 952, 953, 398, 399, 360, 361, 362, 363, 364, 365, 366, 367, 368, \r
+ 369, 386, 387, 962, 963, 988, 989, 370, 371, 372, 373, 374, 375, \r
+ 376, 377, 378, 379, 396, 397, 972, 973, 998, 999, 400, 401, 402, \r
+ 403, 404, 405, 406, 407, 408, 409, 480, 481, 804, 805, 884, 885, \r
+ 410, 411, 412, 413, 414, 415, 416, 417, 418, 419, 490, 491, 814, \r
+ 815, 894, 895, 420, 421, 422, 423, 424, 425, 426, 427, 428, 429, \r
+ 482, 483, 824, 825, 848, 849, 430, 431, 432, 433, 434, 435, 436, \r
+ 437, 438, 439, 492, 493, 834, 835, 858, 859, 440, 441, 442, 443, \r
+ 444, 445, 446, 447, 448, 449, 484, 485, 844, 845, 488, 489, 450, \r
+ 451, 452, 453, 454, 455, 456, 457, 458, 459, 494, 495, 854, 855, \r
+ 498, 499, 460, 461, 462, 463, 464, 465, 466, 467, 468, 469, 486, \r
+ 487, 864, 865, 888, 889, 470, 471, 472, 473, 474, 475, 476, 477, \r
+ 478, 479, 496, 497, 874, 875, 898, 899, 500, 501, 502, 503, 504, \r
+ 505, 506, 507, 508, 509, 580, 581, 904, 905, 984, 985, 510, 511, \r
+ 512, 513, 514, 515, 516, 517, 518, 519, 590, 591, 914, 915, 994, \r
+ 995, 520, 521, 522, 523, 524, 525, 526, 527, 528, 529, 582, 583, \r
+ 924, 925, 948, 949, 530, 531, 532, 533, 534, 535, 536, 537, 538, \r
+ 539, 592, 593, 934, 935, 958, 959, 540, 541, 542, 543, 544, 545, \r
+ 546, 547, 548, 549, 584, 585, 944, 945, 588, 589, 550, 551, 552, \r
+ 553, 554, 555, 556, 557, 558, 559, 594, 595, 954, 955, 598, 599, \r
+ 560, 561, 562, 563, 564, 565, 566, 567, 568, 569, 586, 587, 964, \r
+ 965, 988, 989, 570, 571, 572, 573, 574, 575, 576, 577, 578, 579, \r
+ 596, 597, 974, 975, 998, 999, 600, 601, 602, 603, 604, 605, 606, \r
+ 607, 608, 609, 680, 681, 806, 807, 886, 887, 610, 611, 612, 613, \r
+ 614, 615, 616, 617, 618, 619, 690, 691, 816, 817, 896, 897, 620, \r
+ 621, 622, 623, 624, 625, 626, 627, 628, 629, 682, 683, 826, 827, \r
+ 868, 869, 630, 631, 632, 633, 634, 635, 636, 637, 638, 639, 692, \r
+ 693, 836, 837, 878, 879, 640, 641, 642, 643, 644, 645, 646, 647, \r
+ 648, 649, 684, 685, 846, 847, 688, 689, 650, 651, 652, 653, 654, \r
+ 655, 656, 657, 658, 659, 694, 695, 856, 857, 698, 699, 660, 661, \r
+ 662, 663, 664, 665, 666, 667, 668, 669, 686, 687, 866, 867, 888, \r
+ 889, 670, 671, 672, 673, 674, 675, 676, 677, 678, 679, 696, 697, \r
+ 876, 877, 898, 899, 700, 701, 702, 703, 704, 705, 706, 707, 708, \r
+ 709, 780, 781, 906, 907, 986, 987, 710, 711, 712, 713, 714, 715, \r
+ 716, 717, 718, 719, 790, 791, 916, 917, 996, 997, 720, 721, 722, \r
+ 723, 724, 725, 726, 727, 728, 729, 782, 783, 926, 927, 968, 969, \r
+ 730, 731, 732, 733, 734, 735, 736, 737, 738, 739, 792, 793, 936, \r
+ 937, 978, 979, 740, 741, 742, 743, 744, 745, 746, 747, 748, 749, \r
+ 784, 785, 946, 947, 788, 789, 750, 751, 752, 753, 754, 755, 756, \r
+ 757, 758, 759, 794, 795, 956, 957, 798, 799, 760, 761, 762, 763, \r
+ 764, 765, 766, 767, 768, 769, 786, 787, 966, 967, 988, 989, 770, \r
+ 771, 772, 773, 774, 775, 776, 777, 778, 779, 796, 797, 976, 977, \r
+ 998, 999};\r
+#endif\r
+ \r
+#if defined(DEC_DPD2BINK) && DEC_DPD2BINK==1 && !defined(DECDPD2BINK)\r
+#define DECDPD2BINK\r
+ \r
+const uint32_t DPD2BINK[1024]={ 0, 1000, 2000, 3000, 4000, 5000, \r
+ 6000, 7000, 8000, 9000, 80000, 81000, 800000, 801000, 880000, 881000, \r
+ 10000, 11000, 12000, 13000, 14000, 15000, 16000, 17000, 18000, 19000, \r
+ 90000, 91000, 810000, 811000, 890000, 891000, 20000, 21000, 22000, 23000, \r
+ 24000, 25000, 26000, 27000, 28000, 29000, 82000, 83000, 820000, 821000, \r
+ 808000, 809000, 30000, 31000, 32000, 33000, 34000, 35000, 36000, 37000, \r
+ 38000, 39000, 92000, 93000, 830000, 831000, 818000, 819000, 40000, 41000, \r
+ 42000, 43000, 44000, 45000, 46000, 47000, 48000, 49000, 84000, 85000, \r
+ 840000, 841000, 88000, 89000, 50000, 51000, 52000, 53000, 54000, 55000, \r
+ 56000, 57000, 58000, 59000, 94000, 95000, 850000, 851000, 98000, 99000, \r
+ 60000, 61000, 62000, 63000, 64000, 65000, 66000, 67000, 68000, 69000, \r
+ 86000, 87000, 860000, 861000, 888000, 889000, 70000, 71000, 72000, 73000, \r
+ 74000, 75000, 76000, 77000, 78000, 79000, 96000, 97000, 870000, 871000, \r
+ 898000, 899000, 100000, 101000, 102000, 103000, 104000, 105000, 106000, 107000, \r
+ 108000, 109000, 180000, 181000, 900000, 901000, 980000, 981000, 110000, 111000, \r
+ 112000, 113000, 114000, 115000, 116000, 117000, 118000, 119000, 190000, 191000, \r
+ 910000, 911000, 990000, 991000, 120000, 121000, 122000, 123000, 124000, 125000, \r
+ 126000, 127000, 128000, 129000, 182000, 183000, 920000, 921000, 908000, 909000, \r
+ 130000, 131000, 132000, 133000, 134000, 135000, 136000, 137000, 138000, 139000, \r
+ 192000, 193000, 930000, 931000, 918000, 919000, 140000, 141000, 142000, 143000, \r
+ 144000, 145000, 146000, 147000, 148000, 149000, 184000, 185000, 940000, 941000, \r
+ 188000, 189000, 150000, 151000, 152000, 153000, 154000, 155000, 156000, 157000, \r
+ 158000, 159000, 194000, 195000, 950000, 951000, 198000, 199000, 160000, 161000, \r
+ 162000, 163000, 164000, 165000, 166000, 167000, 168000, 169000, 186000, 187000, \r
+ 960000, 961000, 988000, 989000, 170000, 171000, 172000, 173000, 174000, 175000, \r
+ 176000, 177000, 178000, 179000, 196000, 197000, 970000, 971000, 998000, 999000, \r
+ 200000, 201000, 202000, 203000, 204000, 205000, 206000, 207000, 208000, 209000, \r
+ 280000, 281000, 802000, 803000, 882000, 883000, 210000, 211000, 212000, 213000, \r
+ 214000, 215000, 216000, 217000, 218000, 219000, 290000, 291000, 812000, 813000, \r
+ 892000, 893000, 220000, 221000, 222000, 223000, 224000, 225000, 226000, 227000, \r
+ 228000, 229000, 282000, 283000, 822000, 823000, 828000, 829000, 230000, 231000, \r
+ 232000, 233000, 234000, 235000, 236000, 237000, 238000, 239000, 292000, 293000, \r
+ 832000, 833000, 838000, 839000, 240000, 241000, 242000, 243000, 244000, 245000, \r
+ 246000, 247000, 248000, 249000, 284000, 285000, 842000, 843000, 288000, 289000, \r
+ 250000, 251000, 252000, 253000, 254000, 255000, 256000, 257000, 258000, 259000, \r
+ 294000, 295000, 852000, 853000, 298000, 299000, 260000, 261000, 262000, 263000, \r
+ 264000, 265000, 266000, 267000, 268000, 269000, 286000, 287000, 862000, 863000, \r
+ 888000, 889000, 270000, 271000, 272000, 273000, 274000, 275000, 276000, 277000, \r
+ 278000, 279000, 296000, 297000, 872000, 873000, 898000, 899000, 300000, 301000, \r
+ 302000, 303000, 304000, 305000, 306000, 307000, 308000, 309000, 380000, 381000, \r
+ 902000, 903000, 982000, 983000, 310000, 311000, 312000, 313000, 314000, 315000, \r
+ 316000, 317000, 318000, 319000, 390000, 391000, 912000, 913000, 992000, 993000, \r
+ 320000, 321000, 322000, 323000, 324000, 325000, 326000, 327000, 328000, 329000, \r
+ 382000, 383000, 922000, 923000, 928000, 929000, 330000, 331000, 332000, 333000, \r
+ 334000, 335000, 336000, 337000, 338000, 339000, 392000, 393000, 932000, 933000, \r
+ 938000, 939000, 340000, 341000, 342000, 343000, 344000, 345000, 346000, 347000, \r
+ 348000, 349000, 384000, 385000, 942000, 943000, 388000, 389000, 350000, 351000, \r
+ 352000, 353000, 354000, 355000, 356000, 357000, 358000, 359000, 394000, 395000, \r
+ 952000, 953000, 398000, 399000, 360000, 361000, 362000, 363000, 364000, 365000, \r
+ 366000, 367000, 368000, 369000, 386000, 387000, 962000, 963000, 988000, 989000, \r
+ 370000, 371000, 372000, 373000, 374000, 375000, 376000, 377000, 378000, 379000, \r
+ 396000, 397000, 972000, 973000, 998000, 999000, 400000, 401000, 402000, 403000, \r
+ 404000, 405000, 406000, 407000, 408000, 409000, 480000, 481000, 804000, 805000, \r
+ 884000, 885000, 410000, 411000, 412000, 413000, 414000, 415000, 416000, 417000, \r
+ 418000, 419000, 490000, 491000, 814000, 815000, 894000, 895000, 420000, 421000, \r
+ 422000, 423000, 424000, 425000, 426000, 427000, 428000, 429000, 482000, 483000, \r
+ 824000, 825000, 848000, 849000, 430000, 431000, 432000, 433000, 434000, 435000, \r
+ 436000, 437000, 438000, 439000, 492000, 493000, 834000, 835000, 858000, 859000, \r
+ 440000, 441000, 442000, 443000, 444000, 445000, 446000, 447000, 448000, 449000, \r
+ 484000, 485000, 844000, 845000, 488000, 489000, 450000, 451000, 452000, 453000, \r
+ 454000, 455000, 456000, 457000, 458000, 459000, 494000, 495000, 854000, 855000, \r
+ 498000, 499000, 460000, 461000, 462000, 463000, 464000, 465000, 466000, 467000, \r
+ 468000, 469000, 486000, 487000, 864000, 865000, 888000, 889000, 470000, 471000, \r
+ 472000, 473000, 474000, 475000, 476000, 477000, 478000, 479000, 496000, 497000, \r
+ 874000, 875000, 898000, 899000, 500000, 501000, 502000, 503000, 504000, 505000, \r
+ 506000, 507000, 508000, 509000, 580000, 581000, 904000, 905000, 984000, 985000, \r
+ 510000, 511000, 512000, 513000, 514000, 515000, 516000, 517000, 518000, 519000, \r
+ 590000, 591000, 914000, 915000, 994000, 995000, 520000, 521000, 522000, 523000, \r
+ 524000, 525000, 526000, 527000, 528000, 529000, 582000, 583000, 924000, 925000, \r
+ 948000, 949000, 530000, 531000, 532000, 533000, 534000, 535000, 536000, 537000, \r
+ 538000, 539000, 592000, 593000, 934000, 935000, 958000, 959000, 540000, 541000, \r
+ 542000, 543000, 544000, 545000, 546000, 547000, 548000, 549000, 584000, 585000, \r
+ 944000, 945000, 588000, 589000, 550000, 551000, 552000, 553000, 554000, 555000, \r
+ 556000, 557000, 558000, 559000, 594000, 595000, 954000, 955000, 598000, 599000, \r
+ 560000, 561000, 562000, 563000, 564000, 565000, 566000, 567000, 568000, 569000, \r
+ 586000, 587000, 964000, 965000, 988000, 989000, 570000, 571000, 572000, 573000, \r
+ 574000, 575000, 576000, 577000, 578000, 579000, 596000, 597000, 974000, 975000, \r
+ 998000, 999000, 600000, 601000, 602000, 603000, 604000, 605000, 606000, 607000, \r
+ 608000, 609000, 680000, 681000, 806000, 807000, 886000, 887000, 610000, 611000, \r
+ 612000, 613000, 614000, 615000, 616000, 617000, 618000, 619000, 690000, 691000, \r
+ 816000, 817000, 896000, 897000, 620000, 621000, 622000, 623000, 624000, 625000, \r
+ 626000, 627000, 628000, 629000, 682000, 683000, 826000, 827000, 868000, 869000, \r
+ 630000, 631000, 632000, 633000, 634000, 635000, 636000, 637000, 638000, 639000, \r
+ 692000, 693000, 836000, 837000, 878000, 879000, 640000, 641000, 642000, 643000, \r
+ 644000, 645000, 646000, 647000, 648000, 649000, 684000, 685000, 846000, 847000, \r
+ 688000, 689000, 650000, 651000, 652000, 653000, 654000, 655000, 656000, 657000, \r
+ 658000, 659000, 694000, 695000, 856000, 857000, 698000, 699000, 660000, 661000, \r
+ 662000, 663000, 664000, 665000, 666000, 667000, 668000, 669000, 686000, 687000, \r
+ 866000, 867000, 888000, 889000, 670000, 671000, 672000, 673000, 674000, 675000, \r
+ 676000, 677000, 678000, 679000, 696000, 697000, 876000, 877000, 898000, 899000, \r
+ 700000, 701000, 702000, 703000, 704000, 705000, 706000, 707000, 708000, 709000, \r
+ 780000, 781000, 906000, 907000, 986000, 987000, 710000, 711000, 712000, 713000, \r
+ 714000, 715000, 716000, 717000, 718000, 719000, 790000, 791000, 916000, 917000, \r
+ 996000, 997000, 720000, 721000, 722000, 723000, 724000, 725000, 726000, 727000, \r
+ 728000, 729000, 782000, 783000, 926000, 927000, 968000, 969000, 730000, 731000, \r
+ 732000, 733000, 734000, 735000, 736000, 737000, 738000, 739000, 792000, 793000, \r
+ 936000, 937000, 978000, 979000, 740000, 741000, 742000, 743000, 744000, 745000, \r
+ 746000, 747000, 748000, 749000, 784000, 785000, 946000, 947000, 788000, 789000, \r
+ 750000, 751000, 752000, 753000, 754000, 755000, 756000, 757000, 758000, 759000, \r
+ 794000, 795000, 956000, 957000, 798000, 799000, 760000, 761000, 762000, 763000, \r
+ 764000, 765000, 766000, 767000, 768000, 769000, 786000, 787000, 966000, 967000, \r
+ 988000, 989000, 770000, 771000, 772000, 773000, 774000, 775000, 776000, 777000, \r
+ 778000, 779000, 796000, 797000, 976000, 977000, 998000, 999000};\r
+#endif\r
+ \r
+#if defined(DEC_DPD2BINM) && DEC_DPD2BINM==1 && !defined(DECDPD2BINM)\r
+#define DECDPD2BINM\r
+ \r
+const uint32_t DPD2BINM[1024]={0, 1000000, 2000000, 3000000, 4000000, \r
+ 5000000, 6000000, 7000000, 8000000, 9000000, 80000000, 81000000, \r
+ 800000000, 801000000, 880000000, 881000000, 10000000, 11000000, 12000000, \r
+ 13000000, 14000000, 15000000, 16000000, 17000000, 18000000, 19000000, \r
+ 90000000, 91000000, 810000000, 811000000, 890000000, 891000000, 20000000, \r
+ 21000000, 22000000, 23000000, 24000000, 25000000, 26000000, 27000000, \r
+ 28000000, 29000000, 82000000, 83000000, 820000000, 821000000, 808000000, \r
+ 809000000, 30000000, 31000000, 32000000, 33000000, 34000000, 35000000, \r
+ 36000000, 37000000, 38000000, 39000000, 92000000, 93000000, 830000000, \r
+ 831000000, 818000000, 819000000, 40000000, 41000000, 42000000, 43000000, \r
+ 44000000, 45000000, 46000000, 47000000, 48000000, 49000000, 84000000, \r
+ 85000000, 840000000, 841000000, 88000000, 89000000, 50000000, 51000000, \r
+ 52000000, 53000000, 54000000, 55000000, 56000000, 57000000, 58000000, \r
+ 59000000, 94000000, 95000000, 850000000, 851000000, 98000000, 99000000, \r
+ 60000000, 61000000, 62000000, 63000000, 64000000, 65000000, 66000000, \r
+ 67000000, 68000000, 69000000, 86000000, 87000000, 860000000, 861000000, \r
+ 888000000, 889000000, 70000000, 71000000, 72000000, 73000000, 74000000, \r
+ 75000000, 76000000, 77000000, 78000000, 79000000, 96000000, 97000000, \r
+ 870000000, 871000000, 898000000, 899000000, 100000000, 101000000, 102000000, \r
+ 103000000, 104000000, 105000000, 106000000, 107000000, 108000000, 109000000, \r
+ 180000000, 181000000, 900000000, 901000000, 980000000, 981000000, 110000000, \r
+ 111000000, 112000000, 113000000, 114000000, 115000000, 116000000, 117000000, \r
+ 118000000, 119000000, 190000000, 191000000, 910000000, 911000000, 990000000, \r
+ 991000000, 120000000, 121000000, 122000000, 123000000, 124000000, 125000000, \r
+ 126000000, 127000000, 128000000, 129000000, 182000000, 183000000, 920000000, \r
+ 921000000, 908000000, 909000000, 130000000, 131000000, 132000000, 133000000, \r
+ 134000000, 135000000, 136000000, 137000000, 138000000, 139000000, 192000000, \r
+ 193000000, 930000000, 931000000, 918000000, 919000000, 140000000, 141000000, \r
+ 142000000, 143000000, 144000000, 145000000, 146000000, 147000000, 148000000, \r
+ 149000000, 184000000, 185000000, 940000000, 941000000, 188000000, 189000000, \r
+ 150000000, 151000000, 152000000, 153000000, 154000000, 155000000, 156000000, \r
+ 157000000, 158000000, 159000000, 194000000, 195000000, 950000000, 951000000, \r
+ 198000000, 199000000, 160000000, 161000000, 162000000, 163000000, 164000000, \r
+ 165000000, 166000000, 167000000, 168000000, 169000000, 186000000, 187000000, \r
+ 960000000, 961000000, 988000000, 989000000, 170000000, 171000000, 172000000, \r
+ 173000000, 174000000, 175000000, 176000000, 177000000, 178000000, 179000000, \r
+ 196000000, 197000000, 970000000, 971000000, 998000000, 999000000, 200000000, \r
+ 201000000, 202000000, 203000000, 204000000, 205000000, 206000000, 207000000, \r
+ 208000000, 209000000, 280000000, 281000000, 802000000, 803000000, 882000000, \r
+ 883000000, 210000000, 211000000, 212000000, 213000000, 214000000, 215000000, \r
+ 216000000, 217000000, 218000000, 219000000, 290000000, 291000000, 812000000, \r
+ 813000000, 892000000, 893000000, 220000000, 221000000, 222000000, 223000000, \r
+ 224000000, 225000000, 226000000, 227000000, 228000000, 229000000, 282000000, \r
+ 283000000, 822000000, 823000000, 828000000, 829000000, 230000000, 231000000, \r
+ 232000000, 233000000, 234000000, 235000000, 236000000, 237000000, 238000000, \r
+ 239000000, 292000000, 293000000, 832000000, 833000000, 838000000, 839000000, \r
+ 240000000, 241000000, 242000000, 243000000, 244000000, 245000000, 246000000, \r
+ 247000000, 248000000, 249000000, 284000000, 285000000, 842000000, 843000000, \r
+ 288000000, 289000000, 250000000, 251000000, 252000000, 253000000, 254000000, \r
+ 255000000, 256000000, 257000000, 258000000, 259000000, 294000000, 295000000, \r
+ 852000000, 853000000, 298000000, 299000000, 260000000, 261000000, 262000000, \r
+ 263000000, 264000000, 265000000, 266000000, 267000000, 268000000, 269000000, \r
+ 286000000, 287000000, 862000000, 863000000, 888000000, 889000000, 270000000, \r
+ 271000000, 272000000, 273000000, 274000000, 275000000, 276000000, 277000000, \r
+ 278000000, 279000000, 296000000, 297000000, 872000000, 873000000, 898000000, \r
+ 899000000, 300000000, 301000000, 302000000, 303000000, 304000000, 305000000, \r
+ 306000000, 307000000, 308000000, 309000000, 380000000, 381000000, 902000000, \r
+ 903000000, 982000000, 983000000, 310000000, 311000000, 312000000, 313000000, \r
+ 314000000, 315000000, 316000000, 317000000, 318000000, 319000000, 390000000, \r
+ 391000000, 912000000, 913000000, 992000000, 993000000, 320000000, 321000000, \r
+ 322000000, 323000000, 324000000, 325000000, 326000000, 327000000, 328000000, \r
+ 329000000, 382000000, 383000000, 922000000, 923000000, 928000000, 929000000, \r
+ 330000000, 331000000, 332000000, 333000000, 334000000, 335000000, 336000000, \r
+ 337000000, 338000000, 339000000, 392000000, 393000000, 932000000, 933000000, \r
+ 938000000, 939000000, 340000000, 341000000, 342000000, 343000000, 344000000, \r
+ 345000000, 346000000, 347000000, 348000000, 349000000, 384000000, 385000000, \r
+ 942000000, 943000000, 388000000, 389000000, 350000000, 351000000, 352000000, \r
+ 353000000, 354000000, 355000000, 356000000, 357000000, 358000000, 359000000, \r
+ 394000000, 395000000, 952000000, 953000000, 398000000, 399000000, 360000000, \r
+ 361000000, 362000000, 363000000, 364000000, 365000000, 366000000, 367000000, \r
+ 368000000, 369000000, 386000000, 387000000, 962000000, 963000000, 988000000, \r
+ 989000000, 370000000, 371000000, 372000000, 373000000, 374000000, 375000000, \r
+ 376000000, 377000000, 378000000, 379000000, 396000000, 397000000, 972000000, \r
+ 973000000, 998000000, 999000000, 400000000, 401000000, 402000000, 403000000, \r
+ 404000000, 405000000, 406000000, 407000000, 408000000, 409000000, 480000000, \r
+ 481000000, 804000000, 805000000, 884000000, 885000000, 410000000, 411000000, \r
+ 412000000, 413000000, 414000000, 415000000, 416000000, 417000000, 418000000, \r
+ 419000000, 490000000, 491000000, 814000000, 815000000, 894000000, 895000000, \r
+ 420000000, 421000000, 422000000, 423000000, 424000000, 425000000, 426000000, \r
+ 427000000, 428000000, 429000000, 482000000, 483000000, 824000000, 825000000, \r
+ 848000000, 849000000, 430000000, 431000000, 432000000, 433000000, 434000000, \r
+ 435000000, 436000000, 437000000, 438000000, 439000000, 492000000, 493000000, \r
+ 834000000, 835000000, 858000000, 859000000, 440000000, 441000000, 442000000, \r
+ 443000000, 444000000, 445000000, 446000000, 447000000, 448000000, 449000000, \r
+ 484000000, 485000000, 844000000, 845000000, 488000000, 489000000, 450000000, \r
+ 451000000, 452000000, 453000000, 454000000, 455000000, 456000000, 457000000, \r
+ 458000000, 459000000, 494000000, 495000000, 854000000, 855000000, 498000000, \r
+ 499000000, 460000000, 461000000, 462000000, 463000000, 464000000, 465000000, \r
+ 466000000, 467000000, 468000000, 469000000, 486000000, 487000000, 864000000, \r
+ 865000000, 888000000, 889000000, 470000000, 471000000, 472000000, 473000000, \r
+ 474000000, 475000000, 476000000, 477000000, 478000000, 479000000, 496000000, \r
+ 497000000, 874000000, 875000000, 898000000, 899000000, 500000000, 501000000, \r
+ 502000000, 503000000, 504000000, 505000000, 506000000, 507000000, 508000000, \r
+ 509000000, 580000000, 581000000, 904000000, 905000000, 984000000, 985000000, \r
+ 510000000, 511000000, 512000000, 513000000, 514000000, 515000000, 516000000, \r
+ 517000000, 518000000, 519000000, 590000000, 591000000, 914000000, 915000000, \r
+ 994000000, 995000000, 520000000, 521000000, 522000000, 523000000, 524000000, \r
+ 525000000, 526000000, 527000000, 528000000, 529000000, 582000000, 583000000, \r
+ 924000000, 925000000, 948000000, 949000000, 530000000, 531000000, 532000000, \r
+ 533000000, 534000000, 535000000, 536000000, 537000000, 538000000, 539000000, \r
+ 592000000, 593000000, 934000000, 935000000, 958000000, 959000000, 540000000, \r
+ 541000000, 542000000, 543000000, 544000000, 545000000, 546000000, 547000000, \r
+ 548000000, 549000000, 584000000, 585000000, 944000000, 945000000, 588000000, \r
+ 589000000, 550000000, 551000000, 552000000, 553000000, 554000000, 555000000, \r
+ 556000000, 557000000, 558000000, 559000000, 594000000, 595000000, 954000000, \r
+ 955000000, 598000000, 599000000, 560000000, 561000000, 562000000, 563000000, \r
+ 564000000, 565000000, 566000000, 567000000, 568000000, 569000000, 586000000, \r
+ 587000000, 964000000, 965000000, 988000000, 989000000, 570000000, 571000000, \r
+ 572000000, 573000000, 574000000, 575000000, 576000000, 577000000, 578000000, \r
+ 579000000, 596000000, 597000000, 974000000, 975000000, 998000000, 999000000, \r
+ 600000000, 601000000, 602000000, 603000000, 604000000, 605000000, 606000000, \r
+ 607000000, 608000000, 609000000, 680000000, 681000000, 806000000, 807000000, \r
+ 886000000, 887000000, 610000000, 611000000, 612000000, 613000000, 614000000, \r
+ 615000000, 616000000, 617000000, 618000000, 619000000, 690000000, 691000000, \r
+ 816000000, 817000000, 896000000, 897000000, 620000000, 621000000, 622000000, \r
+ 623000000, 624000000, 625000000, 626000000, 627000000, 628000000, 629000000, \r
+ 682000000, 683000000, 826000000, 827000000, 868000000, 869000000, 630000000, \r
+ 631000000, 632000000, 633000000, 634000000, 635000000, 636000000, 637000000, \r
+ 638000000, 639000000, 692000000, 693000000, 836000000, 837000000, 878000000, \r
+ 879000000, 640000000, 641000000, 642000000, 643000000, 644000000, 645000000, \r
+ 646000000, 647000000, 648000000, 649000000, 684000000, 685000000, 846000000, \r
+ 847000000, 688000000, 689000000, 650000000, 651000000, 652000000, 653000000, \r
+ 654000000, 655000000, 656000000, 657000000, 658000000, 659000000, 694000000, \r
+ 695000000, 856000000, 857000000, 698000000, 699000000, 660000000, 661000000, \r
+ 662000000, 663000000, 664000000, 665000000, 666000000, 667000000, 668000000, \r
+ 669000000, 686000000, 687000000, 866000000, 867000000, 888000000, 889000000, \r
+ 670000000, 671000000, 672000000, 673000000, 674000000, 675000000, 676000000, \r
+ 677000000, 678000000, 679000000, 696000000, 697000000, 876000000, 877000000, \r
+ 898000000, 899000000, 700000000, 701000000, 702000000, 703000000, 704000000, \r
+ 705000000, 706000000, 707000000, 708000000, 709000000, 780000000, 781000000, \r
+ 906000000, 907000000, 986000000, 987000000, 710000000, 711000000, 712000000, \r
+ 713000000, 714000000, 715000000, 716000000, 717000000, 718000000, 719000000, \r
+ 790000000, 791000000, 916000000, 917000000, 996000000, 997000000, 720000000, \r
+ 721000000, 722000000, 723000000, 724000000, 725000000, 726000000, 727000000, \r
+ 728000000, 729000000, 782000000, 783000000, 926000000, 927000000, 968000000, \r
+ 969000000, 730000000, 731000000, 732000000, 733000000, 734000000, 735000000, \r
+ 736000000, 737000000, 738000000, 739000000, 792000000, 793000000, 936000000, \r
+ 937000000, 978000000, 979000000, 740000000, 741000000, 742000000, 743000000, \r
+ 744000000, 745000000, 746000000, 747000000, 748000000, 749000000, 784000000, \r
+ 785000000, 946000000, 947000000, 788000000, 789000000, 750000000, 751000000, \r
+ 752000000, 753000000, 754000000, 755000000, 756000000, 757000000, 758000000, \r
+ 759000000, 794000000, 795000000, 956000000, 957000000, 798000000, 799000000, \r
+ 760000000, 761000000, 762000000, 763000000, 764000000, 765000000, 766000000, \r
+ 767000000, 768000000, 769000000, 786000000, 787000000, 966000000, 967000000, \r
+ 988000000, 989000000, 770000000, 771000000, 772000000, 773000000, 774000000, \r
+ 775000000, 776000000, 777000000, 778000000, 779000000, 796000000, 797000000, \r
+ 976000000, 977000000, 998000000, 999000000};\r
+#endif\r
+ \r
+#if defined(DEC_BIN2CHAR) && DEC_BIN2CHAR==1 && !defined(DECBIN2CHAR)\r
+#define DECBIN2CHAR\r
+ \r
+const uint8_t BIN2CHAR[4001]={\r
+ '\0','0','0','0', '\1','0','0','1', '\1','0','0','2', '\1','0','0','3', '\1','0','0','4', \r
+ '\1','0','0','5', '\1','0','0','6', '\1','0','0','7', '\1','0','0','8', '\1','0','0','9', \r
+ '\2','0','1','0', '\2','0','1','1', '\2','0','1','2', '\2','0','1','3', '\2','0','1','4', \r
+ '\2','0','1','5', '\2','0','1','6', '\2','0','1','7', '\2','0','1','8', '\2','0','1','9', \r
+ '\2','0','2','0', '\2','0','2','1', '\2','0','2','2', '\2','0','2','3', '\2','0','2','4', \r
+ '\2','0','2','5', '\2','0','2','6', '\2','0','2','7', '\2','0','2','8', '\2','0','2','9', \r
+ '\2','0','3','0', '\2','0','3','1', '\2','0','3','2', '\2','0','3','3', '\2','0','3','4', \r
+ '\2','0','3','5', '\2','0','3','6', '\2','0','3','7', '\2','0','3','8', '\2','0','3','9', \r
+ '\2','0','4','0', '\2','0','4','1', '\2','0','4','2', '\2','0','4','3', '\2','0','4','4', \r
+ '\2','0','4','5', '\2','0','4','6', '\2','0','4','7', '\2','0','4','8', '\2','0','4','9', \r
+ '\2','0','5','0', '\2','0','5','1', '\2','0','5','2', '\2','0','5','3', '\2','0','5','4', \r
+ '\2','0','5','5', '\2','0','5','6', '\2','0','5','7', '\2','0','5','8', '\2','0','5','9', \r
+ '\2','0','6','0', '\2','0','6','1', '\2','0','6','2', '\2','0','6','3', '\2','0','6','4', \r
+ '\2','0','6','5', '\2','0','6','6', '\2','0','6','7', '\2','0','6','8', '\2','0','6','9', \r
+ '\2','0','7','0', '\2','0','7','1', '\2','0','7','2', '\2','0','7','3', '\2','0','7','4', \r
+ '\2','0','7','5', '\2','0','7','6', '\2','0','7','7', '\2','0','7','8', '\2','0','7','9', \r
+ '\2','0','8','0', '\2','0','8','1', '\2','0','8','2', '\2','0','8','3', '\2','0','8','4', \r
+ '\2','0','8','5', '\2','0','8','6', '\2','0','8','7', '\2','0','8','8', '\2','0','8','9', \r
+ '\2','0','9','0', '\2','0','9','1', '\2','0','9','2', '\2','0','9','3', '\2','0','9','4', \r
+ '\2','0','9','5', '\2','0','9','6', '\2','0','9','7', '\2','0','9','8', '\2','0','9','9', \r
+ '\3','1','0','0', '\3','1','0','1', '\3','1','0','2', '\3','1','0','3', '\3','1','0','4', \r
+ '\3','1','0','5', '\3','1','0','6', '\3','1','0','7', '\3','1','0','8', '\3','1','0','9', \r
+ '\3','1','1','0', '\3','1','1','1', '\3','1','1','2', '\3','1','1','3', '\3','1','1','4', \r
+ '\3','1','1','5', '\3','1','1','6', '\3','1','1','7', '\3','1','1','8', '\3','1','1','9', \r
+ '\3','1','2','0', '\3','1','2','1', '\3','1','2','2', '\3','1','2','3', '\3','1','2','4', \r
+ '\3','1','2','5', '\3','1','2','6', '\3','1','2','7', '\3','1','2','8', '\3','1','2','9', \r
+ '\3','1','3','0', '\3','1','3','1', '\3','1','3','2', '\3','1','3','3', '\3','1','3','4', \r
+ '\3','1','3','5', '\3','1','3','6', '\3','1','3','7', '\3','1','3','8', '\3','1','3','9', \r
+ '\3','1','4','0', '\3','1','4','1', '\3','1','4','2', '\3','1','4','3', '\3','1','4','4', \r
+ '\3','1','4','5', '\3','1','4','6', '\3','1','4','7', '\3','1','4','8', '\3','1','4','9', \r
+ '\3','1','5','0', '\3','1','5','1', '\3','1','5','2', '\3','1','5','3', '\3','1','5','4', \r
+ '\3','1','5','5', '\3','1','5','6', '\3','1','5','7', '\3','1','5','8', '\3','1','5','9', \r
+ '\3','1','6','0', '\3','1','6','1', '\3','1','6','2', '\3','1','6','3', '\3','1','6','4', \r
+ '\3','1','6','5', '\3','1','6','6', '\3','1','6','7', '\3','1','6','8', '\3','1','6','9', \r
+ '\3','1','7','0', '\3','1','7','1', '\3','1','7','2', '\3','1','7','3', '\3','1','7','4', \r
+ '\3','1','7','5', '\3','1','7','6', '\3','1','7','7', '\3','1','7','8', '\3','1','7','9', \r
+ '\3','1','8','0', '\3','1','8','1', '\3','1','8','2', '\3','1','8','3', '\3','1','8','4', \r
+ '\3','1','8','5', '\3','1','8','6', '\3','1','8','7', '\3','1','8','8', '\3','1','8','9', \r
+ '\3','1','9','0', '\3','1','9','1', '\3','1','9','2', '\3','1','9','3', '\3','1','9','4', \r
+ '\3','1','9','5', '\3','1','9','6', '\3','1','9','7', '\3','1','9','8', '\3','1','9','9', \r
+ '\3','2','0','0', '\3','2','0','1', '\3','2','0','2', '\3','2','0','3', '\3','2','0','4', \r
+ '\3','2','0','5', '\3','2','0','6', '\3','2','0','7', '\3','2','0','8', '\3','2','0','9', \r
+ '\3','2','1','0', '\3','2','1','1', '\3','2','1','2', '\3','2','1','3', '\3','2','1','4', \r
+ '\3','2','1','5', '\3','2','1','6', '\3','2','1','7', '\3','2','1','8', '\3','2','1','9', \r
+ '\3','2','2','0', '\3','2','2','1', '\3','2','2','2', '\3','2','2','3', '\3','2','2','4', \r
+ '\3','2','2','5', '\3','2','2','6', '\3','2','2','7', '\3','2','2','8', '\3','2','2','9', \r
+ '\3','2','3','0', '\3','2','3','1', '\3','2','3','2', '\3','2','3','3', '\3','2','3','4', \r
+ '\3','2','3','5', '\3','2','3','6', '\3','2','3','7', '\3','2','3','8', '\3','2','3','9', \r
+ '\3','2','4','0', '\3','2','4','1', '\3','2','4','2', '\3','2','4','3', '\3','2','4','4', \r
+ '\3','2','4','5', '\3','2','4','6', '\3','2','4','7', '\3','2','4','8', '\3','2','4','9', \r
+ '\3','2','5','0', '\3','2','5','1', '\3','2','5','2', '\3','2','5','3', '\3','2','5','4', \r
+ '\3','2','5','5', '\3','2','5','6', '\3','2','5','7', '\3','2','5','8', '\3','2','5','9', \r
+ '\3','2','6','0', '\3','2','6','1', '\3','2','6','2', '\3','2','6','3', '\3','2','6','4', \r
+ '\3','2','6','5', '\3','2','6','6', '\3','2','6','7', '\3','2','6','8', '\3','2','6','9', \r
+ '\3','2','7','0', '\3','2','7','1', '\3','2','7','2', '\3','2','7','3', '\3','2','7','4', \r
+ '\3','2','7','5', '\3','2','7','6', '\3','2','7','7', '\3','2','7','8', '\3','2','7','9', \r
+ '\3','2','8','0', '\3','2','8','1', '\3','2','8','2', '\3','2','8','3', '\3','2','8','4', \r
+ '\3','2','8','5', '\3','2','8','6', '\3','2','8','7', '\3','2','8','8', '\3','2','8','9', \r
+ '\3','2','9','0', '\3','2','9','1', '\3','2','9','2', '\3','2','9','3', '\3','2','9','4', \r
+ '\3','2','9','5', '\3','2','9','6', '\3','2','9','7', '\3','2','9','8', '\3','2','9','9', \r
+ '\3','3','0','0', '\3','3','0','1', '\3','3','0','2', '\3','3','0','3', '\3','3','0','4', \r
+ '\3','3','0','5', '\3','3','0','6', '\3','3','0','7', '\3','3','0','8', '\3','3','0','9', \r
+ '\3','3','1','0', '\3','3','1','1', '\3','3','1','2', '\3','3','1','3', '\3','3','1','4', \r
+ '\3','3','1','5', '\3','3','1','6', '\3','3','1','7', '\3','3','1','8', '\3','3','1','9', \r
+ '\3','3','2','0', '\3','3','2','1', '\3','3','2','2', '\3','3','2','3', '\3','3','2','4', \r
+ '\3','3','2','5', '\3','3','2','6', '\3','3','2','7', '\3','3','2','8', '\3','3','2','9', \r
+ '\3','3','3','0', '\3','3','3','1', '\3','3','3','2', '\3','3','3','3', '\3','3','3','4', \r
+ '\3','3','3','5', '\3','3','3','6', '\3','3','3','7', '\3','3','3','8', '\3','3','3','9', \r
+ '\3','3','4','0', '\3','3','4','1', '\3','3','4','2', '\3','3','4','3', '\3','3','4','4', \r
+ '\3','3','4','5', '\3','3','4','6', '\3','3','4','7', '\3','3','4','8', '\3','3','4','9', \r
+ '\3','3','5','0', '\3','3','5','1', '\3','3','5','2', '\3','3','5','3', '\3','3','5','4', \r
+ '\3','3','5','5', '\3','3','5','6', '\3','3','5','7', '\3','3','5','8', '\3','3','5','9', \r
+ '\3','3','6','0', '\3','3','6','1', '\3','3','6','2', '\3','3','6','3', '\3','3','6','4', \r
+ '\3','3','6','5', '\3','3','6','6', '\3','3','6','7', '\3','3','6','8', '\3','3','6','9', \r
+ '\3','3','7','0', '\3','3','7','1', '\3','3','7','2', '\3','3','7','3', '\3','3','7','4', \r
+ '\3','3','7','5', '\3','3','7','6', '\3','3','7','7', '\3','3','7','8', '\3','3','7','9', \r
+ '\3','3','8','0', '\3','3','8','1', '\3','3','8','2', '\3','3','8','3', '\3','3','8','4', \r
+ '\3','3','8','5', '\3','3','8','6', '\3','3','8','7', '\3','3','8','8', '\3','3','8','9', \r
+ '\3','3','9','0', '\3','3','9','1', '\3','3','9','2', '\3','3','9','3', '\3','3','9','4', \r
+ '\3','3','9','5', '\3','3','9','6', '\3','3','9','7', '\3','3','9','8', '\3','3','9','9', \r
+ '\3','4','0','0', '\3','4','0','1', '\3','4','0','2', '\3','4','0','3', '\3','4','0','4', \r
+ '\3','4','0','5', '\3','4','0','6', '\3','4','0','7', '\3','4','0','8', '\3','4','0','9', \r
+ '\3','4','1','0', '\3','4','1','1', '\3','4','1','2', '\3','4','1','3', '\3','4','1','4', \r
+ '\3','4','1','5', '\3','4','1','6', '\3','4','1','7', '\3','4','1','8', '\3','4','1','9', \r
+ '\3','4','2','0', '\3','4','2','1', '\3','4','2','2', '\3','4','2','3', '\3','4','2','4', \r
+ '\3','4','2','5', '\3','4','2','6', '\3','4','2','7', '\3','4','2','8', '\3','4','2','9', \r
+ '\3','4','3','0', '\3','4','3','1', '\3','4','3','2', '\3','4','3','3', '\3','4','3','4', \r
+ '\3','4','3','5', '\3','4','3','6', '\3','4','3','7', '\3','4','3','8', '\3','4','3','9', \r
+ '\3','4','4','0', '\3','4','4','1', '\3','4','4','2', '\3','4','4','3', '\3','4','4','4', \r
+ '\3','4','4','5', '\3','4','4','6', '\3','4','4','7', '\3','4','4','8', '\3','4','4','9', \r
+ '\3','4','5','0', '\3','4','5','1', '\3','4','5','2', '\3','4','5','3', '\3','4','5','4', \r
+ '\3','4','5','5', '\3','4','5','6', '\3','4','5','7', '\3','4','5','8', '\3','4','5','9', \r
+ '\3','4','6','0', '\3','4','6','1', '\3','4','6','2', '\3','4','6','3', '\3','4','6','4', \r
+ '\3','4','6','5', '\3','4','6','6', '\3','4','6','7', '\3','4','6','8', '\3','4','6','9', \r
+ '\3','4','7','0', '\3','4','7','1', '\3','4','7','2', '\3','4','7','3', '\3','4','7','4', \r
+ '\3','4','7','5', '\3','4','7','6', '\3','4','7','7', '\3','4','7','8', '\3','4','7','9', \r
+ '\3','4','8','0', '\3','4','8','1', '\3','4','8','2', '\3','4','8','3', '\3','4','8','4', \r
+ '\3','4','8','5', '\3','4','8','6', '\3','4','8','7', '\3','4','8','8', '\3','4','8','9', \r
+ '\3','4','9','0', '\3','4','9','1', '\3','4','9','2', '\3','4','9','3', '\3','4','9','4', \r
+ '\3','4','9','5', '\3','4','9','6', '\3','4','9','7', '\3','4','9','8', '\3','4','9','9', \r
+ '\3','5','0','0', '\3','5','0','1', '\3','5','0','2', '\3','5','0','3', '\3','5','0','4', \r
+ '\3','5','0','5', '\3','5','0','6', '\3','5','0','7', '\3','5','0','8', '\3','5','0','9', \r
+ '\3','5','1','0', '\3','5','1','1', '\3','5','1','2', '\3','5','1','3', '\3','5','1','4', \r
+ '\3','5','1','5', '\3','5','1','6', '\3','5','1','7', '\3','5','1','8', '\3','5','1','9', \r
+ '\3','5','2','0', '\3','5','2','1', '\3','5','2','2', '\3','5','2','3', '\3','5','2','4', \r
+ '\3','5','2','5', '\3','5','2','6', '\3','5','2','7', '\3','5','2','8', '\3','5','2','9', \r
+ '\3','5','3','0', '\3','5','3','1', '\3','5','3','2', '\3','5','3','3', '\3','5','3','4', \r
+ '\3','5','3','5', '\3','5','3','6', '\3','5','3','7', '\3','5','3','8', '\3','5','3','9', \r
+ '\3','5','4','0', '\3','5','4','1', '\3','5','4','2', '\3','5','4','3', '\3','5','4','4', \r
+ '\3','5','4','5', '\3','5','4','6', '\3','5','4','7', '\3','5','4','8', '\3','5','4','9', \r
+ '\3','5','5','0', '\3','5','5','1', '\3','5','5','2', '\3','5','5','3', '\3','5','5','4', \r
+ '\3','5','5','5', '\3','5','5','6', '\3','5','5','7', '\3','5','5','8', '\3','5','5','9', \r
+ '\3','5','6','0', '\3','5','6','1', '\3','5','6','2', '\3','5','6','3', '\3','5','6','4', \r
+ '\3','5','6','5', '\3','5','6','6', '\3','5','6','7', '\3','5','6','8', '\3','5','6','9', \r
+ '\3','5','7','0', '\3','5','7','1', '\3','5','7','2', '\3','5','7','3', '\3','5','7','4', \r
+ '\3','5','7','5', '\3','5','7','6', '\3','5','7','7', '\3','5','7','8', '\3','5','7','9', \r
+ '\3','5','8','0', '\3','5','8','1', '\3','5','8','2', '\3','5','8','3', '\3','5','8','4', \r
+ '\3','5','8','5', '\3','5','8','6', '\3','5','8','7', '\3','5','8','8', '\3','5','8','9', \r
+ '\3','5','9','0', '\3','5','9','1', '\3','5','9','2', '\3','5','9','3', '\3','5','9','4', \r
+ '\3','5','9','5', '\3','5','9','6', '\3','5','9','7', '\3','5','9','8', '\3','5','9','9', \r
+ '\3','6','0','0', '\3','6','0','1', '\3','6','0','2', '\3','6','0','3', '\3','6','0','4', \r
+ '\3','6','0','5', '\3','6','0','6', '\3','6','0','7', '\3','6','0','8', '\3','6','0','9', \r
+ '\3','6','1','0', '\3','6','1','1', '\3','6','1','2', '\3','6','1','3', '\3','6','1','4', \r
+ '\3','6','1','5', '\3','6','1','6', '\3','6','1','7', '\3','6','1','8', '\3','6','1','9', \r
+ '\3','6','2','0', '\3','6','2','1', '\3','6','2','2', '\3','6','2','3', '\3','6','2','4', \r
+ '\3','6','2','5', '\3','6','2','6', '\3','6','2','7', '\3','6','2','8', '\3','6','2','9', \r
+ '\3','6','3','0', '\3','6','3','1', '\3','6','3','2', '\3','6','3','3', '\3','6','3','4', \r
+ '\3','6','3','5', '\3','6','3','6', '\3','6','3','7', '\3','6','3','8', '\3','6','3','9', \r
+ '\3','6','4','0', '\3','6','4','1', '\3','6','4','2', '\3','6','4','3', '\3','6','4','4', \r
+ '\3','6','4','5', '\3','6','4','6', '\3','6','4','7', '\3','6','4','8', '\3','6','4','9', \r
+ '\3','6','5','0', '\3','6','5','1', '\3','6','5','2', '\3','6','5','3', '\3','6','5','4', \r
+ '\3','6','5','5', '\3','6','5','6', '\3','6','5','7', '\3','6','5','8', '\3','6','5','9', \r
+ '\3','6','6','0', '\3','6','6','1', '\3','6','6','2', '\3','6','6','3', '\3','6','6','4', \r
+ '\3','6','6','5', '\3','6','6','6', '\3','6','6','7', '\3','6','6','8', '\3','6','6','9', \r
+ '\3','6','7','0', '\3','6','7','1', '\3','6','7','2', '\3','6','7','3', '\3','6','7','4', \r
+ '\3','6','7','5', '\3','6','7','6', '\3','6','7','7', '\3','6','7','8', '\3','6','7','9', \r
+ '\3','6','8','0', '\3','6','8','1', '\3','6','8','2', '\3','6','8','3', '\3','6','8','4', \r
+ '\3','6','8','5', '\3','6','8','6', '\3','6','8','7', '\3','6','8','8', '\3','6','8','9', \r
+ '\3','6','9','0', '\3','6','9','1', '\3','6','9','2', '\3','6','9','3', '\3','6','9','4', \r
+ '\3','6','9','5', '\3','6','9','6', '\3','6','9','7', '\3','6','9','8', '\3','6','9','9', \r
+ '\3','7','0','0', '\3','7','0','1', '\3','7','0','2', '\3','7','0','3', '\3','7','0','4', \r
+ '\3','7','0','5', '\3','7','0','6', '\3','7','0','7', '\3','7','0','8', '\3','7','0','9', \r
+ '\3','7','1','0', '\3','7','1','1', '\3','7','1','2', '\3','7','1','3', '\3','7','1','4', \r
+ '\3','7','1','5', '\3','7','1','6', '\3','7','1','7', '\3','7','1','8', '\3','7','1','9', \r
+ '\3','7','2','0', '\3','7','2','1', '\3','7','2','2', '\3','7','2','3', '\3','7','2','4', \r
+ '\3','7','2','5', '\3','7','2','6', '\3','7','2','7', '\3','7','2','8', '\3','7','2','9', \r
+ '\3','7','3','0', '\3','7','3','1', '\3','7','3','2', '\3','7','3','3', '\3','7','3','4', \r
+ '\3','7','3','5', '\3','7','3','6', '\3','7','3','7', '\3','7','3','8', '\3','7','3','9', \r
+ '\3','7','4','0', '\3','7','4','1', '\3','7','4','2', '\3','7','4','3', '\3','7','4','4', \r
+ '\3','7','4','5', '\3','7','4','6', '\3','7','4','7', '\3','7','4','8', '\3','7','4','9', \r
+ '\3','7','5','0', '\3','7','5','1', '\3','7','5','2', '\3','7','5','3', '\3','7','5','4', \r
+ '\3','7','5','5', '\3','7','5','6', '\3','7','5','7', '\3','7','5','8', '\3','7','5','9', \r
+ '\3','7','6','0', '\3','7','6','1', '\3','7','6','2', '\3','7','6','3', '\3','7','6','4', \r
+ '\3','7','6','5', '\3','7','6','6', '\3','7','6','7', '\3','7','6','8', '\3','7','6','9', \r
+ '\3','7','7','0', '\3','7','7','1', '\3','7','7','2', '\3','7','7','3', '\3','7','7','4', \r
+ '\3','7','7','5', '\3','7','7','6', '\3','7','7','7', '\3','7','7','8', '\3','7','7','9', \r
+ '\3','7','8','0', '\3','7','8','1', '\3','7','8','2', '\3','7','8','3', '\3','7','8','4', \r
+ '\3','7','8','5', '\3','7','8','6', '\3','7','8','7', '\3','7','8','8', '\3','7','8','9', \r
+ '\3','7','9','0', '\3','7','9','1', '\3','7','9','2', '\3','7','9','3', '\3','7','9','4', \r
+ '\3','7','9','5', '\3','7','9','6', '\3','7','9','7', '\3','7','9','8', '\3','7','9','9', \r
+ '\3','8','0','0', '\3','8','0','1', '\3','8','0','2', '\3','8','0','3', '\3','8','0','4', \r
+ '\3','8','0','5', '\3','8','0','6', '\3','8','0','7', '\3','8','0','8', '\3','8','0','9', \r
+ '\3','8','1','0', '\3','8','1','1', '\3','8','1','2', '\3','8','1','3', '\3','8','1','4', \r
+ '\3','8','1','5', '\3','8','1','6', '\3','8','1','7', '\3','8','1','8', '\3','8','1','9', \r
+ '\3','8','2','0', '\3','8','2','1', '\3','8','2','2', '\3','8','2','3', '\3','8','2','4', \r
+ '\3','8','2','5', '\3','8','2','6', '\3','8','2','7', '\3','8','2','8', '\3','8','2','9', \r
+ '\3','8','3','0', '\3','8','3','1', '\3','8','3','2', '\3','8','3','3', '\3','8','3','4', \r
+ '\3','8','3','5', '\3','8','3','6', '\3','8','3','7', '\3','8','3','8', '\3','8','3','9', \r
+ '\3','8','4','0', '\3','8','4','1', '\3','8','4','2', '\3','8','4','3', '\3','8','4','4', \r
+ '\3','8','4','5', '\3','8','4','6', '\3','8','4','7', '\3','8','4','8', '\3','8','4','9', \r
+ '\3','8','5','0', '\3','8','5','1', '\3','8','5','2', '\3','8','5','3', '\3','8','5','4', \r
+ '\3','8','5','5', '\3','8','5','6', '\3','8','5','7', '\3','8','5','8', '\3','8','5','9', \r
+ '\3','8','6','0', '\3','8','6','1', '\3','8','6','2', '\3','8','6','3', '\3','8','6','4', \r
+ '\3','8','6','5', '\3','8','6','6', '\3','8','6','7', '\3','8','6','8', '\3','8','6','9', \r
+ '\3','8','7','0', '\3','8','7','1', '\3','8','7','2', '\3','8','7','3', '\3','8','7','4', \r
+ '\3','8','7','5', '\3','8','7','6', '\3','8','7','7', '\3','8','7','8', '\3','8','7','9', \r
+ '\3','8','8','0', '\3','8','8','1', '\3','8','8','2', '\3','8','8','3', '\3','8','8','4', \r
+ '\3','8','8','5', '\3','8','8','6', '\3','8','8','7', '\3','8','8','8', '\3','8','8','9', \r
+ '\3','8','9','0', '\3','8','9','1', '\3','8','9','2', '\3','8','9','3', '\3','8','9','4', \r
+ '\3','8','9','5', '\3','8','9','6', '\3','8','9','7', '\3','8','9','8', '\3','8','9','9', \r
+ '\3','9','0','0', '\3','9','0','1', '\3','9','0','2', '\3','9','0','3', '\3','9','0','4', \r
+ '\3','9','0','5', '\3','9','0','6', '\3','9','0','7', '\3','9','0','8', '\3','9','0','9', \r
+ '\3','9','1','0', '\3','9','1','1', '\3','9','1','2', '\3','9','1','3', '\3','9','1','4', \r
+ '\3','9','1','5', '\3','9','1','6', '\3','9','1','7', '\3','9','1','8', '\3','9','1','9', \r
+ '\3','9','2','0', '\3','9','2','1', '\3','9','2','2', '\3','9','2','3', '\3','9','2','4', \r
+ '\3','9','2','5', '\3','9','2','6', '\3','9','2','7', '\3','9','2','8', '\3','9','2','9', \r
+ '\3','9','3','0', '\3','9','3','1', '\3','9','3','2', '\3','9','3','3', '\3','9','3','4', \r
+ '\3','9','3','5', '\3','9','3','6', '\3','9','3','7', '\3','9','3','8', '\3','9','3','9', \r
+ '\3','9','4','0', '\3','9','4','1', '\3','9','4','2', '\3','9','4','3', '\3','9','4','4', \r
+ '\3','9','4','5', '\3','9','4','6', '\3','9','4','7', '\3','9','4','8', '\3','9','4','9', \r
+ '\3','9','5','0', '\3','9','5','1', '\3','9','5','2', '\3','9','5','3', '\3','9','5','4', \r
+ '\3','9','5','5', '\3','9','5','6', '\3','9','5','7', '\3','9','5','8', '\3','9','5','9', \r
+ '\3','9','6','0', '\3','9','6','1', '\3','9','6','2', '\3','9','6','3', '\3','9','6','4', \r
+ '\3','9','6','5', '\3','9','6','6', '\3','9','6','7', '\3','9','6','8', '\3','9','6','9', \r
+ '\3','9','7','0', '\3','9','7','1', '\3','9','7','2', '\3','9','7','3', '\3','9','7','4', \r
+ '\3','9','7','5', '\3','9','7','6', '\3','9','7','7', '\3','9','7','8', '\3','9','7','9', \r
+ '\3','9','8','0', '\3','9','8','1', '\3','9','8','2', '\3','9','8','3', '\3','9','8','4', \r
+ '\3','9','8','5', '\3','9','8','6', '\3','9','8','7', '\3','9','8','8', '\3','9','8','9', \r
+ '\3','9','9','0', '\3','9','9','1', '\3','9','9','2', '\3','9','9','3', '\3','9','9','4', \r
+ '\3','9','9','5', '\3','9','9','6', '\3','9','9','7', '\3','9','9','8', '\3','9','9','9', '\0'};\r
+#endif\r
+ \r
+#if defined(DEC_DPD2BCD8) && DEC_DPD2BCD8==1 && !defined(DECDPD2BCD8)\r
+#define DECDPD2BCD8\r
+ \r
+const uint8_t DPD2BCD8[4096]={\r
+ 0,0,0,0, 0,0,1,1, 0,0,2,1, 0,0,3,1, 0,0,4,1, 0,0,5,1, 0,0,6,1, 0,0,7,1, 0,0,8,1, \r
+ 0,0,9,1, 0,8,0,2, 0,8,1,2, 8,0,0,3, 8,0,1,3, 8,8,0,3, 8,8,1,3, 0,1,0,2, 0,1,1,2, \r
+ 0,1,2,2, 0,1,3,2, 0,1,4,2, 0,1,5,2, 0,1,6,2, 0,1,7,2, 0,1,8,2, 0,1,9,2, 0,9,0,2, \r
+ 0,9,1,2, 8,1,0,3, 8,1,1,3, 8,9,0,3, 8,9,1,3, 0,2,0,2, 0,2,1,2, 0,2,2,2, 0,2,3,2, \r
+ 0,2,4,2, 0,2,5,2, 0,2,6,2, 0,2,7,2, 0,2,8,2, 0,2,9,2, 0,8,2,2, 0,8,3,2, 8,2,0,3, \r
+ 8,2,1,3, 8,0,8,3, 8,0,9,3, 0,3,0,2, 0,3,1,2, 0,3,2,2, 0,3,3,2, 0,3,4,2, 0,3,5,2, \r
+ 0,3,6,2, 0,3,7,2, 0,3,8,2, 0,3,9,2, 0,9,2,2, 0,9,3,2, 8,3,0,3, 8,3,1,3, 8,1,8,3, \r
+ 8,1,9,3, 0,4,0,2, 0,4,1,2, 0,4,2,2, 0,4,3,2, 0,4,4,2, 0,4,5,2, 0,4,6,2, 0,4,7,2, \r
+ 0,4,8,2, 0,4,9,2, 0,8,4,2, 0,8,5,2, 8,4,0,3, 8,4,1,3, 0,8,8,2, 0,8,9,2, 0,5,0,2, \r
+ 0,5,1,2, 0,5,2,2, 0,5,3,2, 0,5,4,2, 0,5,5,2, 0,5,6,2, 0,5,7,2, 0,5,8,2, 0,5,9,2, \r
+ 0,9,4,2, 0,9,5,2, 8,5,0,3, 8,5,1,3, 0,9,8,2, 0,9,9,2, 0,6,0,2, 0,6,1,2, 0,6,2,2, \r
+ 0,6,3,2, 0,6,4,2, 0,6,5,2, 0,6,6,2, 0,6,7,2, 0,6,8,2, 0,6,9,2, 0,8,6,2, 0,8,7,2, \r
+ 8,6,0,3, 8,6,1,3, 8,8,8,3, 8,8,9,3, 0,7,0,2, 0,7,1,2, 0,7,2,2, 0,7,3,2, 0,7,4,2, \r
+ 0,7,5,2, 0,7,6,2, 0,7,7,2, 0,7,8,2, 0,7,9,2, 0,9,6,2, 0,9,7,2, 8,7,0,3, 8,7,1,3, \r
+ 8,9,8,3, 8,9,9,3, 1,0,0,3, 1,0,1,3, 1,0,2,3, 1,0,3,3, 1,0,4,3, 1,0,5,3, 1,0,6,3, \r
+ 1,0,7,3, 1,0,8,3, 1,0,9,3, 1,8,0,3, 1,8,1,3, 9,0,0,3, 9,0,1,3, 9,8,0,3, 9,8,1,3, \r
+ 1,1,0,3, 1,1,1,3, 1,1,2,3, 1,1,3,3, 1,1,4,3, 1,1,5,3, 1,1,6,3, 1,1,7,3, 1,1,8,3, \r
+ 1,1,9,3, 1,9,0,3, 1,9,1,3, 9,1,0,3, 9,1,1,3, 9,9,0,3, 9,9,1,3, 1,2,0,3, 1,2,1,3, \r
+ 1,2,2,3, 1,2,3,3, 1,2,4,3, 1,2,5,3, 1,2,6,3, 1,2,7,3, 1,2,8,3, 1,2,9,3, 1,8,2,3, \r
+ 1,8,3,3, 9,2,0,3, 9,2,1,3, 9,0,8,3, 9,0,9,3, 1,3,0,3, 1,3,1,3, 1,3,2,3, 1,3,3,3, \r
+ 1,3,4,3, 1,3,5,3, 1,3,6,3, 1,3,7,3, 1,3,8,3, 1,3,9,3, 1,9,2,3, 1,9,3,3, 9,3,0,3, \r
+ 9,3,1,3, 9,1,8,3, 9,1,9,3, 1,4,0,3, 1,4,1,3, 1,4,2,3, 1,4,3,3, 1,4,4,3, 1,4,5,3, \r
+ 1,4,6,3, 1,4,7,3, 1,4,8,3, 1,4,9,3, 1,8,4,3, 1,8,5,3, 9,4,0,3, 9,4,1,3, 1,8,8,3, \r
+ 1,8,9,3, 1,5,0,3, 1,5,1,3, 1,5,2,3, 1,5,3,3, 1,5,4,3, 1,5,5,3, 1,5,6,3, 1,5,7,3, \r
+ 1,5,8,3, 1,5,9,3, 1,9,4,3, 1,9,5,3, 9,5,0,3, 9,5,1,3, 1,9,8,3, 1,9,9,3, 1,6,0,3, \r
+ 1,6,1,3, 1,6,2,3, 1,6,3,3, 1,6,4,3, 1,6,5,3, 1,6,6,3, 1,6,7,3, 1,6,8,3, 1,6,9,3, \r
+ 1,8,6,3, 1,8,7,3, 9,6,0,3, 9,6,1,3, 9,8,8,3, 9,8,9,3, 1,7,0,3, 1,7,1,3, 1,7,2,3, \r
+ 1,7,3,3, 1,7,4,3, 1,7,5,3, 1,7,6,3, 1,7,7,3, 1,7,8,3, 1,7,9,3, 1,9,6,3, 1,9,7,3, \r
+ 9,7,0,3, 9,7,1,3, 9,9,8,3, 9,9,9,3, 2,0,0,3, 2,0,1,3, 2,0,2,3, 2,0,3,3, 2,0,4,3, \r
+ 2,0,5,3, 2,0,6,3, 2,0,7,3, 2,0,8,3, 2,0,9,3, 2,8,0,3, 2,8,1,3, 8,0,2,3, 8,0,3,3, \r
+ 8,8,2,3, 8,8,3,3, 2,1,0,3, 2,1,1,3, 2,1,2,3, 2,1,3,3, 2,1,4,3, 2,1,5,3, 2,1,6,3, \r
+ 2,1,7,3, 2,1,8,3, 2,1,9,3, 2,9,0,3, 2,9,1,3, 8,1,2,3, 8,1,3,3, 8,9,2,3, 8,9,3,3, \r
+ 2,2,0,3, 2,2,1,3, 2,2,2,3, 2,2,3,3, 2,2,4,3, 2,2,5,3, 2,2,6,3, 2,2,7,3, 2,2,8,3, \r
+ 2,2,9,3, 2,8,2,3, 2,8,3,3, 8,2,2,3, 8,2,3,3, 8,2,8,3, 8,2,9,3, 2,3,0,3, 2,3,1,3, \r
+ 2,3,2,3, 2,3,3,3, 2,3,4,3, 2,3,5,3, 2,3,6,3, 2,3,7,3, 2,3,8,3, 2,3,9,3, 2,9,2,3, \r
+ 2,9,3,3, 8,3,2,3, 8,3,3,3, 8,3,8,3, 8,3,9,3, 2,4,0,3, 2,4,1,3, 2,4,2,3, 2,4,3,3, \r
+ 2,4,4,3, 2,4,5,3, 2,4,6,3, 2,4,7,3, 2,4,8,3, 2,4,9,3, 2,8,4,3, 2,8,5,3, 8,4,2,3, \r
+ 8,4,3,3, 2,8,8,3, 2,8,9,3, 2,5,0,3, 2,5,1,3, 2,5,2,3, 2,5,3,3, 2,5,4,3, 2,5,5,3, \r
+ 2,5,6,3, 2,5,7,3, 2,5,8,3, 2,5,9,3, 2,9,4,3, 2,9,5,3, 8,5,2,3, 8,5,3,3, 2,9,8,3, \r
+ 2,9,9,3, 2,6,0,3, 2,6,1,3, 2,6,2,3, 2,6,3,3, 2,6,4,3, 2,6,5,3, 2,6,6,3, 2,6,7,3, \r
+ 2,6,8,3, 2,6,9,3, 2,8,6,3, 2,8,7,3, 8,6,2,3, 8,6,3,3, 8,8,8,3, 8,8,9,3, 2,7,0,3, \r
+ 2,7,1,3, 2,7,2,3, 2,7,3,3, 2,7,4,3, 2,7,5,3, 2,7,6,3, 2,7,7,3, 2,7,8,3, 2,7,9,3, \r
+ 2,9,6,3, 2,9,7,3, 8,7,2,3, 8,7,3,3, 8,9,8,3, 8,9,9,3, 3,0,0,3, 3,0,1,3, 3,0,2,3, \r
+ 3,0,3,3, 3,0,4,3, 3,0,5,3, 3,0,6,3, 3,0,7,3, 3,0,8,3, 3,0,9,3, 3,8,0,3, 3,8,1,3, \r
+ 9,0,2,3, 9,0,3,3, 9,8,2,3, 9,8,3,3, 3,1,0,3, 3,1,1,3, 3,1,2,3, 3,1,3,3, 3,1,4,3, \r
+ 3,1,5,3, 3,1,6,3, 3,1,7,3, 3,1,8,3, 3,1,9,3, 3,9,0,3, 3,9,1,3, 9,1,2,3, 9,1,3,3, \r
+ 9,9,2,3, 9,9,3,3, 3,2,0,3, 3,2,1,3, 3,2,2,3, 3,2,3,3, 3,2,4,3, 3,2,5,3, 3,2,6,3, \r
+ 3,2,7,3, 3,2,8,3, 3,2,9,3, 3,8,2,3, 3,8,3,3, 9,2,2,3, 9,2,3,3, 9,2,8,3, 9,2,9,3, \r
+ 3,3,0,3, 3,3,1,3, 3,3,2,3, 3,3,3,3, 3,3,4,3, 3,3,5,3, 3,3,6,3, 3,3,7,3, 3,3,8,3, \r
+ 3,3,9,3, 3,9,2,3, 3,9,3,3, 9,3,2,3, 9,3,3,3, 9,3,8,3, 9,3,9,3, 3,4,0,3, 3,4,1,3, \r
+ 3,4,2,3, 3,4,3,3, 3,4,4,3, 3,4,5,3, 3,4,6,3, 3,4,7,3, 3,4,8,3, 3,4,9,3, 3,8,4,3, \r
+ 3,8,5,3, 9,4,2,3, 9,4,3,3, 3,8,8,3, 3,8,9,3, 3,5,0,3, 3,5,1,3, 3,5,2,3, 3,5,3,3, \r
+ 3,5,4,3, 3,5,5,3, 3,5,6,3, 3,5,7,3, 3,5,8,3, 3,5,9,3, 3,9,4,3, 3,9,5,3, 9,5,2,3, \r
+ 9,5,3,3, 3,9,8,3, 3,9,9,3, 3,6,0,3, 3,6,1,3, 3,6,2,3, 3,6,3,3, 3,6,4,3, 3,6,5,3, \r
+ 3,6,6,3, 3,6,7,3, 3,6,8,3, 3,6,9,3, 3,8,6,3, 3,8,7,3, 9,6,2,3, 9,6,3,3, 9,8,8,3, \r
+ 9,8,9,3, 3,7,0,3, 3,7,1,3, 3,7,2,3, 3,7,3,3, 3,7,4,3, 3,7,5,3, 3,7,6,3, 3,7,7,3, \r
+ 3,7,8,3, 3,7,9,3, 3,9,6,3, 3,9,7,3, 9,7,2,3, 9,7,3,3, 9,9,8,3, 9,9,9,3, 4,0,0,3, \r
+ 4,0,1,3, 4,0,2,3, 4,0,3,3, 4,0,4,3, 4,0,5,3, 4,0,6,3, 4,0,7,3, 4,0,8,3, 4,0,9,3, \r
+ 4,8,0,3, 4,8,1,3, 8,0,4,3, 8,0,5,3, 8,8,4,3, 8,8,5,3, 4,1,0,3, 4,1,1,3, 4,1,2,3, \r
+ 4,1,3,3, 4,1,4,3, 4,1,5,3, 4,1,6,3, 4,1,7,3, 4,1,8,3, 4,1,9,3, 4,9,0,3, 4,9,1,3, \r
+ 8,1,4,3, 8,1,5,3, 8,9,4,3, 8,9,5,3, 4,2,0,3, 4,2,1,3, 4,2,2,3, 4,2,3,3, 4,2,4,3, \r
+ 4,2,5,3, 4,2,6,3, 4,2,7,3, 4,2,8,3, 4,2,9,3, 4,8,2,3, 4,8,3,3, 8,2,4,3, 8,2,5,3, \r
+ 8,4,8,3, 8,4,9,3, 4,3,0,3, 4,3,1,3, 4,3,2,3, 4,3,3,3, 4,3,4,3, 4,3,5,3, 4,3,6,3, \r
+ 4,3,7,3, 4,3,8,3, 4,3,9,3, 4,9,2,3, 4,9,3,3, 8,3,4,3, 8,3,5,3, 8,5,8,3, 8,5,9,3, \r
+ 4,4,0,3, 4,4,1,3, 4,4,2,3, 4,4,3,3, 4,4,4,3, 4,4,5,3, 4,4,6,3, 4,4,7,3, 4,4,8,3, \r
+ 4,4,9,3, 4,8,4,3, 4,8,5,3, 8,4,4,3, 8,4,5,3, 4,8,8,3, 4,8,9,3, 4,5,0,3, 4,5,1,3, \r
+ 4,5,2,3, 4,5,3,3, 4,5,4,3, 4,5,5,3, 4,5,6,3, 4,5,7,3, 4,5,8,3, 4,5,9,3, 4,9,4,3, \r
+ 4,9,5,3, 8,5,4,3, 8,5,5,3, 4,9,8,3, 4,9,9,3, 4,6,0,3, 4,6,1,3, 4,6,2,3, 4,6,3,3, \r
+ 4,6,4,3, 4,6,5,3, 4,6,6,3, 4,6,7,3, 4,6,8,3, 4,6,9,3, 4,8,6,3, 4,8,7,3, 8,6,4,3, \r
+ 8,6,5,3, 8,8,8,3, 8,8,9,3, 4,7,0,3, 4,7,1,3, 4,7,2,3, 4,7,3,3, 4,7,4,3, 4,7,5,3, \r
+ 4,7,6,3, 4,7,7,3, 4,7,8,3, 4,7,9,3, 4,9,6,3, 4,9,7,3, 8,7,4,3, 8,7,5,3, 8,9,8,3, \r
+ 8,9,9,3, 5,0,0,3, 5,0,1,3, 5,0,2,3, 5,0,3,3, 5,0,4,3, 5,0,5,3, 5,0,6,3, 5,0,7,3, \r
+ 5,0,8,3, 5,0,9,3, 5,8,0,3, 5,8,1,3, 9,0,4,3, 9,0,5,3, 9,8,4,3, 9,8,5,3, 5,1,0,3, \r
+ 5,1,1,3, 5,1,2,3, 5,1,3,3, 5,1,4,3, 5,1,5,3, 5,1,6,3, 5,1,7,3, 5,1,8,3, 5,1,9,3, \r
+ 5,9,0,3, 5,9,1,3, 9,1,4,3, 9,1,5,3, 9,9,4,3, 9,9,5,3, 5,2,0,3, 5,2,1,3, 5,2,2,3, \r
+ 5,2,3,3, 5,2,4,3, 5,2,5,3, 5,2,6,3, 5,2,7,3, 5,2,8,3, 5,2,9,3, 5,8,2,3, 5,8,3,3, \r
+ 9,2,4,3, 9,2,5,3, 9,4,8,3, 9,4,9,3, 5,3,0,3, 5,3,1,3, 5,3,2,3, 5,3,3,3, 5,3,4,3, \r
+ 5,3,5,3, 5,3,6,3, 5,3,7,3, 5,3,8,3, 5,3,9,3, 5,9,2,3, 5,9,3,3, 9,3,4,3, 9,3,5,3, \r
+ 9,5,8,3, 9,5,9,3, 5,4,0,3, 5,4,1,3, 5,4,2,3, 5,4,3,3, 5,4,4,3, 5,4,5,3, 5,4,6,3, \r
+ 5,4,7,3, 5,4,8,3, 5,4,9,3, 5,8,4,3, 5,8,5,3, 9,4,4,3, 9,4,5,3, 5,8,8,3, 5,8,9,3, \r
+ 5,5,0,3, 5,5,1,3, 5,5,2,3, 5,5,3,3, 5,5,4,3, 5,5,5,3, 5,5,6,3, 5,5,7,3, 5,5,8,3, \r
+ 5,5,9,3, 5,9,4,3, 5,9,5,3, 9,5,4,3, 9,5,5,3, 5,9,8,3, 5,9,9,3, 5,6,0,3, 5,6,1,3, \r
+ 5,6,2,3, 5,6,3,3, 5,6,4,3, 5,6,5,3, 5,6,6,3, 5,6,7,3, 5,6,8,3, 5,6,9,3, 5,8,6,3, \r
+ 5,8,7,3, 9,6,4,3, 9,6,5,3, 9,8,8,3, 9,8,9,3, 5,7,0,3, 5,7,1,3, 5,7,2,3, 5,7,3,3, \r
+ 5,7,4,3, 5,7,5,3, 5,7,6,3, 5,7,7,3, 5,7,8,3, 5,7,9,3, 5,9,6,3, 5,9,7,3, 9,7,4,3, \r
+ 9,7,5,3, 9,9,8,3, 9,9,9,3, 6,0,0,3, 6,0,1,3, 6,0,2,3, 6,0,3,3, 6,0,4,3, 6,0,5,3, \r
+ 6,0,6,3, 6,0,7,3, 6,0,8,3, 6,0,9,3, 6,8,0,3, 6,8,1,3, 8,0,6,3, 8,0,7,3, 8,8,6,3, \r
+ 8,8,7,3, 6,1,0,3, 6,1,1,3, 6,1,2,3, 6,1,3,3, 6,1,4,3, 6,1,5,3, 6,1,6,3, 6,1,7,3, \r
+ 6,1,8,3, 6,1,9,3, 6,9,0,3, 6,9,1,3, 8,1,6,3, 8,1,7,3, 8,9,6,3, 8,9,7,3, 6,2,0,3, \r
+ 6,2,1,3, 6,2,2,3, 6,2,3,3, 6,2,4,3, 6,2,5,3, 6,2,6,3, 6,2,7,3, 6,2,8,3, 6,2,9,3, \r
+ 6,8,2,3, 6,8,3,3, 8,2,6,3, 8,2,7,3, 8,6,8,3, 8,6,9,3, 6,3,0,3, 6,3,1,3, 6,3,2,3, \r
+ 6,3,3,3, 6,3,4,3, 6,3,5,3, 6,3,6,3, 6,3,7,3, 6,3,8,3, 6,3,9,3, 6,9,2,3, 6,9,3,3, \r
+ 8,3,6,3, 8,3,7,3, 8,7,8,3, 8,7,9,3, 6,4,0,3, 6,4,1,3, 6,4,2,3, 6,4,3,3, 6,4,4,3, \r
+ 6,4,5,3, 6,4,6,3, 6,4,7,3, 6,4,8,3, 6,4,9,3, 6,8,4,3, 6,8,5,3, 8,4,6,3, 8,4,7,3, \r
+ 6,8,8,3, 6,8,9,3, 6,5,0,3, 6,5,1,3, 6,5,2,3, 6,5,3,3, 6,5,4,3, 6,5,5,3, 6,5,6,3, \r
+ 6,5,7,3, 6,5,8,3, 6,5,9,3, 6,9,4,3, 6,9,5,3, 8,5,6,3, 8,5,7,3, 6,9,8,3, 6,9,9,3, \r
+ 6,6,0,3, 6,6,1,3, 6,6,2,3, 6,6,3,3, 6,6,4,3, 6,6,5,3, 6,6,6,3, 6,6,7,3, 6,6,8,3, \r
+ 6,6,9,3, 6,8,6,3, 6,8,7,3, 8,6,6,3, 8,6,7,3, 8,8,8,3, 8,8,9,3, 6,7,0,3, 6,7,1,3, \r
+ 6,7,2,3, 6,7,3,3, 6,7,4,3, 6,7,5,3, 6,7,6,3, 6,7,7,3, 6,7,8,3, 6,7,9,3, 6,9,6,3, \r
+ 6,9,7,3, 8,7,6,3, 8,7,7,3, 8,9,8,3, 8,9,9,3, 7,0,0,3, 7,0,1,3, 7,0,2,3, 7,0,3,3, \r
+ 7,0,4,3, 7,0,5,3, 7,0,6,3, 7,0,7,3, 7,0,8,3, 7,0,9,3, 7,8,0,3, 7,8,1,3, 9,0,6,3, \r
+ 9,0,7,3, 9,8,6,3, 9,8,7,3, 7,1,0,3, 7,1,1,3, 7,1,2,3, 7,1,3,3, 7,1,4,3, 7,1,5,3, \r
+ 7,1,6,3, 7,1,7,3, 7,1,8,3, 7,1,9,3, 7,9,0,3, 7,9,1,3, 9,1,6,3, 9,1,7,3, 9,9,6,3, \r
+ 9,9,7,3, 7,2,0,3, 7,2,1,3, 7,2,2,3, 7,2,3,3, 7,2,4,3, 7,2,5,3, 7,2,6,3, 7,2,7,3, \r
+ 7,2,8,3, 7,2,9,3, 7,8,2,3, 7,8,3,3, 9,2,6,3, 9,2,7,3, 9,6,8,3, 9,6,9,3, 7,3,0,3, \r
+ 7,3,1,3, 7,3,2,3, 7,3,3,3, 7,3,4,3, 7,3,5,3, 7,3,6,3, 7,3,7,3, 7,3,8,3, 7,3,9,3, \r
+ 7,9,2,3, 7,9,3,3, 9,3,6,3, 9,3,7,3, 9,7,8,3, 9,7,9,3, 7,4,0,3, 7,4,1,3, 7,4,2,3, \r
+ 7,4,3,3, 7,4,4,3, 7,4,5,3, 7,4,6,3, 7,4,7,3, 7,4,8,3, 7,4,9,3, 7,8,4,3, 7,8,5,3, \r
+ 9,4,6,3, 9,4,7,3, 7,8,8,3, 7,8,9,3, 7,5,0,3, 7,5,1,3, 7,5,2,3, 7,5,3,3, 7,5,4,3, \r
+ 7,5,5,3, 7,5,6,3, 7,5,7,3, 7,5,8,3, 7,5,9,3, 7,9,4,3, 7,9,5,3, 9,5,6,3, 9,5,7,3, \r
+ 7,9,8,3, 7,9,9,3, 7,6,0,3, 7,6,1,3, 7,6,2,3, 7,6,3,3, 7,6,4,3, 7,6,5,3, 7,6,6,3, \r
+ 7,6,7,3, 7,6,8,3, 7,6,9,3, 7,8,6,3, 7,8,7,3, 9,6,6,3, 9,6,7,3, 9,8,8,3, 9,8,9,3, \r
+ 7,7,0,3, 7,7,1,3, 7,7,2,3, 7,7,3,3, 7,7,4,3, 7,7,5,3, 7,7,6,3, 7,7,7,3, 7,7,8,3, \r
+ 7,7,9,3, 7,9,6,3, 7,9,7,3, 9,7,6,3, 9,7,7,3, 9,9,8,3, 9,9,9,3};\r
+#endif\r
+ \r
+#if defined(DEC_BIN2BCD8) && DEC_BIN2BCD8==1 && !defined(DECBIN2BCD8)\r
+#define DECBIN2BCD8\r
+ \r
+const uint8_t BIN2BCD8[4000]={\r
+ 0,0,0,0, 0,0,1,1, 0,0,2,1, 0,0,3,1, 0,0,4,1, 0,0,5,1, 0,0,6,1, 0,0,7,1, 0,0,8,1, \r
+ 0,0,9,1, 0,1,0,2, 0,1,1,2, 0,1,2,2, 0,1,3,2, 0,1,4,2, 0,1,5,2, 0,1,6,2, 0,1,7,2, \r
+ 0,1,8,2, 0,1,9,2, 0,2,0,2, 0,2,1,2, 0,2,2,2, 0,2,3,2, 0,2,4,2, 0,2,5,2, 0,2,6,2, \r
+ 0,2,7,2, 0,2,8,2, 0,2,9,2, 0,3,0,2, 0,3,1,2, 0,3,2,2, 0,3,3,2, 0,3,4,2, 0,3,5,2, \r
+ 0,3,6,2, 0,3,7,2, 0,3,8,2, 0,3,9,2, 0,4,0,2, 0,4,1,2, 0,4,2,2, 0,4,3,2, 0,4,4,2, \r
+ 0,4,5,2, 0,4,6,2, 0,4,7,2, 0,4,8,2, 0,4,9,2, 0,5,0,2, 0,5,1,2, 0,5,2,2, 0,5,3,2, \r
+ 0,5,4,2, 0,5,5,2, 0,5,6,2, 0,5,7,2, 0,5,8,2, 0,5,9,2, 0,6,0,2, 0,6,1,2, 0,6,2,2, \r
+ 0,6,3,2, 0,6,4,2, 0,6,5,2, 0,6,6,2, 0,6,7,2, 0,6,8,2, 0,6,9,2, 0,7,0,2, 0,7,1,2, \r
+ 0,7,2,2, 0,7,3,2, 0,7,4,2, 0,7,5,2, 0,7,6,2, 0,7,7,2, 0,7,8,2, 0,7,9,2, 0,8,0,2, \r
+ 0,8,1,2, 0,8,2,2, 0,8,3,2, 0,8,4,2, 0,8,5,2, 0,8,6,2, 0,8,7,2, 0,8,8,2, 0,8,9,2, \r
+ 0,9,0,2, 0,9,1,2, 0,9,2,2, 0,9,3,2, 0,9,4,2, 0,9,5,2, 0,9,6,2, 0,9,7,2, 0,9,8,2, \r
+ 0,9,9,2, 1,0,0,3, 1,0,1,3, 1,0,2,3, 1,0,3,3, 1,0,4,3, 1,0,5,3, 1,0,6,3, 1,0,7,3, \r
+ 1,0,8,3, 1,0,9,3, 1,1,0,3, 1,1,1,3, 1,1,2,3, 1,1,3,3, 1,1,4,3, 1,1,5,3, 1,1,6,3, \r
+ 1,1,7,3, 1,1,8,3, 1,1,9,3, 1,2,0,3, 1,2,1,3, 1,2,2,3, 1,2,3,3, 1,2,4,3, 1,2,5,3, \r
+ 1,2,6,3, 1,2,7,3, 1,2,8,3, 1,2,9,3, 1,3,0,3, 1,3,1,3, 1,3,2,3, 1,3,3,3, 1,3,4,3, \r
+ 1,3,5,3, 1,3,6,3, 1,3,7,3, 1,3,8,3, 1,3,9,3, 1,4,0,3, 1,4,1,3, 1,4,2,3, 1,4,3,3, \r
+ 1,4,4,3, 1,4,5,3, 1,4,6,3, 1,4,7,3, 1,4,8,3, 1,4,9,3, 1,5,0,3, 1,5,1,3, 1,5,2,3, \r
+ 1,5,3,3, 1,5,4,3, 1,5,5,3, 1,5,6,3, 1,5,7,3, 1,5,8,3, 1,5,9,3, 1,6,0,3, 1,6,1,3, \r
+ 1,6,2,3, 1,6,3,3, 1,6,4,3, 1,6,5,3, 1,6,6,3, 1,6,7,3, 1,6,8,3, 1,6,9,3, 1,7,0,3, \r
+ 1,7,1,3, 1,7,2,3, 1,7,3,3, 1,7,4,3, 1,7,5,3, 1,7,6,3, 1,7,7,3, 1,7,8,3, 1,7,9,3, \r
+ 1,8,0,3, 1,8,1,3, 1,8,2,3, 1,8,3,3, 1,8,4,3, 1,8,5,3, 1,8,6,3, 1,8,7,3, 1,8,8,3, \r
+ 1,8,9,3, 1,9,0,3, 1,9,1,3, 1,9,2,3, 1,9,3,3, 1,9,4,3, 1,9,5,3, 1,9,6,3, 1,9,7,3, \r
+ 1,9,8,3, 1,9,9,3, 2,0,0,3, 2,0,1,3, 2,0,2,3, 2,0,3,3, 2,0,4,3, 2,0,5,3, 2,0,6,3, \r
+ 2,0,7,3, 2,0,8,3, 2,0,9,3, 2,1,0,3, 2,1,1,3, 2,1,2,3, 2,1,3,3, 2,1,4,3, 2,1,5,3, \r
+ 2,1,6,3, 2,1,7,3, 2,1,8,3, 2,1,9,3, 2,2,0,3, 2,2,1,3, 2,2,2,3, 2,2,3,3, 2,2,4,3, \r
+ 2,2,5,3, 2,2,6,3, 2,2,7,3, 2,2,8,3, 2,2,9,3, 2,3,0,3, 2,3,1,3, 2,3,2,3, 2,3,3,3, \r
+ 2,3,4,3, 2,3,5,3, 2,3,6,3, 2,3,7,3, 2,3,8,3, 2,3,9,3, 2,4,0,3, 2,4,1,3, 2,4,2,3, \r
+ 2,4,3,3, 2,4,4,3, 2,4,5,3, 2,4,6,3, 2,4,7,3, 2,4,8,3, 2,4,9,3, 2,5,0,3, 2,5,1,3, \r
+ 2,5,2,3, 2,5,3,3, 2,5,4,3, 2,5,5,3, 2,5,6,3, 2,5,7,3, 2,5,8,3, 2,5,9,3, 2,6,0,3, \r
+ 2,6,1,3, 2,6,2,3, 2,6,3,3, 2,6,4,3, 2,6,5,3, 2,6,6,3, 2,6,7,3, 2,6,8,3, 2,6,9,3, \r
+ 2,7,0,3, 2,7,1,3, 2,7,2,3, 2,7,3,3, 2,7,4,3, 2,7,5,3, 2,7,6,3, 2,7,7,3, 2,7,8,3, \r
+ 2,7,9,3, 2,8,0,3, 2,8,1,3, 2,8,2,3, 2,8,3,3, 2,8,4,3, 2,8,5,3, 2,8,6,3, 2,8,7,3, \r
+ 2,8,8,3, 2,8,9,3, 2,9,0,3, 2,9,1,3, 2,9,2,3, 2,9,3,3, 2,9,4,3, 2,9,5,3, 2,9,6,3, \r
+ 2,9,7,3, 2,9,8,3, 2,9,9,3, 3,0,0,3, 3,0,1,3, 3,0,2,3, 3,0,3,3, 3,0,4,3, 3,0,5,3, \r
+ 3,0,6,3, 3,0,7,3, 3,0,8,3, 3,0,9,3, 3,1,0,3, 3,1,1,3, 3,1,2,3, 3,1,3,3, 3,1,4,3, \r
+ 3,1,5,3, 3,1,6,3, 3,1,7,3, 3,1,8,3, 3,1,9,3, 3,2,0,3, 3,2,1,3, 3,2,2,3, 3,2,3,3, \r
+ 3,2,4,3, 3,2,5,3, 3,2,6,3, 3,2,7,3, 3,2,8,3, 3,2,9,3, 3,3,0,3, 3,3,1,3, 3,3,2,3, \r
+ 3,3,3,3, 3,3,4,3, 3,3,5,3, 3,3,6,3, 3,3,7,3, 3,3,8,3, 3,3,9,3, 3,4,0,3, 3,4,1,3, \r
+ 3,4,2,3, 3,4,3,3, 3,4,4,3, 3,4,5,3, 3,4,6,3, 3,4,7,3, 3,4,8,3, 3,4,9,3, 3,5,0,3, \r
+ 3,5,1,3, 3,5,2,3, 3,5,3,3, 3,5,4,3, 3,5,5,3, 3,5,6,3, 3,5,7,3, 3,5,8,3, 3,5,9,3, \r
+ 3,6,0,3, 3,6,1,3, 3,6,2,3, 3,6,3,3, 3,6,4,3, 3,6,5,3, 3,6,6,3, 3,6,7,3, 3,6,8,3, \r
+ 3,6,9,3, 3,7,0,3, 3,7,1,3, 3,7,2,3, 3,7,3,3, 3,7,4,3, 3,7,5,3, 3,7,6,3, 3,7,7,3, \r
+ 3,7,8,3, 3,7,9,3, 3,8,0,3, 3,8,1,3, 3,8,2,3, 3,8,3,3, 3,8,4,3, 3,8,5,3, 3,8,6,3, \r
+ 3,8,7,3, 3,8,8,3, 3,8,9,3, 3,9,0,3, 3,9,1,3, 3,9,2,3, 3,9,3,3, 3,9,4,3, 3,9,5,3, \r
+ 3,9,6,3, 3,9,7,3, 3,9,8,3, 3,9,9,3, 4,0,0,3, 4,0,1,3, 4,0,2,3, 4,0,3,3, 4,0,4,3, \r
+ 4,0,5,3, 4,0,6,3, 4,0,7,3, 4,0,8,3, 4,0,9,3, 4,1,0,3, 4,1,1,3, 4,1,2,3, 4,1,3,3, \r
+ 4,1,4,3, 4,1,5,3, 4,1,6,3, 4,1,7,3, 4,1,8,3, 4,1,9,3, 4,2,0,3, 4,2,1,3, 4,2,2,3, \r
+ 4,2,3,3, 4,2,4,3, 4,2,5,3, 4,2,6,3, 4,2,7,3, 4,2,8,3, 4,2,9,3, 4,3,0,3, 4,3,1,3, \r
+ 4,3,2,3, 4,3,3,3, 4,3,4,3, 4,3,5,3, 4,3,6,3, 4,3,7,3, 4,3,8,3, 4,3,9,3, 4,4,0,3, \r
+ 4,4,1,3, 4,4,2,3, 4,4,3,3, 4,4,4,3, 4,4,5,3, 4,4,6,3, 4,4,7,3, 4,4,8,3, 4,4,9,3, \r
+ 4,5,0,3, 4,5,1,3, 4,5,2,3, 4,5,3,3, 4,5,4,3, 4,5,5,3, 4,5,6,3, 4,5,7,3, 4,5,8,3, \r
+ 4,5,9,3, 4,6,0,3, 4,6,1,3, 4,6,2,3, 4,6,3,3, 4,6,4,3, 4,6,5,3, 4,6,6,3, 4,6,7,3, \r
+ 4,6,8,3, 4,6,9,3, 4,7,0,3, 4,7,1,3, 4,7,2,3, 4,7,3,3, 4,7,4,3, 4,7,5,3, 4,7,6,3, \r
+ 4,7,7,3, 4,7,8,3, 4,7,9,3, 4,8,0,3, 4,8,1,3, 4,8,2,3, 4,8,3,3, 4,8,4,3, 4,8,5,3, \r
+ 4,8,6,3, 4,8,7,3, 4,8,8,3, 4,8,9,3, 4,9,0,3, 4,9,1,3, 4,9,2,3, 4,9,3,3, 4,9,4,3, \r
+ 4,9,5,3, 4,9,6,3, 4,9,7,3, 4,9,8,3, 4,9,9,3, 5,0,0,3, 5,0,1,3, 5,0,2,3, 5,0,3,3, \r
+ 5,0,4,3, 5,0,5,3, 5,0,6,3, 5,0,7,3, 5,0,8,3, 5,0,9,3, 5,1,0,3, 5,1,1,3, 5,1,2,3, \r
+ 5,1,3,3, 5,1,4,3, 5,1,5,3, 5,1,6,3, 5,1,7,3, 5,1,8,3, 5,1,9,3, 5,2,0,3, 5,2,1,3, \r
+ 5,2,2,3, 5,2,3,3, 5,2,4,3, 5,2,5,3, 5,2,6,3, 5,2,7,3, 5,2,8,3, 5,2,9,3, 5,3,0,3, \r
+ 5,3,1,3, 5,3,2,3, 5,3,3,3, 5,3,4,3, 5,3,5,3, 5,3,6,3, 5,3,7,3, 5,3,8,3, 5,3,9,3, \r
+ 5,4,0,3, 5,4,1,3, 5,4,2,3, 5,4,3,3, 5,4,4,3, 5,4,5,3, 5,4,6,3, 5,4,7,3, 5,4,8,3, \r
+ 5,4,9,3, 5,5,0,3, 5,5,1,3, 5,5,2,3, 5,5,3,3, 5,5,4,3, 5,5,5,3, 5,5,6,3, 5,5,7,3, \r
+ 5,5,8,3, 5,5,9,3, 5,6,0,3, 5,6,1,3, 5,6,2,3, 5,6,3,3, 5,6,4,3, 5,6,5,3, 5,6,6,3, \r
+ 5,6,7,3, 5,6,8,3, 5,6,9,3, 5,7,0,3, 5,7,1,3, 5,7,2,3, 5,7,3,3, 5,7,4,3, 5,7,5,3, \r
+ 5,7,6,3, 5,7,7,3, 5,7,8,3, 5,7,9,3, 5,8,0,3, 5,8,1,3, 5,8,2,3, 5,8,3,3, 5,8,4,3, \r
+ 5,8,5,3, 5,8,6,3, 5,8,7,3, 5,8,8,3, 5,8,9,3, 5,9,0,3, 5,9,1,3, 5,9,2,3, 5,9,3,3, \r
+ 5,9,4,3, 5,9,5,3, 5,9,6,3, 5,9,7,3, 5,9,8,3, 5,9,9,3, 6,0,0,3, 6,0,1,3, 6,0,2,3, \r
+ 6,0,3,3, 6,0,4,3, 6,0,5,3, 6,0,6,3, 6,0,7,3, 6,0,8,3, 6,0,9,3, 6,1,0,3, 6,1,1,3, \r
+ 6,1,2,3, 6,1,3,3, 6,1,4,3, 6,1,5,3, 6,1,6,3, 6,1,7,3, 6,1,8,3, 6,1,9,3, 6,2,0,3, \r
+ 6,2,1,3, 6,2,2,3, 6,2,3,3, 6,2,4,3, 6,2,5,3, 6,2,6,3, 6,2,7,3, 6,2,8,3, 6,2,9,3, \r
+ 6,3,0,3, 6,3,1,3, 6,3,2,3, 6,3,3,3, 6,3,4,3, 6,3,5,3, 6,3,6,3, 6,3,7,3, 6,3,8,3, \r
+ 6,3,9,3, 6,4,0,3, 6,4,1,3, 6,4,2,3, 6,4,3,3, 6,4,4,3, 6,4,5,3, 6,4,6,3, 6,4,7,3, \r
+ 6,4,8,3, 6,4,9,3, 6,5,0,3, 6,5,1,3, 6,5,2,3, 6,5,3,3, 6,5,4,3, 6,5,5,3, 6,5,6,3, \r
+ 6,5,7,3, 6,5,8,3, 6,5,9,3, 6,6,0,3, 6,6,1,3, 6,6,2,3, 6,6,3,3, 6,6,4,3, 6,6,5,3, \r
+ 6,6,6,3, 6,6,7,3, 6,6,8,3, 6,6,9,3, 6,7,0,3, 6,7,1,3, 6,7,2,3, 6,7,3,3, 6,7,4,3, \r
+ 6,7,5,3, 6,7,6,3, 6,7,7,3, 6,7,8,3, 6,7,9,3, 6,8,0,3, 6,8,1,3, 6,8,2,3, 6,8,3,3, \r
+ 6,8,4,3, 6,8,5,3, 6,8,6,3, 6,8,7,3, 6,8,8,3, 6,8,9,3, 6,9,0,3, 6,9,1,3, 6,9,2,3, \r
+ 6,9,3,3, 6,9,4,3, 6,9,5,3, 6,9,6,3, 6,9,7,3, 6,9,8,3, 6,9,9,3, 7,0,0,3, 7,0,1,3, \r
+ 7,0,2,3, 7,0,3,3, 7,0,4,3, 7,0,5,3, 7,0,6,3, 7,0,7,3, 7,0,8,3, 7,0,9,3, 7,1,0,3, \r
+ 7,1,1,3, 7,1,2,3, 7,1,3,3, 7,1,4,3, 7,1,5,3, 7,1,6,3, 7,1,7,3, 7,1,8,3, 7,1,9,3, \r
+ 7,2,0,3, 7,2,1,3, 7,2,2,3, 7,2,3,3, 7,2,4,3, 7,2,5,3, 7,2,6,3, 7,2,7,3, 7,2,8,3, \r
+ 7,2,9,3, 7,3,0,3, 7,3,1,3, 7,3,2,3, 7,3,3,3, 7,3,4,3, 7,3,5,3, 7,3,6,3, 7,3,7,3, \r
+ 7,3,8,3, 7,3,9,3, 7,4,0,3, 7,4,1,3, 7,4,2,3, 7,4,3,3, 7,4,4,3, 7,4,5,3, 7,4,6,3, \r
+ 7,4,7,3, 7,4,8,3, 7,4,9,3, 7,5,0,3, 7,5,1,3, 7,5,2,3, 7,5,3,3, 7,5,4,3, 7,5,5,3, \r
+ 7,5,6,3, 7,5,7,3, 7,5,8,3, 7,5,9,3, 7,6,0,3, 7,6,1,3, 7,6,2,3, 7,6,3,3, 7,6,4,3, \r
+ 7,6,5,3, 7,6,6,3, 7,6,7,3, 7,6,8,3, 7,6,9,3, 7,7,0,3, 7,7,1,3, 7,7,2,3, 7,7,3,3, \r
+ 7,7,4,3, 7,7,5,3, 7,7,6,3, 7,7,7,3, 7,7,8,3, 7,7,9,3, 7,8,0,3, 7,8,1,3, 7,8,2,3, \r
+ 7,8,3,3, 7,8,4,3, 7,8,5,3, 7,8,6,3, 7,8,7,3, 7,8,8,3, 7,8,9,3, 7,9,0,3, 7,9,1,3, \r
+ 7,9,2,3, 7,9,3,3, 7,9,4,3, 7,9,5,3, 7,9,6,3, 7,9,7,3, 7,9,8,3, 7,9,9,3, 8,0,0,3, \r
+ 8,0,1,3, 8,0,2,3, 8,0,3,3, 8,0,4,3, 8,0,5,3, 8,0,6,3, 8,0,7,3, 8,0,8,3, 8,0,9,3, \r
+ 8,1,0,3, 8,1,1,3, 8,1,2,3, 8,1,3,3, 8,1,4,3, 8,1,5,3, 8,1,6,3, 8,1,7,3, 8,1,8,3, \r
+ 8,1,9,3, 8,2,0,3, 8,2,1,3, 8,2,2,3, 8,2,3,3, 8,2,4,3, 8,2,5,3, 8,2,6,3, 8,2,7,3, \r
+ 8,2,8,3, 8,2,9,3, 8,3,0,3, 8,3,1,3, 8,3,2,3, 8,3,3,3, 8,3,4,3, 8,3,5,3, 8,3,6,3, \r
+ 8,3,7,3, 8,3,8,3, 8,3,9,3, 8,4,0,3, 8,4,1,3, 8,4,2,3, 8,4,3,3, 8,4,4,3, 8,4,5,3, \r
+ 8,4,6,3, 8,4,7,3, 8,4,8,3, 8,4,9,3, 8,5,0,3, 8,5,1,3, 8,5,2,3, 8,5,3,3, 8,5,4,3, \r
+ 8,5,5,3, 8,5,6,3, 8,5,7,3, 8,5,8,3, 8,5,9,3, 8,6,0,3, 8,6,1,3, 8,6,2,3, 8,6,3,3, \r
+ 8,6,4,3, 8,6,5,3, 8,6,6,3, 8,6,7,3, 8,6,8,3, 8,6,9,3, 8,7,0,3, 8,7,1,3, 8,7,2,3, \r
+ 8,7,3,3, 8,7,4,3, 8,7,5,3, 8,7,6,3, 8,7,7,3, 8,7,8,3, 8,7,9,3, 8,8,0,3, 8,8,1,3, \r
+ 8,8,2,3, 8,8,3,3, 8,8,4,3, 8,8,5,3, 8,8,6,3, 8,8,7,3, 8,8,8,3, 8,8,9,3, 8,9,0,3, \r
+ 8,9,1,3, 8,9,2,3, 8,9,3,3, 8,9,4,3, 8,9,5,3, 8,9,6,3, 8,9,7,3, 8,9,8,3, 8,9,9,3, \r
+ 9,0,0,3, 9,0,1,3, 9,0,2,3, 9,0,3,3, 9,0,4,3, 9,0,5,3, 9,0,6,3, 9,0,7,3, 9,0,8,3, \r
+ 9,0,9,3, 9,1,0,3, 9,1,1,3, 9,1,2,3, 9,1,3,3, 9,1,4,3, 9,1,5,3, 9,1,6,3, 9,1,7,3, \r
+ 9,1,8,3, 9,1,9,3, 9,2,0,3, 9,2,1,3, 9,2,2,3, 9,2,3,3, 9,2,4,3, 9,2,5,3, 9,2,6,3, \r
+ 9,2,7,3, 9,2,8,3, 9,2,9,3, 9,3,0,3, 9,3,1,3, 9,3,2,3, 9,3,3,3, 9,3,4,3, 9,3,5,3, \r
+ 9,3,6,3, 9,3,7,3, 9,3,8,3, 9,3,9,3, 9,4,0,3, 9,4,1,3, 9,4,2,3, 9,4,3,3, 9,4,4,3, \r
+ 9,4,5,3, 9,4,6,3, 9,4,7,3, 9,4,8,3, 9,4,9,3, 9,5,0,3, 9,5,1,3, 9,5,2,3, 9,5,3,3, \r
+ 9,5,4,3, 9,5,5,3, 9,5,6,3, 9,5,7,3, 9,5,8,3, 9,5,9,3, 9,6,0,3, 9,6,1,3, 9,6,2,3, \r
+ 9,6,3,3, 9,6,4,3, 9,6,5,3, 9,6,6,3, 9,6,7,3, 9,6,8,3, 9,6,9,3, 9,7,0,3, 9,7,1,3, \r
+ 9,7,2,3, 9,7,3,3, 9,7,4,3, 9,7,5,3, 9,7,6,3, 9,7,7,3, 9,7,8,3, 9,7,9,3, 9,8,0,3, \r
+ 9,8,1,3, 9,8,2,3, 9,8,3,3, 9,8,4,3, 9,8,5,3, 9,8,6,3, 9,8,7,3, 9,8,8,3, 9,8,9,3, \r
+ 9,9,0,3, 9,9,1,3, 9,9,2,3, 9,9,3,3, 9,9,4,3, 9,9,5,3, 9,9,6,3, 9,9,7,3, 9,9,8,3, \r
+ 9,9,9,3};\r
+#endif\r
+ \r
--- /dev/null
+/* ------------------------------------------------------------------ */\r
+/* decDouble.c -- decDouble operations module */\r
+/* ------------------------------------------------------------------ */\r
+/* Copyright (c) IBM Corporation, 2000, 2010. All rights reserved. */\r
+/* */\r
+/* This software is made available under the terms of the */\r
+/* ICU License -- ICU 1.8.1 and later. */\r
+/* */\r
+/* The description and User's Guide ("The decNumber C Library") for */\r
+/* this software is included in the package as decNumber.pdf. This */\r
+/* document is also available in HTML, together with specifications, */\r
+/* testcases, and Web links, on the General Decimal Arithmetic page. */\r
+/* */\r
+/* Please send comments, suggestions, and corrections to the author: */\r
+/* mfc@uk.ibm.com */\r
+/* Mike Cowlishaw, IBM Fellow */\r
+/* IBM UK, PO Box 31, Birmingham Road, Warwick CV34 5JL, UK */\r
+/* ------------------------------------------------------------------ */\r
+/* This module comprises decDouble operations (including conversions) */\r
+/* ------------------------------------------------------------------ */\r
+\r
+#include "decContext.h" // public includes\r
+#include "decDouble.h" // ..\r
+\r
+/* Constant mappings for shared code */\r
+#define DECPMAX DECDOUBLE_Pmax\r
+#define DECEMIN DECDOUBLE_Emin\r
+#define DECEMAX DECDOUBLE_Emax\r
+#define DECEMAXD DECDOUBLE_EmaxD\r
+#define DECBYTES DECDOUBLE_Bytes\r
+#define DECSTRING DECDOUBLE_String\r
+#define DECECONL DECDOUBLE_EconL\r
+#define DECBIAS DECDOUBLE_Bias\r
+#define DECLETS DECDOUBLE_Declets\r
+#define DECQTINY (-DECDOUBLE_Bias)\r
+// parameters of next-wider format\r
+#define DECWBYTES DECQUAD_Bytes\r
+#define DECWPMAX DECQUAD_Pmax\r
+#define DECWECONL DECQUAD_EconL\r
+#define DECWBIAS DECQUAD_Bias\r
+\r
+/* Type and function mappings for shared code */\r
+#define decFloat decDouble // Type name\r
+#define decFloatWider decQuad // Type name\r
+\r
+// Utilities and conversions (binary results, extractors, etc.)\r
+#define decFloatFromBCD decDoubleFromBCD\r
+#define decFloatFromInt32 decDoubleFromInt32\r
+#define decFloatFromPacked decDoubleFromPacked\r
+#define decFloatFromPackedChecked decDoubleFromPackedChecked\r
+#define decFloatFromString decDoubleFromString\r
+#define decFloatFromUInt32 decDoubleFromUInt32\r
+#define decFloatFromWider decDoubleFromWider\r
+#define decFloatGetCoefficient decDoubleGetCoefficient\r
+#define decFloatGetExponent decDoubleGetExponent\r
+#define decFloatSetCoefficient decDoubleSetCoefficient\r
+#define decFloatSetExponent decDoubleSetExponent\r
+#define decFloatShow decDoubleShow\r
+#define decFloatToBCD decDoubleToBCD\r
+#define decFloatToEngString decDoubleToEngString\r
+#define decFloatToInt32 decDoubleToInt32\r
+#define decFloatToInt32Exact decDoubleToInt32Exact\r
+#define decFloatToPacked decDoubleToPacked\r
+#define decFloatToString decDoubleToString\r
+#define decFloatToUInt32 decDoubleToUInt32\r
+#define decFloatToUInt32Exact decDoubleToUInt32Exact\r
+#define decFloatToWider decDoubleToWider\r
+#define decFloatZero decDoubleZero\r
+\r
+// Computational (result is a decFloat)\r
+#define decFloatAbs decDoubleAbs\r
+#define decFloatAdd decDoubleAdd\r
+#define decFloatAnd decDoubleAnd\r
+#define decFloatDivide decDoubleDivide\r
+#define decFloatDivideInteger decDoubleDivideInteger\r
+#define decFloatFMA decDoubleFMA\r
+#define decFloatInvert decDoubleInvert\r
+#define decFloatLogB decDoubleLogB\r
+#define decFloatMax decDoubleMax\r
+#define decFloatMaxMag decDoubleMaxMag\r
+#define decFloatMin decDoubleMin\r
+#define decFloatMinMag decDoubleMinMag\r
+#define decFloatMinus decDoubleMinus\r
+#define decFloatMultiply decDoubleMultiply\r
+#define decFloatNextMinus decDoubleNextMinus\r
+#define decFloatNextPlus decDoubleNextPlus\r
+#define decFloatNextToward decDoubleNextToward\r
+#define decFloatOr decDoubleOr\r
+#define decFloatPlus decDoublePlus\r
+#define decFloatQuantize decDoubleQuantize\r
+#define decFloatReduce decDoubleReduce\r
+#define decFloatRemainder decDoubleRemainder\r
+#define decFloatRemainderNear decDoubleRemainderNear\r
+#define decFloatRotate decDoubleRotate\r
+#define decFloatScaleB decDoubleScaleB\r
+#define decFloatShift decDoubleShift\r
+#define decFloatSubtract decDoubleSubtract\r
+#define decFloatToIntegralValue decDoubleToIntegralValue\r
+#define decFloatToIntegralExact decDoubleToIntegralExact\r
+#define decFloatXor decDoubleXor\r
+\r
+// Comparisons\r
+#define decFloatCompare decDoubleCompare\r
+#define decFloatCompareSignal decDoubleCompareSignal\r
+#define decFloatCompareTotal decDoubleCompareTotal\r
+#define decFloatCompareTotalMag decDoubleCompareTotalMag\r
+\r
+// Copies\r
+#define decFloatCanonical decDoubleCanonical\r
+#define decFloatCopy decDoubleCopy\r
+#define decFloatCopyAbs decDoubleCopyAbs\r
+#define decFloatCopyNegate decDoubleCopyNegate\r
+#define decFloatCopySign decDoubleCopySign\r
+\r
+// Non-computational\r
+#define decFloatClass decDoubleClass\r
+#define decFloatClassString decDoubleClassString\r
+#define decFloatDigits decDoubleDigits\r
+#define decFloatIsCanonical decDoubleIsCanonical\r
+#define decFloatIsFinite decDoubleIsFinite\r
+#define decFloatIsInfinite decDoubleIsInfinite\r
+#define decFloatIsInteger decDoubleIsInteger\r
+#define decFloatIsLogical decDoubleIsLogical\r
+#define decFloatIsNaN decDoubleIsNaN\r
+#define decFloatIsNegative decDoubleIsNegative\r
+#define decFloatIsNormal decDoubleIsNormal\r
+#define decFloatIsPositive decDoubleIsPositive\r
+#define decFloatIsSignaling decDoubleIsSignaling\r
+#define decFloatIsSignalling decDoubleIsSignalling\r
+#define decFloatIsSigned decDoubleIsSigned\r
+#define decFloatIsSubnormal decDoubleIsSubnormal\r
+#define decFloatIsZero decDoubleIsZero\r
+#define decFloatRadix decDoubleRadix\r
+#define decFloatSameQuantum decDoubleSameQuantum\r
+#define decFloatVersion decDoubleVersion\r
+\r
+#include "decNumberLocal.h" // local includes (need DECPMAX)\r
+#include "decCommon.inc" // non-arithmetic decFloat routines\r
+#include "decBasic.inc" // basic formats routines\r
+\r
--- /dev/null
+/* ------------------------------------------------------------------ */\r
+/* decDouble.h -- Decimal 64-bit format module header */\r
+/* ------------------------------------------------------------------ */\r
+/* Copyright (c) IBM Corporation, 2000, 2010. All rights reserved. */\r
+/* */\r
+/* This software is made available under the terms of the */\r
+/* ICU License -- ICU 1.8.1 and later. */\r
+/* */\r
+/* The description and User's Guide ("The decNumber C Library") for */\r
+/* this software is included in the package as decNumber.pdf. This */\r
+/* document is also available in HTML, together with specifications, */\r
+/* testcases, and Web links, on the General Decimal Arithmetic page. */\r
+/* */\r
+/* Please send comments, suggestions, and corrections to the author: */\r
+/* mfc@uk.ibm.com */\r
+/* Mike Cowlishaw, IBM Fellow */\r
+/* IBM UK, PO Box 31, Birmingham Road, Warwick CV34 5JL, UK */\r
+/* ------------------------------------------------------------------ */\r
+\r
+#if !defined(DECDOUBLE)\r
+ #define DECDOUBLE\r
+\r
+ #define DECDOUBLENAME "decimalDouble" /* Short name */\r
+ #define DECDOUBLETITLE "Decimal 64-bit datum" /* Verbose name */\r
+ #define DECDOUBLEAUTHOR "Mike Cowlishaw" /* Who to blame */\r
+\r
+ /* parameters for decDoubles */\r
+ #define DECDOUBLE_Bytes 8 /* length */\r
+ #define DECDOUBLE_Pmax 16 /* maximum precision (digits) */\r
+ #define DECDOUBLE_Emin -383 /* minimum adjusted exponent */\r
+ #define DECDOUBLE_Emax 384 /* maximum adjusted exponent */\r
+ #define DECDOUBLE_EmaxD 3 /* maximum exponent digits */\r
+ #define DECDOUBLE_Bias 398 /* bias for the exponent */\r
+ #define DECDOUBLE_String 25 /* maximum string length, +1 */\r
+ #define DECDOUBLE_EconL 8 /* exponent continuation length */\r
+ #define DECDOUBLE_Declets 5 /* count of declets */\r
+ /* highest biased exponent (Elimit-1) */\r
+ #define DECDOUBLE_Ehigh (DECDOUBLE_Emax + DECDOUBLE_Bias - (DECDOUBLE_Pmax-1))\r
+\r
+ /* Required includes */\r
+ #include "decContext.h"\r
+ #include "decQuad.h"\r
+\r
+ /* The decDouble decimal 64-bit type, accessible by all sizes */\r
+ typedef union {\r
+ uint8_t bytes[DECDOUBLE_Bytes]; /* fields: 1, 5, 8, 50 bits */\r
+ uint16_t shorts[DECDOUBLE_Bytes/2];\r
+ uint32_t words[DECDOUBLE_Bytes/4];\r
+ #if DECUSE64\r
+ uint64_t longs[DECDOUBLE_Bytes/8];\r
+ #endif\r
+ } decDouble;\r
+\r
+ /* ---------------------------------------------------------------- */\r
+ /* Routines -- implemented as decFloat routines in common files */\r
+ /* ---------------------------------------------------------------- */\r
+\r
+ /* Utilities and conversions, extractors, etc.) */\r
+ extern decDouble * decDoubleFromBCD(decDouble *, int32_t, const uint8_t *, int32_t);\r
+ extern decDouble * decDoubleFromInt32(decDouble *, int32_t);\r
+ extern decDouble * decDoubleFromPacked(decDouble *, int32_t, const uint8_t *);\r
+ extern decDouble * decDoubleFromPackedChecked(decDouble *, int32_t, const uint8_t *);\r
+ extern decDouble * decDoubleFromString(decDouble *, const char *, decContext *);\r
+ extern decDouble * decDoubleFromUInt32(decDouble *, uint32_t);\r
+ extern decDouble * decDoubleFromWider(decDouble *, const decQuad *, decContext *);\r
+ extern int32_t decDoubleGetCoefficient(const decDouble *, uint8_t *);\r
+ extern int32_t decDoubleGetExponent(const decDouble *);\r
+ extern decDouble * decDoubleSetCoefficient(decDouble *, const uint8_t *, int32_t);\r
+ extern decDouble * decDoubleSetExponent(decDouble *, decContext *, int32_t);\r
+ extern void decDoubleShow(const decDouble *, const char *);\r
+ extern int32_t decDoubleToBCD(const decDouble *, int32_t *, uint8_t *);\r
+ extern char * decDoubleToEngString(const decDouble *, char *);\r
+ extern int32_t decDoubleToInt32(const decDouble *, decContext *, enum rounding);\r
+ extern int32_t decDoubleToInt32Exact(const decDouble *, decContext *, enum rounding);\r
+ extern int32_t decDoubleToPacked(const decDouble *, int32_t *, uint8_t *);\r
+ extern char * decDoubleToString(const decDouble *, char *);\r
+ extern uint32_t decDoubleToUInt32(const decDouble *, decContext *, enum rounding);\r
+ extern uint32_t decDoubleToUInt32Exact(const decDouble *, decContext *, enum rounding);\r
+ extern decQuad * decDoubleToWider(const decDouble *, decQuad *);\r
+ extern decDouble * decDoubleZero(decDouble *);\r
+\r
+ /* Computational (result is a decDouble) */\r
+ extern decDouble * decDoubleAbs(decDouble *, const decDouble *, decContext *);\r
+ extern decDouble * decDoubleAdd(decDouble *, const decDouble *, const decDouble *, decContext *);\r
+ extern decDouble * decDoubleAnd(decDouble *, const decDouble *, const decDouble *, decContext *);\r
+ extern decDouble * decDoubleDivide(decDouble *, const decDouble *, const decDouble *, decContext *);\r
+ extern decDouble * decDoubleDivideInteger(decDouble *, const decDouble *, const decDouble *, decContext *);\r
+ extern decDouble * decDoubleFMA(decDouble *, const decDouble *, const decDouble *, const decDouble *, decContext *);\r
+ extern decDouble * decDoubleInvert(decDouble *, const decDouble *, decContext *);\r
+ extern decDouble * decDoubleLogB(decDouble *, const decDouble *, decContext *);\r
+ extern decDouble * decDoubleMax(decDouble *, const decDouble *, const decDouble *, decContext *);\r
+ extern decDouble * decDoubleMaxMag(decDouble *, const decDouble *, const decDouble *, decContext *);\r
+ extern decDouble * decDoubleMin(decDouble *, const decDouble *, const decDouble *, decContext *);\r
+ extern decDouble * decDoubleMinMag(decDouble *, const decDouble *, const decDouble *, decContext *);\r
+ extern decDouble * decDoubleMinus(decDouble *, const decDouble *, decContext *);\r
+ extern decDouble * decDoubleMultiply(decDouble *, const decDouble *, const decDouble *, decContext *);\r
+ extern decDouble * decDoubleNextMinus(decDouble *, const decDouble *, decContext *);\r
+ extern decDouble * decDoubleNextPlus(decDouble *, const decDouble *, decContext *);\r
+ extern decDouble * decDoubleNextToward(decDouble *, const decDouble *, const decDouble *, decContext *);\r
+ extern decDouble * decDoubleOr(decDouble *, const decDouble *, const decDouble *, decContext *);\r
+ extern decDouble * decDoublePlus(decDouble *, const decDouble *, decContext *);\r
+ extern decDouble * decDoubleQuantize(decDouble *, const decDouble *, const decDouble *, decContext *);\r
+ extern decDouble * decDoubleReduce(decDouble *, const decDouble *, decContext *);\r
+ extern decDouble * decDoubleRemainder(decDouble *, const decDouble *, const decDouble *, decContext *);\r
+ extern decDouble * decDoubleRemainderNear(decDouble *, const decDouble *, const decDouble *, decContext *);\r
+ extern decDouble * decDoubleRotate(decDouble *, const decDouble *, const decDouble *, decContext *);\r
+ extern decDouble * decDoubleScaleB(decDouble *, const decDouble *, const decDouble *, decContext *);\r
+ extern decDouble * decDoubleShift(decDouble *, const decDouble *, const decDouble *, decContext *);\r
+ extern decDouble * decDoubleSubtract(decDouble *, const decDouble *, const decDouble *, decContext *);\r
+ extern decDouble * decDoubleToIntegralValue(decDouble *, const decDouble *, decContext *, enum rounding);\r
+ extern decDouble * decDoubleToIntegralExact(decDouble *, const decDouble *, decContext *);\r
+ extern decDouble * decDoubleXor(decDouble *, const decDouble *, const decDouble *, decContext *);\r
+\r
+ /* Comparisons */\r
+ extern decDouble * decDoubleCompare(decDouble *, const decDouble *, const decDouble *, decContext *);\r
+ extern decDouble * decDoubleCompareSignal(decDouble *, const decDouble *, const decDouble *, decContext *);\r
+ extern decDouble * decDoubleCompareTotal(decDouble *, const decDouble *, const decDouble *);\r
+ extern decDouble * decDoubleCompareTotalMag(decDouble *, const decDouble *, const decDouble *);\r
+\r
+ /* Copies */\r
+ extern decDouble * decDoubleCanonical(decDouble *, const decDouble *);\r
+ extern decDouble * decDoubleCopy(decDouble *, const decDouble *);\r
+ extern decDouble * decDoubleCopyAbs(decDouble *, const decDouble *);\r
+ extern decDouble * decDoubleCopyNegate(decDouble *, const decDouble *);\r
+ extern decDouble * decDoubleCopySign(decDouble *, const decDouble *, const decDouble *);\r
+\r
+ /* Non-computational */\r
+ extern enum decClass decDoubleClass(const decDouble *);\r
+ extern const char * decDoubleClassString(const decDouble *);\r
+ extern uint32_t decDoubleDigits(const decDouble *);\r
+ extern uint32_t decDoubleIsCanonical(const decDouble *);\r
+ extern uint32_t decDoubleIsFinite(const decDouble *);\r
+ extern uint32_t decDoubleIsInfinite(const decDouble *);\r
+ extern uint32_t decDoubleIsInteger(const decDouble *);\r
+ extern uint32_t decDoubleIsLogical(const decDouble *);\r
+ extern uint32_t decDoubleIsNaN(const decDouble *);\r
+ extern uint32_t decDoubleIsNegative(const decDouble *);\r
+ extern uint32_t decDoubleIsNormal(const decDouble *);\r
+ extern uint32_t decDoubleIsPositive(const decDouble *);\r
+ extern uint32_t decDoubleIsSignaling(const decDouble *);\r
+ extern uint32_t decDoubleIsSignalling(const decDouble *);\r
+ extern uint32_t decDoubleIsSigned(const decDouble *);\r
+ extern uint32_t decDoubleIsSubnormal(const decDouble *);\r
+ extern uint32_t decDoubleIsZero(const decDouble *);\r
+ extern uint32_t decDoubleRadix(const decDouble *);\r
+ extern uint32_t decDoubleSameQuantum(const decDouble *, const decDouble *);\r
+ extern const char * decDoubleVersion(void);\r
+\r
+ /* decNumber conversions; these are implemented as macros so as not */\r
+ /* to force a dependency on decimal64 and decNumber in decDouble. */\r
+ /* decDoubleFromNumber returns a decimal64 * to avoid warnings. */\r
+ #define decDoubleToNumber(dq, dn) decimal64ToNumber((decimal64 *)(dq), dn)\r
+ #define decDoubleFromNumber(dq, dn, set) decimal64FromNumber((decimal64 *)(dq), dn, set)\r
+\r
+#endif\r
--- /dev/null
+/* ------------------------------------------------------------------ */\r
+/* Decimal Number arithmetic module */\r
+/* ------------------------------------------------------------------ */\r
+/* Copyright (c) IBM Corporation, 2000, 2009. All rights reserved. */\r
+/* */\r
+/* This software is made available under the terms of the */\r
+/* ICU License -- ICU 1.8.1 and later. */\r
+/* */\r
+/* The description and User's Guide ("The decNumber C Library") for */\r
+/* this software is called decNumber.pdf. This document is */\r
+/* available, together with arithmetic and format specifications, */\r
+/* testcases, and Web links, on the General Decimal Arithmetic page. */\r
+/* */\r
+/* Please send comments, suggestions, and corrections to the author: */\r
+/* mfc@uk.ibm.com */\r
+/* Mike Cowlishaw, IBM Fellow */\r
+/* IBM UK, PO Box 31, Birmingham Road, Warwick CV34 5JL, UK */\r
+/* ------------------------------------------------------------------ */\r
+/* This module comprises the routines for arbitrary-precision General */\r
+/* Decimal Arithmetic as defined in the specification which may be */\r
+/* found on the General Decimal Arithmetic pages. It implements both */\r
+/* the full ('extended') arithmetic and the simpler ('subset') */\r
+/* arithmetic. */\r
+/* */\r
+/* Usage notes: */\r
+/* */\r
+/* 1. This code is ANSI C89 except: */\r
+/* */\r
+/* a) C99 line comments (double forward slash) are used. (Most C */\r
+/* compilers accept these. If yours does not, a simple script */\r
+/* can be used to convert them to ANSI C comments.) */\r
+/* */\r
+/* b) Types from C99 stdint.h are used. If you do not have this */\r
+/* header file, see the User's Guide section of the decNumber */\r
+/* documentation; this lists the necessary definitions. */\r
+/* */\r
+/* c) If DECDPUN>4 or DECUSE64=1, the C99 64-bit int64_t and */\r
+/* uint64_t types may be used. To avoid these, set DECUSE64=0 */\r
+/* and DECDPUN<=4 (see documentation). */\r
+/* */\r
+/* The code also conforms to C99 restrictions; in particular, */\r
+/* strict aliasing rules are observed. */\r
+/* */\r
+/* 2. The decNumber format which this library uses is optimized for */\r
+/* efficient processing of relatively short numbers; in particular */\r
+/* it allows the use of fixed sized structures and minimizes copy */\r
+/* and move operations. It does, however, support arbitrary */\r
+/* precision (up to 999,999,999 digits) and arbitrary exponent */\r
+/* range (Emax in the range 0 through 999,999,999 and Emin in the */\r
+/* range -999,999,999 through 0). Mathematical functions (for */\r
+/* example decNumberExp) as identified below are restricted more */\r
+/* tightly: digits, emax, and -emin in the context must be <= */\r
+/* DEC_MAX_MATH (999999), and their operand(s) must be within */\r
+/* these bounds. */\r
+/* */\r
+/* 3. Logical functions are further restricted; their operands must */\r
+/* be finite, positive, have an exponent of zero, and all digits */\r
+/* must be either 0 or 1. The result will only contain digits */\r
+/* which are 0 or 1 (and will have exponent=0 and a sign of 0). */\r
+/* */\r
+/* 4. Operands to operator functions are never modified unless they */\r
+/* are also specified to be the result number (which is always */\r
+/* permitted). Other than that case, operands must not overlap. */\r
+/* */\r
+/* 5. Error handling: the type of the error is ORed into the status */\r
+/* flags in the current context (decContext structure). The */\r
+/* SIGFPE signal is then raised if the corresponding trap-enabler */\r
+/* flag in the decContext is set (is 1). */\r
+/* */\r
+/* It is the responsibility of the caller to clear the status */\r
+/* flags as required. */\r
+/* */\r
+/* The result of any routine which returns a number will always */\r
+/* be a valid number (which may be a special value, such as an */\r
+/* Infinity or NaN). */\r
+/* */\r
+/* 6. The decNumber format is not an exchangeable concrete */\r
+/* representation as it comprises fields which may be machine- */\r
+/* dependent (packed or unpacked, or special length, for example). */\r
+/* Canonical conversions to and from strings are provided; other */\r
+/* conversions are available in separate modules. */\r
+/* */\r
+/* 7. Normally, input operands are assumed to be valid. Set DECCHECK */\r
+/* to 1 for extended operand checking (including NULL operands). */\r
+/* Results are undefined if a badly-formed structure (or a NULL */\r
+/* pointer to a structure) is provided, though with DECCHECK */\r
+/* enabled the operator routines are protected against exceptions. */\r
+/* (Except if the result pointer is NULL, which is unrecoverable.) */\r
+/* */\r
+/* However, the routines will never cause exceptions if they are */\r
+/* given well-formed operands, even if the value of the operands */\r
+/* is inappropriate for the operation and DECCHECK is not set. */\r
+/* (Except for SIGFPE, as and where documented.) */\r
+/* */\r
+/* 8. Subset arithmetic is available only if DECSUBSET is set to 1. */\r
+/* ------------------------------------------------------------------ */\r
+/* Implementation notes for maintenance of this module: */\r
+/* */\r
+/* 1. Storage leak protection: Routines which use malloc are not */\r
+/* permitted to use return for fastpath or error exits (i.e., */\r
+/* they follow strict structured programming conventions). */\r
+/* Instead they have a do{}while(0); construct surrounding the */\r
+/* code which is protected -- break may be used to exit this. */\r
+/* Other routines can safely use the return statement inline. */\r
+/* */\r
+/* Storage leak accounting can be enabled using DECALLOC. */\r
+/* */\r
+/* 2. All loops use the for(;;) construct. Any do construct does */\r
+/* not loop; it is for allocation protection as just described. */\r
+/* */\r
+/* 3. Setting status in the context must always be the very last */\r
+/* action in a routine, as non-0 status may raise a trap and hence */\r
+/* the call to set status may not return (if the handler uses long */\r
+/* jump). Therefore all cleanup must be done first. In general, */\r
+/* to achieve this status is accumulated and is only applied just */\r
+/* before return by calling decContextSetStatus (via decStatus). */\r
+/* */\r
+/* Routines which allocate storage cannot, in general, use the */\r
+/* 'top level' routines which could cause a non-returning */\r
+/* transfer of control. The decXxxxOp routines are safe (do not */\r
+/* call decStatus even if traps are set in the context) and should */\r
+/* be used instead (they are also a little faster). */\r
+/* */\r
+/* 4. Exponent checking is minimized by allowing the exponent to */\r
+/* grow outside its limits during calculations, provided that */\r
+/* the decFinalize function is called later. Multiplication and */\r
+/* division, and intermediate calculations in exponentiation, */\r
+/* require more careful checks because of the risk of 31-bit */\r
+/* overflow (the most negative valid exponent is -1999999997, for */\r
+/* a 999999999-digit number with adjusted exponent of -999999999). */\r
+/* */\r
+/* 5. Rounding is deferred until finalization of results, with any */\r
+/* 'off to the right' data being represented as a single digit */\r
+/* residue (in the range -1 through 9). This avoids any double- */\r
+/* rounding when more than one shortening takes place (for */\r
+/* example, when a result is subnormal). */\r
+/* */\r
+/* 6. The digits count is allowed to rise to a multiple of DECDPUN */\r
+/* during many operations, so whole Units are handled and exact */\r
+/* accounting of digits is not needed. The correct digits value */\r
+/* is found by decGetDigits, which accounts for leading zeros. */\r
+/* This must be called before any rounding if the number of digits */\r
+/* is not known exactly. */\r
+/* */\r
+/* 7. The multiply-by-reciprocal 'trick' is used for partitioning */\r
+/* numbers up to four digits, using appropriate constants. This */\r
+/* is not useful for longer numbers because overflow of 32 bits */\r
+/* would lead to 4 multiplies, which is almost as expensive as */\r
+/* a divide (unless a floating-point or 64-bit multiply is */\r
+/* assumed to be available). */\r
+/* */\r
+/* 8. Unusual abbreviations that may be used in the commentary: */\r
+/* lhs -- left hand side (operand, of an operation) */\r
+/* lsd -- least significant digit (of coefficient) */\r
+/* lsu -- least significant Unit (of coefficient) */\r
+/* msd -- most significant digit (of coefficient) */\r
+/* msi -- most significant item (in an array) */\r
+/* msu -- most significant Unit (of coefficient) */\r
+/* rhs -- right hand side (operand, of an operation) */\r
+/* +ve -- positive */\r
+/* -ve -- negative */\r
+/* ** -- raise to the power */\r
+/* ------------------------------------------------------------------ */\r
+\r
+#include <stdlib.h> // for malloc, free, etc.\r
+#include <stdio.h> // for printf [if needed]\r
+#include <string.h> // for strcpy\r
+#include <ctype.h> // for lower\r
+#include "decNumber.h" // base number library\r
+#include "decNumberLocal.h" // decNumber local types, etc.\r
+\r
+/* Constants */\r
+// Public lookup table used by the D2U macro\r
+const uByte d2utable[DECMAXD2U+1]=D2UTABLE;\r
+\r
+#define DECVERB 1 // set to 1 for verbose DECCHECK\r
+#define powers DECPOWERS // old internal name\r
+\r
+// Local constants\r
+#define DIVIDE 0x80 // Divide operators\r
+#define REMAINDER 0x40 // ..\r
+#define DIVIDEINT 0x20 // ..\r
+#define REMNEAR 0x10 // ..\r
+#define COMPARE 0x01 // Compare operators\r
+#define COMPMAX 0x02 // ..\r
+#define COMPMIN 0x03 // ..\r
+#define COMPTOTAL 0x04 // ..\r
+#define COMPNAN 0x05 // .. [NaN processing]\r
+#define COMPSIG 0x06 // .. [signaling COMPARE]\r
+#define COMPMAXMAG 0x07 // ..\r
+#define COMPMINMAG 0x08 // ..\r
+\r
+#define DEC_sNaN 0x40000000 // local status: sNaN signal\r
+#define BADINT (Int)0x80000000 // most-negative Int; error indicator\r
+// Next two indicate an integer >= 10**6, and its parity (bottom bit)\r
+#define BIGEVEN (Int)0x80000002\r
+#define BIGODD (Int)0x80000003\r
+\r
+static Unit uarrone[1]={1}; // Unit array of 1, used for incrementing\r
+\r
+/* Granularity-dependent code */\r
+#if DECDPUN<=4\r
+ #define eInt Int // extended integer\r
+ #define ueInt uInt // unsigned extended integer\r
+ // Constant multipliers for divide-by-power-of five using reciprocal\r
+ // multiply, after removing powers of 2 by shifting, and final shift\r
+ // of 17 [we only need up to **4]\r
+ static const uInt multies[]={131073, 26215, 5243, 1049, 210};\r
+ // QUOT10 -- macro to return the quotient of unit u divided by 10**n\r
+ #define QUOT10(u, n) ((((uInt)(u)>>(n))*multies[n])>>17)\r
+#else\r
+ // For DECDPUN>4 non-ANSI-89 64-bit types are needed.\r
+ #if !DECUSE64\r
+ #error decNumber.c: DECUSE64 must be 1 when DECDPUN>4\r
+ #endif\r
+ #define eInt Long // extended integer\r
+ #define ueInt uLong // unsigned extended integer\r
+#endif\r
+\r
+/* Local routines */\r
+static decNumber * decAddOp(decNumber *, const decNumber *, const decNumber *,\r
+ decContext *, uByte, uInt *);\r
+static Flag decBiStr(const char *, const char *, const char *);\r
+static uInt decCheckMath(const decNumber *, decContext *, uInt *);\r
+static void decApplyRound(decNumber *, decContext *, Int, uInt *);\r
+static Int decCompare(const decNumber *lhs, const decNumber *rhs, Flag);\r
+static decNumber * decCompareOp(decNumber *, const decNumber *,\r
+ const decNumber *, decContext *,\r
+ Flag, uInt *);\r
+static void decCopyFit(decNumber *, const decNumber *, decContext *,\r
+ Int *, uInt *);\r
+static decNumber * decDecap(decNumber *, Int);\r
+static decNumber * decDivideOp(decNumber *, const decNumber *,\r
+ const decNumber *, decContext *, Flag, uInt *);\r
+static decNumber * decExpOp(decNumber *, const decNumber *,\r
+ decContext *, uInt *);\r
+static void decFinalize(decNumber *, decContext *, Int *, uInt *);\r
+static Int decGetDigits(Unit *, Int);\r
+static Int decGetInt(const decNumber *);\r
+static decNumber * decLnOp(decNumber *, const decNumber *,\r
+ decContext *, uInt *);\r
+static decNumber * decMultiplyOp(decNumber *, const decNumber *,\r
+ const decNumber *, decContext *,\r
+ uInt *);\r
+static decNumber * decNaNs(decNumber *, const decNumber *,\r
+ const decNumber *, decContext *, uInt *);\r
+static decNumber * decQuantizeOp(decNumber *, const decNumber *,\r
+ const decNumber *, decContext *, Flag,\r
+ uInt *);\r
+static void decReverse(Unit *, Unit *);\r
+static void decSetCoeff(decNumber *, decContext *, const Unit *,\r
+ Int, Int *, uInt *);\r
+static void decSetMaxValue(decNumber *, decContext *);\r
+static void decSetOverflow(decNumber *, decContext *, uInt *);\r
+static void decSetSubnormal(decNumber *, decContext *, Int *, uInt *);\r
+static Int decShiftToLeast(Unit *, Int, Int);\r
+static Int decShiftToMost(Unit *, Int, Int);\r
+static void decStatus(decNumber *, uInt, decContext *);\r
+static void decToString(const decNumber *, char[], Flag);\r
+static decNumber * decTrim(decNumber *, decContext *, Flag, Flag, Int *);\r
+static Int decUnitAddSub(const Unit *, Int, const Unit *, Int, Int,\r
+ Unit *, Int);\r
+static Int decUnitCompare(const Unit *, Int, const Unit *, Int, Int);\r
+\r
+#if !DECSUBSET\r
+/* decFinish == decFinalize when no subset arithmetic needed */\r
+#define decFinish(a,b,c,d) decFinalize(a,b,c,d)\r
+#else\r
+static void decFinish(decNumber *, decContext *, Int *, uInt *);\r
+static decNumber * decRoundOperand(const decNumber *, decContext *, uInt *);\r
+#endif\r
+\r
+/* Local macros */\r
+// masked special-values bits\r
+#define SPECIALARG (rhs->bits & DECSPECIAL)\r
+#define SPECIALARGS ((lhs->bits | rhs->bits) & DECSPECIAL)\r
+\r
+/* Diagnostic macros, etc. */\r
+#if DECALLOC\r
+// Handle malloc/free accounting. If enabled, our accountable routines\r
+// are used; otherwise the code just goes straight to the system malloc\r
+// and free routines.\r
+#define malloc(a) decMalloc(a)\r
+#define free(a) decFree(a)\r
+#define DECFENCE 0x5a // corruption detector\r
+// 'Our' malloc and free:\r
+static void *decMalloc(size_t);\r
+static void decFree(void *);\r
+uInt decAllocBytes=0; // count of bytes allocated\r
+// Note that DECALLOC code only checks for storage buffer overflow.\r
+// To check for memory leaks, the decAllocBytes variable must be\r
+// checked to be 0 at appropriate times (e.g., after the test\r
+// harness completes a set of tests). This checking may be unreliable\r
+// if the testing is done in a multi-thread environment.\r
+#endif\r
+\r
+#if DECCHECK\r
+// Optional checking routines. Enabling these means that decNumber\r
+// and decContext operands to operator routines are checked for\r
+// correctness. This roughly doubles the execution time of the\r
+// fastest routines (and adds 600+ bytes), so should not normally be\r
+// used in 'production'.\r
+// decCheckInexact is used to check that inexact results have a full\r
+// complement of digits (where appropriate -- this is not the case\r
+// for Quantize, for example)\r
+#define DECUNRESU ((decNumber *)(void *)0xffffffff)\r
+#define DECUNUSED ((const decNumber *)(void *)0xffffffff)\r
+#define DECUNCONT ((decContext *)(void *)(0xffffffff))\r
+static Flag decCheckOperands(decNumber *, const decNumber *,\r
+ const decNumber *, decContext *);\r
+static Flag decCheckNumber(const decNumber *);\r
+static void decCheckInexact(const decNumber *, decContext *);\r
+#endif\r
+\r
+#if DECTRACE || DECCHECK\r
+// Optional trace/debugging routines (may or may not be used)\r
+void decNumberShow(const decNumber *); // displays the components of a number\r
+static void decDumpAr(char, const Unit *, Int);\r
+#endif\r
+\r
+/* ================================================================== */\r
+/* Conversions */\r
+/* ================================================================== */\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* from-int32 -- conversion from Int or uInt */\r
+/* */\r
+/* dn is the decNumber to receive the integer */\r
+/* in or uin is the integer to be converted */\r
+/* returns dn */\r
+/* */\r
+/* No error is possible. */\r
+/* ------------------------------------------------------------------ */\r
+decNumber * decNumberFromInt32(decNumber *dn, Int in) {\r
+ uInt unsig;\r
+ if (in>=0) unsig=in;\r
+ else { // negative (possibly BADINT)\r
+ if (in==BADINT) unsig=(uInt)1073741824*2; // special case\r
+ else unsig=-in; // invert\r
+ }\r
+ // in is now positive\r
+ decNumberFromUInt32(dn, unsig);\r
+ if (in<0) dn->bits=DECNEG; // sign needed\r
+ return dn;\r
+ } // decNumberFromInt32\r
+\r
+decNumber * decNumberFromUInt32(decNumber *dn, uInt uin) {\r
+ Unit *up; // work pointer\r
+ decNumberZero(dn); // clean\r
+ if (uin==0) return dn; // [or decGetDigits bad call]\r
+ for (up=dn->lsu; uin>0; up++) {\r
+ *up=(Unit)(uin%(DECDPUNMAX+1));\r
+ uin=uin/(DECDPUNMAX+1);\r
+ }\r
+ dn->digits=decGetDigits(dn->lsu, up-dn->lsu);\r
+ return dn;\r
+ } // decNumberFromUInt32\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* to-int32 -- conversion to Int or uInt */\r
+/* */\r
+/* dn is the decNumber to convert */\r
+/* set is the context for reporting errors */\r
+/* returns the converted decNumber, or 0 if Invalid is set */\r
+/* */\r
+/* Invalid is set if the decNumber does not have exponent==0 or if */\r
+/* it is a NaN, Infinite, or out-of-range. */\r
+/* ------------------------------------------------------------------ */\r
+Int decNumberToInt32(const decNumber *dn, decContext *set) {\r
+ #if DECCHECK\r
+ if (decCheckOperands(DECUNRESU, DECUNUSED, dn, set)) return 0;\r
+ #endif\r
+\r
+ // special or too many digits, or bad exponent\r
+ if (dn->bits&DECSPECIAL || dn->digits>10 || dn->exponent!=0) ; // bad\r
+ else { // is a finite integer with 10 or fewer digits\r
+ Int d; // work\r
+ const Unit *up; // ..\r
+ uInt hi=0, lo; // ..\r
+ up=dn->lsu; // -> lsu\r
+ lo=*up; // get 1 to 9 digits\r
+ #if DECDPUN>1 // split to higher\r
+ hi=lo/10;\r
+ lo=lo%10;\r
+ #endif\r
+ up++;\r
+ // collect remaining Units, if any, into hi\r
+ for (d=DECDPUN; d<dn->digits; up++, d+=DECDPUN) hi+=*up*powers[d-1];\r
+ // now low has the lsd, hi the remainder\r
+ if (hi>214748364 || (hi==214748364 && lo>7)) { // out of range?\r
+ // most-negative is a reprieve\r
+ if (dn->bits&DECNEG && hi==214748364 && lo==8) return 0x80000000;\r
+ // bad -- drop through\r
+ }\r
+ else { // in-range always\r
+ Int i=X10(hi)+lo;\r
+ if (dn->bits&DECNEG) return -i;\r
+ return i;\r
+ }\r
+ } // integer\r
+ decContextSetStatus(set, DEC_Invalid_operation); // [may not return]\r
+ return 0;\r
+ } // decNumberToInt32\r
+\r
+uInt decNumberToUInt32(const decNumber *dn, decContext *set) {\r
+ #if DECCHECK\r
+ if (decCheckOperands(DECUNRESU, DECUNUSED, dn, set)) return 0;\r
+ #endif\r
+ // special or too many digits, or bad exponent, or negative (<0)\r
+ if (dn->bits&DECSPECIAL || dn->digits>10 || dn->exponent!=0\r
+ || (dn->bits&DECNEG && !ISZERO(dn))); // bad\r
+ else { // is a finite integer with 10 or fewer digits\r
+ Int d; // work\r
+ const Unit *up; // ..\r
+ uInt hi=0, lo; // ..\r
+ up=dn->lsu; // -> lsu\r
+ lo=*up; // get 1 to 9 digits\r
+ #if DECDPUN>1 // split to higher\r
+ hi=lo/10;\r
+ lo=lo%10;\r
+ #endif\r
+ up++;\r
+ // collect remaining Units, if any, into hi\r
+ for (d=DECDPUN; d<dn->digits; up++, d+=DECDPUN) hi+=*up*powers[d-1];\r
+\r
+ // now low has the lsd, hi the remainder\r
+ if (hi>429496729 || (hi==429496729 && lo>5)) ; // no reprieve possible\r
+ else return X10(hi)+lo;\r
+ } // integer\r
+ decContextSetStatus(set, DEC_Invalid_operation); // [may not return]\r
+ return 0;\r
+ } // decNumberToUInt32\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* to-scientific-string -- conversion to numeric string */\r
+/* to-engineering-string -- conversion to numeric string */\r
+/* */\r
+/* decNumberToString(dn, string); */\r
+/* decNumberToEngString(dn, string); */\r
+/* */\r
+/* dn is the decNumber to convert */\r
+/* string is the string where the result will be laid out */\r
+/* */\r
+/* string must be at least dn->digits+14 characters long */\r
+/* */\r
+/* No error is possible, and no status can be set. */\r
+/* ------------------------------------------------------------------ */\r
+char * decNumberToString(const decNumber *dn, char *string){\r
+ decToString(dn, string, 0);\r
+ return string;\r
+ } // DecNumberToString\r
+\r
+char * decNumberToEngString(const decNumber *dn, char *string){\r
+ decToString(dn, string, 1);\r
+ return string;\r
+ } // DecNumberToEngString\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* to-number -- conversion from numeric string */\r
+/* */\r
+/* decNumberFromString -- convert string to decNumber */\r
+/* dn -- the number structure to fill */\r
+/* chars[] -- the string to convert ('\0' terminated) */\r
+/* set -- the context used for processing any error, */\r
+/* determining the maximum precision available */\r
+/* (set.digits), determining the maximum and minimum */\r
+/* exponent (set.emax and set.emin), determining if */\r
+/* extended values are allowed, and checking the */\r
+/* rounding mode if overflow occurs or rounding is */\r
+/* needed. */\r
+/* */\r
+/* The length of the coefficient and the size of the exponent are */\r
+/* checked by this routine, so the correct error (Underflow or */\r
+/* Overflow) can be reported or rounding applied, as necessary. */\r
+/* */\r
+/* If bad syntax is detected, the result will be a quiet NaN. */\r
+/* ------------------------------------------------------------------ */\r
+decNumber * decNumberFromString(decNumber *dn, const char chars[],\r
+ decContext *set) {\r
+ Int exponent=0; // working exponent [assume 0]\r
+ uByte bits=0; // working flags [assume +ve]\r
+ Unit *res; // where result will be built\r
+ Unit resbuff[SD2U(DECBUFFER+9)];// local buffer in case need temporary\r
+ // [+9 allows for ln() constants]\r
+ Unit *allocres=NULL; // -> allocated result, iff allocated\r
+ Int d=0; // count of digits found in decimal part\r
+ const char *dotchar=NULL; // where dot was found\r
+ const char *cfirst=chars; // -> first character of decimal part\r
+ const char *last=NULL; // -> last digit of decimal part\r
+ const char *c; // work\r
+ Unit *up; // ..\r
+ #if DECDPUN>1\r
+ Int cut, out; // ..\r
+ #endif\r
+ Int residue; // rounding residue\r
+ uInt status=0; // error code\r
+\r
+ #if DECCHECK\r
+ if (decCheckOperands(DECUNRESU, DECUNUSED, DECUNUSED, set))\r
+ return decNumberZero(dn);\r
+ #endif\r
+\r
+ do { // status & malloc protection\r
+ for (c=chars;; c++) { // -> input character\r
+ if (*c>='0' && *c<='9') { // test for Arabic digit\r
+ last=c;\r
+ d++; // count of real digits\r
+ continue; // still in decimal part\r
+ }\r
+ if (*c=='.' && dotchar==NULL) { // first '.'\r
+ dotchar=c; // record offset into decimal part\r
+ if (c==cfirst) cfirst++; // first digit must follow\r
+ continue;}\r
+ if (c==chars) { // first in string...\r
+ if (*c=='-') { // valid - sign\r
+ cfirst++;\r
+ bits=DECNEG;\r
+ continue;}\r
+ if (*c=='+') { // valid + sign\r
+ cfirst++;\r
+ continue;}\r
+ }\r
+ // *c is not a digit, or a valid +, -, or '.'\r
+ break;\r
+ } // c\r
+\r
+ if (last==NULL) { // no digits yet\r
+ status=DEC_Conversion_syntax;// assume the worst\r
+ if (*c=='\0') break; // and no more to come...\r
+ #if DECSUBSET\r
+ // if subset then infinities and NaNs are not allowed\r
+ if (!set->extended) break; // hopeless\r
+ #endif\r
+ // Infinities and NaNs are possible, here\r
+ if (dotchar!=NULL) break; // .. unless had a dot\r
+ decNumberZero(dn); // be optimistic\r
+ if (decBiStr(c, "infinity", "INFINITY")\r
+ || decBiStr(c, "inf", "INF")) {\r
+ dn->bits=bits | DECINF;\r
+ status=0; // is OK\r
+ break; // all done\r
+ }\r
+ // a NaN expected\r
+ // 2003.09.10 NaNs are now permitted to have a sign\r
+ dn->bits=bits | DECNAN; // assume simple NaN\r
+ if (*c=='s' || *c=='S') { // looks like an sNaN\r
+ c++;\r
+ dn->bits=bits | DECSNAN;\r
+ }\r
+ if (*c!='n' && *c!='N') break; // check caseless "NaN"\r
+ c++;\r
+ if (*c!='a' && *c!='A') break; // ..\r
+ c++;\r
+ if (*c!='n' && *c!='N') break; // ..\r
+ c++;\r
+ // now either nothing, or nnnn payload, expected\r
+ // -> start of integer and skip leading 0s [including plain 0]\r
+ for (cfirst=c; *cfirst=='0';) cfirst++;\r
+ if (*cfirst=='\0') { // "NaN" or "sNaN", maybe with all 0s\r
+ status=0; // it's good\r
+ break; // ..\r
+ }\r
+ // something other than 0s; setup last and d as usual [no dots]\r
+ for (c=cfirst;; c++, d++) {\r
+ if (*c<'0' || *c>'9') break; // test for Arabic digit\r
+ last=c;\r
+ }\r
+ if (*c!='\0') break; // not all digits\r
+ if (d>set->digits-1) {\r
+ // [NB: payload in a decNumber can be full length unless\r
+ // clamped, in which case can only be digits-1]\r
+ if (set->clamp) break;\r
+ if (d>set->digits) break;\r
+ } // too many digits?\r
+ // good; drop through to convert the integer to coefficient\r
+ status=0; // syntax is OK\r
+ bits=dn->bits; // for copy-back\r
+ } // last==NULL\r
+\r
+ else if (*c!='\0') { // more to process...\r
+ // had some digits; exponent is only valid sequence now\r
+ Flag nege; // 1=negative exponent\r
+ const char *firstexp; // -> first significant exponent digit\r
+ status=DEC_Conversion_syntax;// assume the worst\r
+ if (*c!='e' && *c!='E') break;\r
+ /* Found 'e' or 'E' -- now process explicit exponent */\r
+ // 1998.07.11: sign no longer required\r
+ nege=0;\r
+ c++; // to (possible) sign\r
+ if (*c=='-') {nege=1; c++;}\r
+ else if (*c=='+') c++;\r
+ if (*c=='\0') break;\r
+\r
+ for (; *c=='0' && *(c+1)!='\0';) c++; // strip insignificant zeros\r
+ firstexp=c; // save exponent digit place\r
+ for (; ;c++) {\r
+ if (*c<'0' || *c>'9') break; // not a digit\r
+ exponent=X10(exponent)+(Int)*c-(Int)'0';\r
+ } // c\r
+ // if not now on a '\0', *c must not be a digit\r
+ if (*c!='\0') break;\r
+\r
+ // (this next test must be after the syntax checks)\r
+ // if it was too long the exponent may have wrapped, so check\r
+ // carefully and set it to a certain overflow if wrap possible\r
+ if (c>=firstexp+9+1) {\r
+ if (c>firstexp+9+1 || *firstexp>'1') exponent=DECNUMMAXE*2;\r
+ // [up to 1999999999 is OK, for example 1E-1000000998]\r
+ }\r
+ if (nege) exponent=-exponent; // was negative\r
+ status=0; // is OK\r
+ } // stuff after digits\r
+\r
+ // Here when whole string has been inspected; syntax is good\r
+ // cfirst->first digit (never dot), last->last digit (ditto)\r
+\r
+ // strip leading zeros/dot [leave final 0 if all 0's]\r
+ if (*cfirst=='0') { // [cfirst has stepped over .]\r
+ for (c=cfirst; c<last; c++, cfirst++) {\r
+ if (*c=='.') continue; // ignore dots\r
+ if (*c!='0') break; // non-zero found\r
+ d--; // 0 stripped\r
+ } // c\r
+ #if DECSUBSET\r
+ // make a rapid exit for easy zeros if !extended\r
+ if (*cfirst=='0' && !set->extended) {\r
+ decNumberZero(dn); // clean result\r
+ break; // [could be return]\r
+ }\r
+ #endif\r
+ } // at least one leading 0\r
+\r
+ // Handle decimal point...\r
+ if (dotchar!=NULL && dotchar<last) // non-trailing '.' found?\r
+ exponent-=(last-dotchar); // adjust exponent\r
+ // [we can now ignore the .]\r
+\r
+ // OK, the digits string is good. Assemble in the decNumber, or in\r
+ // a temporary units array if rounding is needed\r
+ if (d<=set->digits) res=dn->lsu; // fits into supplied decNumber\r
+ else { // rounding needed\r
+ Int needbytes=D2U(d)*sizeof(Unit);// bytes needed\r
+ res=resbuff; // assume use local buffer\r
+ if (needbytes>(Int)sizeof(resbuff)) { // too big for local\r
+ allocres=(Unit *)malloc(needbytes);\r
+ if (allocres==NULL) {status|=DEC_Insufficient_storage; break;}\r
+ res=allocres;\r
+ }\r
+ }\r
+ // res now -> number lsu, buffer, or allocated storage for Unit array\r
+\r
+ // Place the coefficient into the selected Unit array\r
+ // [this is often 70% of the cost of this function when DECDPUN>1]\r
+ #if DECDPUN>1\r
+ out=0; // accumulator\r
+ up=res+D2U(d)-1; // -> msu\r
+ cut=d-(up-res)*DECDPUN; // digits in top unit\r
+ for (c=cfirst;; c++) { // along the digits\r
+ if (*c=='.') continue; // ignore '.' [don't decrement cut]\r
+ out=X10(out)+(Int)*c-(Int)'0';\r
+ if (c==last) break; // done [never get to trailing '.']\r
+ cut--;\r
+ if (cut>0) continue; // more for this unit\r
+ *up=(Unit)out; // write unit\r
+ up--; // prepare for unit below..\r
+ cut=DECDPUN; // ..\r
+ out=0; // ..\r
+ } // c\r
+ *up=(Unit)out; // write lsu\r
+\r
+ #else\r
+ // DECDPUN==1\r
+ up=res; // -> lsu\r
+ for (c=last; c>=cfirst; c--) { // over each character, from least\r
+ if (*c=='.') continue; // ignore . [don't step up]\r
+ *up=(Unit)((Int)*c-(Int)'0');\r
+ up++;\r
+ } // c\r
+ #endif\r
+\r
+ dn->bits=bits;\r
+ dn->exponent=exponent;\r
+ dn->digits=d;\r
+\r
+ // if not in number (too long) shorten into the number\r
+ if (d>set->digits) {\r
+ residue=0;\r
+ decSetCoeff(dn, set, res, d, &residue, &status);\r
+ // always check for overflow or subnormal and round as needed\r
+ decFinalize(dn, set, &residue, &status);\r
+ }\r
+ else { // no rounding, but may still have overflow or subnormal\r
+ // [these tests are just for performance; finalize repeats them]\r
+ if ((dn->exponent-1<set->emin-dn->digits)\r
+ || (dn->exponent-1>set->emax-set->digits)) {\r
+ residue=0;\r
+ decFinalize(dn, set, &residue, &status);\r
+ }\r
+ }\r
+ // decNumberShow(dn);\r
+ } while(0); // [for break]\r
+\r
+ if (allocres!=NULL) free(allocres); // drop any storage used\r
+ if (status!=0) decStatus(dn, status, set);\r
+ return dn;\r
+ } /* decNumberFromString */\r
+\r
+/* ================================================================== */\r
+/* Operators */\r
+/* ================================================================== */\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decNumberAbs -- absolute value operator */\r
+/* */\r
+/* This computes C = abs(A) */\r
+/* */\r
+/* res is C, the result. C may be A */\r
+/* rhs is A */\r
+/* set is the context */\r
+/* */\r
+/* See also decNumberCopyAbs for a quiet bitwise version of this. */\r
+/* C must have space for set->digits digits. */\r
+/* ------------------------------------------------------------------ */\r
+/* This has the same effect as decNumberPlus unless A is negative, */\r
+/* in which case it has the same effect as decNumberMinus. */\r
+/* ------------------------------------------------------------------ */\r
+decNumber * decNumberAbs(decNumber *res, const decNumber *rhs,\r
+ decContext *set) {\r
+ decNumber dzero; // for 0\r
+ uInt status=0; // accumulator\r
+\r
+ #if DECCHECK\r
+ if (decCheckOperands(res, DECUNUSED, rhs, set)) return res;\r
+ #endif\r
+\r
+ decNumberZero(&dzero); // set 0\r
+ dzero.exponent=rhs->exponent; // [no coefficient expansion]\r
+ decAddOp(res, &dzero, rhs, set, (uByte)(rhs->bits & DECNEG), &status);\r
+ if (status!=0) decStatus(res, status, set);\r
+ #if DECCHECK\r
+ decCheckInexact(res, set);\r
+ #endif\r
+ return res;\r
+ } // decNumberAbs\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decNumberAdd -- add two Numbers */\r
+/* */\r
+/* This computes C = A + B */\r
+/* */\r
+/* res is C, the result. C may be A and/or B (e.g., X=X+X) */\r
+/* lhs is A */\r
+/* rhs is B */\r
+/* set is the context */\r
+/* */\r
+/* C must have space for set->digits digits. */\r
+/* ------------------------------------------------------------------ */\r
+/* This just calls the routine shared with Subtract */\r
+decNumber * decNumberAdd(decNumber *res, const decNumber *lhs,\r
+ const decNumber *rhs, decContext *set) {\r
+ uInt status=0; // accumulator\r
+ decAddOp(res, lhs, rhs, set, 0, &status);\r
+ if (status!=0) decStatus(res, status, set);\r
+ #if DECCHECK\r
+ decCheckInexact(res, set);\r
+ #endif\r
+ return res;\r
+ } // decNumberAdd\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decNumberAnd -- AND two Numbers, digitwise */\r
+/* */\r
+/* This computes C = A & B */\r
+/* */\r
+/* res is C, the result. C may be A and/or B (e.g., X=X&X) */\r
+/* lhs is A */\r
+/* rhs is B */\r
+/* set is the context (used for result length and error report) */\r
+/* */\r
+/* C must have space for set->digits digits. */\r
+/* */\r
+/* Logical function restrictions apply (see above); a NaN is */\r
+/* returned with Invalid_operation if a restriction is violated. */\r
+/* ------------------------------------------------------------------ */\r
+decNumber * decNumberAnd(decNumber *res, const decNumber *lhs,\r
+ const decNumber *rhs, decContext *set) {\r
+ const Unit *ua, *ub; // -> operands\r
+ const Unit *msua, *msub; // -> operand msus\r
+ Unit *uc, *msuc; // -> result and its msu\r
+ Int msudigs; // digits in res msu\r
+ #if DECCHECK\r
+ if (decCheckOperands(res, lhs, rhs, set)) return res;\r
+ #endif\r
+\r
+ if (lhs->exponent!=0 || decNumberIsSpecial(lhs) || decNumberIsNegative(lhs)\r
+ || rhs->exponent!=0 || decNumberIsSpecial(rhs) || decNumberIsNegative(rhs)) {\r
+ decStatus(res, DEC_Invalid_operation, set);\r
+ return res;\r
+ }\r
+\r
+ // operands are valid\r
+ ua=lhs->lsu; // bottom-up\r
+ ub=rhs->lsu; // ..\r
+ uc=res->lsu; // ..\r
+ msua=ua+D2U(lhs->digits)-1; // -> msu of lhs\r
+ msub=ub+D2U(rhs->digits)-1; // -> msu of rhs\r
+ msuc=uc+D2U(set->digits)-1; // -> msu of result\r
+ msudigs=MSUDIGITS(set->digits); // [faster than remainder]\r
+ for (; uc<=msuc; ua++, ub++, uc++) { // Unit loop\r
+ Unit a, b; // extract units\r
+ if (ua>msua) a=0;\r
+ else a=*ua;\r
+ if (ub>msub) b=0;\r
+ else b=*ub;\r
+ *uc=0; // can now write back\r
+ if (a|b) { // maybe 1 bits to examine\r
+ Int i, j;\r
+ *uc=0; // can now write back\r
+ // This loop could be unrolled and/or use BIN2BCD tables\r
+ for (i=0; i<DECDPUN; i++) {\r
+ if (a&b&1) *uc=*uc+(Unit)powers[i]; // effect AND\r
+ j=a%10;\r
+ a=a/10;\r
+ j|=b%10;\r
+ b=b/10;\r
+ if (j>1) {\r
+ decStatus(res, DEC_Invalid_operation, set);\r
+ return res;\r
+ }\r
+ if (uc==msuc && i==msudigs-1) break; // just did final digit\r
+ } // each digit\r
+ } // both OK\r
+ } // each unit\r
+ // [here uc-1 is the msu of the result]\r
+ res->digits=decGetDigits(res->lsu, uc-res->lsu);\r
+ res->exponent=0; // integer\r
+ res->bits=0; // sign=0\r
+ return res; // [no status to set]\r
+ } // decNumberAnd\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decNumberCompare -- compare two Numbers */\r
+/* */\r
+/* This computes C = A ? B */\r
+/* */\r
+/* res is C, the result. C may be A and/or B (e.g., X=X?X) */\r
+/* lhs is A */\r
+/* rhs is B */\r
+/* set is the context */\r
+/* */\r
+/* C must have space for one digit (or NaN). */\r
+/* ------------------------------------------------------------------ */\r
+decNumber * decNumberCompare(decNumber *res, const decNumber *lhs,\r
+ const decNumber *rhs, decContext *set) {\r
+ uInt status=0; // accumulator\r
+ decCompareOp(res, lhs, rhs, set, COMPARE, &status);\r
+ if (status!=0) decStatus(res, status, set);\r
+ return res;\r
+ } // decNumberCompare\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decNumberCompareSignal -- compare, signalling on all NaNs */\r
+/* */\r
+/* This computes C = A ? B */\r
+/* */\r
+/* res is C, the result. C may be A and/or B (e.g., X=X?X) */\r
+/* lhs is A */\r
+/* rhs is B */\r
+/* set is the context */\r
+/* */\r
+/* C must have space for one digit (or NaN). */\r
+/* ------------------------------------------------------------------ */\r
+decNumber * decNumberCompareSignal(decNumber *res, const decNumber *lhs,\r
+ const decNumber *rhs, decContext *set) {\r
+ uInt status=0; // accumulator\r
+ decCompareOp(res, lhs, rhs, set, COMPSIG, &status);\r
+ if (status!=0) decStatus(res, status, set);\r
+ return res;\r
+ } // decNumberCompareSignal\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decNumberCompareTotal -- compare two Numbers, using total ordering */\r
+/* */\r
+/* This computes C = A ? B, under total ordering */\r
+/* */\r
+/* res is C, the result. C may be A and/or B (e.g., X=X?X) */\r
+/* lhs is A */\r
+/* rhs is B */\r
+/* set is the context */\r
+/* */\r
+/* C must have space for one digit; the result will always be one of */\r
+/* -1, 0, or 1. */\r
+/* ------------------------------------------------------------------ */\r
+decNumber * decNumberCompareTotal(decNumber *res, const decNumber *lhs,\r
+ const decNumber *rhs, decContext *set) {\r
+ uInt status=0; // accumulator\r
+ decCompareOp(res, lhs, rhs, set, COMPTOTAL, &status);\r
+ if (status!=0) decStatus(res, status, set);\r
+ return res;\r
+ } // decNumberCompareTotal\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decNumberCompareTotalMag -- compare, total ordering of magnitudes */\r
+/* */\r
+/* This computes C = |A| ? |B|, under total ordering */\r
+/* */\r
+/* res is C, the result. C may be A and/or B (e.g., X=X?X) */\r
+/* lhs is A */\r
+/* rhs is B */\r
+/* set is the context */\r
+/* */\r
+/* C must have space for one digit; the result will always be one of */\r
+/* -1, 0, or 1. */\r
+/* ------------------------------------------------------------------ */\r
+decNumber * decNumberCompareTotalMag(decNumber *res, const decNumber *lhs,\r
+ const decNumber *rhs, decContext *set) {\r
+ uInt status=0; // accumulator\r
+ uInt needbytes; // for space calculations\r
+ decNumber bufa[D2N(DECBUFFER+1)];// +1 in case DECBUFFER=0\r
+ decNumber *allocbufa=NULL; // -> allocated bufa, iff allocated\r
+ decNumber bufb[D2N(DECBUFFER+1)];\r
+ decNumber *allocbufb=NULL; // -> allocated bufb, iff allocated\r
+ decNumber *a, *b; // temporary pointers\r
+\r
+ #if DECCHECK\r
+ if (decCheckOperands(res, lhs, rhs, set)) return res;\r
+ #endif\r
+\r
+ do { // protect allocated storage\r
+ // if either is negative, take a copy and absolute\r
+ if (decNumberIsNegative(lhs)) { // lhs<0\r
+ a=bufa;\r
+ needbytes=sizeof(decNumber)+(D2U(lhs->digits)-1)*sizeof(Unit);\r
+ if (needbytes>sizeof(bufa)) { // need malloc space\r
+ allocbufa=(decNumber *)malloc(needbytes);\r
+ if (allocbufa==NULL) { // hopeless -- abandon\r
+ status|=DEC_Insufficient_storage;\r
+ break;}\r
+ a=allocbufa; // use the allocated space\r
+ }\r
+ decNumberCopy(a, lhs); // copy content\r
+ a->bits&=~DECNEG; // .. and clear the sign\r
+ lhs=a; // use copy from here on\r
+ }\r
+ if (decNumberIsNegative(rhs)) { // rhs<0\r
+ b=bufb;\r
+ needbytes=sizeof(decNumber)+(D2U(rhs->digits)-1)*sizeof(Unit);\r
+ if (needbytes>sizeof(bufb)) { // need malloc space\r
+ allocbufb=(decNumber *)malloc(needbytes);\r
+ if (allocbufb==NULL) { // hopeless -- abandon\r
+ status|=DEC_Insufficient_storage;\r
+ break;}\r
+ b=allocbufb; // use the allocated space\r
+ }\r
+ decNumberCopy(b, rhs); // copy content\r
+ b->bits&=~DECNEG; // .. and clear the sign\r
+ rhs=b; // use copy from here on\r
+ }\r
+ decCompareOp(res, lhs, rhs, set, COMPTOTAL, &status);\r
+ } while(0); // end protected\r
+\r
+ if (allocbufa!=NULL) free(allocbufa); // drop any storage used\r
+ if (allocbufb!=NULL) free(allocbufb); // ..\r
+ if (status!=0) decStatus(res, status, set);\r
+ return res;\r
+ } // decNumberCompareTotalMag\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decNumberDivide -- divide one number by another */\r
+/* */\r
+/* This computes C = A / B */\r
+/* */\r
+/* res is C, the result. C may be A and/or B (e.g., X=X/X) */\r
+/* lhs is A */\r
+/* rhs is B */\r
+/* set is the context */\r
+/* */\r
+/* C must have space for set->digits digits. */\r
+/* ------------------------------------------------------------------ */\r
+decNumber * decNumberDivide(decNumber *res, const decNumber *lhs,\r
+ const decNumber *rhs, decContext *set) {\r
+ uInt status=0; // accumulator\r
+ decDivideOp(res, lhs, rhs, set, DIVIDE, &status);\r
+ if (status!=0) decStatus(res, status, set);\r
+ #if DECCHECK\r
+ decCheckInexact(res, set);\r
+ #endif\r
+ return res;\r
+ } // decNumberDivide\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decNumberDivideInteger -- divide and return integer quotient */\r
+/* */\r
+/* This computes C = A # B, where # is the integer divide operator */\r
+/* */\r
+/* res is C, the result. C may be A and/or B (e.g., X=X#X) */\r
+/* lhs is A */\r
+/* rhs is B */\r
+/* set is the context */\r
+/* */\r
+/* C must have space for set->digits digits. */\r
+/* ------------------------------------------------------------------ */\r
+decNumber * decNumberDivideInteger(decNumber *res, const decNumber *lhs,\r
+ const decNumber *rhs, decContext *set) {\r
+ uInt status=0; // accumulator\r
+ decDivideOp(res, lhs, rhs, set, DIVIDEINT, &status);\r
+ if (status!=0) decStatus(res, status, set);\r
+ return res;\r
+ } // decNumberDivideInteger\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decNumberExp -- exponentiation */\r
+/* */\r
+/* This computes C = exp(A) */\r
+/* */\r
+/* res is C, the result. C may be A */\r
+/* rhs is A */\r
+/* set is the context; note that rounding mode has no effect */\r
+/* */\r
+/* C must have space for set->digits digits. */\r
+/* */\r
+/* Mathematical function restrictions apply (see above); a NaN is */\r
+/* returned with Invalid_operation if a restriction is violated. */\r
+/* */\r
+/* Finite results will always be full precision and Inexact, except */\r
+/* when A is a zero or -Infinity (giving 1 or 0 respectively). */\r
+/* */\r
+/* An Inexact result is rounded using DEC_ROUND_HALF_EVEN; it will */\r
+/* almost always be correctly rounded, but may be up to 1 ulp in */\r
+/* error in rare cases. */\r
+/* ------------------------------------------------------------------ */\r
+/* This is a wrapper for decExpOp which can handle the slightly wider */\r
+/* (double) range needed by Ln (which has to be able to calculate */\r
+/* exp(-a) where a can be the tiniest number (Ntiny). */\r
+/* ------------------------------------------------------------------ */\r
+decNumber * decNumberExp(decNumber *res, const decNumber *rhs,\r
+ decContext *set) {\r
+ uInt status=0; // accumulator\r
+ #if DECSUBSET\r
+ decNumber *allocrhs=NULL; // non-NULL if rounded rhs allocated\r
+ #endif\r
+\r
+ #if DECCHECK\r
+ if (decCheckOperands(res, DECUNUSED, rhs, set)) return res;\r
+ #endif\r
+\r
+ // Check restrictions; these restrictions ensure that if h=8 (see\r
+ // decExpOp) then the result will either overflow or underflow to 0.\r
+ // Other math functions restrict the input range, too, for inverses.\r
+ // If not violated then carry out the operation.\r
+ if (!decCheckMath(rhs, set, &status)) do { // protect allocation\r
+ #if DECSUBSET\r
+ if (!set->extended) {\r
+ // reduce operand and set lostDigits status, as needed\r
+ if (rhs->digits>set->digits) {\r
+ allocrhs=decRoundOperand(rhs, set, &status);\r
+ if (allocrhs==NULL) break;\r
+ rhs=allocrhs;\r
+ }\r
+ }\r
+ #endif\r
+ decExpOp(res, rhs, set, &status);\r
+ } while(0); // end protected\r
+\r
+ #if DECSUBSET\r
+ if (allocrhs !=NULL) free(allocrhs); // drop any storage used\r
+ #endif\r
+ // apply significant status\r
+ if (status!=0) decStatus(res, status, set);\r
+ #if DECCHECK\r
+ decCheckInexact(res, set);\r
+ #endif\r
+ return res;\r
+ } // decNumberExp\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decNumberFMA -- fused multiply add */\r
+/* */\r
+/* This computes D = (A * B) + C with only one rounding */\r
+/* */\r
+/* res is D, the result. D may be A or B or C (e.g., X=FMA(X,X,X)) */\r
+/* lhs is A */\r
+/* rhs is B */\r
+/* fhs is C [far hand side] */\r
+/* set is the context */\r
+/* */\r
+/* Mathematical function restrictions apply (see above); a NaN is */\r
+/* returned with Invalid_operation if a restriction is violated. */\r
+/* */\r
+/* C must have space for set->digits digits. */\r
+/* ------------------------------------------------------------------ */\r
+decNumber * decNumberFMA(decNumber *res, const decNumber *lhs,\r
+ const decNumber *rhs, const decNumber *fhs,\r
+ decContext *set) {\r
+ uInt status=0; // accumulator\r
+ decContext dcmul; // context for the multiplication\r
+ uInt needbytes; // for space calculations\r
+ decNumber bufa[D2N(DECBUFFER*2+1)];\r
+ decNumber *allocbufa=NULL; // -> allocated bufa, iff allocated\r
+ decNumber *acc; // accumulator pointer\r
+ decNumber dzero; // work\r
+\r
+ #if DECCHECK\r
+ if (decCheckOperands(res, lhs, rhs, set)) return res;\r
+ if (decCheckOperands(res, fhs, DECUNUSED, set)) return res;\r
+ #endif\r
+\r
+ do { // protect allocated storage\r
+ #if DECSUBSET\r
+ if (!set->extended) { // [undefined if subset]\r
+ status|=DEC_Invalid_operation;\r
+ break;}\r
+ #endif\r
+ // Check math restrictions [these ensure no overflow or underflow]\r
+ if ((!decNumberIsSpecial(lhs) && decCheckMath(lhs, set, &status))\r
+ || (!decNumberIsSpecial(rhs) && decCheckMath(rhs, set, &status))\r
+ || (!decNumberIsSpecial(fhs) && decCheckMath(fhs, set, &status))) break;\r
+ // set up context for multiply\r
+ dcmul=*set;\r
+ dcmul.digits=lhs->digits+rhs->digits; // just enough\r
+ // [The above may be an over-estimate for subset arithmetic, but that's OK]\r
+ dcmul.emax=DEC_MAX_EMAX; // effectively unbounded ..\r
+ dcmul.emin=DEC_MIN_EMIN; // [thanks to Math restrictions]\r
+ // set up decNumber space to receive the result of the multiply\r
+ acc=bufa; // may fit\r
+ needbytes=sizeof(decNumber)+(D2U(dcmul.digits)-1)*sizeof(Unit);\r
+ if (needbytes>sizeof(bufa)) { // need malloc space\r
+ allocbufa=(decNumber *)malloc(needbytes);\r
+ if (allocbufa==NULL) { // hopeless -- abandon\r
+ status|=DEC_Insufficient_storage;\r
+ break;}\r
+ acc=allocbufa; // use the allocated space\r
+ }\r
+ // multiply with extended range and necessary precision\r
+ //printf("emin=%ld\n", dcmul.emin);\r
+ decMultiplyOp(acc, lhs, rhs, &dcmul, &status);\r
+ // Only Invalid operation (from sNaN or Inf * 0) is possible in\r
+ // status; if either is seen than ignore fhs (in case it is\r
+ // another sNaN) and set acc to NaN unless we had an sNaN\r
+ // [decMultiplyOp leaves that to caller]\r
+ // Note sNaN has to go through addOp to shorten payload if\r
+ // necessary\r
+ if ((status&DEC_Invalid_operation)!=0) {\r
+ if (!(status&DEC_sNaN)) { // but be true invalid\r
+ decNumberZero(res); // acc not yet set\r
+ res->bits=DECNAN;\r
+ break;\r
+ }\r
+ decNumberZero(&dzero); // make 0 (any non-NaN would do)\r
+ fhs=&dzero; // use that\r
+ }\r
+ #if DECCHECK\r
+ else { // multiply was OK\r
+ if (status!=0) printf("Status=%08lx after FMA multiply\n", (LI)status);\r
+ }\r
+ #endif\r
+ // add the third operand and result -> res, and all is done\r
+ decAddOp(res, acc, fhs, set, 0, &status);\r
+ } while(0); // end protected\r
+\r
+ if (allocbufa!=NULL) free(allocbufa); // drop any storage used\r
+ if (status!=0) decStatus(res, status, set);\r
+ #if DECCHECK\r
+ decCheckInexact(res, set);\r
+ #endif\r
+ return res;\r
+ } // decNumberFMA\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decNumberInvert -- invert a Number, digitwise */\r
+/* */\r
+/* This computes C = ~A */\r
+/* */\r
+/* res is C, the result. C may be A (e.g., X=~X) */\r
+/* rhs is A */\r
+/* set is the context (used for result length and error report) */\r
+/* */\r
+/* C must have space for set->digits digits. */\r
+/* */\r
+/* Logical function restrictions apply (see above); a NaN is */\r
+/* returned with Invalid_operation if a restriction is violated. */\r
+/* ------------------------------------------------------------------ */\r
+decNumber * decNumberInvert(decNumber *res, const decNumber *rhs,\r
+ decContext *set) {\r
+ const Unit *ua, *msua; // -> operand and its msu\r
+ Unit *uc, *msuc; // -> result and its msu\r
+ Int msudigs; // digits in res msu\r
+ #if DECCHECK\r
+ if (decCheckOperands(res, DECUNUSED, rhs, set)) return res;\r
+ #endif\r
+\r
+ if (rhs->exponent!=0 || decNumberIsSpecial(rhs) || decNumberIsNegative(rhs)) {\r
+ decStatus(res, DEC_Invalid_operation, set);\r
+ return res;\r
+ }\r
+ // operand is valid\r
+ ua=rhs->lsu; // bottom-up\r
+ uc=res->lsu; // ..\r
+ msua=ua+D2U(rhs->digits)-1; // -> msu of rhs\r
+ msuc=uc+D2U(set->digits)-1; // -> msu of result\r
+ msudigs=MSUDIGITS(set->digits); // [faster than remainder]\r
+ for (; uc<=msuc; ua++, uc++) { // Unit loop\r
+ Unit a; // extract unit\r
+ Int i, j; // work\r
+ if (ua>msua) a=0;\r
+ else a=*ua;\r
+ *uc=0; // can now write back\r
+ // always need to examine all bits in rhs\r
+ // This loop could be unrolled and/or use BIN2BCD tables\r
+ for (i=0; i<DECDPUN; i++) {\r
+ if ((~a)&1) *uc=*uc+(Unit)powers[i]; // effect INVERT\r
+ j=a%10;\r
+ a=a/10;\r
+ if (j>1) {\r
+ decStatus(res, DEC_Invalid_operation, set);\r
+ return res;\r
+ }\r
+ if (uc==msuc && i==msudigs-1) break; // just did final digit\r
+ } // each digit\r
+ } // each unit\r
+ // [here uc-1 is the msu of the result]\r
+ res->digits=decGetDigits(res->lsu, uc-res->lsu);\r
+ res->exponent=0; // integer\r
+ res->bits=0; // sign=0\r
+ return res; // [no status to set]\r
+ } // decNumberInvert\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decNumberLn -- natural logarithm */\r
+/* */\r
+/* This computes C = ln(A) */\r
+/* */\r
+/* res is C, the result. C may be A */\r
+/* rhs is A */\r
+/* set is the context; note that rounding mode has no effect */\r
+/* */\r
+/* C must have space for set->digits digits. */\r
+/* */\r
+/* Notable cases: */\r
+/* A<0 -> Invalid */\r
+/* A=0 -> -Infinity (Exact) */\r
+/* A=+Infinity -> +Infinity (Exact) */\r
+/* A=1 exactly -> 0 (Exact) */\r
+/* */\r
+/* Mathematical function restrictions apply (see above); a NaN is */\r
+/* returned with Invalid_operation if a restriction is violated. */\r
+/* */\r
+/* An Inexact result is rounded using DEC_ROUND_HALF_EVEN; it will */\r
+/* almost always be correctly rounded, but may be up to 1 ulp in */\r
+/* error in rare cases. */\r
+/* ------------------------------------------------------------------ */\r
+/* This is a wrapper for decLnOp which can handle the slightly wider */\r
+/* (+11) range needed by Ln, Log10, etc. (which may have to be able */\r
+/* to calculate at p+e+2). */\r
+/* ------------------------------------------------------------------ */\r
+decNumber * decNumberLn(decNumber *res, const decNumber *rhs,\r
+ decContext *set) {\r
+ uInt status=0; // accumulator\r
+ #if DECSUBSET\r
+ decNumber *allocrhs=NULL; // non-NULL if rounded rhs allocated\r
+ #endif\r
+\r
+ #if DECCHECK\r
+ if (decCheckOperands(res, DECUNUSED, rhs, set)) return res;\r
+ #endif\r
+\r
+ // Check restrictions; this is a math function; if not violated\r
+ // then carry out the operation.\r
+ if (!decCheckMath(rhs, set, &status)) do { // protect allocation\r
+ #if DECSUBSET\r
+ if (!set->extended) {\r
+ // reduce operand and set lostDigits status, as needed\r
+ if (rhs->digits>set->digits) {\r
+ allocrhs=decRoundOperand(rhs, set, &status);\r
+ if (allocrhs==NULL) break;\r
+ rhs=allocrhs;\r
+ }\r
+ // special check in subset for rhs=0\r
+ if (ISZERO(rhs)) { // +/- zeros -> error\r
+ status|=DEC_Invalid_operation;\r
+ break;}\r
+ } // extended=0\r
+ #endif\r
+ decLnOp(res, rhs, set, &status);\r
+ } while(0); // end protected\r
+\r
+ #if DECSUBSET\r
+ if (allocrhs !=NULL) free(allocrhs); // drop any storage used\r
+ #endif\r
+ // apply significant status\r
+ if (status!=0) decStatus(res, status, set);\r
+ #if DECCHECK\r
+ decCheckInexact(res, set);\r
+ #endif\r
+ return res;\r
+ } // decNumberLn\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decNumberLogB - get adjusted exponent, by 754 rules */\r
+/* */\r
+/* This computes C = adjustedexponent(A) */\r
+/* */\r
+/* res is C, the result. C may be A */\r
+/* rhs is A */\r
+/* set is the context, used only for digits and status */\r
+/* */\r
+/* For an unrounded result, digits may need to be 10 (A might have */\r
+/* 10**9 digits and an exponent of +999999999, or one digit and an */\r
+/* exponent of -1999999999). */\r
+/* */\r
+/* This returns the adjusted exponent of A after (in theory) padding */\r
+/* with zeros on the right to set->digits digits while keeping the */\r
+/* same value. The exponent is not limited by emin/emax. */\r
+/* */\r
+/* Notable cases: */\r
+/* A<0 -> Use |A| */\r
+/* A=0 -> -Infinity (Division by zero) */\r
+/* A=Infinite -> +Infinity (Exact) */\r
+/* A=1 exactly -> 0 (Exact) */\r
+/* NaNs are propagated as usual */\r
+/* ------------------------------------------------------------------ */\r
+decNumber * decNumberLogB(decNumber *res, const decNumber *rhs,\r
+ decContext *set) {\r
+ uInt status=0; // accumulator\r
+\r
+ #if DECCHECK\r
+ if (decCheckOperands(res, DECUNUSED, rhs, set)) return res;\r
+ #endif\r
+\r
+ // NaNs as usual; Infinities return +Infinity; 0->oops\r
+ if (decNumberIsNaN(rhs)) decNaNs(res, rhs, NULL, set, &status);\r
+ else if (decNumberIsInfinite(rhs)) decNumberCopyAbs(res, rhs);\r
+ else if (decNumberIsZero(rhs)) {\r
+ decNumberZero(res); // prepare for Infinity\r
+ res->bits=DECNEG|DECINF; // -Infinity\r
+ status|=DEC_Division_by_zero; // as per 754\r
+ }\r
+ else { // finite non-zero\r
+ Int ae=rhs->exponent+rhs->digits-1; // adjusted exponent\r
+ if (set->digits>=10) decNumberFromInt32(res, ae); // lay it out\r
+ else {\r
+ decNumber buft[D2N(10)]; // temporary number\r
+ decNumber *t=buft; // ..\r
+ decNumberFromInt32(t, ae); // lay it out\r
+ decNumberPlus(res, t, set); // round as necessary\r
+ }\r
+ }\r
+\r
+ if (status!=0) decStatus(res, status, set);\r
+ return res;\r
+ } // decNumberLogB\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decNumberLog10 -- logarithm in base 10 */\r
+/* */\r
+/* This computes C = log10(A) */\r
+/* */\r
+/* res is C, the result. C may be A */\r
+/* rhs is A */\r
+/* set is the context; note that rounding mode has no effect */\r
+/* */\r
+/* C must have space for set->digits digits. */\r
+/* */\r
+/* Notable cases: */\r
+/* A<0 -> Invalid */\r
+/* A=0 -> -Infinity (Exact) */\r
+/* A=+Infinity -> +Infinity (Exact) */\r
+/* A=10**n (if n is an integer) -> n (Exact) */\r
+/* */\r
+/* Mathematical function restrictions apply (see above); a NaN is */\r
+/* returned with Invalid_operation if a restriction is violated. */\r
+/* */\r
+/* An Inexact result is rounded using DEC_ROUND_HALF_EVEN; it will */\r
+/* almost always be correctly rounded, but may be up to 1 ulp in */\r
+/* error in rare cases. */\r
+/* ------------------------------------------------------------------ */\r
+/* This calculates ln(A)/ln(10) using appropriate precision. For */\r
+/* ln(A) this is the max(p, rhs->digits + t) + 3, where p is the */\r
+/* requested digits and t is the number of digits in the exponent */\r
+/* (maximum 6). For ln(10) it is p + 3; this is often handled by the */\r
+/* fastpath in decLnOp. The final division is done to the requested */\r
+/* precision. */\r
+/* ------------------------------------------------------------------ */\r
+decNumber * decNumberLog10(decNumber *res, const decNumber *rhs,\r
+ decContext *set) {\r
+ uInt status=0, ignore=0; // status accumulators\r
+ uInt needbytes; // for space calculations\r
+ Int p; // working precision\r
+ Int t; // digits in exponent of A\r
+\r
+ // buffers for a and b working decimals\r
+ // (adjustment calculator, same size)\r
+ decNumber bufa[D2N(DECBUFFER+2)];\r
+ decNumber *allocbufa=NULL; // -> allocated bufa, iff allocated\r
+ decNumber *a=bufa; // temporary a\r
+ decNumber bufb[D2N(DECBUFFER+2)];\r
+ decNumber *allocbufb=NULL; // -> allocated bufb, iff allocated\r
+ decNumber *b=bufb; // temporary b\r
+ decNumber bufw[D2N(10)]; // working 2-10 digit number\r
+ decNumber *w=bufw; // ..\r
+ #if DECSUBSET\r
+ decNumber *allocrhs=NULL; // non-NULL if rounded rhs allocated\r
+ #endif\r
+\r
+ decContext aset; // working context\r
+\r
+ #if DECCHECK\r
+ if (decCheckOperands(res, DECUNUSED, rhs, set)) return res;\r
+ #endif\r
+\r
+ // Check restrictions; this is a math function; if not violated\r
+ // then carry out the operation.\r
+ if (!decCheckMath(rhs, set, &status)) do { // protect malloc\r
+ #if DECSUBSET\r
+ if (!set->extended) {\r
+ // reduce operand and set lostDigits status, as needed\r
+ if (rhs->digits>set->digits) {\r
+ allocrhs=decRoundOperand(rhs, set, &status);\r
+ if (allocrhs==NULL) break;\r
+ rhs=allocrhs;\r
+ }\r
+ // special check in subset for rhs=0\r
+ if (ISZERO(rhs)) { // +/- zeros -> error\r
+ status|=DEC_Invalid_operation;\r
+ break;}\r
+ } // extended=0\r
+ #endif\r
+\r
+ decContextDefault(&aset, DEC_INIT_DECIMAL64); // clean context\r
+\r
+ // handle exact powers of 10; only check if +ve finite\r
+ if (!(rhs->bits&(DECNEG|DECSPECIAL)) && !ISZERO(rhs)) {\r
+ Int residue=0; // (no residue)\r
+ uInt copystat=0; // clean status\r
+\r
+ // round to a single digit...\r
+ aset.digits=1;\r
+ decCopyFit(w, rhs, &aset, &residue, ©stat); // copy & shorten\r
+ // if exact and the digit is 1, rhs is a power of 10\r
+ if (!(copystat&DEC_Inexact) && w->lsu[0]==1) {\r
+ // the exponent, conveniently, is the power of 10; making\r
+ // this the result needs a little care as it might not fit,\r
+ // so first convert it into the working number, and then move\r
+ // to res\r
+ decNumberFromInt32(w, w->exponent);\r
+ residue=0;\r
+ decCopyFit(res, w, set, &residue, &status); // copy & round\r
+ decFinish(res, set, &residue, &status); // cleanup/set flags\r
+ break;\r
+ } // not a power of 10\r
+ } // not a candidate for exact\r
+\r
+ // simplify the information-content calculation to use 'total\r
+ // number of digits in a, including exponent' as compared to the\r
+ // requested digits, as increasing this will only rarely cost an\r
+ // iteration in ln(a) anyway\r
+ t=6; // it can never be >6\r
+\r
+ // allocate space when needed...\r
+ p=(rhs->digits+t>set->digits?rhs->digits+t:set->digits)+3;\r
+ needbytes=sizeof(decNumber)+(D2U(p)-1)*sizeof(Unit);\r
+ if (needbytes>sizeof(bufa)) { // need malloc space\r
+ allocbufa=(decNumber *)malloc(needbytes);\r
+ if (allocbufa==NULL) { // hopeless -- abandon\r
+ status|=DEC_Insufficient_storage;\r
+ break;}\r
+ a=allocbufa; // use the allocated space\r
+ }\r
+ aset.digits=p; // as calculated\r
+ aset.emax=DEC_MAX_MATH; // usual bounds\r
+ aset.emin=-DEC_MAX_MATH; // ..\r
+ aset.clamp=0; // and no concrete format\r
+ decLnOp(a, rhs, &aset, &status); // a=ln(rhs)\r
+\r
+ // skip the division if the result so far is infinite, NaN, or\r
+ // zero, or there was an error; note NaN from sNaN needs copy\r
+ if (status&DEC_NaNs && !(status&DEC_sNaN)) break;\r
+ if (a->bits&DECSPECIAL || ISZERO(a)) {\r
+ decNumberCopy(res, a); // [will fit]\r
+ break;}\r
+\r
+ // for ln(10) an extra 3 digits of precision are needed\r
+ p=set->digits+3;\r
+ needbytes=sizeof(decNumber)+(D2U(p)-1)*sizeof(Unit);\r
+ if (needbytes>sizeof(bufb)) { // need malloc space\r
+ allocbufb=(decNumber *)malloc(needbytes);\r
+ if (allocbufb==NULL) { // hopeless -- abandon\r
+ status|=DEC_Insufficient_storage;\r
+ break;}\r
+ b=allocbufb; // use the allocated space\r
+ }\r
+ decNumberZero(w); // set up 10...\r
+ #if DECDPUN==1\r
+ w->lsu[1]=1; w->lsu[0]=0; // ..\r
+ #else\r
+ w->lsu[0]=10; // ..\r
+ #endif\r
+ w->digits=2; // ..\r
+\r
+ aset.digits=p;\r
+ decLnOp(b, w, &aset, &ignore); // b=ln(10)\r
+\r
+ aset.digits=set->digits; // for final divide\r
+ decDivideOp(res, a, b, &aset, DIVIDE, &status); // into result\r
+ } while(0); // [for break]\r
+\r
+ if (allocbufa!=NULL) free(allocbufa); // drop any storage used\r
+ if (allocbufb!=NULL) free(allocbufb); // ..\r
+ #if DECSUBSET\r
+ if (allocrhs !=NULL) free(allocrhs); // ..\r
+ #endif\r
+ // apply significant status\r
+ if (status!=0) decStatus(res, status, set);\r
+ #if DECCHECK\r
+ decCheckInexact(res, set);\r
+ #endif\r
+ return res;\r
+ } // decNumberLog10\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decNumberMax -- compare two Numbers and return the maximum */\r
+/* */\r
+/* This computes C = A ? B, returning the maximum by 754 rules */\r
+/* */\r
+/* res is C, the result. C may be A and/or B (e.g., X=X?X) */\r
+/* lhs is A */\r
+/* rhs is B */\r
+/* set is the context */\r
+/* */\r
+/* C must have space for set->digits digits. */\r
+/* ------------------------------------------------------------------ */\r
+decNumber * decNumberMax(decNumber *res, const decNumber *lhs,\r
+ const decNumber *rhs, decContext *set) {\r
+ uInt status=0; // accumulator\r
+ decCompareOp(res, lhs, rhs, set, COMPMAX, &status);\r
+ if (status!=0) decStatus(res, status, set);\r
+ #if DECCHECK\r
+ decCheckInexact(res, set);\r
+ #endif\r
+ return res;\r
+ } // decNumberMax\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decNumberMaxMag -- compare and return the maximum by magnitude */\r
+/* */\r
+/* This computes C = A ? B, returning the maximum by 754 rules */\r
+/* */\r
+/* res is C, the result. C may be A and/or B (e.g., X=X?X) */\r
+/* lhs is A */\r
+/* rhs is B */\r
+/* set is the context */\r
+/* */\r
+/* C must have space for set->digits digits. */\r
+/* ------------------------------------------------------------------ */\r
+decNumber * decNumberMaxMag(decNumber *res, const decNumber *lhs,\r
+ const decNumber *rhs, decContext *set) {\r
+ uInt status=0; // accumulator\r
+ decCompareOp(res, lhs, rhs, set, COMPMAXMAG, &status);\r
+ if (status!=0) decStatus(res, status, set);\r
+ #if DECCHECK\r
+ decCheckInexact(res, set);\r
+ #endif\r
+ return res;\r
+ } // decNumberMaxMag\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decNumberMin -- compare two Numbers and return the minimum */\r
+/* */\r
+/* This computes C = A ? B, returning the minimum by 754 rules */\r
+/* */\r
+/* res is C, the result. C may be A and/or B (e.g., X=X?X) */\r
+/* lhs is A */\r
+/* rhs is B */\r
+/* set is the context */\r
+/* */\r
+/* C must have space for set->digits digits. */\r
+/* ------------------------------------------------------------------ */\r
+decNumber * decNumberMin(decNumber *res, const decNumber *lhs,\r
+ const decNumber *rhs, decContext *set) {\r
+ uInt status=0; // accumulator\r
+ decCompareOp(res, lhs, rhs, set, COMPMIN, &status);\r
+ if (status!=0) decStatus(res, status, set);\r
+ #if DECCHECK\r
+ decCheckInexact(res, set);\r
+ #endif\r
+ return res;\r
+ } // decNumberMin\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decNumberMinMag -- compare and return the minimum by magnitude */\r
+/* */\r
+/* This computes C = A ? B, returning the minimum by 754 rules */\r
+/* */\r
+/* res is C, the result. C may be A and/or B (e.g., X=X?X) */\r
+/* lhs is A */\r
+/* rhs is B */\r
+/* set is the context */\r
+/* */\r
+/* C must have space for set->digits digits. */\r
+/* ------------------------------------------------------------------ */\r
+decNumber * decNumberMinMag(decNumber *res, const decNumber *lhs,\r
+ const decNumber *rhs, decContext *set) {\r
+ uInt status=0; // accumulator\r
+ decCompareOp(res, lhs, rhs, set, COMPMINMAG, &status);\r
+ if (status!=0) decStatus(res, status, set);\r
+ #if DECCHECK\r
+ decCheckInexact(res, set);\r
+ #endif\r
+ return res;\r
+ } // decNumberMinMag\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decNumberMinus -- prefix minus operator */\r
+/* */\r
+/* This computes C = 0 - A */\r
+/* */\r
+/* res is C, the result. C may be A */\r
+/* rhs is A */\r
+/* set is the context */\r
+/* */\r
+/* See also decNumberCopyNegate for a quiet bitwise version of this. */\r
+/* C must have space for set->digits digits. */\r
+/* ------------------------------------------------------------------ */\r
+/* Simply use AddOp for the subtract, which will do the necessary. */\r
+/* ------------------------------------------------------------------ */\r
+decNumber * decNumberMinus(decNumber *res, const decNumber *rhs,\r
+ decContext *set) {\r
+ decNumber dzero;\r
+ uInt status=0; // accumulator\r
+\r
+ #if DECCHECK\r
+ if (decCheckOperands(res, DECUNUSED, rhs, set)) return res;\r
+ #endif\r
+\r
+ decNumberZero(&dzero); // make 0\r
+ dzero.exponent=rhs->exponent; // [no coefficient expansion]\r
+ decAddOp(res, &dzero, rhs, set, DECNEG, &status);\r
+ if (status!=0) decStatus(res, status, set);\r
+ #if DECCHECK\r
+ decCheckInexact(res, set);\r
+ #endif\r
+ return res;\r
+ } // decNumberMinus\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decNumberNextMinus -- next towards -Infinity */\r
+/* */\r
+/* This computes C = A - infinitesimal, rounded towards -Infinity */\r
+/* */\r
+/* res is C, the result. C may be A */\r
+/* rhs is A */\r
+/* set is the context */\r
+/* */\r
+/* This is a generalization of 754 NextDown. */\r
+/* ------------------------------------------------------------------ */\r
+decNumber * decNumberNextMinus(decNumber *res, const decNumber *rhs,\r
+ decContext *set) {\r
+ decNumber dtiny; // constant\r
+ decContext workset=*set; // work\r
+ uInt status=0; // accumulator\r
+ #if DECCHECK\r
+ if (decCheckOperands(res, DECUNUSED, rhs, set)) return res;\r
+ #endif\r
+\r
+ // +Infinity is the special case\r
+ if ((rhs->bits&(DECINF|DECNEG))==DECINF) {\r
+ decSetMaxValue(res, set); // is +ve\r
+ // there is no status to set\r
+ return res;\r
+ }\r
+ decNumberZero(&dtiny); // start with 0\r
+ dtiny.lsu[0]=1; // make number that is ..\r
+ dtiny.exponent=DEC_MIN_EMIN-1; // .. smaller than tiniest\r
+ workset.round=DEC_ROUND_FLOOR;\r
+ decAddOp(res, rhs, &dtiny, &workset, DECNEG, &status);\r
+ status&=DEC_Invalid_operation|DEC_sNaN; // only sNaN Invalid please\r
+ if (status!=0) decStatus(res, status, set);\r
+ return res;\r
+ } // decNumberNextMinus\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decNumberNextPlus -- next towards +Infinity */\r
+/* */\r
+/* This computes C = A + infinitesimal, rounded towards +Infinity */\r
+/* */\r
+/* res is C, the result. C may be A */\r
+/* rhs is A */\r
+/* set is the context */\r
+/* */\r
+/* This is a generalization of 754 NextUp. */\r
+/* ------------------------------------------------------------------ */\r
+decNumber * decNumberNextPlus(decNumber *res, const decNumber *rhs,\r
+ decContext *set) {\r
+ decNumber dtiny; // constant\r
+ decContext workset=*set; // work\r
+ uInt status=0; // accumulator\r
+ #if DECCHECK\r
+ if (decCheckOperands(res, DECUNUSED, rhs, set)) return res;\r
+ #endif\r
+\r
+ // -Infinity is the special case\r
+ if ((rhs->bits&(DECINF|DECNEG))==(DECINF|DECNEG)) {\r
+ decSetMaxValue(res, set);\r
+ res->bits=DECNEG; // negative\r
+ // there is no status to set\r
+ return res;\r
+ }\r
+ decNumberZero(&dtiny); // start with 0\r
+ dtiny.lsu[0]=1; // make number that is ..\r
+ dtiny.exponent=DEC_MIN_EMIN-1; // .. smaller than tiniest\r
+ workset.round=DEC_ROUND_CEILING;\r
+ decAddOp(res, rhs, &dtiny, &workset, 0, &status);\r
+ status&=DEC_Invalid_operation|DEC_sNaN; // only sNaN Invalid please\r
+ if (status!=0) decStatus(res, status, set);\r
+ return res;\r
+ } // decNumberNextPlus\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decNumberNextToward -- next towards rhs */\r
+/* */\r
+/* This computes C = A +/- infinitesimal, rounded towards */\r
+/* +/-Infinity in the direction of B, as per 754-1985 nextafter */\r
+/* modified during revision but dropped from 754-2008. */\r
+/* */\r
+/* res is C, the result. C may be A or B. */\r
+/* lhs is A */\r
+/* rhs is B */\r
+/* set is the context */\r
+/* */\r
+/* This is a generalization of 754-1985 NextAfter. */\r
+/* ------------------------------------------------------------------ */\r
+decNumber * decNumberNextToward(decNumber *res, const decNumber *lhs,\r
+ const decNumber *rhs, decContext *set) {\r
+ decNumber dtiny; // constant\r
+ decContext workset=*set; // work\r
+ Int result; // ..\r
+ uInt status=0; // accumulator\r
+ #if DECCHECK\r
+ if (decCheckOperands(res, lhs, rhs, set)) return res;\r
+ #endif\r
+\r
+ if (decNumberIsNaN(lhs) || decNumberIsNaN(rhs)) {\r
+ decNaNs(res, lhs, rhs, set, &status);\r
+ }\r
+ else { // Is numeric, so no chance of sNaN Invalid, etc.\r
+ result=decCompare(lhs, rhs, 0); // sign matters\r
+ if (result==BADINT) status|=DEC_Insufficient_storage; // rare\r
+ else { // valid compare\r
+ if (result==0) decNumberCopySign(res, lhs, rhs); // easy\r
+ else { // differ: need NextPlus or NextMinus\r
+ uByte sub; // add or subtract\r
+ if (result<0) { // lhs<rhs, do nextplus\r
+ // -Infinity is the special case\r
+ if ((lhs->bits&(DECINF|DECNEG))==(DECINF|DECNEG)) {\r
+ decSetMaxValue(res, set);\r
+ res->bits=DECNEG; // negative\r
+ return res; // there is no status to set\r
+ }\r
+ workset.round=DEC_ROUND_CEILING;\r
+ sub=0; // add, please\r
+ } // plus\r
+ else { // lhs>rhs, do nextminus\r
+ // +Infinity is the special case\r
+ if ((lhs->bits&(DECINF|DECNEG))==DECINF) {\r
+ decSetMaxValue(res, set);\r
+ return res; // there is no status to set\r
+ }\r
+ workset.round=DEC_ROUND_FLOOR;\r
+ sub=DECNEG; // subtract, please\r
+ } // minus\r
+ decNumberZero(&dtiny); // start with 0\r
+ dtiny.lsu[0]=1; // make number that is ..\r
+ dtiny.exponent=DEC_MIN_EMIN-1; // .. smaller than tiniest\r
+ decAddOp(res, lhs, &dtiny, &workset, sub, &status); // + or -\r
+ // turn off exceptions if the result is a normal number\r
+ // (including Nmin), otherwise let all status through\r
+ if (decNumberIsNormal(res, set)) status=0;\r
+ } // unequal\r
+ } // compare OK\r
+ } // numeric\r
+ if (status!=0) decStatus(res, status, set);\r
+ return res;\r
+ } // decNumberNextToward\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decNumberOr -- OR two Numbers, digitwise */\r
+/* */\r
+/* This computes C = A | B */\r
+/* */\r
+/* res is C, the result. C may be A and/or B (e.g., X=X|X) */\r
+/* lhs is A */\r
+/* rhs is B */\r
+/* set is the context (used for result length and error report) */\r
+/* */\r
+/* C must have space for set->digits digits. */\r
+/* */\r
+/* Logical function restrictions apply (see above); a NaN is */\r
+/* returned with Invalid_operation if a restriction is violated. */\r
+/* ------------------------------------------------------------------ */\r
+decNumber * decNumberOr(decNumber *res, const decNumber *lhs,\r
+ const decNumber *rhs, decContext *set) {\r
+ const Unit *ua, *ub; // -> operands\r
+ const Unit *msua, *msub; // -> operand msus\r
+ Unit *uc, *msuc; // -> result and its msu\r
+ Int msudigs; // digits in res msu\r
+ #if DECCHECK\r
+ if (decCheckOperands(res, lhs, rhs, set)) return res;\r
+ #endif\r
+\r
+ if (lhs->exponent!=0 || decNumberIsSpecial(lhs) || decNumberIsNegative(lhs)\r
+ || rhs->exponent!=0 || decNumberIsSpecial(rhs) || decNumberIsNegative(rhs)) {\r
+ decStatus(res, DEC_Invalid_operation, set);\r
+ return res;\r
+ }\r
+ // operands are valid\r
+ ua=lhs->lsu; // bottom-up\r
+ ub=rhs->lsu; // ..\r
+ uc=res->lsu; // ..\r
+ msua=ua+D2U(lhs->digits)-1; // -> msu of lhs\r
+ msub=ub+D2U(rhs->digits)-1; // -> msu of rhs\r
+ msuc=uc+D2U(set->digits)-1; // -> msu of result\r
+ msudigs=MSUDIGITS(set->digits); // [faster than remainder]\r
+ for (; uc<=msuc; ua++, ub++, uc++) { // Unit loop\r
+ Unit a, b; // extract units\r
+ if (ua>msua) a=0;\r
+ else a=*ua;\r
+ if (ub>msub) b=0;\r
+ else b=*ub;\r
+ *uc=0; // can now write back\r
+ if (a|b) { // maybe 1 bits to examine\r
+ Int i, j;\r
+ // This loop could be unrolled and/or use BIN2BCD tables\r
+ for (i=0; i<DECDPUN; i++) {\r
+ if ((a|b)&1) *uc=*uc+(Unit)powers[i]; // effect OR\r
+ j=a%10;\r
+ a=a/10;\r
+ j|=b%10;\r
+ b=b/10;\r
+ if (j>1) {\r
+ decStatus(res, DEC_Invalid_operation, set);\r
+ return res;\r
+ }\r
+ if (uc==msuc && i==msudigs-1) break; // just did final digit\r
+ } // each digit\r
+ } // non-zero\r
+ } // each unit\r
+ // [here uc-1 is the msu of the result]\r
+ res->digits=decGetDigits(res->lsu, uc-res->lsu);\r
+ res->exponent=0; // integer\r
+ res->bits=0; // sign=0\r
+ return res; // [no status to set]\r
+ } // decNumberOr\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decNumberPlus -- prefix plus operator */\r
+/* */\r
+/* This computes C = 0 + A */\r
+/* */\r
+/* res is C, the result. C may be A */\r
+/* rhs is A */\r
+/* set is the context */\r
+/* */\r
+/* See also decNumberCopy for a quiet bitwise version of this. */\r
+/* C must have space for set->digits digits. */\r
+/* ------------------------------------------------------------------ */\r
+/* This simply uses AddOp; Add will take fast path after preparing A. */\r
+/* Performance is a concern here, as this routine is often used to */\r
+/* check operands and apply rounding and overflow/underflow testing. */\r
+/* ------------------------------------------------------------------ */\r
+decNumber * decNumberPlus(decNumber *res, const decNumber *rhs,\r
+ decContext *set) {\r
+ decNumber dzero;\r
+ uInt status=0; // accumulator\r
+ #if DECCHECK\r
+ if (decCheckOperands(res, DECUNUSED, rhs, set)) return res;\r
+ #endif\r
+\r
+ decNumberZero(&dzero); // make 0\r
+ dzero.exponent=rhs->exponent; // [no coefficient expansion]\r
+ decAddOp(res, &dzero, rhs, set, 0, &status);\r
+ if (status!=0) decStatus(res, status, set);\r
+ #if DECCHECK\r
+ decCheckInexact(res, set);\r
+ #endif\r
+ return res;\r
+ } // decNumberPlus\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decNumberMultiply -- multiply two Numbers */\r
+/* */\r
+/* This computes C = A x B */\r
+/* */\r
+/* res is C, the result. C may be A and/or B (e.g., X=X+X) */\r
+/* lhs is A */\r
+/* rhs is B */\r
+/* set is the context */\r
+/* */\r
+/* C must have space for set->digits digits. */\r
+/* ------------------------------------------------------------------ */\r
+decNumber * decNumberMultiply(decNumber *res, const decNumber *lhs,\r
+ const decNumber *rhs, decContext *set) {\r
+ uInt status=0; // accumulator\r
+ decMultiplyOp(res, lhs, rhs, set, &status);\r
+ if (status!=0) decStatus(res, status, set);\r
+ #if DECCHECK\r
+ decCheckInexact(res, set);\r
+ #endif\r
+ return res;\r
+ } // decNumberMultiply\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decNumberPower -- raise a number to a power */\r
+/* */\r
+/* This computes C = A ** B */\r
+/* */\r
+/* res is C, the result. C may be A and/or B (e.g., X=X**X) */\r
+/* lhs is A */\r
+/* rhs is B */\r
+/* set is the context */\r
+/* */\r
+/* C must have space for set->digits digits. */\r
+/* */\r
+/* Mathematical function restrictions apply (see above); a NaN is */\r
+/* returned with Invalid_operation if a restriction is violated. */\r
+/* */\r
+/* However, if 1999999997<=B<=999999999 and B is an integer then the */\r
+/* restrictions on A and the context are relaxed to the usual bounds, */\r
+/* for compatibility with the earlier (integer power only) version */\r
+/* of this function. */\r
+/* */\r
+/* When B is an integer, the result may be exact, even if rounded. */\r
+/* */\r
+/* The final result is rounded according to the context; it will */\r
+/* almost always be correctly rounded, but may be up to 1 ulp in */\r
+/* error in rare cases. */\r
+/* ------------------------------------------------------------------ */\r
+decNumber * decNumberPower(decNumber *res, const decNumber *lhs,\r
+ const decNumber *rhs, decContext *set) {\r
+ #if DECSUBSET\r
+ decNumber *alloclhs=NULL; // non-NULL if rounded lhs allocated\r
+ decNumber *allocrhs=NULL; // .., rhs\r
+ #endif\r
+ decNumber *allocdac=NULL; // -> allocated acc buffer, iff used\r
+ decNumber *allocinv=NULL; // -> allocated 1/x buffer, iff used\r
+ Int reqdigits=set->digits; // requested DIGITS\r
+ Int n; // rhs in binary\r
+ Flag rhsint=0; // 1 if rhs is an integer\r
+ Flag useint=0; // 1 if can use integer calculation\r
+ Flag isoddint=0; // 1 if rhs is an integer and odd\r
+ Int i; // work\r
+ #if DECSUBSET\r
+ Int dropped; // ..\r
+ #endif\r
+ uInt needbytes; // buffer size needed\r
+ Flag seenbit; // seen a bit while powering\r
+ Int residue=0; // rounding residue\r
+ uInt status=0; // accumulators\r
+ uByte bits=0; // result sign if errors\r
+ decContext aset; // working context\r
+ decNumber dnOne; // work value 1...\r
+ // local accumulator buffer [a decNumber, with digits+elength+1 digits]\r
+ decNumber dacbuff[D2N(DECBUFFER+9)];\r
+ decNumber *dac=dacbuff; // -> result accumulator\r
+ // same again for possible 1/lhs calculation\r
+ decNumber invbuff[D2N(DECBUFFER+9)];\r
+\r
+ #if DECCHECK\r
+ if (decCheckOperands(res, lhs, rhs, set)) return res;\r
+ #endif\r
+\r
+ do { // protect allocated storage\r
+ #if DECSUBSET\r
+ if (!set->extended) { // reduce operands and set status, as needed\r
+ if (lhs->digits>reqdigits) {\r
+ alloclhs=decRoundOperand(lhs, set, &status);\r
+ if (alloclhs==NULL) break;\r
+ lhs=alloclhs;\r
+ }\r
+ if (rhs->digits>reqdigits) {\r
+ allocrhs=decRoundOperand(rhs, set, &status);\r
+ if (allocrhs==NULL) break;\r
+ rhs=allocrhs;\r
+ }\r
+ }\r
+ #endif\r
+ // [following code does not require input rounding]\r
+\r
+ // handle NaNs and rhs Infinity (lhs infinity is harder)\r
+ if (SPECIALARGS) {\r
+ if (decNumberIsNaN(lhs) || decNumberIsNaN(rhs)) { // NaNs\r
+ decNaNs(res, lhs, rhs, set, &status);\r
+ break;}\r
+ if (decNumberIsInfinite(rhs)) { // rhs Infinity\r
+ Flag rhsneg=rhs->bits&DECNEG; // save rhs sign\r
+ if (decNumberIsNegative(lhs) // lhs<0\r
+ && !decNumberIsZero(lhs)) // ..\r
+ status|=DEC_Invalid_operation;\r
+ else { // lhs >=0\r
+ decNumberZero(&dnOne); // set up 1\r
+ dnOne.lsu[0]=1;\r
+ decNumberCompare(dac, lhs, &dnOne, set); // lhs ? 1\r
+ decNumberZero(res); // prepare for 0/1/Infinity\r
+ if (decNumberIsNegative(dac)) { // lhs<1\r
+ if (rhsneg) res->bits|=DECINF; // +Infinity [else is +0]\r
+ }\r
+ else if (dac->lsu[0]==0) { // lhs=1\r
+ // 1**Infinity is inexact, so return fully-padded 1.0000\r
+ Int shift=set->digits-1;\r
+ *res->lsu=1; // was 0, make int 1\r
+ res->digits=decShiftToMost(res->lsu, 1, shift);\r
+ res->exponent=-shift; // make 1.0000...\r
+ status|=DEC_Inexact|DEC_Rounded; // deemed inexact\r
+ }\r
+ else { // lhs>1\r
+ if (!rhsneg) res->bits|=DECINF; // +Infinity [else is +0]\r
+ }\r
+ } // lhs>=0\r
+ break;}\r
+ // [lhs infinity drops through]\r
+ } // specials\r
+\r
+ // Original rhs may be an integer that fits and is in range\r
+ n=decGetInt(rhs);\r
+ if (n!=BADINT) { // it is an integer\r
+ rhsint=1; // record the fact for 1**n\r
+ isoddint=(Flag)n&1; // [works even if big]\r
+ if (n!=BIGEVEN && n!=BIGODD) // can use integer path?\r
+ useint=1; // looks good\r
+ }\r
+\r
+ if (decNumberIsNegative(lhs) // -x ..\r
+ && isoddint) bits=DECNEG; // .. to an odd power\r
+\r
+ // handle LHS infinity\r
+ if (decNumberIsInfinite(lhs)) { // [NaNs already handled]\r
+ uByte rbits=rhs->bits; // save\r
+ decNumberZero(res); // prepare\r
+ if (n==0) *res->lsu=1; // [-]Inf**0 => 1\r
+ else {\r
+ // -Inf**nonint -> error\r
+ if (!rhsint && decNumberIsNegative(lhs)) {\r
+ status|=DEC_Invalid_operation; // -Inf**nonint is error\r
+ break;}\r
+ if (!(rbits & DECNEG)) bits|=DECINF; // was not a **-n\r
+ // [otherwise will be 0 or -0]\r
+ res->bits=bits;\r
+ }\r
+ break;}\r
+\r
+ // similarly handle LHS zero\r
+ if (decNumberIsZero(lhs)) {\r
+ if (n==0) { // 0**0 => Error\r
+ #if DECSUBSET\r
+ if (!set->extended) { // [unless subset]\r
+ decNumberZero(res);\r
+ *res->lsu=1; // return 1\r
+ break;}\r
+ #endif\r
+ status|=DEC_Invalid_operation;\r
+ }\r
+ else { // 0**x\r
+ uByte rbits=rhs->bits; // save\r
+ if (rbits & DECNEG) { // was a 0**(-n)\r
+ #if DECSUBSET\r
+ if (!set->extended) { // [bad if subset]\r
+ status|=DEC_Invalid_operation;\r
+ break;}\r
+ #endif\r
+ bits|=DECINF;\r
+ }\r
+ decNumberZero(res); // prepare\r
+ // [otherwise will be 0 or -0]\r
+ res->bits=bits;\r
+ }\r
+ break;}\r
+\r
+ // here both lhs and rhs are finite; rhs==0 is handled in the\r
+ // integer path. Next handle the non-integer cases\r
+ if (!useint) { // non-integral rhs\r
+ // any -ve lhs is bad, as is either operand or context out of\r
+ // bounds\r
+ if (decNumberIsNegative(lhs)) {\r
+ status|=DEC_Invalid_operation;\r
+ break;}\r
+ if (decCheckMath(lhs, set, &status)\r
+ || decCheckMath(rhs, set, &status)) break; // variable status\r
+\r
+ decContextDefault(&aset, DEC_INIT_DECIMAL64); // clean context\r
+ aset.emax=DEC_MAX_MATH; // usual bounds\r
+ aset.emin=-DEC_MAX_MATH; // ..\r
+ aset.clamp=0; // and no concrete format\r
+\r
+ // calculate the result using exp(ln(lhs)*rhs), which can\r
+ // all be done into the accumulator, dac. The precision needed\r
+ // is enough to contain the full information in the lhs (which\r
+ // is the total digits, including exponent), or the requested\r
+ // precision, if larger, + 4; 6 is used for the exponent\r
+ // maximum length, and this is also used when it is shorter\r
+ // than the requested digits as it greatly reduces the >0.5 ulp\r
+ // cases at little cost (because Ln doubles digits each\r
+ // iteration so a few extra digits rarely causes an extra\r
+ // iteration)\r
+ aset.digits=MAXI(lhs->digits, set->digits)+6+4;\r
+ } // non-integer rhs\r
+\r
+ else { // rhs is in-range integer\r
+ if (n==0) { // x**0 = 1\r
+ // (0**0 was handled above)\r
+ decNumberZero(res); // result=1\r
+ *res->lsu=1; // ..\r
+ break;}\r
+ // rhs is a non-zero integer\r
+ if (n<0) n=-n; // use abs(n)\r
+\r
+ aset=*set; // clone the context\r
+ aset.round=DEC_ROUND_HALF_EVEN; // internally use balanced\r
+ // calculate the working DIGITS\r
+ aset.digits=reqdigits+(rhs->digits+rhs->exponent)+2;\r
+ #if DECSUBSET\r
+ if (!set->extended) aset.digits--; // use classic precision\r
+ #endif\r
+ // it's an error if this is more than can be handled\r
+ if (aset.digits>DECNUMMAXP) {status|=DEC_Invalid_operation; break;}\r
+ } // integer path\r
+\r
+ // aset.digits is the count of digits for the accumulator needed\r
+ // if accumulator is too long for local storage, then allocate\r
+ needbytes=sizeof(decNumber)+(D2U(aset.digits)-1)*sizeof(Unit);\r
+ // [needbytes also used below if 1/lhs needed]\r
+ if (needbytes>sizeof(dacbuff)) {\r
+ allocdac=(decNumber *)malloc(needbytes);\r
+ if (allocdac==NULL) { // hopeless -- abandon\r
+ status|=DEC_Insufficient_storage;\r
+ break;}\r
+ dac=allocdac; // use the allocated space\r
+ }\r
+ // here, aset is set up and accumulator is ready for use\r
+\r
+ if (!useint) { // non-integral rhs\r
+ // x ** y; special-case x=1 here as it will otherwise always\r
+ // reduce to integer 1; decLnOp has a fastpath which detects\r
+ // the case of x=1\r
+ decLnOp(dac, lhs, &aset, &status); // dac=ln(lhs)\r
+ // [no error possible, as lhs 0 already handled]\r
+ if (ISZERO(dac)) { // x==1, 1.0, etc.\r
+ // need to return fully-padded 1.0000 etc., but rhsint->1\r
+ *dac->lsu=1; // was 0, make int 1\r
+ if (!rhsint) { // add padding\r
+ Int shift=set->digits-1;\r
+ dac->digits=decShiftToMost(dac->lsu, 1, shift);\r
+ dac->exponent=-shift; // make 1.0000...\r
+ status|=DEC_Inexact|DEC_Rounded; // deemed inexact\r
+ }\r
+ }\r
+ else {\r
+ decMultiplyOp(dac, dac, rhs, &aset, &status); // dac=dac*rhs\r
+ decExpOp(dac, dac, &aset, &status); // dac=exp(dac)\r
+ }\r
+ // and drop through for final rounding\r
+ } // non-integer rhs\r
+\r
+ else { // carry on with integer\r
+ decNumberZero(dac); // acc=1\r
+ *dac->lsu=1; // ..\r
+\r
+ // if a negative power the constant 1 is needed, and if not subset\r
+ // invert the lhs now rather than inverting the result later\r
+ if (decNumberIsNegative(rhs)) { // was a **-n [hence digits>0]\r
+ decNumber *inv=invbuff; // asssume use fixed buffer\r
+ decNumberCopy(&dnOne, dac); // dnOne=1; [needed now or later]\r
+ #if DECSUBSET\r
+ if (set->extended) { // need to calculate 1/lhs\r
+ #endif\r
+ // divide lhs into 1, putting result in dac [dac=1/dac]\r
+ decDivideOp(dac, &dnOne, lhs, &aset, DIVIDE, &status);\r
+ // now locate or allocate space for the inverted lhs\r
+ if (needbytes>sizeof(invbuff)) {\r
+ allocinv=(decNumber *)malloc(needbytes);\r
+ if (allocinv==NULL) { // hopeless -- abandon\r
+ status|=DEC_Insufficient_storage;\r
+ break;}\r
+ inv=allocinv; // use the allocated space\r
+ }\r
+ // [inv now points to big-enough buffer or allocated storage]\r
+ decNumberCopy(inv, dac); // copy the 1/lhs\r
+ decNumberCopy(dac, &dnOne); // restore acc=1\r
+ lhs=inv; // .. and go forward with new lhs\r
+ #if DECSUBSET\r
+ }\r
+ #endif\r
+ }\r
+\r
+ // Raise-to-the-power loop...\r
+ seenbit=0; // set once a 1-bit is encountered\r
+ for (i=1;;i++){ // for each bit [top bit ignored]\r
+ // abandon if had overflow or terminal underflow\r
+ if (status & (DEC_Overflow|DEC_Underflow)) { // interesting?\r
+ if (status&DEC_Overflow || ISZERO(dac)) break;\r
+ }\r
+ // [the following two lines revealed an optimizer bug in a C++\r
+ // compiler, with symptom: 5**3 -> 25, when n=n+n was used]\r
+ n=n<<1; // move next bit to testable position\r
+ if (n<0) { // top bit is set\r
+ seenbit=1; // OK, significant bit seen\r
+ decMultiplyOp(dac, dac, lhs, &aset, &status); // dac=dac*x\r
+ }\r
+ if (i==31) break; // that was the last bit\r
+ if (!seenbit) continue; // no need to square 1\r
+ decMultiplyOp(dac, dac, dac, &aset, &status); // dac=dac*dac [square]\r
+ } /*i*/ // 32 bits\r
+\r
+ // complete internal overflow or underflow processing\r
+ if (status & (DEC_Overflow|DEC_Underflow)) {\r
+ #if DECSUBSET\r
+ // If subset, and power was negative, reverse the kind of -erflow\r
+ // [1/x not yet done]\r
+ if (!set->extended && decNumberIsNegative(rhs)) {\r
+ if (status & DEC_Overflow)\r
+ status^=DEC_Overflow | DEC_Underflow | DEC_Subnormal;\r
+ else { // trickier -- Underflow may or may not be set\r
+ status&=~(DEC_Underflow | DEC_Subnormal); // [one or both]\r
+ status|=DEC_Overflow;\r
+ }\r
+ }\r
+ #endif\r
+ dac->bits=(dac->bits & ~DECNEG) | bits; // force correct sign\r
+ // round subnormals [to set.digits rather than aset.digits]\r
+ // or set overflow result similarly as required\r
+ decFinalize(dac, set, &residue, &status);\r
+ decNumberCopy(res, dac); // copy to result (is now OK length)\r
+ break;\r
+ }\r
+\r
+ #if DECSUBSET\r
+ if (!set->extended && // subset math\r
+ decNumberIsNegative(rhs)) { // was a **-n [hence digits>0]\r
+ // so divide result into 1 [dac=1/dac]\r
+ decDivideOp(dac, &dnOne, dac, &aset, DIVIDE, &status);\r
+ }\r
+ #endif\r
+ } // rhs integer path\r
+\r
+ // reduce result to the requested length and copy to result\r
+ decCopyFit(res, dac, set, &residue, &status);\r
+ decFinish(res, set, &residue, &status); // final cleanup\r
+ #if DECSUBSET\r
+ if (!set->extended) decTrim(res, set, 0, 1, &dropped); // trailing zeros\r
+ #endif\r
+ } while(0); // end protected\r
+\r
+ if (allocdac!=NULL) free(allocdac); // drop any storage used\r
+ if (allocinv!=NULL) free(allocinv); // ..\r
+ #if DECSUBSET\r
+ if (alloclhs!=NULL) free(alloclhs); // ..\r
+ if (allocrhs!=NULL) free(allocrhs); // ..\r
+ #endif\r
+ if (status!=0) decStatus(res, status, set);\r
+ #if DECCHECK\r
+ decCheckInexact(res, set);\r
+ #endif\r
+ return res;\r
+ } // decNumberPower\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decNumberQuantize -- force exponent to requested value */\r
+/* */\r
+/* This computes C = op(A, B), where op adjusts the coefficient */\r
+/* of C (by rounding or shifting) such that the exponent (-scale) */\r
+/* of C has exponent of B. The numerical value of C will equal A, */\r
+/* except for the effects of any rounding that occurred. */\r
+/* */\r
+/* res is C, the result. C may be A or B */\r
+/* lhs is A, the number to adjust */\r
+/* rhs is B, the number with exponent to match */\r
+/* set is the context */\r
+/* */\r
+/* C must have space for set->digits digits. */\r
+/* */\r
+/* Unless there is an error or the result is infinite, the exponent */\r
+/* after the operation is guaranteed to be equal to that of B. */\r
+/* ------------------------------------------------------------------ */\r
+decNumber * decNumberQuantize(decNumber *res, const decNumber *lhs,\r
+ const decNumber *rhs, decContext *set) {\r
+ uInt status=0; // accumulator\r
+ decQuantizeOp(res, lhs, rhs, set, 1, &status);\r
+ if (status!=0) decStatus(res, status, set);\r
+ return res;\r
+ } // decNumberQuantize\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decNumberReduce -- remove trailing zeros */\r
+/* */\r
+/* This computes C = 0 + A, and normalizes the result */\r
+/* */\r
+/* res is C, the result. C may be A */\r
+/* rhs is A */\r
+/* set is the context */\r
+/* */\r
+/* C must have space for set->digits digits. */\r
+/* ------------------------------------------------------------------ */\r
+// Previously known as Normalize\r
+decNumber * decNumberNormalize(decNumber *res, const decNumber *rhs,\r
+ decContext *set) {\r
+ return decNumberReduce(res, rhs, set);\r
+ } // decNumberNormalize\r
+\r
+decNumber * decNumberReduce(decNumber *res, const decNumber *rhs,\r
+ decContext *set) {\r
+ #if DECSUBSET\r
+ decNumber *allocrhs=NULL; // non-NULL if rounded rhs allocated\r
+ #endif\r
+ uInt status=0; // as usual\r
+ Int residue=0; // as usual\r
+ Int dropped; // work\r
+\r
+ #if DECCHECK\r
+ if (decCheckOperands(res, DECUNUSED, rhs, set)) return res;\r
+ #endif\r
+\r
+ do { // protect allocated storage\r
+ #if DECSUBSET\r
+ if (!set->extended) {\r
+ // reduce operand and set lostDigits status, as needed\r
+ if (rhs->digits>set->digits) {\r
+ allocrhs=decRoundOperand(rhs, set, &status);\r
+ if (allocrhs==NULL) break;\r
+ rhs=allocrhs;\r
+ }\r
+ }\r
+ #endif\r
+ // [following code does not require input rounding]\r
+\r
+ // Infinities copy through; NaNs need usual treatment\r
+ if (decNumberIsNaN(rhs)) {\r
+ decNaNs(res, rhs, NULL, set, &status);\r
+ break;\r
+ }\r
+\r
+ // reduce result to the requested length and copy to result\r
+ decCopyFit(res, rhs, set, &residue, &status); // copy & round\r
+ decFinish(res, set, &residue, &status); // cleanup/set flags\r
+ decTrim(res, set, 1, 0, &dropped); // normalize in place\r
+ // [may clamp]\r
+ } while(0); // end protected\r
+\r
+ #if DECSUBSET\r
+ if (allocrhs !=NULL) free(allocrhs); // ..\r
+ #endif\r
+ if (status!=0) decStatus(res, status, set);// then report status\r
+ return res;\r
+ } // decNumberReduce\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decNumberRescale -- force exponent to requested value */\r
+/* */\r
+/* This computes C = op(A, B), where op adjusts the coefficient */\r
+/* of C (by rounding or shifting) such that the exponent (-scale) */\r
+/* of C has the value B. The numerical value of C will equal A, */\r
+/* except for the effects of any rounding that occurred. */\r
+/* */\r
+/* res is C, the result. C may be A or B */\r
+/* lhs is A, the number to adjust */\r
+/* rhs is B, the requested exponent */\r
+/* set is the context */\r
+/* */\r
+/* C must have space for set->digits digits. */\r
+/* */\r
+/* Unless there is an error or the result is infinite, the exponent */\r
+/* after the operation is guaranteed to be equal to B. */\r
+/* ------------------------------------------------------------------ */\r
+decNumber * decNumberRescale(decNumber *res, const decNumber *lhs,\r
+ const decNumber *rhs, decContext *set) {\r
+ uInt status=0; // accumulator\r
+ decQuantizeOp(res, lhs, rhs, set, 0, &status);\r
+ if (status!=0) decStatus(res, status, set);\r
+ return res;\r
+ } // decNumberRescale\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decNumberRemainder -- divide and return remainder */\r
+/* */\r
+/* This computes C = A % B */\r
+/* */\r
+/* res is C, the result. C may be A and/or B (e.g., X=X%X) */\r
+/* lhs is A */\r
+/* rhs is B */\r
+/* set is the context */\r
+/* */\r
+/* C must have space for set->digits digits. */\r
+/* ------------------------------------------------------------------ */\r
+decNumber * decNumberRemainder(decNumber *res, const decNumber *lhs,\r
+ const decNumber *rhs, decContext *set) {\r
+ uInt status=0; // accumulator\r
+ decDivideOp(res, lhs, rhs, set, REMAINDER, &status);\r
+ if (status!=0) decStatus(res, status, set);\r
+ #if DECCHECK\r
+ decCheckInexact(res, set);\r
+ #endif\r
+ return res;\r
+ } // decNumberRemainder\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decNumberRemainderNear -- divide and return remainder from nearest */\r
+/* */\r
+/* This computes C = A % B, where % is the IEEE remainder operator */\r
+/* */\r
+/* res is C, the result. C may be A and/or B (e.g., X=X%X) */\r
+/* lhs is A */\r
+/* rhs is B */\r
+/* set is the context */\r
+/* */\r
+/* C must have space for set->digits digits. */\r
+/* ------------------------------------------------------------------ */\r
+decNumber * decNumberRemainderNear(decNumber *res, const decNumber *lhs,\r
+ const decNumber *rhs, decContext *set) {\r
+ uInt status=0; // accumulator\r
+ decDivideOp(res, lhs, rhs, set, REMNEAR, &status);\r
+ if (status!=0) decStatus(res, status, set);\r
+ #if DECCHECK\r
+ decCheckInexact(res, set);\r
+ #endif\r
+ return res;\r
+ } // decNumberRemainderNear\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decNumberRotate -- rotate the coefficient of a Number left/right */\r
+/* */\r
+/* This computes C = A rot B (in base ten and rotating set->digits */\r
+/* digits). */\r
+/* */\r
+/* res is C, the result. C may be A and/or B (e.g., X=XrotX) */\r
+/* lhs is A */\r
+/* rhs is B, the number of digits to rotate (-ve to right) */\r
+/* set is the context */\r
+/* */\r
+/* The digits of the coefficient of A are rotated to the left (if B */\r
+/* is positive) or to the right (if B is negative) without adjusting */\r
+/* the exponent or the sign of A. If lhs->digits is less than */\r
+/* set->digits the coefficient is padded with zeros on the left */\r
+/* before the rotate. Any leading zeros in the result are removed */\r
+/* as usual. */\r
+/* */\r
+/* B must be an integer (q=0) and in the range -set->digits through */\r
+/* +set->digits. */\r
+/* C must have space for set->digits digits. */\r
+/* NaNs are propagated as usual. Infinities are unaffected (but */\r
+/* B must be valid). No status is set unless B is invalid or an */\r
+/* operand is an sNaN. */\r
+/* ------------------------------------------------------------------ */\r
+decNumber * decNumberRotate(decNumber *res, const decNumber *lhs,\r
+ const decNumber *rhs, decContext *set) {\r
+ uInt status=0; // accumulator\r
+ Int rotate; // rhs as an Int\r
+\r
+ #if DECCHECK\r
+ if (decCheckOperands(res, lhs, rhs, set)) return res;\r
+ #endif\r
+\r
+ // NaNs propagate as normal\r
+ if (decNumberIsNaN(lhs) || decNumberIsNaN(rhs))\r
+ decNaNs(res, lhs, rhs, set, &status);\r
+ // rhs must be an integer\r
+ else if (decNumberIsInfinite(rhs) || rhs->exponent!=0)\r
+ status=DEC_Invalid_operation;\r
+ else { // both numeric, rhs is an integer\r
+ rotate=decGetInt(rhs); // [cannot fail]\r
+ if (rotate==BADINT // something bad ..\r
+ || rotate==BIGODD || rotate==BIGEVEN // .. very big ..\r
+ || abs(rotate)>set->digits) // .. or out of range\r
+ status=DEC_Invalid_operation;\r
+ else { // rhs is OK\r
+ decNumberCopy(res, lhs);\r
+ // convert -ve rotate to equivalent positive rotation\r
+ if (rotate<0) rotate=set->digits+rotate;\r
+ if (rotate!=0 && rotate!=set->digits // zero or full rotation\r
+ && !decNumberIsInfinite(res)) { // lhs was infinite\r
+ // left-rotate to do; 0 < rotate < set->digits\r
+ uInt units, shift; // work\r
+ uInt msudigits; // digits in result msu\r
+ Unit *msu=res->lsu+D2U(res->digits)-1; // current msu\r
+ Unit *msumax=res->lsu+D2U(set->digits)-1; // rotation msu\r
+ for (msu++; msu<=msumax; msu++) *msu=0; // ensure high units=0\r
+ res->digits=set->digits; // now full-length\r
+ msudigits=MSUDIGITS(res->digits); // actual digits in msu\r
+\r
+ // rotation here is done in-place, in three steps\r
+ // 1. shift all to least up to one unit to unit-align final\r
+ // lsd [any digits shifted out are rotated to the left,\r
+ // abutted to the original msd (which may require split)]\r
+ //\r
+ // [if there are no whole units left to rotate, the\r
+ // rotation is now complete]\r
+ //\r
+ // 2. shift to least, from below the split point only, so that\r
+ // the final msd is in the right place in its Unit [any\r
+ // digits shifted out will fit exactly in the current msu,\r
+ // left aligned, no split required]\r
+ //\r
+ // 3. rotate all the units by reversing left part, right\r
+ // part, and then whole\r
+ //\r
+ // example: rotate right 8 digits (2 units + 2), DECDPUN=3.\r
+ //\r
+ // start: 00a bcd efg hij klm npq\r
+ //\r
+ // 1a 000 0ab cde fgh|ijk lmn [pq saved]\r
+ // 1b 00p qab cde fgh|ijk lmn\r
+ //\r
+ // 2a 00p qab cde fgh|00i jkl [mn saved]\r
+ // 2b mnp qab cde fgh|00i jkl\r
+ //\r
+ // 3a fgh cde qab mnp|00i jkl\r
+ // 3b fgh cde qab mnp|jkl 00i\r
+ // 3c 00i jkl mnp qab cde fgh\r
+\r
+ // Step 1: amount to shift is the partial right-rotate count\r
+ rotate=set->digits-rotate; // make it right-rotate\r
+ units=rotate/DECDPUN; // whole units to rotate\r
+ shift=rotate%DECDPUN; // left-over digits count\r
+ if (shift>0) { // not an exact number of units\r
+ uInt save=res->lsu[0]%powers[shift]; // save low digit(s)\r
+ decShiftToLeast(res->lsu, D2U(res->digits), shift);\r
+ if (shift>msudigits) { // msumax-1 needs >0 digits\r
+ uInt rem=save%powers[shift-msudigits];// split save\r
+ *msumax=(Unit)(save/powers[shift-msudigits]); // and insert\r
+ *(msumax-1)=*(msumax-1)\r
+ +(Unit)(rem*powers[DECDPUN-(shift-msudigits)]); // ..\r
+ }\r
+ else { // all fits in msumax\r
+ *msumax=*msumax+(Unit)(save*powers[msudigits-shift]); // [maybe *1]\r
+ }\r
+ } // digits shift needed\r
+\r
+ // If whole units to rotate...\r
+ if (units>0) { // some to do\r
+ // Step 2: the units to touch are the whole ones in rotate,\r
+ // if any, and the shift is DECDPUN-msudigits (which may be\r
+ // 0, again)\r
+ shift=DECDPUN-msudigits;\r
+ if (shift>0) { // not an exact number of units\r
+ uInt save=res->lsu[0]%powers[shift]; // save low digit(s)\r
+ decShiftToLeast(res->lsu, units, shift);\r
+ *msumax=*msumax+(Unit)(save*powers[msudigits]);\r
+ } // partial shift needed\r
+\r
+ // Step 3: rotate the units array using triple reverse\r
+ // (reversing is easy and fast)\r
+ decReverse(res->lsu+units, msumax); // left part\r
+ decReverse(res->lsu, res->lsu+units-1); // right part\r
+ decReverse(res->lsu, msumax); // whole\r
+ } // whole units to rotate\r
+ // the rotation may have left an undetermined number of zeros\r
+ // on the left, so true length needs to be calculated\r
+ res->digits=decGetDigits(res->lsu, msumax-res->lsu+1);\r
+ } // rotate needed\r
+ } // rhs OK\r
+ } // numerics\r
+ if (status!=0) decStatus(res, status, set);\r
+ return res;\r
+ } // decNumberRotate\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decNumberSameQuantum -- test for equal exponents */\r
+/* */\r
+/* res is the result number, which will contain either 0 or 1 */\r
+/* lhs is a number to test */\r
+/* rhs is the second (usually a pattern) */\r
+/* */\r
+/* No errors are possible and no context is needed. */\r
+/* ------------------------------------------------------------------ */\r
+decNumber * decNumberSameQuantum(decNumber *res, const decNumber *lhs,\r
+ const decNumber *rhs) {\r
+ Unit ret=0; // return value\r
+\r
+ #if DECCHECK\r
+ if (decCheckOperands(res, lhs, rhs, DECUNCONT)) return res;\r
+ #endif\r
+\r
+ if (SPECIALARGS) {\r
+ if (decNumberIsNaN(lhs) && decNumberIsNaN(rhs)) ret=1;\r
+ else if (decNumberIsInfinite(lhs) && decNumberIsInfinite(rhs)) ret=1;\r
+ // [anything else with a special gives 0]\r
+ }\r
+ else if (lhs->exponent==rhs->exponent) ret=1;\r
+\r
+ decNumberZero(res); // OK to overwrite an operand now\r
+ *res->lsu=ret;\r
+ return res;\r
+ } // decNumberSameQuantum\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decNumberScaleB -- multiply by a power of 10 */\r
+/* */\r
+/* This computes C = A x 10**B where B is an integer (q=0) with */\r
+/* maximum magnitude 2*(emax+digits) */\r
+/* */\r
+/* res is C, the result. C may be A or B */\r
+/* lhs is A, the number to adjust */\r
+/* rhs is B, the requested power of ten to use */\r
+/* set is the context */\r
+/* */\r
+/* C must have space for set->digits digits. */\r
+/* */\r
+/* The result may underflow or overflow. */\r
+/* ------------------------------------------------------------------ */\r
+decNumber * decNumberScaleB(decNumber *res, const decNumber *lhs,\r
+ const decNumber *rhs, decContext *set) {\r
+ Int reqexp; // requested exponent change [B]\r
+ uInt status=0; // accumulator\r
+ Int residue; // work\r
+\r
+ #if DECCHECK\r
+ if (decCheckOperands(res, lhs, rhs, set)) return res;\r
+ #endif\r
+\r
+ // Handle special values except lhs infinite\r
+ if (decNumberIsNaN(lhs) || decNumberIsNaN(rhs))\r
+ decNaNs(res, lhs, rhs, set, &status);\r
+ // rhs must be an integer\r
+ else if (decNumberIsInfinite(rhs) || rhs->exponent!=0)\r
+ status=DEC_Invalid_operation;\r
+ else {\r
+ // lhs is a number; rhs is a finite with q==0\r
+ reqexp=decGetInt(rhs); // [cannot fail]\r
+ // maximum range is larger than getInt can handle, so this is\r
+ // more restrictive than the specification\r
+ if (reqexp==BADINT // something bad ..\r
+ || reqexp==BIGODD || reqexp==BIGEVEN // it was huge\r
+ || (abs(reqexp)+1)/2>(set->digits+set->emax)) // .. or out of range\r
+ status=DEC_Invalid_operation;\r
+ else { // rhs is OK\r
+ decNumberCopy(res, lhs); // all done if infinite lhs\r
+ if (!decNumberIsInfinite(res)) { // prepare to scale\r
+ Int exp=res->exponent; // save for overflow test\r
+ res->exponent+=reqexp; // adjust the exponent\r
+ if (((exp^reqexp)>=0) // same sign ...\r
+ && ((exp^res->exponent)<0)) { // .. but result had different\r
+ // the calculation overflowed, so force right treatment\r
+ if (exp<0) res->exponent=DEC_MIN_EMIN-DEC_MAX_DIGITS;\r
+ else res->exponent=DEC_MAX_EMAX+1;\r
+ }\r
+ residue=0;\r
+ decFinalize(res, set, &residue, &status); // final check\r
+ } // finite LHS\r
+ } // rhs OK\r
+ } // rhs finite\r
+ if (status!=0) decStatus(res, status, set);\r
+ return res;\r
+ } // decNumberScaleB\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decNumberShift -- shift the coefficient of a Number left or right */\r
+/* */\r
+/* This computes C = A << B or C = A >> -B (in base ten). */\r
+/* */\r
+/* res is C, the result. C may be A and/or B (e.g., X=X<<X) */\r
+/* lhs is A */\r
+/* rhs is B, the number of digits to shift (-ve to right) */\r
+/* set is the context */\r
+/* */\r
+/* The digits of the coefficient of A are shifted to the left (if B */\r
+/* is positive) or to the right (if B is negative) without adjusting */\r
+/* the exponent or the sign of A. */\r
+/* */\r
+/* B must be an integer (q=0) and in the range -set->digits through */\r
+/* +set->digits. */\r
+/* C must have space for set->digits digits. */\r
+/* NaNs are propagated as usual. Infinities are unaffected (but */\r
+/* B must be valid). No status is set unless B is invalid or an */\r
+/* operand is an sNaN. */\r
+/* ------------------------------------------------------------------ */\r
+decNumber * decNumberShift(decNumber *res, const decNumber *lhs,\r
+ const decNumber *rhs, decContext *set) {\r
+ uInt status=0; // accumulator\r
+ Int shift; // rhs as an Int\r
+\r
+ #if DECCHECK\r
+ if (decCheckOperands(res, lhs, rhs, set)) return res;\r
+ #endif\r
+\r
+ // NaNs propagate as normal\r
+ if (decNumberIsNaN(lhs) || decNumberIsNaN(rhs))\r
+ decNaNs(res, lhs, rhs, set, &status);\r
+ // rhs must be an integer\r
+ else if (decNumberIsInfinite(rhs) || rhs->exponent!=0)\r
+ status=DEC_Invalid_operation;\r
+ else { // both numeric, rhs is an integer\r
+ shift=decGetInt(rhs); // [cannot fail]\r
+ if (shift==BADINT // something bad ..\r
+ || shift==BIGODD || shift==BIGEVEN // .. very big ..\r
+ || abs(shift)>set->digits) // .. or out of range\r
+ status=DEC_Invalid_operation;\r
+ else { // rhs is OK\r
+ decNumberCopy(res, lhs);\r
+ if (shift!=0 && !decNumberIsInfinite(res)) { // something to do\r
+ if (shift>0) { // to left\r
+ if (shift==set->digits) { // removing all\r
+ *res->lsu=0; // so place 0\r
+ res->digits=1; // ..\r
+ }\r
+ else { //\r
+ // first remove leading digits if necessary\r
+ if (res->digits+shift>set->digits) {\r
+ decDecap(res, res->digits+shift-set->digits);\r
+ // that updated res->digits; may have gone to 1 (for a\r
+ // single digit or for zero\r
+ }\r
+ if (res->digits>1 || *res->lsu) // if non-zero..\r
+ res->digits=decShiftToMost(res->lsu, res->digits, shift);\r
+ } // partial left\r
+ } // left\r
+ else { // to right\r
+ if (-shift>=res->digits) { // discarding all\r
+ *res->lsu=0; // so place 0\r
+ res->digits=1; // ..\r
+ }\r
+ else {\r
+ decShiftToLeast(res->lsu, D2U(res->digits), -shift);\r
+ res->digits-=(-shift);\r
+ }\r
+ } // to right\r
+ } // non-0 non-Inf shift\r
+ } // rhs OK\r
+ } // numerics\r
+ if (status!=0) decStatus(res, status, set);\r
+ return res;\r
+ } // decNumberShift\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decNumberSquareRoot -- square root operator */\r
+/* */\r
+/* This computes C = squareroot(A) */\r
+/* */\r
+/* res is C, the result. C may be A */\r
+/* rhs is A */\r
+/* set is the context; note that rounding mode has no effect */\r
+/* */\r
+/* C must have space for set->digits digits. */\r
+/* ------------------------------------------------------------------ */\r
+/* This uses the following varying-precision algorithm in: */\r
+/* */\r
+/* Properly Rounded Variable Precision Square Root, T. E. Hull and */\r
+/* A. Abrham, ACM Transactions on Mathematical Software, Vol 11 #3, */\r
+/* pp229-237, ACM, September 1985. */\r
+/* */\r
+/* The square-root is calculated using Newton's method, after which */\r
+/* a check is made to ensure the result is correctly rounded. */\r
+/* */\r
+/* % [Reformatted original Numerical Turing source code follows.] */\r
+/* function sqrt(x : real) : real */\r
+/* % sqrt(x) returns the properly rounded approximation to the square */\r
+/* % root of x, in the precision of the calling environment, or it */\r
+/* % fails if x < 0. */\r
+/* % t e hull and a abrham, august, 1984 */\r
+/* if x <= 0 then */\r
+/* if x < 0 then */\r
+/* assert false */\r
+/* else */\r
+/* result 0 */\r
+/* end if */\r
+/* end if */\r
+/* var f := setexp(x, 0) % fraction part of x [0.1 <= x < 1] */\r
+/* var e := getexp(x) % exponent part of x */\r
+/* var approx : real */\r
+/* if e mod 2 = 0 then */\r
+/* approx := .259 + .819 * f % approx to root of f */\r
+/* else */\r
+/* f := f/l0 % adjustments */\r
+/* e := e + 1 % for odd */\r
+/* approx := .0819 + 2.59 * f % exponent */\r
+/* end if */\r
+/* */\r
+/* var p:= 3 */\r
+/* const maxp := currentprecision + 2 */\r
+/* loop */\r
+/* p := min(2*p - 2, maxp) % p = 4,6,10, . . . , maxp */\r
+/* precision p */\r
+/* approx := .5 * (approx + f/approx) */\r
+/* exit when p = maxp */\r
+/* end loop */\r
+/* */\r
+/* % approx is now within 1 ulp of the properly rounded square root */\r
+/* % of f; to ensure proper rounding, compare squares of (approx - */\r
+/* % l/2 ulp) and (approx + l/2 ulp) with f. */\r
+/* p := currentprecision */\r
+/* begin */\r
+/* precision p + 2 */\r
+/* const approxsubhalf := approx - setexp(.5, -p) */\r
+/* if mulru(approxsubhalf, approxsubhalf) > f then */\r
+/* approx := approx - setexp(.l, -p + 1) */\r
+/* else */\r
+/* const approxaddhalf := approx + setexp(.5, -p) */\r
+/* if mulrd(approxaddhalf, approxaddhalf) < f then */\r
+/* approx := approx + setexp(.l, -p + 1) */\r
+/* end if */\r
+/* end if */\r
+/* end */\r
+/* result setexp(approx, e div 2) % fix exponent */\r
+/* end sqrt */\r
+/* ------------------------------------------------------------------ */\r
+decNumber * decNumberSquareRoot(decNumber *res, const decNumber *rhs,\r
+ decContext *set) {\r
+ decContext workset, approxset; // work contexts\r
+ decNumber dzero; // used for constant zero\r
+ Int maxp; // largest working precision\r
+ Int workp; // working precision\r
+ Int residue=0; // rounding residue\r
+ uInt status=0, ignore=0; // status accumulators\r
+ uInt rstatus; // ..\r
+ Int exp; // working exponent\r
+ Int ideal; // ideal (preferred) exponent\r
+ Int needbytes; // work\r
+ Int dropped; // ..\r
+\r
+ #if DECSUBSET\r
+ decNumber *allocrhs=NULL; // non-NULL if rounded rhs allocated\r
+ #endif\r
+ // buffer for f [needs +1 in case DECBUFFER 0]\r
+ decNumber buff[D2N(DECBUFFER+1)];\r
+ // buffer for a [needs +2 to match likely maxp]\r
+ decNumber bufa[D2N(DECBUFFER+2)];\r
+ // buffer for temporary, b [must be same size as a]\r
+ decNumber bufb[D2N(DECBUFFER+2)];\r
+ decNumber *allocbuff=NULL; // -> allocated buff, iff allocated\r
+ decNumber *allocbufa=NULL; // -> allocated bufa, iff allocated\r
+ decNumber *allocbufb=NULL; // -> allocated bufb, iff allocated\r
+ decNumber *f=buff; // reduced fraction\r
+ decNumber *a=bufa; // approximation to result\r
+ decNumber *b=bufb; // intermediate result\r
+ // buffer for temporary variable, up to 3 digits\r
+ decNumber buft[D2N(3)];\r
+ decNumber *t=buft; // up-to-3-digit constant or work\r
+\r
+ #if DECCHECK\r
+ if (decCheckOperands(res, DECUNUSED, rhs, set)) return res;\r
+ #endif\r
+\r
+ do { // protect allocated storage\r
+ #if DECSUBSET\r
+ if (!set->extended) {\r
+ // reduce operand and set lostDigits status, as needed\r
+ if (rhs->digits>set->digits) {\r
+ allocrhs=decRoundOperand(rhs, set, &status);\r
+ if (allocrhs==NULL) break;\r
+ // [Note: 'f' allocation below could reuse this buffer if\r
+ // used, but as this is rare they are kept separate for clarity.]\r
+ rhs=allocrhs;\r
+ }\r
+ }\r
+ #endif\r
+ // [following code does not require input rounding]\r
+\r
+ // handle infinities and NaNs\r
+ if (SPECIALARG) {\r
+ if (decNumberIsInfinite(rhs)) { // an infinity\r
+ if (decNumberIsNegative(rhs)) status|=DEC_Invalid_operation;\r
+ else decNumberCopy(res, rhs); // +Infinity\r
+ }\r
+ else decNaNs(res, rhs, NULL, set, &status); // a NaN\r
+ break;\r
+ }\r
+\r
+ // calculate the ideal (preferred) exponent [floor(exp/2)]\r
+ // [It would be nicer to write: ideal=rhs->exponent>>1, but this\r
+ // generates a compiler warning. Generated code is the same.]\r
+ ideal=(rhs->exponent&~1)/2; // target\r
+\r
+ // handle zeros\r
+ if (ISZERO(rhs)) {\r
+ decNumberCopy(res, rhs); // could be 0 or -0\r
+ res->exponent=ideal; // use the ideal [safe]\r
+ // use decFinish to clamp any out-of-range exponent, etc.\r
+ decFinish(res, set, &residue, &status);\r
+ break;\r
+ }\r
+\r
+ // any other -x is an oops\r
+ if (decNumberIsNegative(rhs)) {\r
+ status|=DEC_Invalid_operation;\r
+ break;\r
+ }\r
+\r
+ // space is needed for three working variables\r
+ // f -- the same precision as the RHS, reduced to 0.01->0.99...\r
+ // a -- Hull's approximation -- precision, when assigned, is\r
+ // currentprecision+1 or the input argument precision,\r
+ // whichever is larger (+2 for use as temporary)\r
+ // b -- intermediate temporary result (same size as a)\r
+ // if any is too long for local storage, then allocate\r
+ workp=MAXI(set->digits+1, rhs->digits); // actual rounding precision\r
+ workp=MAXI(workp, 7); // at least 7 for low cases\r
+ maxp=workp+2; // largest working precision\r
+\r
+ needbytes=sizeof(decNumber)+(D2U(rhs->digits)-1)*sizeof(Unit);\r
+ if (needbytes>(Int)sizeof(buff)) {\r
+ allocbuff=(decNumber *)malloc(needbytes);\r
+ if (allocbuff==NULL) { // hopeless -- abandon\r
+ status|=DEC_Insufficient_storage;\r
+ break;}\r
+ f=allocbuff; // use the allocated space\r
+ }\r
+ // a and b both need to be able to hold a maxp-length number\r
+ needbytes=sizeof(decNumber)+(D2U(maxp)-1)*sizeof(Unit);\r
+ if (needbytes>(Int)sizeof(bufa)) { // [same applies to b]\r
+ allocbufa=(decNumber *)malloc(needbytes);\r
+ allocbufb=(decNumber *)malloc(needbytes);\r
+ if (allocbufa==NULL || allocbufb==NULL) { // hopeless\r
+ status|=DEC_Insufficient_storage;\r
+ break;}\r
+ a=allocbufa; // use the allocated spaces\r
+ b=allocbufb; // ..\r
+ }\r
+\r
+ // copy rhs -> f, save exponent, and reduce so 0.1 <= f < 1\r
+ decNumberCopy(f, rhs);\r
+ exp=f->exponent+f->digits; // adjusted to Hull rules\r
+ f->exponent=-(f->digits); // to range\r
+\r
+ // set up working context\r
+ decContextDefault(&workset, DEC_INIT_DECIMAL64);\r
+ workset.emax=DEC_MAX_EMAX;\r
+ workset.emin=DEC_MIN_EMIN;\r
+\r
+ // [Until further notice, no error is possible and status bits\r
+ // (Rounded, etc.) should be ignored, not accumulated.]\r
+\r
+ // Calculate initial approximation, and allow for odd exponent\r
+ workset.digits=workp; // p for initial calculation\r
+ t->bits=0; t->digits=3;\r
+ a->bits=0; a->digits=3;\r
+ if ((exp & 1)==0) { // even exponent\r
+ // Set t=0.259, a=0.819\r
+ t->exponent=-3;\r
+ a->exponent=-3;\r
+ #if DECDPUN>=3\r
+ t->lsu[0]=259;\r
+ a->lsu[0]=819;\r
+ #elif DECDPUN==2\r
+ t->lsu[0]=59; t->lsu[1]=2;\r
+ a->lsu[0]=19; a->lsu[1]=8;\r
+ #else\r
+ t->lsu[0]=9; t->lsu[1]=5; t->lsu[2]=2;\r
+ a->lsu[0]=9; a->lsu[1]=1; a->lsu[2]=8;\r
+ #endif\r
+ }\r
+ else { // odd exponent\r
+ // Set t=0.0819, a=2.59\r
+ f->exponent--; // f=f/10\r
+ exp++; // e=e+1\r
+ t->exponent=-4;\r
+ a->exponent=-2;\r
+ #if DECDPUN>=3\r
+ t->lsu[0]=819;\r
+ a->lsu[0]=259;\r
+ #elif DECDPUN==2\r
+ t->lsu[0]=19; t->lsu[1]=8;\r
+ a->lsu[0]=59; a->lsu[1]=2;\r
+ #else\r
+ t->lsu[0]=9; t->lsu[1]=1; t->lsu[2]=8;\r
+ a->lsu[0]=9; a->lsu[1]=5; a->lsu[2]=2;\r
+ #endif\r
+ }\r
+\r
+ decMultiplyOp(a, a, f, &workset, &ignore); // a=a*f\r
+ decAddOp(a, a, t, &workset, 0, &ignore); // ..+t\r
+ // [a is now the initial approximation for sqrt(f), calculated with\r
+ // currentprecision, which is also a's precision.]\r
+\r
+ // the main calculation loop\r
+ decNumberZero(&dzero); // make 0\r
+ decNumberZero(t); // set t = 0.5\r
+ t->lsu[0]=5; // ..\r
+ t->exponent=-1; // ..\r
+ workset.digits=3; // initial p\r
+ for (; workset.digits<maxp;) {\r
+ // set p to min(2*p - 2, maxp) [hence 3; or: 4, 6, 10, ... , maxp]\r
+ workset.digits=MINI(workset.digits*2-2, maxp);\r
+ // a = 0.5 * (a + f/a)\r
+ // [calculated at p then rounded to currentprecision]\r
+ decDivideOp(b, f, a, &workset, DIVIDE, &ignore); // b=f/a\r
+ decAddOp(b, b, a, &workset, 0, &ignore); // b=b+a\r
+ decMultiplyOp(a, b, t, &workset, &ignore); // a=b*0.5\r
+ } // loop\r
+\r
+ // Here, 0.1 <= a < 1 [Hull], and a has maxp digits\r
+ // now reduce to length, etc.; this needs to be done with a\r
+ // having the correct exponent so as to handle subnormals\r
+ // correctly\r
+ approxset=*set; // get emin, emax, etc.\r
+ approxset.round=DEC_ROUND_HALF_EVEN;\r
+ a->exponent+=exp/2; // set correct exponent\r
+ rstatus=0; // clear status\r
+ residue=0; // .. and accumulator\r
+ decCopyFit(a, a, &approxset, &residue, &rstatus); // reduce (if needed)\r
+ decFinish(a, &approxset, &residue, &rstatus); // clean and finalize\r
+\r
+ // Overflow was possible if the input exponent was out-of-range,\r
+ // in which case quit\r
+ if (rstatus&DEC_Overflow) {\r
+ status=rstatus; // use the status as-is\r
+ decNumberCopy(res, a); // copy to result\r
+ break;\r
+ }\r
+\r
+ // Preserve status except Inexact/Rounded\r
+ status|=(rstatus & ~(DEC_Rounded|DEC_Inexact));\r
+\r
+ // Carry out the Hull correction\r
+ a->exponent-=exp/2; // back to 0.1->1\r
+\r
+ // a is now at final precision and within 1 ulp of the properly\r
+ // rounded square root of f; to ensure proper rounding, compare\r
+ // squares of (a - l/2 ulp) and (a + l/2 ulp) with f.\r
+ // Here workset.digits=maxp and t=0.5, and a->digits determines\r
+ // the ulp\r
+ workset.digits--; // maxp-1 is OK now\r
+ t->exponent=-a->digits-1; // make 0.5 ulp\r
+ decAddOp(b, a, t, &workset, DECNEG, &ignore); // b = a - 0.5 ulp\r
+ workset.round=DEC_ROUND_UP;\r
+ decMultiplyOp(b, b, b, &workset, &ignore); // b = mulru(b, b)\r
+ decCompareOp(b, f, b, &workset, COMPARE, &ignore); // b ? f, reversed\r
+ if (decNumberIsNegative(b)) { // f < b [i.e., b > f]\r
+ // this is the more common adjustment, though both are rare\r
+ t->exponent++; // make 1.0 ulp\r
+ t->lsu[0]=1; // ..\r
+ decAddOp(a, a, t, &workset, DECNEG, &ignore); // a = a - 1 ulp\r
+ // assign to approx [round to length]\r
+ approxset.emin-=exp/2; // adjust to match a\r
+ approxset.emax-=exp/2;\r
+ decAddOp(a, &dzero, a, &approxset, 0, &ignore);\r
+ }\r
+ else {\r
+ decAddOp(b, a, t, &workset, 0, &ignore); // b = a + 0.5 ulp\r
+ workset.round=DEC_ROUND_DOWN;\r
+ decMultiplyOp(b, b, b, &workset, &ignore); // b = mulrd(b, b)\r
+ decCompareOp(b, b, f, &workset, COMPARE, &ignore); // b ? f\r
+ if (decNumberIsNegative(b)) { // b < f\r
+ t->exponent++; // make 1.0 ulp\r
+ t->lsu[0]=1; // ..\r
+ decAddOp(a, a, t, &workset, 0, &ignore); // a = a + 1 ulp\r
+ // assign to approx [round to length]\r
+ approxset.emin-=exp/2; // adjust to match a\r
+ approxset.emax-=exp/2;\r
+ decAddOp(a, &dzero, a, &approxset, 0, &ignore);\r
+ }\r
+ }\r
+ // [no errors are possible in the above, and rounding/inexact during\r
+ // estimation are irrelevant, so status was not accumulated]\r
+\r
+ // Here, 0.1 <= a < 1 (still), so adjust back\r
+ a->exponent+=exp/2; // set correct exponent\r
+\r
+ // count droppable zeros [after any subnormal rounding] by\r
+ // trimming a copy\r
+ decNumberCopy(b, a);\r
+ decTrim(b, set, 1, 1, &dropped); // [drops trailing zeros]\r
+\r
+ // Set Inexact and Rounded. The answer can only be exact if\r
+ // it is short enough so that squaring it could fit in workp\r
+ // digits, so this is the only (relatively rare) condition that\r
+ // a careful check is needed\r
+ if (b->digits*2-1 > workp) { // cannot fit\r
+ status|=DEC_Inexact|DEC_Rounded;\r
+ }\r
+ else { // could be exact/unrounded\r
+ uInt mstatus=0; // local status\r
+ decMultiplyOp(b, b, b, &workset, &mstatus); // try the multiply\r
+ if (mstatus&DEC_Overflow) { // result just won't fit\r
+ status|=DEC_Inexact|DEC_Rounded;\r
+ }\r
+ else { // plausible\r
+ decCompareOp(t, b, rhs, &workset, COMPARE, &mstatus); // b ? rhs\r
+ if (!ISZERO(t)) status|=DEC_Inexact|DEC_Rounded; // not equal\r
+ else { // is Exact\r
+ // here, dropped is the count of trailing zeros in 'a'\r
+ // use closest exponent to ideal...\r
+ Int todrop=ideal-a->exponent; // most that can be dropped\r
+ if (todrop<0) status|=DEC_Rounded; // ideally would add 0s\r
+ else { // unrounded\r
+ // there are some to drop, but emax may not allow all\r
+ Int maxexp=set->emax-set->digits+1;\r
+ Int maxdrop=maxexp-a->exponent;\r
+ if (todrop>maxdrop && set->clamp) { // apply clamping\r
+ todrop=maxdrop;\r
+ status|=DEC_Clamped;\r
+ }\r
+ if (dropped<todrop) { // clamp to those available\r
+ todrop=dropped;\r
+ status|=DEC_Clamped;\r
+ }\r
+ if (todrop>0) { // have some to drop\r
+ decShiftToLeast(a->lsu, D2U(a->digits), todrop);\r
+ a->exponent+=todrop; // maintain numerical value\r
+ a->digits-=todrop; // new length\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ // double-check Underflow, as perhaps the result could not have\r
+ // been subnormal (initial argument too big), or it is now Exact\r
+ if (status&DEC_Underflow) {\r
+ Int ae=rhs->exponent+rhs->digits-1; // adjusted exponent\r
+ // check if truly subnormal\r
+ #if DECEXTFLAG // DEC_Subnormal too\r
+ if (ae>=set->emin*2) status&=~(DEC_Subnormal|DEC_Underflow);\r
+ #else\r
+ if (ae>=set->emin*2) status&=~DEC_Underflow;\r
+ #endif\r
+ // check if truly inexact\r
+ if (!(status&DEC_Inexact)) status&=~DEC_Underflow;\r
+ }\r
+\r
+ decNumberCopy(res, a); // a is now the result\r
+ } while(0); // end protected\r
+\r
+ if (allocbuff!=NULL) free(allocbuff); // drop any storage used\r
+ if (allocbufa!=NULL) free(allocbufa); // ..\r
+ if (allocbufb!=NULL) free(allocbufb); // ..\r
+ #if DECSUBSET\r
+ if (allocrhs !=NULL) free(allocrhs); // ..\r
+ #endif\r
+ if (status!=0) decStatus(res, status, set);// then report status\r
+ #if DECCHECK\r
+ decCheckInexact(res, set);\r
+ #endif\r
+ return res;\r
+ } // decNumberSquareRoot\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decNumberSubtract -- subtract two Numbers */\r
+/* */\r
+/* This computes C = A - B */\r
+/* */\r
+/* res is C, the result. C may be A and/or B (e.g., X=X-X) */\r
+/* lhs is A */\r
+/* rhs is B */\r
+/* set is the context */\r
+/* */\r
+/* C must have space for set->digits digits. */\r
+/* ------------------------------------------------------------------ */\r
+decNumber * decNumberSubtract(decNumber *res, const decNumber *lhs,\r
+ const decNumber *rhs, decContext *set) {\r
+ uInt status=0; // accumulator\r
+\r
+ decAddOp(res, lhs, rhs, set, DECNEG, &status);\r
+ if (status!=0) decStatus(res, status, set);\r
+ #if DECCHECK\r
+ decCheckInexact(res, set);\r
+ #endif\r
+ return res;\r
+ } // decNumberSubtract\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decNumberToIntegralExact -- round-to-integral-value with InExact */\r
+/* decNumberToIntegralValue -- round-to-integral-value */\r
+/* */\r
+/* res is the result */\r
+/* rhs is input number */\r
+/* set is the context */\r
+/* */\r
+/* res must have space for any value of rhs. */\r
+/* */\r
+/* This implements the IEEE special operators and therefore treats */\r
+/* special values as valid. For finite numbers it returns */\r
+/* rescale(rhs, 0) if rhs->exponent is <0. */\r
+/* Otherwise the result is rhs (so no error is possible, except for */\r
+/* sNaN). */\r
+/* */\r
+/* The context is used for rounding mode and status after sNaN, but */\r
+/* the digits setting is ignored. The Exact version will signal */\r
+/* Inexact if the result differs numerically from rhs; the other */\r
+/* never signals Inexact. */\r
+/* ------------------------------------------------------------------ */\r
+decNumber * decNumberToIntegralExact(decNumber *res, const decNumber *rhs,\r
+ decContext *set) {\r
+ decNumber dn;\r
+ decContext workset; // working context\r
+ uInt status=0; // accumulator\r
+\r
+ #if DECCHECK\r
+ if (decCheckOperands(res, DECUNUSED, rhs, set)) return res;\r
+ #endif\r
+\r
+ // handle infinities and NaNs\r
+ if (SPECIALARG) {\r
+ if (decNumberIsInfinite(rhs)) decNumberCopy(res, rhs); // an Infinity\r
+ else decNaNs(res, rhs, NULL, set, &status); // a NaN\r
+ }\r
+ else { // finite\r
+ // have a finite number; no error possible (res must be big enough)\r
+ if (rhs->exponent>=0) return decNumberCopy(res, rhs);\r
+ // that was easy, but if negative exponent there is work to do...\r
+ workset=*set; // clone rounding, etc.\r
+ workset.digits=rhs->digits; // no length rounding\r
+ workset.traps=0; // no traps\r
+ decNumberZero(&dn); // make a number with exponent 0\r
+ decNumberQuantize(res, rhs, &dn, &workset);\r
+ status|=workset.status;\r
+ }\r
+ if (status!=0) decStatus(res, status, set);\r
+ return res;\r
+ } // decNumberToIntegralExact\r
+\r
+decNumber * decNumberToIntegralValue(decNumber *res, const decNumber *rhs,\r
+ decContext *set) {\r
+ decContext workset=*set; // working context\r
+ workset.traps=0; // no traps\r
+ decNumberToIntegralExact(res, rhs, &workset);\r
+ // this never affects set, except for sNaNs; NaN will have been set\r
+ // or propagated already, so no need to call decStatus\r
+ set->status|=workset.status&DEC_Invalid_operation;\r
+ return res;\r
+ } // decNumberToIntegralValue\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decNumberXor -- XOR two Numbers, digitwise */\r
+/* */\r
+/* This computes C = A ^ B */\r
+/* */\r
+/* res is C, the result. C may be A and/or B (e.g., X=X^X) */\r
+/* lhs is A */\r
+/* rhs is B */\r
+/* set is the context (used for result length and error report) */\r
+/* */\r
+/* C must have space for set->digits digits. */\r
+/* */\r
+/* Logical function restrictions apply (see above); a NaN is */\r
+/* returned with Invalid_operation if a restriction is violated. */\r
+/* ------------------------------------------------------------------ */\r
+decNumber * decNumberXor(decNumber *res, const decNumber *lhs,\r
+ const decNumber *rhs, decContext *set) {\r
+ const Unit *ua, *ub; // -> operands\r
+ const Unit *msua, *msub; // -> operand msus\r
+ Unit *uc, *msuc; // -> result and its msu\r
+ Int msudigs; // digits in res msu\r
+ #if DECCHECK\r
+ if (decCheckOperands(res, lhs, rhs, set)) return res;\r
+ #endif\r
+\r
+ if (lhs->exponent!=0 || decNumberIsSpecial(lhs) || decNumberIsNegative(lhs)\r
+ || rhs->exponent!=0 || decNumberIsSpecial(rhs) || decNumberIsNegative(rhs)) {\r
+ decStatus(res, DEC_Invalid_operation, set);\r
+ return res;\r
+ }\r
+ // operands are valid\r
+ ua=lhs->lsu; // bottom-up\r
+ ub=rhs->lsu; // ..\r
+ uc=res->lsu; // ..\r
+ msua=ua+D2U(lhs->digits)-1; // -> msu of lhs\r
+ msub=ub+D2U(rhs->digits)-1; // -> msu of rhs\r
+ msuc=uc+D2U(set->digits)-1; // -> msu of result\r
+ msudigs=MSUDIGITS(set->digits); // [faster than remainder]\r
+ for (; uc<=msuc; ua++, ub++, uc++) { // Unit loop\r
+ Unit a, b; // extract units\r
+ if (ua>msua) a=0;\r
+ else a=*ua;\r
+ if (ub>msub) b=0;\r
+ else b=*ub;\r
+ *uc=0; // can now write back\r
+ if (a|b) { // maybe 1 bits to examine\r
+ Int i, j;\r
+ // This loop could be unrolled and/or use BIN2BCD tables\r
+ for (i=0; i<DECDPUN; i++) {\r
+ if ((a^b)&1) *uc=*uc+(Unit)powers[i]; // effect XOR\r
+ j=a%10;\r
+ a=a/10;\r
+ j|=b%10;\r
+ b=b/10;\r
+ if (j>1) {\r
+ decStatus(res, DEC_Invalid_operation, set);\r
+ return res;\r
+ }\r
+ if (uc==msuc && i==msudigs-1) break; // just did final digit\r
+ } // each digit\r
+ } // non-zero\r
+ } // each unit\r
+ // [here uc-1 is the msu of the result]\r
+ res->digits=decGetDigits(res->lsu, uc-res->lsu);\r
+ res->exponent=0; // integer\r
+ res->bits=0; // sign=0\r
+ return res; // [no status to set]\r
+ } // decNumberXor\r
+\r
+\r
+/* ================================================================== */\r
+/* Utility routines */\r
+/* ================================================================== */\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decNumberClass -- return the decClass of a decNumber */\r
+/* dn -- the decNumber to test */\r
+/* set -- the context to use for Emin */\r
+/* returns the decClass enum */\r
+/* ------------------------------------------------------------------ */\r
+enum decClass decNumberClass(const decNumber *dn, decContext *set) {\r
+ if (decNumberIsSpecial(dn)) {\r
+ if (decNumberIsQNaN(dn)) return DEC_CLASS_QNAN;\r
+ if (decNumberIsSNaN(dn)) return DEC_CLASS_SNAN;\r
+ // must be an infinity\r
+ if (decNumberIsNegative(dn)) return DEC_CLASS_NEG_INF;\r
+ return DEC_CLASS_POS_INF;\r
+ }\r
+ // is finite\r
+ if (decNumberIsNormal(dn, set)) { // most common\r
+ if (decNumberIsNegative(dn)) return DEC_CLASS_NEG_NORMAL;\r
+ return DEC_CLASS_POS_NORMAL;\r
+ }\r
+ // is subnormal or zero\r
+ if (decNumberIsZero(dn)) { // most common\r
+ if (decNumberIsNegative(dn)) return DEC_CLASS_NEG_ZERO;\r
+ return DEC_CLASS_POS_ZERO;\r
+ }\r
+ if (decNumberIsNegative(dn)) return DEC_CLASS_NEG_SUBNORMAL;\r
+ return DEC_CLASS_POS_SUBNORMAL;\r
+ } // decNumberClass\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decNumberClassToString -- convert decClass to a string */\r
+/* */\r
+/* eclass is a valid decClass */\r
+/* returns a constant string describing the class (max 13+1 chars) */\r
+/* ------------------------------------------------------------------ */\r
+const char *decNumberClassToString(enum decClass eclass) {\r
+ if (eclass==DEC_CLASS_POS_NORMAL) return DEC_ClassString_PN;\r
+ if (eclass==DEC_CLASS_NEG_NORMAL) return DEC_ClassString_NN;\r
+ if (eclass==DEC_CLASS_POS_ZERO) return DEC_ClassString_PZ;\r
+ if (eclass==DEC_CLASS_NEG_ZERO) return DEC_ClassString_NZ;\r
+ if (eclass==DEC_CLASS_POS_SUBNORMAL) return DEC_ClassString_PS;\r
+ if (eclass==DEC_CLASS_NEG_SUBNORMAL) return DEC_ClassString_NS;\r
+ if (eclass==DEC_CLASS_POS_INF) return DEC_ClassString_PI;\r
+ if (eclass==DEC_CLASS_NEG_INF) return DEC_ClassString_NI;\r
+ if (eclass==DEC_CLASS_QNAN) return DEC_ClassString_QN;\r
+ if (eclass==DEC_CLASS_SNAN) return DEC_ClassString_SN;\r
+ return DEC_ClassString_UN; // Unknown\r
+ } // decNumberClassToString\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decNumberCopy -- copy a number */\r
+/* */\r
+/* dest is the target decNumber */\r
+/* src is the source decNumber */\r
+/* returns dest */\r
+/* */\r
+/* (dest==src is allowed and is a no-op) */\r
+/* All fields are updated as required. This is a utility operation, */\r
+/* so special values are unchanged and no error is possible. */\r
+/* ------------------------------------------------------------------ */\r
+decNumber * decNumberCopy(decNumber *dest, const decNumber *src) {\r
+\r
+ #if DECCHECK\r
+ if (src==NULL) return decNumberZero(dest);\r
+ #endif\r
+\r
+ if (dest==src) return dest; // no copy required\r
+\r
+ // Use explicit assignments here as structure assignment could copy\r
+ // more than just the lsu (for small DECDPUN). This would not affect\r
+ // the value of the results, but could disturb test harness spill\r
+ // checking.\r
+ dest->bits=src->bits;\r
+ dest->exponent=src->exponent;\r
+ dest->digits=src->digits;\r
+ dest->lsu[0]=src->lsu[0];\r
+ if (src->digits>DECDPUN) { // more Units to come\r
+ const Unit *smsup, *s; // work\r
+ Unit *d; // ..\r
+ // memcpy for the remaining Units would be safe as they cannot\r
+ // overlap. However, this explicit loop is faster in short cases.\r
+ d=dest->lsu+1; // -> first destination\r
+ smsup=src->lsu+D2U(src->digits); // -> source msu+1\r
+ for (s=src->lsu+1; s<smsup; s++, d++) *d=*s;\r
+ }\r
+ return dest;\r
+ } // decNumberCopy\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decNumberCopyAbs -- quiet absolute value operator */\r
+/* */\r
+/* This sets C = abs(A) */\r
+/* */\r
+/* res is C, the result. C may be A */\r
+/* rhs is A */\r
+/* */\r
+/* C must have space for set->digits digits. */\r
+/* No exception or error can occur; this is a quiet bitwise operation.*/\r
+/* See also decNumberAbs for a checking version of this. */\r
+/* ------------------------------------------------------------------ */\r
+decNumber * decNumberCopyAbs(decNumber *res, const decNumber *rhs) {\r
+ #if DECCHECK\r
+ if (decCheckOperands(res, DECUNUSED, rhs, DECUNCONT)) return res;\r
+ #endif\r
+ decNumberCopy(res, rhs);\r
+ res->bits&=~DECNEG; // turn off sign\r
+ return res;\r
+ } // decNumberCopyAbs\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decNumberCopyNegate -- quiet negate value operator */\r
+/* */\r
+/* This sets C = negate(A) */\r
+/* */\r
+/* res is C, the result. C may be A */\r
+/* rhs is A */\r
+/* */\r
+/* C must have space for set->digits digits. */\r
+/* No exception or error can occur; this is a quiet bitwise operation.*/\r
+/* See also decNumberMinus for a checking version of this. */\r
+/* ------------------------------------------------------------------ */\r
+decNumber * decNumberCopyNegate(decNumber *res, const decNumber *rhs) {\r
+ #if DECCHECK\r
+ if (decCheckOperands(res, DECUNUSED, rhs, DECUNCONT)) return res;\r
+ #endif\r
+ decNumberCopy(res, rhs);\r
+ res->bits^=DECNEG; // invert the sign\r
+ return res;\r
+ } // decNumberCopyNegate\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decNumberCopySign -- quiet copy and set sign operator */\r
+/* */\r
+/* This sets C = A with the sign of B */\r
+/* */\r
+/* res is C, the result. C may be A */\r
+/* lhs is A */\r
+/* rhs is B */\r
+/* */\r
+/* C must have space for set->digits digits. */\r
+/* No exception or error can occur; this is a quiet bitwise operation.*/\r
+/* ------------------------------------------------------------------ */\r
+decNumber * decNumberCopySign(decNumber *res, const decNumber *lhs,\r
+ const decNumber *rhs) {\r
+ uByte sign; // rhs sign\r
+ #if DECCHECK\r
+ if (decCheckOperands(res, DECUNUSED, rhs, DECUNCONT)) return res;\r
+ #endif\r
+ sign=rhs->bits & DECNEG; // save sign bit\r
+ decNumberCopy(res, lhs);\r
+ res->bits&=~DECNEG; // clear the sign\r
+ res->bits|=sign; // set from rhs\r
+ return res;\r
+ } // decNumberCopySign\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decNumberGetBCD -- get the coefficient in BCD8 */\r
+/* dn is the source decNumber */\r
+/* bcd is the uInt array that will receive dn->digits BCD bytes, */\r
+/* most-significant at offset 0 */\r
+/* returns bcd */\r
+/* */\r
+/* bcd must have at least dn->digits bytes. No error is possible; if */\r
+/* dn is a NaN or Infinite, digits must be 1 and the coefficient 0. */\r
+/* ------------------------------------------------------------------ */\r
+uByte * decNumberGetBCD(const decNumber *dn, uByte *bcd) {\r
+ uByte *ub=bcd+dn->digits-1; // -> lsd\r
+ const Unit *up=dn->lsu; // Unit pointer, -> lsu\r
+\r
+ #if DECDPUN==1 // trivial simple copy\r
+ for (; ub>=bcd; ub--, up++) *ub=*up;\r
+ #else // chopping needed\r
+ uInt u=*up; // work\r
+ uInt cut=DECDPUN; // downcounter through unit\r
+ for (; ub>=bcd; ub--) {\r
+ *ub=(uByte)(u%10); // [*6554 trick inhibits, here]\r
+ u=u/10;\r
+ cut--;\r
+ if (cut>0) continue; // more in this unit\r
+ up++;\r
+ u=*up;\r
+ cut=DECDPUN;\r
+ }\r
+ #endif\r
+ return bcd;\r
+ } // decNumberGetBCD\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decNumberSetBCD -- set (replace) the coefficient from BCD8 */\r
+/* dn is the target decNumber */\r
+/* bcd is the uInt array that will source n BCD bytes, most- */\r
+/* significant at offset 0 */\r
+/* n is the number of digits in the source BCD array (bcd) */\r
+/* returns dn */\r
+/* */\r
+/* dn must have space for at least n digits. No error is possible; */\r
+/* if dn is a NaN, or Infinite, or is to become a zero, n must be 1 */\r
+/* and bcd[0] zero. */\r
+/* ------------------------------------------------------------------ */\r
+decNumber * decNumberSetBCD(decNumber *dn, const uByte *bcd, uInt n) {\r
+ Unit *up=dn->lsu+D2U(dn->digits)-1; // -> msu [target pointer]\r
+ const uByte *ub=bcd; // -> source msd\r
+\r
+ #if DECDPUN==1 // trivial simple copy\r
+ for (; ub<bcd+n; ub++, up--) *up=*ub;\r
+ #else // some assembly needed\r
+ // calculate how many digits in msu, and hence first cut\r
+ Int cut=MSUDIGITS(n); // [faster than remainder]\r
+ for (;up>=dn->lsu; up--) { // each Unit from msu\r
+ *up=0; // will take <=DECDPUN digits\r
+ for (; cut>0; ub++, cut--) *up=X10(*up)+*ub;\r
+ cut=DECDPUN; // next Unit has all digits\r
+ }\r
+ #endif\r
+ dn->digits=n; // set digit count\r
+ return dn;\r
+ } // decNumberSetBCD\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decNumberIsNormal -- test normality of a decNumber */\r
+/* dn is the decNumber to test */\r
+/* set is the context to use for Emin */\r
+/* returns 1 if |dn| is finite and >=Nmin, 0 otherwise */\r
+/* ------------------------------------------------------------------ */\r
+Int decNumberIsNormal(const decNumber *dn, decContext *set) {\r
+ Int ae; // adjusted exponent\r
+ #if DECCHECK\r
+ if (decCheckOperands(DECUNRESU, DECUNUSED, dn, set)) return 0;\r
+ #endif\r
+\r
+ if (decNumberIsSpecial(dn)) return 0; // not finite\r
+ if (decNumberIsZero(dn)) return 0; // not non-zero\r
+\r
+ ae=dn->exponent+dn->digits-1; // adjusted exponent\r
+ if (ae<set->emin) return 0; // is subnormal\r
+ return 1;\r
+ } // decNumberIsNormal\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decNumberIsSubnormal -- test subnormality of a decNumber */\r
+/* dn is the decNumber to test */\r
+/* set is the context to use for Emin */\r
+/* returns 1 if |dn| is finite, non-zero, and <Nmin, 0 otherwise */\r
+/* ------------------------------------------------------------------ */\r
+Int decNumberIsSubnormal(const decNumber *dn, decContext *set) {\r
+ Int ae; // adjusted exponent\r
+ #if DECCHECK\r
+ if (decCheckOperands(DECUNRESU, DECUNUSED, dn, set)) return 0;\r
+ #endif\r
+\r
+ if (decNumberIsSpecial(dn)) return 0; // not finite\r
+ if (decNumberIsZero(dn)) return 0; // not non-zero\r
+\r
+ ae=dn->exponent+dn->digits-1; // adjusted exponent\r
+ if (ae<set->emin) return 1; // is subnormal\r
+ return 0;\r
+ } // decNumberIsSubnormal\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decNumberTrim -- remove insignificant zeros */\r
+/* */\r
+/* dn is the number to trim */\r
+/* returns dn */\r
+/* */\r
+/* All fields are updated as required. This is a utility operation, */\r
+/* so special values are unchanged and no error is possible. The */\r
+/* zeros are removed unconditionally. */\r
+/* ------------------------------------------------------------------ */\r
+decNumber * decNumberTrim(decNumber *dn) {\r
+ Int dropped; // work\r
+ decContext set; // ..\r
+ #if DECCHECK\r
+ if (decCheckOperands(DECUNRESU, DECUNUSED, dn, DECUNCONT)) return dn;\r
+ #endif\r
+ decContextDefault(&set, DEC_INIT_BASE); // clamp=0\r
+ return decTrim(dn, &set, 0, 1, &dropped);\r
+ } // decNumberTrim\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decNumberVersion -- return the name and version of this module */\r
+/* */\r
+/* No error is possible. */\r
+/* ------------------------------------------------------------------ */\r
+const char * decNumberVersion(void) {\r
+ return DECVERSION;\r
+ } // decNumberVersion\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decNumberZero -- set a number to 0 */\r
+/* */\r
+/* dn is the number to set, with space for one digit */\r
+/* returns dn */\r
+/* */\r
+/* No error is possible. */\r
+/* ------------------------------------------------------------------ */\r
+// Memset is not used as it is much slower in some environments.\r
+decNumber * decNumberZero(decNumber *dn) {\r
+\r
+ #if DECCHECK\r
+ if (decCheckOperands(dn, DECUNUSED, DECUNUSED, DECUNCONT)) return dn;\r
+ #endif\r
+\r
+ dn->bits=0;\r
+ dn->exponent=0;\r
+ dn->digits=1;\r
+ dn->lsu[0]=0;\r
+ return dn;\r
+ } // decNumberZero\r
+\r
+/* ================================================================== */\r
+/* Local routines */\r
+/* ================================================================== */\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decToString -- lay out a number into a string */\r
+/* */\r
+/* dn is the number to lay out */\r
+/* string is where to lay out the number */\r
+/* eng is 1 if Engineering, 0 if Scientific */\r
+/* */\r
+/* string must be at least dn->digits+14 characters long */\r
+/* No error is possible. */\r
+/* */\r
+/* Note that this routine can generate a -0 or 0.000. These are */\r
+/* never generated in subset to-number or arithmetic, but can occur */\r
+/* in non-subset arithmetic (e.g., -1*0 or 1.234-1.234). */\r
+/* ------------------------------------------------------------------ */\r
+// If DECCHECK is enabled the string "?" is returned if a number is\r
+// invalid.\r
+static void decToString(const decNumber *dn, char *string, Flag eng) {\r
+ Int exp=dn->exponent; // local copy\r
+ Int e; // E-part value\r
+ Int pre; // digits before the '.'\r
+ Int cut; // for counting digits in a Unit\r
+ char *c=string; // work [output pointer]\r
+ const Unit *up=dn->lsu+D2U(dn->digits)-1; // -> msu [input pointer]\r
+ uInt u, pow; // work\r
+\r
+ #if DECCHECK\r
+ if (decCheckOperands(DECUNRESU, dn, DECUNUSED, DECUNCONT)) {\r
+ strcpy(string, "?");\r
+ return;}\r
+ #endif\r
+\r
+ if (decNumberIsNegative(dn)) { // Negatives get a minus\r
+ *c='-';\r
+ c++;\r
+ }\r
+ if (dn->bits&DECSPECIAL) { // Is a special value\r
+ if (decNumberIsInfinite(dn)) {\r
+ strcpy(c, "Inf");\r
+ strcpy(c+3, "inity");\r
+ return;}\r
+ // a NaN\r
+ if (dn->bits&DECSNAN) { // signalling NaN\r
+ *c='s';\r
+ c++;\r
+ }\r
+ strcpy(c, "NaN");\r
+ c+=3; // step past\r
+ // if not a clean non-zero coefficient, that's all there is in a\r
+ // NaN string\r
+ if (exp!=0 || (*dn->lsu==0 && dn->digits==1)) return;\r
+ // [drop through to add integer]\r
+ }\r
+\r
+ // calculate how many digits in msu, and hence first cut\r
+ cut=MSUDIGITS(dn->digits); // [faster than remainder]\r
+ cut--; // power of ten for digit\r
+\r
+ if (exp==0) { // simple integer [common fastpath]\r
+ for (;up>=dn->lsu; up--) { // each Unit from msu\r
+ u=*up; // contains DECDPUN digits to lay out\r
+ for (; cut>=0; c++, cut--) TODIGIT(u, cut, c, pow);\r
+ cut=DECDPUN-1; // next Unit has all digits\r
+ }\r
+ *c='\0'; // terminate the string\r
+ return;}\r
+\r
+ /* non-0 exponent -- assume plain form */\r
+ pre=dn->digits+exp; // digits before '.'\r
+ e=0; // no E\r
+ if ((exp>0) || (pre<-5)) { // need exponential form\r
+ e=exp+dn->digits-1; // calculate E value\r
+ pre=1; // assume one digit before '.'\r
+ if (eng && (e!=0)) { // engineering: may need to adjust\r
+ Int adj; // adjustment\r
+ // The C remainder operator is undefined for negative numbers, so\r
+ // a positive remainder calculation must be used here\r
+ if (e<0) {\r
+ adj=(-e)%3;\r
+ if (adj!=0) adj=3-adj;\r
+ }\r
+ else { // e>0\r
+ adj=e%3;\r
+ }\r
+ e=e-adj;\r
+ // if dealing with zero still produce an exponent which is a\r
+ // multiple of three, as expected, but there will only be the\r
+ // one zero before the E, still. Otherwise note the padding.\r
+ if (!ISZERO(dn)) pre+=adj;\r
+ else { // is zero\r
+ if (adj!=0) { // 0.00Esnn needed\r
+ e=e+3;\r
+ pre=-(2-adj);\r
+ }\r
+ } // zero\r
+ } // eng\r
+ } // need exponent\r
+\r
+ /* lay out the digits of the coefficient, adding 0s and . as needed */\r
+ u=*up;\r
+ if (pre>0) { // xxx.xxx or xx00 (engineering) form\r
+ Int n=pre;\r
+ for (; pre>0; pre--, c++, cut--) {\r
+ if (cut<0) { // need new Unit\r
+ if (up==dn->lsu) break; // out of input digits (pre>digits)\r
+ up--;\r
+ cut=DECDPUN-1;\r
+ u=*up;\r
+ }\r
+ TODIGIT(u, cut, c, pow);\r
+ }\r
+ if (n<dn->digits) { // more to come, after '.'\r
+ *c='.'; c++;\r
+ for (;; c++, cut--) {\r
+ if (cut<0) { // need new Unit\r
+ if (up==dn->lsu) break; // out of input digits\r
+ up--;\r
+ cut=DECDPUN-1;\r
+ u=*up;\r
+ }\r
+ TODIGIT(u, cut, c, pow);\r
+ }\r
+ }\r
+ else for (; pre>0; pre--, c++) *c='0'; // 0 padding (for engineering) needed\r
+ }\r
+ else { // 0.xxx or 0.000xxx form\r
+ *c='0'; c++;\r
+ *c='.'; c++;\r
+ for (; pre<0; pre++, c++) *c='0'; // add any 0's after '.'\r
+ for (; ; c++, cut--) {\r
+ if (cut<0) { // need new Unit\r
+ if (up==dn->lsu) break; // out of input digits\r
+ up--;\r
+ cut=DECDPUN-1;\r
+ u=*up;\r
+ }\r
+ TODIGIT(u, cut, c, pow);\r
+ }\r
+ }\r
+\r
+ /* Finally add the E-part, if needed. It will never be 0, has a\r
+ base maximum and minimum of +999999999 through -999999999, but\r
+ could range down to -1999999998 for anormal numbers */\r
+ if (e!=0) {\r
+ Flag had=0; // 1=had non-zero\r
+ *c='E'; c++;\r
+ *c='+'; c++; // assume positive\r
+ u=e; // ..\r
+ if (e<0) {\r
+ *(c-1)='-'; // oops, need -\r
+ u=-e; // uInt, please\r
+ }\r
+ // lay out the exponent [_itoa or equivalent is not ANSI C]\r
+ for (cut=9; cut>=0; cut--) {\r
+ TODIGIT(u, cut, c, pow);\r
+ if (*c=='0' && !had) continue; // skip leading zeros\r
+ had=1; // had non-0\r
+ c++; // step for next\r
+ } // cut\r
+ }\r
+ *c='\0'; // terminate the string (all paths)\r
+ return;\r
+ } // decToString\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decAddOp -- add/subtract operation */\r
+/* */\r
+/* This computes C = A + B */\r
+/* */\r
+/* res is C, the result. C may be A and/or B (e.g., X=X+X) */\r
+/* lhs is A */\r
+/* rhs is B */\r
+/* set is the context */\r
+/* negate is DECNEG if rhs should be negated, or 0 otherwise */\r
+/* status accumulates status for the caller */\r
+/* */\r
+/* C must have space for set->digits digits. */\r
+/* Inexact in status must be 0 for correct Exact zero sign in result */\r
+/* ------------------------------------------------------------------ */\r
+/* If possible, the coefficient is calculated directly into C. */\r
+/* However, if: */\r
+/* -- a digits+1 calculation is needed because the numbers are */\r
+/* unaligned and span more than set->digits digits */\r
+/* -- a carry to digits+1 digits looks possible */\r
+/* -- C is the same as A or B, and the result would destructively */\r
+/* overlap the A or B coefficient */\r
+/* then the result must be calculated into a temporary buffer. In */\r
+/* this case a local (stack) buffer is used if possible, and only if */\r
+/* too long for that does malloc become the final resort. */\r
+/* */\r
+/* Misalignment is handled as follows: */\r
+/* Apad: (AExp>BExp) Swap operands and proceed as for BExp>AExp. */\r
+/* BPad: Apply the padding by a combination of shifting (whole */\r
+/* units) and multiplication (part units). */\r
+/* */\r
+/* Addition, especially x=x+1, is speed-critical. */\r
+/* The static buffer is larger than might be expected to allow for */\r
+/* calls from higher-level funtions (notable exp). */\r
+/* ------------------------------------------------------------------ */\r
+static decNumber * decAddOp(decNumber *res, const decNumber *lhs,\r
+ const decNumber *rhs, decContext *set,\r
+ uByte negate, uInt *status) {\r
+ #if DECSUBSET\r
+ decNumber *alloclhs=NULL; // non-NULL if rounded lhs allocated\r
+ decNumber *allocrhs=NULL; // .., rhs\r
+ #endif\r
+ Int rhsshift; // working shift (in Units)\r
+ Int maxdigits; // longest logical length\r
+ Int mult; // multiplier\r
+ Int residue; // rounding accumulator\r
+ uByte bits; // result bits\r
+ Flag diffsign; // non-0 if arguments have different sign\r
+ Unit *acc; // accumulator for result\r
+ Unit accbuff[SD2U(DECBUFFER*2+20)]; // local buffer [*2+20 reduces many\r
+ // allocations when called from\r
+ // other operations, notable exp]\r
+ Unit *allocacc=NULL; // -> allocated acc buffer, iff allocated\r
+ Int reqdigits=set->digits; // local copy; requested DIGITS\r
+ Int padding; // work\r
+\r
+ #if DECCHECK\r
+ if (decCheckOperands(res, lhs, rhs, set)) return res;\r
+ #endif\r
+\r
+ do { // protect allocated storage\r
+ #if DECSUBSET\r
+ if (!set->extended) {\r
+ // reduce operands and set lostDigits status, as needed\r
+ if (lhs->digits>reqdigits) {\r
+ alloclhs=decRoundOperand(lhs, set, status);\r
+ if (alloclhs==NULL) break;\r
+ lhs=alloclhs;\r
+ }\r
+ if (rhs->digits>reqdigits) {\r
+ allocrhs=decRoundOperand(rhs, set, status);\r
+ if (allocrhs==NULL) break;\r
+ rhs=allocrhs;\r
+ }\r
+ }\r
+ #endif\r
+ // [following code does not require input rounding]\r
+\r
+ // note whether signs differ [used all paths]\r
+ diffsign=(Flag)((lhs->bits^rhs->bits^negate)&DECNEG);\r
+\r
+ // handle infinities and NaNs\r
+ if (SPECIALARGS) { // a special bit set\r
+ if (SPECIALARGS & (DECSNAN | DECNAN)) // a NaN\r
+ decNaNs(res, lhs, rhs, set, status);\r
+ else { // one or two infinities\r
+ if (decNumberIsInfinite(lhs)) { // LHS is infinity\r
+ // two infinities with different signs is invalid\r
+ if (decNumberIsInfinite(rhs) && diffsign) {\r
+ *status|=DEC_Invalid_operation;\r
+ break;\r
+ }\r
+ bits=lhs->bits & DECNEG; // get sign from LHS\r
+ }\r
+ else bits=(rhs->bits^negate) & DECNEG;// RHS must be Infinity\r
+ bits|=DECINF;\r
+ decNumberZero(res);\r
+ res->bits=bits; // set +/- infinity\r
+ } // an infinity\r
+ break;\r
+ }\r
+\r
+ // Quick exit for add 0s; return the non-0, modified as need be\r
+ if (ISZERO(lhs)) {\r
+ Int adjust; // work\r
+ Int lexp=lhs->exponent; // save in case LHS==RES\r
+ bits=lhs->bits; // ..\r
+ residue=0; // clear accumulator\r
+ decCopyFit(res, rhs, set, &residue, status); // copy (as needed)\r
+ res->bits^=negate; // flip if rhs was negated\r
+ #if DECSUBSET\r
+ if (set->extended) { // exponents on zeros count\r
+ #endif\r
+ // exponent will be the lower of the two\r
+ adjust=lexp-res->exponent; // adjustment needed [if -ve]\r
+ if (ISZERO(res)) { // both 0: special IEEE 754 rules\r
+ if (adjust<0) res->exponent=lexp; // set exponent\r
+ // 0-0 gives +0 unless rounding to -infinity, and -0-0 gives -0\r
+ if (diffsign) {\r
+ if (set->round!=DEC_ROUND_FLOOR) res->bits=0;\r
+ else res->bits=DECNEG; // preserve 0 sign\r
+ }\r
+ }\r
+ else { // non-0 res\r
+ if (adjust<0) { // 0-padding needed\r
+ if ((res->digits-adjust)>set->digits) {\r
+ adjust=res->digits-set->digits; // to fit exactly\r
+ *status|=DEC_Rounded; // [but exact]\r
+ }\r
+ res->digits=decShiftToMost(res->lsu, res->digits, -adjust);\r
+ res->exponent+=adjust; // set the exponent.\r
+ }\r
+ } // non-0 res\r
+ #if DECSUBSET\r
+ } // extended\r
+ #endif\r
+ decFinish(res, set, &residue, status); // clean and finalize\r
+ break;}\r
+\r
+ if (ISZERO(rhs)) { // [lhs is non-zero]\r
+ Int adjust; // work\r
+ Int rexp=rhs->exponent; // save in case RHS==RES\r
+ bits=rhs->bits; // be clean\r
+ residue=0; // clear accumulator\r
+ decCopyFit(res, lhs, set, &residue, status); // copy (as needed)\r
+ #if DECSUBSET\r
+ if (set->extended) { // exponents on zeros count\r
+ #endif\r
+ // exponent will be the lower of the two\r
+ // [0-0 case handled above]\r
+ adjust=rexp-res->exponent; // adjustment needed [if -ve]\r
+ if (adjust<0) { // 0-padding needed\r
+ if ((res->digits-adjust)>set->digits) {\r
+ adjust=res->digits-set->digits; // to fit exactly\r
+ *status|=DEC_Rounded; // [but exact]\r
+ }\r
+ res->digits=decShiftToMost(res->lsu, res->digits, -adjust);\r
+ res->exponent+=adjust; // set the exponent.\r
+ }\r
+ #if DECSUBSET\r
+ } // extended\r
+ #endif\r
+ decFinish(res, set, &residue, status); // clean and finalize\r
+ break;}\r
+\r
+ // [NB: both fastpath and mainpath code below assume these cases\r
+ // (notably 0-0) have already been handled]\r
+\r
+ // calculate the padding needed to align the operands\r
+ padding=rhs->exponent-lhs->exponent;\r
+\r
+ // Fastpath cases where the numbers are aligned and normal, the RHS\r
+ // is all in one unit, no operand rounding is needed, and no carry,\r
+ // lengthening, or borrow is needed\r
+ if (padding==0\r
+ && rhs->digits<=DECDPUN\r
+ && rhs->exponent>=set->emin // [some normals drop through]\r
+ && rhs->exponent<=set->emax-set->digits+1 // [could clamp]\r
+ && rhs->digits<=reqdigits\r
+ && lhs->digits<=reqdigits) {\r
+ Int partial=*lhs->lsu;\r
+ if (!diffsign) { // adding\r
+ partial+=*rhs->lsu;\r
+ if ((partial<=DECDPUNMAX) // result fits in unit\r
+ && (lhs->digits>=DECDPUN || // .. and no digits-count change\r
+ partial<(Int)powers[lhs->digits])) { // ..\r
+ if (res!=lhs) decNumberCopy(res, lhs); // not in place\r
+ *res->lsu=(Unit)partial; // [copy could have overwritten RHS]\r
+ break;\r
+ }\r
+ // else drop out for careful add\r
+ }\r
+ else { // signs differ\r
+ partial-=*rhs->lsu;\r
+ if (partial>0) { // no borrow needed, and non-0 result\r
+ if (res!=lhs) decNumberCopy(res, lhs); // not in place\r
+ *res->lsu=(Unit)partial;\r
+ // this could have reduced digits [but result>0]\r
+ res->digits=decGetDigits(res->lsu, D2U(res->digits));\r
+ break;\r
+ }\r
+ // else drop out for careful subtract\r
+ }\r
+ }\r
+\r
+ // Now align (pad) the lhs or rhs so they can be added or\r
+ // subtracted, as necessary. If one number is much larger than\r
+ // the other (that is, if in plain form there is a least one\r
+ // digit between the lowest digit of one and the highest of the\r
+ // other) padding with up to DIGITS-1 trailing zeros may be\r
+ // needed; then apply rounding (as exotic rounding modes may be\r
+ // affected by the residue).\r
+ rhsshift=0; // rhs shift to left (padding) in Units\r
+ bits=lhs->bits; // assume sign is that of LHS\r
+ mult=1; // likely multiplier\r
+\r
+ // [if padding==0 the operands are aligned; no padding is needed]\r
+ if (padding!=0) {\r
+ // some padding needed; always pad the RHS, as any required\r
+ // padding can then be effected by a simple combination of\r
+ // shifts and a multiply\r
+ Flag swapped=0;\r
+ if (padding<0) { // LHS needs the padding\r
+ const decNumber *t;\r
+ padding=-padding; // will be +ve\r
+ bits=(uByte)(rhs->bits^negate); // assumed sign is now that of RHS\r
+ t=lhs; lhs=rhs; rhs=t;\r
+ swapped=1;\r
+ }\r
+\r
+ // If, after pad, rhs would be longer than lhs by digits+1 or\r
+ // more then lhs cannot affect the answer, except as a residue,\r
+ // so only need to pad up to a length of DIGITS+1.\r
+ if (rhs->digits+padding > lhs->digits+reqdigits+1) {\r
+ // The RHS is sufficient\r
+ // for residue use the relative sign indication...\r
+ Int shift=reqdigits-rhs->digits; // left shift needed\r
+ residue=1; // residue for rounding\r
+ if (diffsign) residue=-residue; // signs differ\r
+ // copy, shortening if necessary\r
+ decCopyFit(res, rhs, set, &residue, status);\r
+ // if it was already shorter, then need to pad with zeros\r
+ if (shift>0) {\r
+ res->digits=decShiftToMost(res->lsu, res->digits, shift);\r
+ res->exponent-=shift; // adjust the exponent.\r
+ }\r
+ // flip the result sign if unswapped and rhs was negated\r
+ if (!swapped) res->bits^=negate;\r
+ decFinish(res, set, &residue, status); // done\r
+ break;}\r
+\r
+ // LHS digits may affect result\r
+ rhsshift=D2U(padding+1)-1; // this much by Unit shift ..\r
+ mult=powers[padding-(rhsshift*DECDPUN)]; // .. this by multiplication\r
+ } // padding needed\r
+\r
+ if (diffsign) mult=-mult; // signs differ\r
+\r
+ // determine the longer operand\r
+ maxdigits=rhs->digits+padding; // virtual length of RHS\r
+ if (lhs->digits>maxdigits) maxdigits=lhs->digits;\r
+\r
+ // Decide on the result buffer to use; if possible place directly\r
+ // into result.\r
+ acc=res->lsu; // assume add direct to result\r
+ // If destructive overlap, or the number is too long, or a carry or\r
+ // borrow to DIGITS+1 might be possible, a buffer must be used.\r
+ // [Might be worth more sophisticated tests when maxdigits==reqdigits]\r
+ if ((maxdigits>=reqdigits) // is, or could be, too large\r
+ || (res==rhs && rhsshift>0)) { // destructive overlap\r
+ // buffer needed, choose it; units for maxdigits digits will be\r
+ // needed, +1 Unit for carry or borrow\r
+ Int need=D2U(maxdigits)+1;\r
+ acc=accbuff; // assume use local buffer\r
+ if (need*sizeof(Unit)>sizeof(accbuff)) {\r
+ // printf("malloc add %ld %ld\n", need, sizeof(accbuff));\r
+ allocacc=(Unit *)malloc(need*sizeof(Unit));\r
+ if (allocacc==NULL) { // hopeless -- abandon\r
+ *status|=DEC_Insufficient_storage;\r
+ break;}\r
+ acc=allocacc;\r
+ }\r
+ }\r
+\r
+ res->bits=(uByte)(bits&DECNEG); // it's now safe to overwrite..\r
+ res->exponent=lhs->exponent; // .. operands (even if aliased)\r
+\r
+ #if DECTRACE\r
+ decDumpAr('A', lhs->lsu, D2U(lhs->digits));\r
+ decDumpAr('B', rhs->lsu, D2U(rhs->digits));\r
+ printf(" :h: %ld %ld\n", rhsshift, mult);\r
+ #endif\r
+\r
+ // add [A+B*m] or subtract [A+B*(-m)]\r
+ res->digits=decUnitAddSub(lhs->lsu, D2U(lhs->digits),\r
+ rhs->lsu, D2U(rhs->digits),\r
+ rhsshift, acc, mult)\r
+ *DECDPUN; // [units -> digits]\r
+ if (res->digits<0) { // borrowed...\r
+ res->digits=-res->digits;\r
+ res->bits^=DECNEG; // flip the sign\r
+ }\r
+ #if DECTRACE\r
+ decDumpAr('+', acc, D2U(res->digits));\r
+ #endif\r
+\r
+ // If a buffer was used the result must be copied back, possibly\r
+ // shortening. (If no buffer was used then the result must have\r
+ // fit, so can't need rounding and residue must be 0.)\r
+ residue=0; // clear accumulator\r
+ if (acc!=res->lsu) {\r
+ #if DECSUBSET\r
+ if (set->extended) { // round from first significant digit\r
+ #endif\r
+ // remove leading zeros that were added due to rounding up to\r
+ // integral Units -- before the test for rounding.\r
+ if (res->digits>reqdigits)\r
+ res->digits=decGetDigits(acc, D2U(res->digits));\r
+ decSetCoeff(res, set, acc, res->digits, &residue, status);\r
+ #if DECSUBSET\r
+ }\r
+ else { // subset arithmetic rounds from original significant digit\r
+ // May have an underestimate. This only occurs when both\r
+ // numbers fit in DECDPUN digits and are padding with a\r
+ // negative multiple (-10, -100...) and the top digit(s) become\r
+ // 0. (This only matters when using X3.274 rules where the\r
+ // leading zero could be included in the rounding.)\r
+ if (res->digits<maxdigits) {\r
+ *(acc+D2U(res->digits))=0; // ensure leading 0 is there\r
+ res->digits=maxdigits;\r
+ }\r
+ else {\r
+ // remove leading zeros that added due to rounding up to\r
+ // integral Units (but only those in excess of the original\r
+ // maxdigits length, unless extended) before test for rounding.\r
+ if (res->digits>reqdigits) {\r
+ res->digits=decGetDigits(acc, D2U(res->digits));\r
+ if (res->digits<maxdigits) res->digits=maxdigits;\r
+ }\r
+ }\r
+ decSetCoeff(res, set, acc, res->digits, &residue, status);\r
+ // Now apply rounding if needed before removing leading zeros.\r
+ // This is safe because subnormals are not a possibility\r
+ if (residue!=0) {\r
+ decApplyRound(res, set, residue, status);\r
+ residue=0; // did what needed to be done\r
+ }\r
+ } // subset\r
+ #endif\r
+ } // used buffer\r
+\r
+ // strip leading zeros [these were left on in case of subset subtract]\r
+ res->digits=decGetDigits(res->lsu, D2U(res->digits));\r
+\r
+ // apply checks and rounding\r
+ decFinish(res, set, &residue, status);\r
+\r
+ // "When the sum of two operands with opposite signs is exactly\r
+ // zero, the sign of that sum shall be '+' in all rounding modes\r
+ // except round toward -Infinity, in which mode that sign shall be\r
+ // '-'." [Subset zeros also never have '-', set by decFinish.]\r
+ if (ISZERO(res) && diffsign\r
+ #if DECSUBSET\r
+ && set->extended\r
+ #endif\r
+ && (*status&DEC_Inexact)==0) {\r
+ if (set->round==DEC_ROUND_FLOOR) res->bits|=DECNEG; // sign -\r
+ else res->bits&=~DECNEG; // sign +\r
+ }\r
+ } while(0); // end protected\r
+\r
+ if (allocacc!=NULL) free(allocacc); // drop any storage used\r
+ #if DECSUBSET\r
+ if (allocrhs!=NULL) free(allocrhs); // ..\r
+ if (alloclhs!=NULL) free(alloclhs); // ..\r
+ #endif\r
+ return res;\r
+ } // decAddOp\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decDivideOp -- division operation */\r
+/* */\r
+/* This routine performs the calculations for all four division */\r
+/* operators (divide, divideInteger, remainder, remainderNear). */\r
+/* */\r
+/* C=A op B */\r
+/* */\r
+/* res is C, the result. C may be A and/or B (e.g., X=X/X) */\r
+/* lhs is A */\r
+/* rhs is B */\r
+/* set is the context */\r
+/* op is DIVIDE, DIVIDEINT, REMAINDER, or REMNEAR respectively. */\r
+/* status is the usual accumulator */\r
+/* */\r
+/* C must have space for set->digits digits. */\r
+/* */\r
+/* ------------------------------------------------------------------ */\r
+/* The underlying algorithm of this routine is the same as in the */\r
+/* 1981 S/370 implementation, that is, non-restoring long division */\r
+/* with bi-unit (rather than bi-digit) estimation for each unit */\r
+/* multiplier. In this pseudocode overview, complications for the */\r
+/* Remainder operators and division residues for exact rounding are */\r
+/* omitted for clarity. */\r
+/* */\r
+/* Prepare operands and handle special values */\r
+/* Test for x/0 and then 0/x */\r
+/* Exp =Exp1 - Exp2 */\r
+/* Exp =Exp +len(var1) -len(var2) */\r
+/* Sign=Sign1 * Sign2 */\r
+/* Pad accumulator (Var1) to double-length with 0's (pad1) */\r
+/* Pad Var2 to same length as Var1 */\r
+/* msu2pair/plus=1st 2 or 1 units of var2, +1 to allow for round */\r
+/* have=0 */\r
+/* Do until (have=digits+1 OR residue=0) */\r
+/* if exp<0 then if integer divide/residue then leave */\r
+/* this_unit=0 */\r
+/* Do forever */\r
+/* compare numbers */\r
+/* if <0 then leave inner_loop */\r
+/* if =0 then (* quick exit without subtract *) do */\r
+/* this_unit=this_unit+1; output this_unit */\r
+/* leave outer_loop; end */\r
+/* Compare lengths of numbers (mantissae): */\r
+/* If same then tops2=msu2pair -- {units 1&2 of var2} */\r
+/* else tops2=msu2plus -- {0, unit 1 of var2} */\r
+/* tops1=first_unit_of_Var1*10**DECDPUN +second_unit_of_var1 */\r
+/* mult=tops1/tops2 -- Good and safe guess at divisor */\r
+/* if mult=0 then mult=1 */\r
+/* this_unit=this_unit+mult */\r
+/* subtract */\r
+/* end inner_loop */\r
+/* if have\=0 | this_unit\=0 then do */\r
+/* output this_unit */\r
+/* have=have+1; end */\r
+/* var2=var2/10 */\r
+/* exp=exp-1 */\r
+/* end outer_loop */\r
+/* exp=exp+1 -- set the proper exponent */\r
+/* if have=0 then generate answer=0 */\r
+/* Return (Result is defined by Var1) */\r
+/* */\r
+/* ------------------------------------------------------------------ */\r
+/* Two working buffers are needed during the division; one (digits+ */\r
+/* 1) to accumulate the result, and the other (up to 2*digits+1) for */\r
+/* long subtractions. These are acc and var1 respectively. */\r
+/* var1 is a copy of the lhs coefficient, var2 is the rhs coefficient.*/\r
+/* The static buffers may be larger than might be expected to allow */\r
+/* for calls from higher-level funtions (notable exp). */\r
+/* ------------------------------------------------------------------ */\r
+static decNumber * decDivideOp(decNumber *res,\r
+ const decNumber *lhs, const decNumber *rhs,\r
+ decContext *set, Flag op, uInt *status) {\r
+ #if DECSUBSET\r
+ decNumber *alloclhs=NULL; // non-NULL if rounded lhs allocated\r
+ decNumber *allocrhs=NULL; // .., rhs\r
+ #endif\r
+ Unit accbuff[SD2U(DECBUFFER+DECDPUN+10)]; // local buffer\r
+ Unit *acc=accbuff; // -> accumulator array for result\r
+ Unit *allocacc=NULL; // -> allocated buffer, iff allocated\r
+ Unit *accnext; // -> where next digit will go\r
+ Int acclength; // length of acc needed [Units]\r
+ Int accunits; // count of units accumulated\r
+ Int accdigits; // count of digits accumulated\r
+\r
+ Unit varbuff[SD2U(DECBUFFER*2+DECDPUN)]; // buffer for var1\r
+ Unit *var1=varbuff; // -> var1 array for long subtraction\r
+ Unit *varalloc=NULL; // -> allocated buffer, iff used\r
+ Unit *msu1; // -> msu of var1\r
+\r
+ const Unit *var2; // -> var2 array\r
+ const Unit *msu2; // -> msu of var2\r
+ Int msu2plus; // msu2 plus one [does not vary]\r
+ eInt msu2pair; // msu2 pair plus one [does not vary]\r
+\r
+ Int var1units, var2units; // actual lengths\r
+ Int var2ulen; // logical length (units)\r
+ Int var1initpad=0; // var1 initial padding (digits)\r
+ Int maxdigits; // longest LHS or required acc length\r
+ Int mult; // multiplier for subtraction\r
+ Unit thisunit; // current unit being accumulated\r
+ Int residue; // for rounding\r
+ Int reqdigits=set->digits; // requested DIGITS\r
+ Int exponent; // working exponent\r
+ Int maxexponent=0; // DIVIDE maximum exponent if unrounded\r
+ uByte bits; // working sign\r
+ Unit *target; // work\r
+ const Unit *source; // ..\r
+ uInt const *pow; // ..\r
+ Int shift, cut; // ..\r
+ #if DECSUBSET\r
+ Int dropped; // work\r
+ #endif\r
+\r
+ #if DECCHECK\r
+ if (decCheckOperands(res, lhs, rhs, set)) return res;\r
+ #endif\r
+\r
+ do { // protect allocated storage\r
+ #if DECSUBSET\r
+ if (!set->extended) {\r
+ // reduce operands and set lostDigits status, as needed\r
+ if (lhs->digits>reqdigits) {\r
+ alloclhs=decRoundOperand(lhs, set, status);\r
+ if (alloclhs==NULL) break;\r
+ lhs=alloclhs;\r
+ }\r
+ if (rhs->digits>reqdigits) {\r
+ allocrhs=decRoundOperand(rhs, set, status);\r
+ if (allocrhs==NULL) break;\r
+ rhs=allocrhs;\r
+ }\r
+ }\r
+ #endif\r
+ // [following code does not require input rounding]\r
+\r
+ bits=(lhs->bits^rhs->bits)&DECNEG; // assumed sign for divisions\r
+\r
+ // handle infinities and NaNs\r
+ if (SPECIALARGS) { // a special bit set\r
+ if (SPECIALARGS & (DECSNAN | DECNAN)) { // one or two NaNs\r
+ decNaNs(res, lhs, rhs, set, status);\r
+ break;\r
+ }\r
+ // one or two infinities\r
+ if (decNumberIsInfinite(lhs)) { // LHS (dividend) is infinite\r
+ if (decNumberIsInfinite(rhs) || // two infinities are invalid ..\r
+ op & (REMAINDER | REMNEAR)) { // as is remainder of infinity\r
+ *status|=DEC_Invalid_operation;\r
+ break;\r
+ }\r
+ // [Note that infinity/0 raises no exceptions]\r
+ decNumberZero(res);\r
+ res->bits=bits|DECINF; // set +/- infinity\r
+ break;\r
+ }\r
+ else { // RHS (divisor) is infinite\r
+ residue=0;\r
+ if (op&(REMAINDER|REMNEAR)) {\r
+ // result is [finished clone of] lhs\r
+ decCopyFit(res, lhs, set, &residue, status);\r
+ }\r
+ else { // a division\r
+ decNumberZero(res);\r
+ res->bits=bits; // set +/- zero\r
+ // for DIVIDEINT the exponent is always 0. For DIVIDE, result\r
+ // is a 0 with infinitely negative exponent, clamped to minimum\r
+ if (op&DIVIDE) {\r
+ res->exponent=set->emin-set->digits+1;\r
+ *status|=DEC_Clamped;\r
+ }\r
+ }\r
+ decFinish(res, set, &residue, status);\r
+ break;\r
+ }\r
+ }\r
+\r
+ // handle 0 rhs (x/0)\r
+ if (ISZERO(rhs)) { // x/0 is always exceptional\r
+ if (ISZERO(lhs)) {\r
+ decNumberZero(res); // [after lhs test]\r
+ *status|=DEC_Division_undefined;// 0/0 will become NaN\r
+ }\r
+ else {\r
+ decNumberZero(res);\r
+ if (op&(REMAINDER|REMNEAR)) *status|=DEC_Invalid_operation;\r
+ else {\r
+ *status|=DEC_Division_by_zero; // x/0\r
+ res->bits=bits|DECINF; // .. is +/- Infinity\r
+ }\r
+ }\r
+ break;}\r
+\r
+ // handle 0 lhs (0/x)\r
+ if (ISZERO(lhs)) { // 0/x [x!=0]\r
+ #if DECSUBSET\r
+ if (!set->extended) decNumberZero(res);\r
+ else {\r
+ #endif\r
+ if (op&DIVIDE) {\r
+ residue=0;\r
+ exponent=lhs->exponent-rhs->exponent; // ideal exponent\r
+ decNumberCopy(res, lhs); // [zeros always fit]\r
+ res->bits=bits; // sign as computed\r
+ res->exponent=exponent; // exponent, too\r
+ decFinalize(res, set, &residue, status); // check exponent\r
+ }\r
+ else if (op&DIVIDEINT) {\r
+ decNumberZero(res); // integer 0\r
+ res->bits=bits; // sign as computed\r
+ }\r
+ else { // a remainder\r
+ exponent=rhs->exponent; // [save in case overwrite]\r
+ decNumberCopy(res, lhs); // [zeros always fit]\r
+ if (exponent<res->exponent) res->exponent=exponent; // use lower\r
+ }\r
+ #if DECSUBSET\r
+ }\r
+ #endif\r
+ break;}\r
+\r
+ // Precalculate exponent. This starts off adjusted (and hence fits\r
+ // in 31 bits) and becomes the usual unadjusted exponent as the\r
+ // division proceeds. The order of evaluation is important, here,\r
+ // to avoid wrap.\r
+ exponent=(lhs->exponent+lhs->digits)-(rhs->exponent+rhs->digits);\r
+\r
+ // If the working exponent is -ve, then some quick exits are\r
+ // possible because the quotient is known to be <1\r
+ // [for REMNEAR, it needs to be < -1, as -0.5 could need work]\r
+ if (exponent<0 && !(op==DIVIDE)) {\r
+ if (op&DIVIDEINT) {\r
+ decNumberZero(res); // integer part is 0\r
+ #if DECSUBSET\r
+ if (set->extended)\r
+ #endif\r
+ res->bits=bits; // set +/- zero\r
+ break;}\r
+ // fastpath remainders so long as the lhs has the smaller\r
+ // (or equal) exponent\r
+ if (lhs->exponent<=rhs->exponent) {\r
+ if (op&REMAINDER || exponent<-1) {\r
+ // It is REMAINDER or safe REMNEAR; result is [finished\r
+ // clone of] lhs (r = x - 0*y)\r
+ residue=0;\r
+ decCopyFit(res, lhs, set, &residue, status);\r
+ decFinish(res, set, &residue, status);\r
+ break;\r
+ }\r
+ // [unsafe REMNEAR drops through]\r
+ }\r
+ } // fastpaths\r
+\r
+ /* Long (slow) division is needed; roll up the sleeves... */\r
+\r
+ // The accumulator will hold the quotient of the division.\r
+ // If it needs to be too long for stack storage, then allocate.\r
+ acclength=D2U(reqdigits+DECDPUN); // in Units\r
+ if (acclength*sizeof(Unit)>sizeof(accbuff)) {\r
+ // printf("malloc dvacc %ld units\n", acclength);\r
+ allocacc=(Unit *)malloc(acclength*sizeof(Unit));\r
+ if (allocacc==NULL) { // hopeless -- abandon\r
+ *status|=DEC_Insufficient_storage;\r
+ break;}\r
+ acc=allocacc; // use the allocated space\r
+ }\r
+\r
+ // var1 is the padded LHS ready for subtractions.\r
+ // If it needs to be too long for stack storage, then allocate.\r
+ // The maximum units needed for var1 (long subtraction) is:\r
+ // Enough for\r
+ // (rhs->digits+reqdigits-1) -- to allow full slide to right\r
+ // or (lhs->digits) -- to allow for long lhs\r
+ // whichever is larger\r
+ // +1 -- for rounding of slide to right\r
+ // +1 -- for leading 0s\r
+ // +1 -- for pre-adjust if a remainder or DIVIDEINT\r
+ // [Note: unused units do not participate in decUnitAddSub data]\r
+ maxdigits=rhs->digits+reqdigits-1;\r
+ if (lhs->digits>maxdigits) maxdigits=lhs->digits;\r
+ var1units=D2U(maxdigits)+2;\r
+ // allocate a guard unit above msu1 for REMAINDERNEAR\r
+ if (!(op&DIVIDE)) var1units++;\r
+ if ((var1units+1)*sizeof(Unit)>sizeof(varbuff)) {\r
+ // printf("malloc dvvar %ld units\n", var1units+1);\r
+ varalloc=(Unit *)malloc((var1units+1)*sizeof(Unit));\r
+ if (varalloc==NULL) { // hopeless -- abandon\r
+ *status|=DEC_Insufficient_storage;\r
+ break;}\r
+ var1=varalloc; // use the allocated space\r
+ }\r
+\r
+ // Extend the lhs and rhs to full long subtraction length. The lhs\r
+ // is truly extended into the var1 buffer, with 0 padding, so a\r
+ // subtract in place is always possible. The rhs (var2) has\r
+ // virtual padding (implemented by decUnitAddSub).\r
+ // One guard unit was allocated above msu1 for rem=rem+rem in\r
+ // REMAINDERNEAR.\r
+ msu1=var1+var1units-1; // msu of var1\r
+ source=lhs->lsu+D2U(lhs->digits)-1; // msu of input array\r
+ for (target=msu1; source>=lhs->lsu; source--, target--) *target=*source;\r
+ for (; target>=var1; target--) *target=0;\r
+\r
+ // rhs (var2) is left-aligned with var1 at the start\r
+ var2ulen=var1units; // rhs logical length (units)\r
+ var2units=D2U(rhs->digits); // rhs actual length (units)\r
+ var2=rhs->lsu; // -> rhs array\r
+ msu2=var2+var2units-1; // -> msu of var2 [never changes]\r
+ // now set up the variables which will be used for estimating the\r
+ // multiplication factor. If these variables are not exact, add\r
+ // 1 to make sure that the multiplier is never overestimated.\r
+ msu2plus=*msu2; // it's value ..\r
+ if (var2units>1) msu2plus++; // .. +1 if any more\r
+ msu2pair=(eInt)*msu2*(DECDPUNMAX+1);// top two pair ..\r
+ if (var2units>1) { // .. [else treat 2nd as 0]\r
+ msu2pair+=*(msu2-1); // ..\r
+ if (var2units>2) msu2pair++; // .. +1 if any more\r
+ }\r
+\r
+ // The calculation is working in units, which may have leading zeros,\r
+ // but the exponent was calculated on the assumption that they are\r
+ // both left-aligned. Adjust the exponent to compensate: add the\r
+ // number of leading zeros in var1 msu and subtract those in var2 msu.\r
+ // [This is actually done by counting the digits and negating, as\r
+ // lead1=DECDPUN-digits1, and similarly for lead2.]\r
+ for (pow=&powers[1]; *msu1>=*pow; pow++) exponent--;\r
+ for (pow=&powers[1]; *msu2>=*pow; pow++) exponent++;\r
+\r
+ // Now, if doing an integer divide or remainder, ensure that\r
+ // the result will be Unit-aligned. To do this, shift the var1\r
+ // accumulator towards least if need be. (It's much easier to\r
+ // do this now than to reassemble the residue afterwards, if\r
+ // doing a remainder.) Also ensure the exponent is not negative.\r
+ if (!(op&DIVIDE)) {\r
+ Unit *u; // work\r
+ // save the initial 'false' padding of var1, in digits\r
+ var1initpad=(var1units-D2U(lhs->digits))*DECDPUN;\r
+ // Determine the shift to do.\r
+ if (exponent<0) cut=-exponent;\r
+ else cut=DECDPUN-exponent%DECDPUN;\r
+ decShiftToLeast(var1, var1units, cut);\r
+ exponent+=cut; // maintain numerical value\r
+ var1initpad-=cut; // .. and reduce padding\r
+ // clean any most-significant units which were just emptied\r
+ for (u=msu1; cut>=DECDPUN; cut-=DECDPUN, u--) *u=0;\r
+ } // align\r
+ else { // is DIVIDE\r
+ maxexponent=lhs->exponent-rhs->exponent; // save\r
+ // optimization: if the first iteration will just produce 0,\r
+ // preadjust to skip it [valid for DIVIDE only]\r
+ if (*msu1<*msu2) {\r
+ var2ulen--; // shift down\r
+ exponent-=DECDPUN; // update the exponent\r
+ }\r
+ }\r
+\r
+ // ---- start the long-division loops ------------------------------\r
+ accunits=0; // no units accumulated yet\r
+ accdigits=0; // .. or digits\r
+ accnext=acc+acclength-1; // -> msu of acc [NB: allows digits+1]\r
+ for (;;) { // outer forever loop\r
+ thisunit=0; // current unit assumed 0\r
+ // find the next unit\r
+ for (;;) { // inner forever loop\r
+ // strip leading zero units [from either pre-adjust or from\r
+ // subtract last time around]. Leave at least one unit.\r
+ for (; *msu1==0 && msu1>var1; msu1--) var1units--;\r
+\r
+ if (var1units<var2ulen) break; // var1 too low for subtract\r
+ if (var1units==var2ulen) { // unit-by-unit compare needed\r
+ // compare the two numbers, from msu\r
+ const Unit *pv1, *pv2;\r
+ Unit v2; // units to compare\r
+ pv2=msu2; // -> msu\r
+ for (pv1=msu1; ; pv1--, pv2--) {\r
+ // v1=*pv1 -- always OK\r
+ v2=0; // assume in padding\r
+ if (pv2>=var2) v2=*pv2; // in range\r
+ if (*pv1!=v2) break; // no longer the same\r
+ if (pv1==var1) break; // done; leave pv1 as is\r
+ }\r
+ // here when all inspected or a difference seen\r
+ if (*pv1<v2) break; // var1 too low to subtract\r
+ if (*pv1==v2) { // var1 == var2\r
+ // reach here if var1 and var2 are identical; subtraction\r
+ // would increase digit by one, and the residue will be 0 so\r
+ // the calculation is done; leave the loop with residue=0.\r
+ thisunit++; // as though subtracted\r
+ *var1=0; // set var1 to 0\r
+ var1units=1; // ..\r
+ break; // from inner\r
+ } // var1 == var2\r
+ // *pv1>v2. Prepare for real subtraction; the lengths are equal\r
+ // Estimate the multiplier (there's always a msu1-1)...\r
+ // Bring in two units of var2 to provide a good estimate.\r
+ mult=(Int)(((eInt)*msu1*(DECDPUNMAX+1)+*(msu1-1))/msu2pair);\r
+ } // lengths the same\r
+ else { // var1units > var2ulen, so subtraction is safe\r
+ // The var2 msu is one unit towards the lsu of the var1 msu,\r
+ // so only one unit for var2 can be used.\r
+ mult=(Int)(((eInt)*msu1*(DECDPUNMAX+1)+*(msu1-1))/msu2plus);\r
+ }\r
+ if (mult==0) mult=1; // must always be at least 1\r
+ // subtraction needed; var1 is > var2\r
+ thisunit=(Unit)(thisunit+mult); // accumulate\r
+ // subtract var1-var2, into var1; only the overlap needs\r
+ // processing, as this is an in-place calculation\r
+ shift=var2ulen-var2units;\r
+ #if DECTRACE\r
+ decDumpAr('1', &var1[shift], var1units-shift);\r
+ decDumpAr('2', var2, var2units);\r
+ printf("m=%ld\n", -mult);\r
+ #endif\r
+ decUnitAddSub(&var1[shift], var1units-shift,\r
+ var2, var2units, 0,\r
+ &var1[shift], -mult);\r
+ #if DECTRACE\r
+ decDumpAr('#', &var1[shift], var1units-shift);\r
+ #endif\r
+ // var1 now probably has leading zeros; these are removed at the\r
+ // top of the inner loop.\r
+ } // inner loop\r
+\r
+ // The next unit has been calculated in full; unless it's a\r
+ // leading zero, add to acc\r
+ if (accunits!=0 || thisunit!=0) { // is first or non-zero\r
+ *accnext=thisunit; // store in accumulator\r
+ // account exactly for the new digits\r
+ if (accunits==0) {\r
+ accdigits++; // at least one\r
+ for (pow=&powers[1]; thisunit>=*pow; pow++) accdigits++;\r
+ }\r
+ else accdigits+=DECDPUN;\r
+ accunits++; // update count\r
+ accnext--; // ready for next\r
+ if (accdigits>reqdigits) break; // have enough digits\r
+ }\r
+\r
+ // if the residue is zero, the operation is done (unless divide\r
+ // or divideInteger and still not enough digits yet)\r
+ if (*var1==0 && var1units==1) { // residue is 0\r
+ if (op&(REMAINDER|REMNEAR)) break;\r
+ if ((op&DIVIDE) && (exponent<=maxexponent)) break;\r
+ // [drop through if divideInteger]\r
+ }\r
+ // also done enough if calculating remainder or integer\r
+ // divide and just did the last ('units') unit\r
+ if (exponent==0 && !(op&DIVIDE)) break;\r
+\r
+ // to get here, var1 is less than var2, so divide var2 by the per-\r
+ // Unit power of ten and go for the next digit\r
+ var2ulen--; // shift down\r
+ exponent-=DECDPUN; // update the exponent\r
+ } // outer loop\r
+\r
+ // ---- division is complete ---------------------------------------\r
+ // here: acc has at least reqdigits+1 of good results (or fewer\r
+ // if early stop), starting at accnext+1 (its lsu)\r
+ // var1 has any residue at the stopping point\r
+ // accunits is the number of digits collected in acc\r
+ if (accunits==0) { // acc is 0\r
+ accunits=1; // show have a unit ..\r
+ accdigits=1; // ..\r
+ *accnext=0; // .. whose value is 0\r
+ }\r
+ else accnext++; // back to last placed\r
+ // accnext now -> lowest unit of result\r
+\r
+ residue=0; // assume no residue\r
+ if (op&DIVIDE) {\r
+ // record the presence of any residue, for rounding\r
+ if (*var1!=0 || var1units>1) residue=1;\r
+ else { // no residue\r
+ // Had an exact division; clean up spurious trailing 0s.\r
+ // There will be at most DECDPUN-1, from the final multiply,\r
+ // and then only if the result is non-0 (and even) and the\r
+ // exponent is 'loose'.\r
+ #if DECDPUN>1\r
+ Unit lsu=*accnext;\r
+ if (!(lsu&0x01) && (lsu!=0)) {\r
+ // count the trailing zeros\r
+ Int drop=0;\r
+ for (;; drop++) { // [will terminate because lsu!=0]\r
+ if (exponent>=maxexponent) break; // don't chop real 0s\r
+ #if DECDPUN<=4\r
+ if ((lsu-QUOT10(lsu, drop+1)\r
+ *powers[drop+1])!=0) break; // found non-0 digit\r
+ #else\r
+ if (lsu%powers[drop+1]!=0) break; // found non-0 digit\r
+ #endif\r
+ exponent++;\r
+ }\r
+ if (drop>0) {\r
+ accunits=decShiftToLeast(accnext, accunits, drop);\r
+ accdigits=decGetDigits(accnext, accunits);\r
+ accunits=D2U(accdigits);\r
+ // [exponent was adjusted in the loop]\r
+ }\r
+ } // neither odd nor 0\r
+ #endif\r
+ } // exact divide\r
+ } // divide\r
+ else /* op!=DIVIDE */ {\r
+ // check for coefficient overflow\r
+ if (accdigits+exponent>reqdigits) {\r
+ *status|=DEC_Division_impossible;\r
+ break;\r
+ }\r
+ if (op & (REMAINDER|REMNEAR)) {\r
+ // [Here, the exponent will be 0, because var1 was adjusted\r
+ // appropriately.]\r
+ Int postshift; // work\r
+ Flag wasodd=0; // integer was odd\r
+ Unit *quotlsu; // for save\r
+ Int quotdigits; // ..\r
+\r
+ bits=lhs->bits; // remainder sign is always as lhs\r
+\r
+ // Fastpath when residue is truly 0 is worthwhile [and\r
+ // simplifies the code below]\r
+ if (*var1==0 && var1units==1) { // residue is 0\r
+ Int exp=lhs->exponent; // save min(exponents)\r
+ if (rhs->exponent<exp) exp=rhs->exponent;\r
+ decNumberZero(res); // 0 coefficient\r
+ #if DECSUBSET\r
+ if (set->extended)\r
+ #endif\r
+ res->exponent=exp; // .. with proper exponent\r
+ res->bits=(uByte)(bits&DECNEG); // [cleaned]\r
+ decFinish(res, set, &residue, status); // might clamp\r
+ break;\r
+ }\r
+ // note if the quotient was odd\r
+ if (*accnext & 0x01) wasodd=1; // acc is odd\r
+ quotlsu=accnext; // save in case need to reinspect\r
+ quotdigits=accdigits; // ..\r
+\r
+ // treat the residue, in var1, as the value to return, via acc\r
+ // calculate the unused zero digits. This is the smaller of:\r
+ // var1 initial padding (saved above)\r
+ // var2 residual padding, which happens to be given by:\r
+ postshift=var1initpad+exponent-lhs->exponent+rhs->exponent;\r
+ // [the 'exponent' term accounts for the shifts during divide]\r
+ if (var1initpad<postshift) postshift=var1initpad;\r
+\r
+ // shift var1 the requested amount, and adjust its digits\r
+ var1units=decShiftToLeast(var1, var1units, postshift);\r
+ accnext=var1;\r
+ accdigits=decGetDigits(var1, var1units);\r
+ accunits=D2U(accdigits);\r
+\r
+ exponent=lhs->exponent; // exponent is smaller of lhs & rhs\r
+ if (rhs->exponent<exponent) exponent=rhs->exponent;\r
+\r
+ // Now correct the result if doing remainderNear; if it\r
+ // (looking just at coefficients) is > rhs/2, or == rhs/2 and\r
+ // the integer was odd then the result should be rem-rhs.\r
+ if (op&REMNEAR) {\r
+ Int compare, tarunits; // work\r
+ Unit *up; // ..\r
+ // calculate remainder*2 into the var1 buffer (which has\r
+ // 'headroom' of an extra unit and hence enough space)\r
+ // [a dedicated 'double' loop would be faster, here]\r
+ tarunits=decUnitAddSub(accnext, accunits, accnext, accunits,\r
+ 0, accnext, 1);\r
+ // decDumpAr('r', accnext, tarunits);\r
+\r
+ // Here, accnext (var1) holds tarunits Units with twice the\r
+ // remainder's coefficient, which must now be compared to the\r
+ // RHS. The remainder's exponent may be smaller than the RHS's.\r
+ compare=decUnitCompare(accnext, tarunits, rhs->lsu, D2U(rhs->digits),\r
+ rhs->exponent-exponent);\r
+ if (compare==BADINT) { // deep trouble\r
+ *status|=DEC_Insufficient_storage;\r
+ break;}\r
+\r
+ // now restore the remainder by dividing by two; the lsu\r
+ // is known to be even.\r
+ for (up=accnext; up<accnext+tarunits; up++) {\r
+ Int half; // half to add to lower unit\r
+ half=*up & 0x01;\r
+ *up/=2; // [shift]\r
+ if (!half) continue;\r
+ *(up-1)+=(DECDPUNMAX+1)/2;\r
+ }\r
+ // [accunits still describes the original remainder length]\r
+\r
+ if (compare>0 || (compare==0 && wasodd)) { // adjustment needed\r
+ Int exp, expunits, exprem; // work\r
+ // This is effectively causing round-up of the quotient,\r
+ // so if it was the rare case where it was full and all\r
+ // nines, it would overflow and hence division-impossible\r
+ // should be raised\r
+ Flag allnines=0; // 1 if quotient all nines\r
+ if (quotdigits==reqdigits) { // could be borderline\r
+ for (up=quotlsu; ; up++) {\r
+ if (quotdigits>DECDPUN) {\r
+ if (*up!=DECDPUNMAX) break;// non-nines\r
+ }\r
+ else { // this is the last Unit\r
+ if (*up==powers[quotdigits]-1) allnines=1;\r
+ break;\r
+ }\r
+ quotdigits-=DECDPUN; // checked those digits\r
+ } // up\r
+ } // borderline check\r
+ if (allnines) {\r
+ *status|=DEC_Division_impossible;\r
+ break;}\r
+\r
+ // rem-rhs is needed; the sign will invert. Again, var1\r
+ // can safely be used for the working Units array.\r
+ exp=rhs->exponent-exponent; // RHS padding needed\r
+ // Calculate units and remainder from exponent.\r
+ expunits=exp/DECDPUN;\r
+ exprem=exp%DECDPUN;\r
+ // subtract [A+B*(-m)]; the result will always be negative\r
+ accunits=-decUnitAddSub(accnext, accunits,\r
+ rhs->lsu, D2U(rhs->digits),\r
+ expunits, accnext, -(Int)powers[exprem]);\r
+ accdigits=decGetDigits(accnext, accunits); // count digits exactly\r
+ accunits=D2U(accdigits); // and recalculate the units for copy\r
+ // [exponent is as for original remainder]\r
+ bits^=DECNEG; // flip the sign\r
+ }\r
+ } // REMNEAR\r
+ } // REMAINDER or REMNEAR\r
+ } // not DIVIDE\r
+\r
+ // Set exponent and bits\r
+ res->exponent=exponent;\r
+ res->bits=(uByte)(bits&DECNEG); // [cleaned]\r
+\r
+ // Now the coefficient.\r
+ decSetCoeff(res, set, accnext, accdigits, &residue, status);\r
+\r
+ decFinish(res, set, &residue, status); // final cleanup\r
+\r
+ #if DECSUBSET\r
+ // If a divide then strip trailing zeros if subset [after round]\r
+ if (!set->extended && (op==DIVIDE)) decTrim(res, set, 0, 1, &dropped);\r
+ #endif\r
+ } while(0); // end protected\r
+\r
+ if (varalloc!=NULL) free(varalloc); // drop any storage used\r
+ if (allocacc!=NULL) free(allocacc); // ..\r
+ #if DECSUBSET\r
+ if (allocrhs!=NULL) free(allocrhs); // ..\r
+ if (alloclhs!=NULL) free(alloclhs); // ..\r
+ #endif\r
+ return res;\r
+ } // decDivideOp\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decMultiplyOp -- multiplication operation */\r
+/* */\r
+/* This routine performs the multiplication C=A x B. */\r
+/* */\r
+/* res is C, the result. C may be A and/or B (e.g., X=X*X) */\r
+/* lhs is A */\r
+/* rhs is B */\r
+/* set is the context */\r
+/* status is the usual accumulator */\r
+/* */\r
+/* C must have space for set->digits digits. */\r
+/* */\r
+/* ------------------------------------------------------------------ */\r
+/* 'Classic' multiplication is used rather than Karatsuba, as the */\r
+/* latter would give only a minor improvement for the short numbers */\r
+/* expected to be handled most (and uses much more memory). */\r
+/* */\r
+/* There are two major paths here: the general-purpose ('old code') */\r
+/* path which handles all DECDPUN values, and a fastpath version */\r
+/* which is used if 64-bit ints are available, DECDPUN<=4, and more */\r
+/* than two calls to decUnitAddSub would be made. */\r
+/* */\r
+/* The fastpath version lumps units together into 8-digit or 9-digit */\r
+/* chunks, and also uses a lazy carry strategy to minimise expensive */\r
+/* 64-bit divisions. The chunks are then broken apart again into */\r
+/* units for continuing processing. Despite this overhead, the */\r
+/* fastpath can speed up some 16-digit operations by 10x (and much */\r
+/* more for higher-precision calculations). */\r
+/* */\r
+/* A buffer always has to be used for the accumulator; in the */\r
+/* fastpath, buffers are also always needed for the chunked copies of */\r
+/* of the operand coefficients. */\r
+/* Static buffers are larger than needed just for multiply, to allow */\r
+/* for calls from other operations (notably exp). */\r
+/* ------------------------------------------------------------------ */\r
+#define FASTMUL (DECUSE64 && DECDPUN<5)\r
+static decNumber * decMultiplyOp(decNumber *res, const decNumber *lhs,\r
+ const decNumber *rhs, decContext *set,\r
+ uInt *status) {\r
+ Int accunits; // Units of accumulator in use\r
+ Int exponent; // work\r
+ Int residue=0; // rounding residue\r
+ uByte bits; // result sign\r
+ Unit *acc; // -> accumulator Unit array\r
+ Int needbytes; // size calculator\r
+ void *allocacc=NULL; // -> allocated accumulator, iff allocated\r
+ Unit accbuff[SD2U(DECBUFFER*4+1)]; // buffer (+1 for DECBUFFER==0,\r
+ // *4 for calls from other operations)\r
+ const Unit *mer, *mermsup; // work\r
+ Int madlength; // Units in multiplicand\r
+ Int shift; // Units to shift multiplicand by\r
+\r
+ #if FASTMUL\r
+ // if DECDPUN is 1 or 3 work in base 10**9, otherwise\r
+ // (DECDPUN is 2 or 4) then work in base 10**8\r
+ #if DECDPUN & 1 // odd\r
+ #define FASTBASE 1000000000 // base\r
+ #define FASTDIGS 9 // digits in base\r
+ #define FASTLAZY 18 // carry resolution point [1->18]\r
+ #else\r
+ #define FASTBASE 100000000\r
+ #define FASTDIGS 8\r
+ #define FASTLAZY 1844 // carry resolution point [1->1844]\r
+ #endif\r
+ // three buffers are used, two for chunked copies of the operands\r
+ // (base 10**8 or base 10**9) and one base 2**64 accumulator with\r
+ // lazy carry evaluation\r
+ uInt zlhibuff[(DECBUFFER*2+1)/8+1]; // buffer (+1 for DECBUFFER==0)\r
+ uInt *zlhi=zlhibuff; // -> lhs array\r
+ uInt *alloclhi=NULL; // -> allocated buffer, iff allocated\r
+ uInt zrhibuff[(DECBUFFER*2+1)/8+1]; // buffer (+1 for DECBUFFER==0)\r
+ uInt *zrhi=zrhibuff; // -> rhs array\r
+ uInt *allocrhi=NULL; // -> allocated buffer, iff allocated\r
+ uLong zaccbuff[(DECBUFFER*2+1)/4+2]; // buffer (+1 for DECBUFFER==0)\r
+ // [allocacc is shared for both paths, as only one will run]\r
+ uLong *zacc=zaccbuff; // -> accumulator array for exact result\r
+ #if DECDPUN==1\r
+ Int zoff; // accumulator offset\r
+ #endif\r
+ uInt *lip, *rip; // item pointers\r
+ uInt *lmsi, *rmsi; // most significant items\r
+ Int ilhs, irhs, iacc; // item counts in the arrays\r
+ Int lazy; // lazy carry counter\r
+ uLong lcarry; // uLong carry\r
+ uInt carry; // carry (NB not uLong)\r
+ Int count; // work\r
+ const Unit *cup; // ..\r
+ Unit *up; // ..\r
+ uLong *lp; // ..\r
+ Int p; // ..\r
+ #endif\r
+\r
+ #if DECSUBSET\r
+ decNumber *alloclhs=NULL; // -> allocated buffer, iff allocated\r
+ decNumber *allocrhs=NULL; // -> allocated buffer, iff allocated\r
+ #endif\r
+\r
+ #if DECCHECK\r
+ if (decCheckOperands(res, lhs, rhs, set)) return res;\r
+ #endif\r
+\r
+ // precalculate result sign\r
+ bits=(uByte)((lhs->bits^rhs->bits)&DECNEG);\r
+\r
+ // handle infinities and NaNs\r
+ if (SPECIALARGS) { // a special bit set\r
+ if (SPECIALARGS & (DECSNAN | DECNAN)) { // one or two NaNs\r
+ decNaNs(res, lhs, rhs, set, status);\r
+ return res;}\r
+ // one or two infinities; Infinity * 0 is invalid\r
+ if (((lhs->bits & DECINF)==0 && ISZERO(lhs))\r
+ ||((rhs->bits & DECINF)==0 && ISZERO(rhs))) {\r
+ *status|=DEC_Invalid_operation;\r
+ return res;}\r
+ decNumberZero(res);\r
+ res->bits=bits|DECINF; // infinity\r
+ return res;}\r
+\r
+ // For best speed, as in DMSRCN [the original Rexx numerics\r
+ // module], use the shorter number as the multiplier (rhs) and\r
+ // the longer as the multiplicand (lhs) to minimise the number of\r
+ // adds (partial products)\r
+ if (lhs->digits<rhs->digits) { // swap...\r
+ const decNumber *hold=lhs;\r
+ lhs=rhs;\r
+ rhs=hold;\r
+ }\r
+\r
+ do { // protect allocated storage\r
+ #if DECSUBSET\r
+ if (!set->extended) {\r
+ // reduce operands and set lostDigits status, as needed\r
+ if (lhs->digits>set->digits) {\r
+ alloclhs=decRoundOperand(lhs, set, status);\r
+ if (alloclhs==NULL) break;\r
+ lhs=alloclhs;\r
+ }\r
+ if (rhs->digits>set->digits) {\r
+ allocrhs=decRoundOperand(rhs, set, status);\r
+ if (allocrhs==NULL) break;\r
+ rhs=allocrhs;\r
+ }\r
+ }\r
+ #endif\r
+ // [following code does not require input rounding]\r
+\r
+ #if FASTMUL // fastpath can be used\r
+ // use the fast path if there are enough digits in the shorter\r
+ // operand to make the setup and takedown worthwhile\r
+ #define NEEDTWO (DECDPUN*2) // within two decUnitAddSub calls\r
+ if (rhs->digits>NEEDTWO) { // use fastpath...\r
+ // calculate the number of elements in each array\r
+ ilhs=(lhs->digits+FASTDIGS-1)/FASTDIGS; // [ceiling]\r
+ irhs=(rhs->digits+FASTDIGS-1)/FASTDIGS; // ..\r
+ iacc=ilhs+irhs;\r
+\r
+ // allocate buffers if required, as usual\r
+ needbytes=ilhs*sizeof(uInt);\r
+ if (needbytes>(Int)sizeof(zlhibuff)) {\r
+ alloclhi=(uInt *)malloc(needbytes);\r
+ zlhi=alloclhi;}\r
+ needbytes=irhs*sizeof(uInt);\r
+ if (needbytes>(Int)sizeof(zrhibuff)) {\r
+ allocrhi=(uInt *)malloc(needbytes);\r
+ zrhi=allocrhi;}\r
+\r
+ // Allocating the accumulator space needs a special case when\r
+ // DECDPUN=1 because when converting the accumulator to Units\r
+ // after the multiplication each 8-byte item becomes 9 1-byte\r
+ // units. Therefore iacc extra bytes are needed at the front\r
+ // (rounded up to a multiple of 8 bytes), and the uLong\r
+ // accumulator starts offset the appropriate number of units\r
+ // to the right to avoid overwrite during the unchunking.\r
+ needbytes=iacc*sizeof(uLong);\r
+ #if DECDPUN==1\r
+ zoff=(iacc+7)/8; // items to offset by\r
+ needbytes+=zoff*8;\r
+ #endif\r
+ if (needbytes>(Int)sizeof(zaccbuff)) {\r
+ allocacc=(uLong *)malloc(needbytes);\r
+ zacc=(uLong *)allocacc;}\r
+ if (zlhi==NULL||zrhi==NULL||zacc==NULL) {\r
+ *status|=DEC_Insufficient_storage;\r
+ break;}\r
+\r
+ acc=(Unit *)zacc; // -> target Unit array\r
+ #if DECDPUN==1\r
+ zacc+=zoff; // start uLong accumulator to right\r
+ #endif\r
+\r
+ // assemble the chunked copies of the left and right sides\r
+ for (count=lhs->digits, cup=lhs->lsu, lip=zlhi; count>0; lip++)\r
+ for (p=0, *lip=0; p<FASTDIGS && count>0;\r
+ p+=DECDPUN, cup++, count-=DECDPUN)\r
+ *lip+=*cup*powers[p];\r
+ lmsi=lip-1; // save -> msi\r
+ for (count=rhs->digits, cup=rhs->lsu, rip=zrhi; count>0; rip++)\r
+ for (p=0, *rip=0; p<FASTDIGS && count>0;\r
+ p+=DECDPUN, cup++, count-=DECDPUN)\r
+ *rip+=*cup*powers[p];\r
+ rmsi=rip-1; // save -> msi\r
+\r
+ // zero the accumulator\r
+ for (lp=zacc; lp<zacc+iacc; lp++) *lp=0;\r
+\r
+ /* Start the multiplication */\r
+ // Resolving carries can dominate the cost of accumulating the\r
+ // partial products, so this is only done when necessary.\r
+ // Each uLong item in the accumulator can hold values up to\r
+ // 2**64-1, and each partial product can be as large as\r
+ // (10**FASTDIGS-1)**2. When FASTDIGS=9, this can be added to\r
+ // itself 18.4 times in a uLong without overflowing, so during\r
+ // the main calculation resolution is carried out every 18th\r
+ // add -- every 162 digits. Similarly, when FASTDIGS=8, the\r
+ // partial products can be added to themselves 1844.6 times in\r
+ // a uLong without overflowing, so intermediate carry\r
+ // resolution occurs only every 14752 digits. Hence for common\r
+ // short numbers usually only the one final carry resolution\r
+ // occurs.\r
+ // (The count is set via FASTLAZY to simplify experiments to\r
+ // measure the value of this approach: a 35% improvement on a\r
+ // [34x34] multiply.)\r
+ lazy=FASTLAZY; // carry delay count\r
+ for (rip=zrhi; rip<=rmsi; rip++) { // over each item in rhs\r
+ lp=zacc+(rip-zrhi); // where to add the lhs\r
+ for (lip=zlhi; lip<=lmsi; lip++, lp++) { // over each item in lhs\r
+ *lp+=(uLong)(*lip)*(*rip); // [this should in-line]\r
+ } // lip loop\r
+ lazy--;\r
+ if (lazy>0 && rip!=rmsi) continue;\r
+ lazy=FASTLAZY; // reset delay count\r
+ // spin up the accumulator resolving overflows\r
+ for (lp=zacc; lp<zacc+iacc; lp++) {\r
+ if (*lp<FASTBASE) continue; // it fits\r
+ lcarry=*lp/FASTBASE; // top part [slow divide]\r
+ // lcarry can exceed 2**32-1, so check again; this check\r
+ // and occasional extra divide (slow) is well worth it, as\r
+ // it allows FASTLAZY to be increased to 18 rather than 4\r
+ // in the FASTDIGS=9 case\r
+ if (lcarry<FASTBASE) carry=(uInt)lcarry; // [usual]\r
+ else { // two-place carry [fairly rare]\r
+ uInt carry2=(uInt)(lcarry/FASTBASE); // top top part\r
+ *(lp+2)+=carry2; // add to item+2\r
+ *lp-=((uLong)FASTBASE*FASTBASE*carry2); // [slow]\r
+ carry=(uInt)(lcarry-((uLong)FASTBASE*carry2)); // [inline]\r
+ }\r
+ *(lp+1)+=carry; // add to item above [inline]\r
+ *lp-=((uLong)FASTBASE*carry); // [inline]\r
+ } // carry resolution\r
+ } // rip loop\r
+\r
+ // The multiplication is complete; time to convert back into\r
+ // units. This can be done in-place in the accumulator and in\r
+ // 32-bit operations, because carries were resolved after the\r
+ // final add. This needs N-1 divides and multiplies for\r
+ // each item in the accumulator (which will become up to N\r
+ // units, where 2<=N<=9).\r
+ for (lp=zacc, up=acc; lp<zacc+iacc; lp++) {\r
+ uInt item=(uInt)*lp; // decapitate to uInt\r
+ for (p=0; p<FASTDIGS-DECDPUN; p+=DECDPUN, up++) {\r
+ uInt part=item/(DECDPUNMAX+1);\r
+ *up=(Unit)(item-(part*(DECDPUNMAX+1)));\r
+ item=part;\r
+ } // p\r
+ *up=(Unit)item; up++; // [final needs no division]\r
+ } // lp\r
+ accunits=up-acc; // count of units\r
+ }\r
+ else { // here to use units directly, without chunking ['old code']\r
+ #endif\r
+\r
+ // if accumulator will be too long for local storage, then allocate\r
+ acc=accbuff; // -> assume buffer for accumulator\r
+ needbytes=(D2U(lhs->digits)+D2U(rhs->digits))*sizeof(Unit);\r
+ if (needbytes>(Int)sizeof(accbuff)) {\r
+ allocacc=(Unit *)malloc(needbytes);\r
+ if (allocacc==NULL) {*status|=DEC_Insufficient_storage; break;}\r
+ acc=(Unit *)allocacc; // use the allocated space\r
+ }\r
+\r
+ /* Now the main long multiplication loop */\r
+ // Unlike the equivalent in the IBM Java implementation, there\r
+ // is no advantage in calculating from msu to lsu. So, do it\r
+ // by the book, as it were.\r
+ // Each iteration calculates ACC=ACC+MULTAND*MULT\r
+ accunits=1; // accumulator starts at '0'\r
+ *acc=0; // .. (lsu=0)\r
+ shift=0; // no multiplicand shift at first\r
+ madlength=D2U(lhs->digits); // this won't change\r
+ mermsup=rhs->lsu+D2U(rhs->digits); // -> msu+1 of multiplier\r
+\r
+ for (mer=rhs->lsu; mer<mermsup; mer++) {\r
+ // Here, *mer is the next Unit in the multiplier to use\r
+ // If non-zero [optimization] add it...\r
+ if (*mer!=0) accunits=decUnitAddSub(&acc[shift], accunits-shift,\r
+ lhs->lsu, madlength, 0,\r
+ &acc[shift], *mer)\r
+ + shift;\r
+ else { // extend acc with a 0; it will be used shortly\r
+ *(acc+accunits)=0; // [this avoids length of <=0 later]\r
+ accunits++;\r
+ }\r
+ // multiply multiplicand by 10**DECDPUN for next Unit to left\r
+ shift++; // add this for 'logical length'\r
+ } // n\r
+ #if FASTMUL\r
+ } // unchunked units\r
+ #endif\r
+ // common end-path\r
+ #if DECTRACE\r
+ decDumpAr('*', acc, accunits); // Show exact result\r
+ #endif\r
+\r
+ // acc now contains the exact result of the multiplication,\r
+ // possibly with a leading zero unit; build the decNumber from\r
+ // it, noting if any residue\r
+ res->bits=bits; // set sign\r
+ res->digits=decGetDigits(acc, accunits); // count digits exactly\r
+\r
+ // There can be a 31-bit wrap in calculating the exponent.\r
+ // This can only happen if both input exponents are negative and\r
+ // both their magnitudes are large. If there was a wrap, set a\r
+ // safe very negative exponent, from which decFinalize() will\r
+ // raise a hard underflow shortly.\r
+ exponent=lhs->exponent+rhs->exponent; // calculate exponent\r
+ if (lhs->exponent<0 && rhs->exponent<0 && exponent>0)\r
+ exponent=-2*DECNUMMAXE; // force underflow\r
+ res->exponent=exponent; // OK to overwrite now\r
+\r
+\r
+ // Set the coefficient. If any rounding, residue records\r
+ decSetCoeff(res, set, acc, res->digits, &residue, status);\r
+ decFinish(res, set, &residue, status); // final cleanup\r
+ } while(0); // end protected\r
+\r
+ if (allocacc!=NULL) free(allocacc); // drop any storage used\r
+ #if DECSUBSET\r
+ if (allocrhs!=NULL) free(allocrhs); // ..\r
+ if (alloclhs!=NULL) free(alloclhs); // ..\r
+ #endif\r
+ #if FASTMUL\r
+ if (allocrhi!=NULL) free(allocrhi); // ..\r
+ if (alloclhi!=NULL) free(alloclhi); // ..\r
+ #endif\r
+ return res;\r
+ } // decMultiplyOp\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decExpOp -- effect exponentiation */\r
+/* */\r
+/* This computes C = exp(A) */\r
+/* */\r
+/* res is C, the result. C may be A */\r
+/* rhs is A */\r
+/* set is the context; note that rounding mode has no effect */\r
+/* */\r
+/* C must have space for set->digits digits. status is updated but */\r
+/* not set. */\r
+/* */\r
+/* Restrictions: */\r
+/* */\r
+/* digits, emax, and -emin in the context must be less than */\r
+/* 2*DEC_MAX_MATH (1999998), and the rhs must be within these */\r
+/* bounds or a zero. This is an internal routine, so these */\r
+/* restrictions are contractual and not enforced. */\r
+/* */\r
+/* A finite result is rounded using DEC_ROUND_HALF_EVEN; it will */\r
+/* almost always be correctly rounded, but may be up to 1 ulp in */\r
+/* error in rare cases. */\r
+/* */\r
+/* Finite results will always be full precision and Inexact, except */\r
+/* when A is a zero or -Infinity (giving 1 or 0 respectively). */\r
+/* ------------------------------------------------------------------ */\r
+/* This approach used here is similar to the algorithm described in */\r
+/* */\r
+/* Variable Precision Exponential Function, T. E. Hull and */\r
+/* A. Abrham, ACM Transactions on Mathematical Software, Vol 12 #2, */\r
+/* pp79-91, ACM, June 1986. */\r
+/* */\r
+/* with the main difference being that the iterations in the series */\r
+/* evaluation are terminated dynamically (which does not require the */\r
+/* extra variable-precision variables which are expensive in this */\r
+/* context). */\r
+/* */\r
+/* The error analysis in Hull & Abrham's paper applies except for the */\r
+/* round-off error accumulation during the series evaluation. This */\r
+/* code does not precalculate the number of iterations and so cannot */\r
+/* use Horner's scheme. Instead, the accumulation is done at double- */\r
+/* precision, which ensures that the additions of the terms are exact */\r
+/* and do not accumulate round-off (and any round-off errors in the */\r
+/* terms themselves move 'to the right' faster than they can */\r
+/* accumulate). This code also extends the calculation by allowing, */\r
+/* in the spirit of other decNumber operators, the input to be more */\r
+/* precise than the result (the precision used is based on the more */\r
+/* precise of the input or requested result). */\r
+/* */\r
+/* Implementation notes: */\r
+/* */\r
+/* 1. This is separated out as decExpOp so it can be called from */\r
+/* other Mathematical functions (notably Ln) with a wider range */\r
+/* than normal. In particular, it can handle the slightly wider */\r
+/* (double) range needed by Ln (which has to be able to calculate */\r
+/* exp(-x) where x can be the tiniest number (Ntiny). */\r
+/* */\r
+/* 2. Normalizing x to be <=0.1 (instead of <=1) reduces loop */\r
+/* iterations by appoximately a third with additional (although */\r
+/* diminishing) returns as the range is reduced to even smaller */\r
+/* fractions. However, h (the power of 10 used to correct the */\r
+/* result at the end, see below) must be kept <=8 as otherwise */\r
+/* the final result cannot be computed. Hence the leverage is a */\r
+/* sliding value (8-h), where potentially the range is reduced */\r
+/* more for smaller values. */\r
+/* */\r
+/* The leverage that can be applied in this way is severely */\r
+/* limited by the cost of the raise-to-the power at the end, */\r
+/* which dominates when the number of iterations is small (less */\r
+/* than ten) or when rhs is short. As an example, the adjustment */\r
+/* x**10,000,000 needs 31 multiplications, all but one full-width. */\r
+/* */\r
+/* 3. The restrictions (especially precision) could be raised with */\r
+/* care, but the full decNumber range seems very hard within the */\r
+/* 32-bit limits. */\r
+/* */\r
+/* 4. The working precisions for the static buffers are twice the */\r
+/* obvious size to allow for calls from decNumberPower. */\r
+/* ------------------------------------------------------------------ */\r
+decNumber * decExpOp(decNumber *res, const decNumber *rhs,\r
+ decContext *set, uInt *status) {\r
+ uInt ignore=0; // working status\r
+ Int h; // adjusted exponent for 0.xxxx\r
+ Int p; // working precision\r
+ Int residue; // rounding residue\r
+ uInt needbytes; // for space calculations\r
+ const decNumber *x=rhs; // (may point to safe copy later)\r
+ decContext aset, tset, dset; // working contexts\r
+ Int comp; // work\r
+\r
+ // the argument is often copied to normalize it, so (unusually) it\r
+ // is treated like other buffers, using DECBUFFER, +1 in case\r
+ // DECBUFFER is 0\r
+ decNumber bufr[D2N(DECBUFFER*2+1)];\r
+ decNumber *allocrhs=NULL; // non-NULL if rhs buffer allocated\r
+\r
+ // the working precision will be no more than set->digits+8+1\r
+ // so for on-stack buffers DECBUFFER+9 is used, +1 in case DECBUFFER\r
+ // is 0 (and twice that for the accumulator)\r
+\r
+ // buffer for t, term (working precision plus)\r
+ decNumber buft[D2N(DECBUFFER*2+9+1)];\r
+ decNumber *allocbuft=NULL; // -> allocated buft, iff allocated\r
+ decNumber *t=buft; // term\r
+ // buffer for a, accumulator (working precision * 2), at least 9\r
+ decNumber bufa[D2N(DECBUFFER*4+18+1)];\r
+ decNumber *allocbufa=NULL; // -> allocated bufa, iff allocated\r
+ decNumber *a=bufa; // accumulator\r
+ // decNumber for the divisor term; this needs at most 9 digits\r
+ // and so can be fixed size [16 so can use standard context]\r
+ decNumber bufd[D2N(16)];\r
+ decNumber *d=bufd; // divisor\r
+ decNumber numone; // constant 1\r
+\r
+ #if DECCHECK\r
+ Int iterations=0; // for later sanity check\r
+ if (decCheckOperands(res, DECUNUSED, rhs, set)) return res;\r
+ #endif\r
+\r
+ do { // protect allocated storage\r
+ if (SPECIALARG) { // handle infinities and NaNs\r
+ if (decNumberIsInfinite(rhs)) { // an infinity\r
+ if (decNumberIsNegative(rhs)) // -Infinity -> +0\r
+ decNumberZero(res);\r
+ else decNumberCopy(res, rhs); // +Infinity -> self\r
+ }\r
+ else decNaNs(res, rhs, NULL, set, status); // a NaN\r
+ break;}\r
+\r
+ if (ISZERO(rhs)) { // zeros -> exact 1\r
+ decNumberZero(res); // make clean 1\r
+ *res->lsu=1; // ..\r
+ break;} // [no status to set]\r
+\r
+ // e**x when 0 < x < 0.66 is < 1+3x/2, hence can fast-path\r
+ // positive and negative tiny cases which will result in inexact\r
+ // 1. This also allows the later add-accumulate to always be\r
+ // exact (because its length will never be more than twice the\r
+ // working precision).\r
+ // The comparator (tiny) needs just one digit, so use the\r
+ // decNumber d for it (reused as the divisor, etc., below); its\r
+ // exponent is such that if x is positive it will have\r
+ // set->digits-1 zeros between the decimal point and the digit,\r
+ // which is 4, and if x is negative one more zero there as the\r
+ // more precise result will be of the form 0.9999999 rather than\r
+ // 1.0000001. Hence, tiny will be 0.0000004 if digits=7 and x>0\r
+ // or 0.00000004 if digits=7 and x<0. If RHS not larger than\r
+ // this then the result will be 1.000000\r
+ decNumberZero(d); // clean\r
+ *d->lsu=4; // set 4 ..\r
+ d->exponent=-set->digits; // * 10**(-d)\r
+ if (decNumberIsNegative(rhs)) d->exponent--; // negative case\r
+ comp=decCompare(d, rhs, 1); // signless compare\r
+ if (comp==BADINT) {\r
+ *status|=DEC_Insufficient_storage;\r
+ break;}\r
+ if (comp>=0) { // rhs < d\r
+ Int shift=set->digits-1;\r
+ decNumberZero(res); // set 1\r
+ *res->lsu=1; // ..\r
+ res->digits=decShiftToMost(res->lsu, 1, shift);\r
+ res->exponent=-shift; // make 1.0000...\r
+ *status|=DEC_Inexact | DEC_Rounded; // .. inexactly\r
+ break;} // tiny\r
+\r
+ // set up the context to be used for calculating a, as this is\r
+ // used on both paths below\r
+ decContextDefault(&aset, DEC_INIT_DECIMAL64);\r
+ // accumulator bounds are as requested (could underflow)\r
+ aset.emax=set->emax; // usual bounds\r
+ aset.emin=set->emin; // ..\r
+ aset.clamp=0; // and no concrete format\r
+\r
+ // calculate the adjusted (Hull & Abrham) exponent (where the\r
+ // decimal point is just to the left of the coefficient msd)\r
+ h=rhs->exponent+rhs->digits;\r
+ // if h>8 then 10**h cannot be calculated safely; however, when\r
+ // h=8 then exp(|rhs|) will be at least exp(1E+7) which is at\r
+ // least 6.59E+4342944, so (due to the restriction on Emax/Emin)\r
+ // overflow (or underflow to 0) is guaranteed -- so this case can\r
+ // be handled by simply forcing the appropriate excess\r
+ if (h>8) { // overflow/underflow\r
+ // set up here so Power call below will over or underflow to\r
+ // zero; set accumulator to either 2 or 0.02\r
+ // [stack buffer for a is always big enough for this]\r
+ decNumberZero(a);\r
+ *a->lsu=2; // not 1 but < exp(1)\r
+ if (decNumberIsNegative(rhs)) a->exponent=-2; // make 0.02\r
+ h=8; // clamp so 10**h computable\r
+ p=9; // set a working precision\r
+ }\r
+ else { // h<=8\r
+ Int maxlever=(rhs->digits>8?1:0);\r
+ // [could/should increase this for precisions >40 or so, too]\r
+\r
+ // if h is 8, cannot normalize to a lower upper limit because\r
+ // the final result will not be computable (see notes above),\r
+ // but leverage can be applied whenever h is less than 8.\r
+ // Apply as much as possible, up to a MAXLEVER digits, which\r
+ // sets the tradeoff against the cost of the later a**(10**h).\r
+ // As h is increased, the working precision below also\r
+ // increases to compensate for the "constant digits at the\r
+ // front" effect.\r
+ Int lever=MINI(8-h, maxlever); // leverage attainable\r
+ Int use=-rhs->digits-lever; // exponent to use for RHS\r
+ h+=lever; // apply leverage selected\r
+ if (h<0) { // clamp\r
+ use+=h; // [may end up subnormal]\r
+ h=0;\r
+ }\r
+ // Take a copy of RHS if it needs normalization (true whenever x>=1)\r
+ if (rhs->exponent!=use) {\r
+ decNumber *newrhs=bufr; // assume will fit on stack\r
+ needbytes=sizeof(decNumber)+(D2U(rhs->digits)-1)*sizeof(Unit);\r
+ if (needbytes>sizeof(bufr)) { // need malloc space\r
+ allocrhs=(decNumber *)malloc(needbytes);\r
+ if (allocrhs==NULL) { // hopeless -- abandon\r
+ *status|=DEC_Insufficient_storage;\r
+ break;}\r
+ newrhs=allocrhs; // use the allocated space\r
+ }\r
+ decNumberCopy(newrhs, rhs); // copy to safe space\r
+ newrhs->exponent=use; // normalize; now <1\r
+ x=newrhs; // ready for use\r
+ // decNumberShow(x);\r
+ }\r
+\r
+ // Now use the usual power series to evaluate exp(x). The\r
+ // series starts as 1 + x + x^2/2 ... so prime ready for the\r
+ // third term by setting the term variable t=x, the accumulator\r
+ // a=1, and the divisor d=2.\r
+\r
+ // First determine the working precision. From Hull & Abrham\r
+ // this is set->digits+h+2. However, if x is 'over-precise' we\r
+ // need to allow for all its digits to potentially participate\r
+ // (consider an x where all the excess digits are 9s) so in\r
+ // this case use x->digits+h+2\r
+ p=MAXI(x->digits, set->digits)+h+2; // [h<=8]\r
+\r
+ // a and t are variable precision, and depend on p, so space\r
+ // must be allocated for them if necessary\r
+\r
+ // the accumulator needs to be able to hold 2p digits so that\r
+ // the additions on the second and subsequent iterations are\r
+ // sufficiently exact.\r
+ needbytes=sizeof(decNumber)+(D2U(p*2)-1)*sizeof(Unit);\r
+ if (needbytes>sizeof(bufa)) { // need malloc space\r
+ allocbufa=(decNumber *)malloc(needbytes);\r
+ if (allocbufa==NULL) { // hopeless -- abandon\r
+ *status|=DEC_Insufficient_storage;\r
+ break;}\r
+ a=allocbufa; // use the allocated space\r
+ }\r
+ // the term needs to be able to hold p digits (which is\r
+ // guaranteed to be larger than x->digits, so the initial copy\r
+ // is safe); it may also be used for the raise-to-power\r
+ // calculation below, which needs an extra two digits\r
+ needbytes=sizeof(decNumber)+(D2U(p+2)-1)*sizeof(Unit);\r
+ if (needbytes>sizeof(buft)) { // need malloc space\r
+ allocbuft=(decNumber *)malloc(needbytes);\r
+ if (allocbuft==NULL) { // hopeless -- abandon\r
+ *status|=DEC_Insufficient_storage;\r
+ break;}\r
+ t=allocbuft; // use the allocated space\r
+ }\r
+\r
+ decNumberCopy(t, x); // term=x\r
+ decNumberZero(a); *a->lsu=1; // accumulator=1\r
+ decNumberZero(d); *d->lsu=2; // divisor=2\r
+ decNumberZero(&numone); *numone.lsu=1; // constant 1 for increment\r
+\r
+ // set up the contexts for calculating a, t, and d\r
+ decContextDefault(&tset, DEC_INIT_DECIMAL64);\r
+ dset=tset;\r
+ // accumulator bounds are set above, set precision now\r
+ aset.digits=p*2; // double\r
+ // term bounds avoid any underflow or overflow\r
+ tset.digits=p;\r
+ tset.emin=DEC_MIN_EMIN; // [emax is plenty]\r
+ // [dset.digits=16, etc., are sufficient]\r
+\r
+ // finally ready to roll\r
+ for (;;) {\r
+ #if DECCHECK\r
+ iterations++;\r
+ #endif\r
+ // only the status from the accumulation is interesting\r
+ // [but it should remain unchanged after first add]\r
+ decAddOp(a, a, t, &aset, 0, status); // a=a+t\r
+ decMultiplyOp(t, t, x, &tset, &ignore); // t=t*x\r
+ decDivideOp(t, t, d, &tset, DIVIDE, &ignore); // t=t/d\r
+ // the iteration ends when the term cannot affect the result,\r
+ // if rounded to p digits, which is when its value is smaller\r
+ // than the accumulator by p+1 digits. There must also be\r
+ // full precision in a.\r
+ if (((a->digits+a->exponent)>=(t->digits+t->exponent+p+1))\r
+ && (a->digits>=p)) break;\r
+ decAddOp(d, d, &numone, &dset, 0, &ignore); // d=d+1\r
+ } // iterate\r
+\r
+ #if DECCHECK\r
+ // just a sanity check; comment out test to show always\r
+ if (iterations>p+3)\r
+ printf("Exp iterations=%ld, status=%08lx, p=%ld, d=%ld\n",\r
+ (LI)iterations, (LI)*status, (LI)p, (LI)x->digits);\r
+ #endif\r
+ } // h<=8\r
+\r
+ // apply postconditioning: a=a**(10**h) -- this is calculated\r
+ // at a slightly higher precision than Hull & Abrham suggest\r
+ if (h>0) {\r
+ Int seenbit=0; // set once a 1-bit is seen\r
+ Int i; // counter\r
+ Int n=powers[h]; // always positive\r
+ aset.digits=p+2; // sufficient precision\r
+ // avoid the overhead and many extra digits of decNumberPower\r
+ // as all that is needed is the short 'multipliers' loop; here\r
+ // accumulate the answer into t\r
+ decNumberZero(t); *t->lsu=1; // acc=1\r
+ for (i=1;;i++){ // for each bit [top bit ignored]\r
+ // abandon if have had overflow or terminal underflow\r
+ if (*status & (DEC_Overflow|DEC_Underflow)) { // interesting?\r
+ if (*status&DEC_Overflow || ISZERO(t)) break;}\r
+ n=n<<1; // move next bit to testable position\r
+ if (n<0) { // top bit is set\r
+ seenbit=1; // OK, have a significant bit\r
+ decMultiplyOp(t, t, a, &aset, status); // acc=acc*x\r
+ }\r
+ if (i==31) break; // that was the last bit\r
+ if (!seenbit) continue; // no need to square 1\r
+ decMultiplyOp(t, t, t, &aset, status); // acc=acc*acc [square]\r
+ } /*i*/ // 32 bits\r
+ // decNumberShow(t);\r
+ a=t; // and carry on using t instead of a\r
+ }\r
+\r
+ // Copy and round the result to res\r
+ residue=1; // indicate dirt to right ..\r
+ if (ISZERO(a)) residue=0; // .. unless underflowed to 0\r
+ aset.digits=set->digits; // [use default rounding]\r
+ decCopyFit(res, a, &aset, &residue, status); // copy & shorten\r
+ decFinish(res, set, &residue, status); // cleanup/set flags\r
+ } while(0); // end protected\r
+\r
+ if (allocrhs !=NULL) free(allocrhs); // drop any storage used\r
+ if (allocbufa!=NULL) free(allocbufa); // ..\r
+ if (allocbuft!=NULL) free(allocbuft); // ..\r
+ // [status is handled by caller]\r
+ return res;\r
+ } // decExpOp\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* Initial-estimate natural logarithm table */\r
+/* */\r
+/* LNnn -- 90-entry 16-bit table for values from .10 through .99. */\r
+/* The result is a 4-digit encode of the coefficient (c=the */\r
+/* top 14 bits encoding 0-9999) and a 2-digit encode of the */\r
+/* exponent (e=the bottom 2 bits encoding 0-3) */\r
+/* */\r
+/* The resulting value is given by: */\r
+/* */\r
+/* v = -c * 10**(-e-3) */\r
+/* */\r
+/* where e and c are extracted from entry k = LNnn[x-10] */\r
+/* where x is truncated (NB) into the range 10 through 99, */\r
+/* and then c = k>>2 and e = k&3. */\r
+/* ------------------------------------------------------------------ */\r
+const uShort LNnn[90]={9016, 8652, 8316, 8008, 7724, 7456, 7208,\r
+ 6972, 6748, 6540, 6340, 6148, 5968, 5792, 5628, 5464, 5312,\r
+ 5164, 5020, 4884, 4748, 4620, 4496, 4376, 4256, 4144, 4032,\r
+ 39233, 38181, 37157, 36157, 35181, 34229, 33297, 32389, 31501, 30629,\r
+ 29777, 28945, 28129, 27329, 26545, 25777, 25021, 24281, 23553, 22837,\r
+ 22137, 21445, 20769, 20101, 19445, 18801, 18165, 17541, 16925, 16321,\r
+ 15721, 15133, 14553, 13985, 13421, 12865, 12317, 11777, 11241, 10717,\r
+ 10197, 9685, 9177, 8677, 8185, 7697, 7213, 6737, 6269, 5801,\r
+ 5341, 4889, 4437, 39930, 35534, 31186, 26886, 22630, 18418, 14254,\r
+ 10130, 6046, 20055};\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decLnOp -- effect natural logarithm */\r
+/* */\r
+/* This computes C = ln(A) */\r
+/* */\r
+/* res is C, the result. C may be A */\r
+/* rhs is A */\r
+/* set is the context; note that rounding mode has no effect */\r
+/* */\r
+/* C must have space for set->digits digits. */\r
+/* */\r
+/* Notable cases: */\r
+/* A<0 -> Invalid */\r
+/* A=0 -> -Infinity (Exact) */\r
+/* A=+Infinity -> +Infinity (Exact) */\r
+/* A=1 exactly -> 0 (Exact) */\r
+/* */\r
+/* Restrictions (as for Exp): */\r
+/* */\r
+/* digits, emax, and -emin in the context must be less than */\r
+/* DEC_MAX_MATH+11 (1000010), and the rhs must be within these */\r
+/* bounds or a zero. This is an internal routine, so these */\r
+/* restrictions are contractual and not enforced. */\r
+/* */\r
+/* A finite result is rounded using DEC_ROUND_HALF_EVEN; it will */\r
+/* almost always be correctly rounded, but may be up to 1 ulp in */\r
+/* error in rare cases. */\r
+/* ------------------------------------------------------------------ */\r
+/* The result is calculated using Newton's method, with each */\r
+/* iteration calculating a' = a + x * exp(-a) - 1. See, for example, */\r
+/* Epperson 1989. */\r
+/* */\r
+/* The iteration ends when the adjustment x*exp(-a)-1 is tiny enough. */\r
+/* This has to be calculated at the sum of the precision of x and the */\r
+/* working precision. */\r
+/* */\r
+/* Implementation notes: */\r
+/* */\r
+/* 1. This is separated out as decLnOp so it can be called from */\r
+/* other Mathematical functions (e.g., Log 10) with a wider range */\r
+/* than normal. In particular, it can handle the slightly wider */\r
+/* (+9+2) range needed by a power function. */\r
+/* */\r
+/* 2. The speed of this function is about 10x slower than exp, as */\r
+/* it typically needs 4-6 iterations for short numbers, and the */\r
+/* extra precision needed adds a squaring effect, twice. */\r
+/* */\r
+/* 3. Fastpaths are included for ln(10) and ln(2), up to length 40, */\r
+/* as these are common requests. ln(10) is used by log10(x). */\r
+/* */\r
+/* 4. An iteration might be saved by widening the LNnn table, and */\r
+/* would certainly save at least one if it were made ten times */\r
+/* bigger, too (for truncated fractions 0.100 through 0.999). */\r
+/* However, for most practical evaluations, at least four or five */\r
+/* iterations will be neede -- so this would only speed up by */\r
+/* 20-25% and that probably does not justify increasing the table */\r
+/* size. */\r
+/* */\r
+/* 5. The static buffers are larger than might be expected to allow */\r
+/* for calls from decNumberPower. */\r
+/* ------------------------------------------------------------------ */\r
+decNumber * decLnOp(decNumber *res, const decNumber *rhs,\r
+ decContext *set, uInt *status) {\r
+ uInt ignore=0; // working status accumulator\r
+ uInt needbytes; // for space calculations\r
+ Int residue; // rounding residue\r
+ Int r; // rhs=f*10**r [see below]\r
+ Int p; // working precision\r
+ Int pp; // precision for iteration\r
+ Int t; // work\r
+\r
+ // buffers for a (accumulator, typically precision+2) and b\r
+ // (adjustment calculator, same size)\r
+ decNumber bufa[D2N(DECBUFFER+12)];\r
+ decNumber *allocbufa=NULL; // -> allocated bufa, iff allocated\r
+ decNumber *a=bufa; // accumulator/work\r
+ decNumber bufb[D2N(DECBUFFER*2+2)];\r
+ decNumber *allocbufb=NULL; // -> allocated bufa, iff allocated\r
+ decNumber *b=bufb; // adjustment/work\r
+\r
+ decNumber numone; // constant 1\r
+ decNumber cmp; // work\r
+ decContext aset, bset; // working contexts\r
+\r
+ #if DECCHECK\r
+ Int iterations=0; // for later sanity check\r
+ if (decCheckOperands(res, DECUNUSED, rhs, set)) return res;\r
+ #endif\r
+\r
+ do { // protect allocated storage\r
+ if (SPECIALARG) { // handle infinities and NaNs\r
+ if (decNumberIsInfinite(rhs)) { // an infinity\r
+ if (decNumberIsNegative(rhs)) // -Infinity -> error\r
+ *status|=DEC_Invalid_operation;\r
+ else decNumberCopy(res, rhs); // +Infinity -> self\r
+ }\r
+ else decNaNs(res, rhs, NULL, set, status); // a NaN\r
+ break;}\r
+\r
+ if (ISZERO(rhs)) { // +/- zeros -> -Infinity\r
+ decNumberZero(res); // make clean\r
+ res->bits=DECINF|DECNEG; // set - infinity\r
+ break;} // [no status to set]\r
+\r
+ // Non-zero negatives are bad...\r
+ if (decNumberIsNegative(rhs)) { // -x -> error\r
+ *status|=DEC_Invalid_operation;\r
+ break;}\r
+\r
+ // Here, rhs is positive, finite, and in range\r
+\r
+ // lookaside fastpath code for ln(2) and ln(10) at common lengths\r
+ if (rhs->exponent==0 && set->digits<=40) {\r
+ #if DECDPUN==1\r
+ if (rhs->lsu[0]==0 && rhs->lsu[1]==1 && rhs->digits==2) { // ln(10)\r
+ #else\r
+ if (rhs->lsu[0]==10 && rhs->digits==2) { // ln(10)\r
+ #endif\r
+ aset=*set; aset.round=DEC_ROUND_HALF_EVEN;\r
+ #define LN10 "2.302585092994045684017991454684364207601"\r
+ decNumberFromString(res, LN10, &aset);\r
+ *status|=(DEC_Inexact | DEC_Rounded); // is inexact\r
+ break;}\r
+ if (rhs->lsu[0]==2 && rhs->digits==1) { // ln(2)\r
+ aset=*set; aset.round=DEC_ROUND_HALF_EVEN;\r
+ #define LN2 "0.6931471805599453094172321214581765680755"\r
+ decNumberFromString(res, LN2, &aset);\r
+ *status|=(DEC_Inexact | DEC_Rounded);\r
+ break;}\r
+ } // integer and short\r
+\r
+ // Determine the working precision. This is normally the\r
+ // requested precision + 2, with a minimum of 9. However, if\r
+ // the rhs is 'over-precise' then allow for all its digits to\r
+ // potentially participate (consider an rhs where all the excess\r
+ // digits are 9s) so in this case use rhs->digits+2.\r
+ p=MAXI(rhs->digits, MAXI(set->digits, 7))+2;\r
+\r
+ // Allocate space for the accumulator and the high-precision\r
+ // adjustment calculator, if necessary. The accumulator must\r
+ // be able to hold p digits, and the adjustment up to\r
+ // rhs->digits+p digits. They are also made big enough for 16\r
+ // digits so that they can be used for calculating the initial\r
+ // estimate.\r
+ needbytes=sizeof(decNumber)+(D2U(MAXI(p,16))-1)*sizeof(Unit);\r
+ if (needbytes>sizeof(bufa)) { // need malloc space\r
+ allocbufa=(decNumber *)malloc(needbytes);\r
+ if (allocbufa==NULL) { // hopeless -- abandon\r
+ *status|=DEC_Insufficient_storage;\r
+ break;}\r
+ a=allocbufa; // use the allocated space\r
+ }\r
+ pp=p+rhs->digits;\r
+ needbytes=sizeof(decNumber)+(D2U(MAXI(pp,16))-1)*sizeof(Unit);\r
+ if (needbytes>sizeof(bufb)) { // need malloc space\r
+ allocbufb=(decNumber *)malloc(needbytes);\r
+ if (allocbufb==NULL) { // hopeless -- abandon\r
+ *status|=DEC_Insufficient_storage;\r
+ break;}\r
+ b=allocbufb; // use the allocated space\r
+ }\r
+\r
+ // Prepare an initial estimate in acc. Calculate this by\r
+ // considering the coefficient of x to be a normalized fraction,\r
+ // f, with the decimal point at far left and multiplied by\r
+ // 10**r. Then, rhs=f*10**r and 0.1<=f<1, and\r
+ // ln(x) = ln(f) + ln(10)*r\r
+ // Get the initial estimate for ln(f) from a small lookup\r
+ // table (see above) indexed by the first two digits of f,\r
+ // truncated.\r
+\r
+ decContextDefault(&aset, DEC_INIT_DECIMAL64); // 16-digit extended\r
+ r=rhs->exponent+rhs->digits; // 'normalised' exponent\r
+ decNumberFromInt32(a, r); // a=r\r
+ decNumberFromInt32(b, 2302585); // b=ln(10) (2.302585)\r
+ b->exponent=-6; // ..\r
+ decMultiplyOp(a, a, b, &aset, &ignore); // a=a*b\r
+ // now get top two digits of rhs into b by simple truncate and\r
+ // force to integer\r
+ residue=0; // (no residue)\r
+ aset.digits=2; aset.round=DEC_ROUND_DOWN;\r
+ decCopyFit(b, rhs, &aset, &residue, &ignore); // copy & shorten\r
+ b->exponent=0; // make integer\r
+ t=decGetInt(b); // [cannot fail]\r
+ if (t<10) t=X10(t); // adjust single-digit b\r
+ t=LNnn[t-10]; // look up ln(b)\r
+ decNumberFromInt32(b, t>>2); // b=ln(b) coefficient\r
+ b->exponent=-(t&3)-3; // set exponent\r
+ b->bits=DECNEG; // ln(0.10)->ln(0.99) always -ve\r
+ aset.digits=16; aset.round=DEC_ROUND_HALF_EVEN; // restore\r
+ decAddOp(a, a, b, &aset, 0, &ignore); // acc=a+b\r
+ // the initial estimate is now in a, with up to 4 digits correct.\r
+ // When rhs is at or near Nmax the estimate will be low, so we\r
+ // will approach it from below, avoiding overflow when calling exp.\r
+\r
+ decNumberZero(&numone); *numone.lsu=1; // constant 1 for adjustment\r
+\r
+ // accumulator bounds are as requested (could underflow, but\r
+ // cannot overflow)\r
+ aset.emax=set->emax;\r
+ aset.emin=set->emin;\r
+ aset.clamp=0; // no concrete format\r
+ // set up a context to be used for the multiply and subtract\r
+ bset=aset;\r
+ bset.emax=DEC_MAX_MATH*2; // use double bounds for the\r
+ bset.emin=-DEC_MAX_MATH*2; // adjustment calculation\r
+ // [see decExpOp call below]\r
+ // for each iteration double the number of digits to calculate,\r
+ // up to a maximum of p\r
+ pp=9; // initial precision\r
+ // [initially 9 as then the sequence starts 7+2, 16+2, and\r
+ // 34+2, which is ideal for standard-sized numbers]\r
+ aset.digits=pp; // working context\r
+ bset.digits=pp+rhs->digits; // wider context\r
+ for (;;) { // iterate\r
+ #if DECCHECK\r
+ iterations++;\r
+ if (iterations>24) break; // consider 9 * 2**24\r
+ #endif\r
+ // calculate the adjustment (exp(-a)*x-1) into b. This is a\r
+ // catastrophic subtraction but it really is the difference\r
+ // from 1 that is of interest.\r
+ // Use the internal entry point to Exp as it allows the double\r
+ // range for calculating exp(-a) when a is the tiniest subnormal.\r
+ a->bits^=DECNEG; // make -a\r
+ decExpOp(b, a, &bset, &ignore); // b=exp(-a)\r
+ a->bits^=DECNEG; // restore sign of a\r
+ // now multiply by rhs and subtract 1, at the wider precision\r
+ decMultiplyOp(b, b, rhs, &bset, &ignore); // b=b*rhs\r
+ decAddOp(b, b, &numone, &bset, DECNEG, &ignore); // b=b-1\r
+\r
+ // the iteration ends when the adjustment cannot affect the\r
+ // result by >=0.5 ulp (at the requested digits), which\r
+ // is when its value is smaller than the accumulator by\r
+ // set->digits+1 digits (or it is zero) -- this is a looser\r
+ // requirement than for Exp because all that happens to the\r
+ // accumulator after this is the final rounding (but note that\r
+ // there must also be full precision in a, or a=0).\r
+\r
+ if (decNumberIsZero(b) ||\r
+ (a->digits+a->exponent)>=(b->digits+b->exponent+set->digits+1)) {\r
+ if (a->digits==p) break;\r
+ if (decNumberIsZero(a)) {\r
+ decCompareOp(&cmp, rhs, &numone, &aset, COMPARE, &ignore); // rhs=1 ?\r
+ if (cmp.lsu[0]==0) a->exponent=0; // yes, exact 0\r
+ else *status|=(DEC_Inexact | DEC_Rounded); // no, inexact\r
+ break;\r
+ }\r
+ // force padding if adjustment has gone to 0 before full length\r
+ if (decNumberIsZero(b)) b->exponent=a->exponent-p;\r
+ }\r
+\r
+ // not done yet ...\r
+ decAddOp(a, a, b, &aset, 0, &ignore); // a=a+b for next estimate\r
+ if (pp==p) continue; // precision is at maximum\r
+ // lengthen the next calculation\r
+ pp=pp*2; // double precision\r
+ if (pp>p) pp=p; // clamp to maximum\r
+ aset.digits=pp; // working context\r
+ bset.digits=pp+rhs->digits; // wider context\r
+ } // Newton's iteration\r
+\r
+ #if DECCHECK\r
+ // just a sanity check; remove the test to show always\r
+ if (iterations>24)\r
+ printf("Ln iterations=%ld, status=%08lx, p=%ld, d=%ld\n",\r
+ (LI)iterations, (LI)*status, (LI)p, (LI)rhs->digits);\r
+ #endif\r
+\r
+ // Copy and round the result to res\r
+ residue=1; // indicate dirt to right\r
+ if (ISZERO(a)) residue=0; // .. unless underflowed to 0\r
+ aset.digits=set->digits; // [use default rounding]\r
+ decCopyFit(res, a, &aset, &residue, status); // copy & shorten\r
+ decFinish(res, set, &residue, status); // cleanup/set flags\r
+ } while(0); // end protected\r
+\r
+ if (allocbufa!=NULL) free(allocbufa); // drop any storage used\r
+ if (allocbufb!=NULL) free(allocbufb); // ..\r
+ // [status is handled by caller]\r
+ return res;\r
+ } // decLnOp\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decQuantizeOp -- force exponent to requested value */\r
+/* */\r
+/* This computes C = op(A, B), where op adjusts the coefficient */\r
+/* of C (by rounding or shifting) such that the exponent (-scale) */\r
+/* of C has the value B or matches the exponent of B. */\r
+/* The numerical value of C will equal A, except for the effects of */\r
+/* any rounding that occurred. */\r
+/* */\r
+/* res is C, the result. C may be A or B */\r
+/* lhs is A, the number to adjust */\r
+/* rhs is B, the requested exponent */\r
+/* set is the context */\r
+/* quant is 1 for quantize or 0 for rescale */\r
+/* status is the status accumulator (this can be called without */\r
+/* risk of control loss) */\r
+/* */\r
+/* C must have space for set->digits digits. */\r
+/* */\r
+/* Unless there is an error or the result is infinite, the exponent */\r
+/* after the operation is guaranteed to be that requested. */\r
+/* ------------------------------------------------------------------ */\r
+static decNumber * decQuantizeOp(decNumber *res, const decNumber *lhs,\r
+ const decNumber *rhs, decContext *set,\r
+ Flag quant, uInt *status) {\r
+ #if DECSUBSET\r
+ decNumber *alloclhs=NULL; // non-NULL if rounded lhs allocated\r
+ decNumber *allocrhs=NULL; // .., rhs\r
+ #endif\r
+ const decNumber *inrhs=rhs; // save original rhs\r
+ Int reqdigits=set->digits; // requested DIGITS\r
+ Int reqexp; // requested exponent [-scale]\r
+ Int residue=0; // rounding residue\r
+ Int etiny=set->emin-(reqdigits-1);\r
+\r
+ #if DECCHECK\r
+ if (decCheckOperands(res, lhs, rhs, set)) return res;\r
+ #endif\r
+\r
+ do { // protect allocated storage\r
+ #if DECSUBSET\r
+ if (!set->extended) {\r
+ // reduce operands and set lostDigits status, as needed\r
+ if (lhs->digits>reqdigits) {\r
+ alloclhs=decRoundOperand(lhs, set, status);\r
+ if (alloclhs==NULL) break;\r
+ lhs=alloclhs;\r
+ }\r
+ if (rhs->digits>reqdigits) { // [this only checks lostDigits]\r
+ allocrhs=decRoundOperand(rhs, set, status);\r
+ if (allocrhs==NULL) break;\r
+ rhs=allocrhs;\r
+ }\r
+ }\r
+ #endif\r
+ // [following code does not require input rounding]\r
+\r
+ // Handle special values\r
+ if (SPECIALARGS) {\r
+ // NaNs get usual processing\r
+ if (SPECIALARGS & (DECSNAN | DECNAN))\r
+ decNaNs(res, lhs, rhs, set, status);\r
+ // one infinity but not both is bad\r
+ else if ((lhs->bits ^ rhs->bits) & DECINF)\r
+ *status|=DEC_Invalid_operation;\r
+ // both infinity: return lhs\r
+ else decNumberCopy(res, lhs); // [nop if in place]\r
+ break;\r
+ }\r
+\r
+ // set requested exponent\r
+ if (quant) reqexp=inrhs->exponent; // quantize -- match exponents\r
+ else { // rescale -- use value of rhs\r
+ // Original rhs must be an integer that fits and is in range,\r
+ // which could be from -1999999997 to +999999999, thanks to\r
+ // subnormals\r
+ reqexp=decGetInt(inrhs); // [cannot fail]\r
+ }\r
+\r
+ #if DECSUBSET\r
+ if (!set->extended) etiny=set->emin; // no subnormals\r
+ #endif\r
+\r
+ if (reqexp==BADINT // bad (rescale only) or ..\r
+ || reqexp==BIGODD || reqexp==BIGEVEN // very big (ditto) or ..\r
+ || (reqexp<etiny) // < lowest\r
+ || (reqexp>set->emax)) { // > emax\r
+ *status|=DEC_Invalid_operation;\r
+ break;}\r
+\r
+ // the RHS has been processed, so it can be overwritten now if necessary\r
+ if (ISZERO(lhs)) { // zero coefficient unchanged\r
+ decNumberCopy(res, lhs); // [nop if in place]\r
+ res->exponent=reqexp; // .. just set exponent\r
+ #if DECSUBSET\r
+ if (!set->extended) res->bits=0; // subset specification; no -0\r
+ #endif\r
+ }\r
+ else { // non-zero lhs\r
+ Int adjust=reqexp-lhs->exponent; // digit adjustment needed\r
+ // if adjusted coefficient will definitely not fit, give up now\r
+ if ((lhs->digits-adjust)>reqdigits) {\r
+ *status|=DEC_Invalid_operation;\r
+ break;\r
+ }\r
+\r
+ if (adjust>0) { // increasing exponent\r
+ // this will decrease the length of the coefficient by adjust\r
+ // digits, and must round as it does so\r
+ decContext workset; // work\r
+ workset=*set; // clone rounding, etc.\r
+ workset.digits=lhs->digits-adjust; // set requested length\r
+ // [note that the latter can be <1, here]\r
+ decCopyFit(res, lhs, &workset, &residue, status); // fit to result\r
+ decApplyRound(res, &workset, residue, status); // .. and round\r
+ residue=0; // [used]\r
+ // If just rounded a 999s case, exponent will be off by one;\r
+ // adjust back (after checking space), if so.\r
+ if (res->exponent>reqexp) {\r
+ // re-check needed, e.g., for quantize(0.9999, 0.001) under\r
+ // set->digits==3\r
+ if (res->digits==reqdigits) { // cannot shift by 1\r
+ *status&=~(DEC_Inexact | DEC_Rounded); // [clean these]\r
+ *status|=DEC_Invalid_operation;\r
+ break;\r
+ }\r
+ res->digits=decShiftToMost(res->lsu, res->digits, 1); // shift\r
+ res->exponent--; // (re)adjust the exponent.\r
+ }\r
+ #if DECSUBSET\r
+ if (ISZERO(res) && !set->extended) res->bits=0; // subset; no -0\r
+ #endif\r
+ } // increase\r
+ else /* adjust<=0 */ { // decreasing or = exponent\r
+ // this will increase the length of the coefficient by -adjust\r
+ // digits, by adding zero or more trailing zeros; this is\r
+ // already checked for fit, above\r
+ decNumberCopy(res, lhs); // [it will fit]\r
+ // if padding needed (adjust<0), add it now...\r
+ if (adjust<0) {\r
+ res->digits=decShiftToMost(res->lsu, res->digits, -adjust);\r
+ res->exponent+=adjust; // adjust the exponent\r
+ }\r
+ } // decrease\r
+ } // non-zero\r
+\r
+ // Check for overflow [do not use Finalize in this case, as an\r
+ // overflow here is a "don't fit" situation]\r
+ if (res->exponent>set->emax-res->digits+1) { // too big\r
+ *status|=DEC_Invalid_operation;\r
+ break;\r
+ }\r
+ else {\r
+ decFinalize(res, set, &residue, status); // set subnormal flags\r
+ *status&=~DEC_Underflow; // suppress Underflow [as per 754]\r
+ }\r
+ } while(0); // end protected\r
+\r
+ #if DECSUBSET\r
+ if (allocrhs!=NULL) free(allocrhs); // drop any storage used\r
+ if (alloclhs!=NULL) free(alloclhs); // ..\r
+ #endif\r
+ return res;\r
+ } // decQuantizeOp\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decCompareOp -- compare, min, or max two Numbers */\r
+/* */\r
+/* This computes C = A ? B and carries out one of four operations: */\r
+/* COMPARE -- returns the signum (as a number) giving the */\r
+/* result of a comparison unless one or both */\r
+/* operands is a NaN (in which case a NaN results) */\r
+/* COMPSIG -- as COMPARE except that a quiet NaN raises */\r
+/* Invalid operation. */\r
+/* COMPMAX -- returns the larger of the operands, using the */\r
+/* 754 maxnum operation */\r
+/* COMPMAXMAG -- ditto, comparing absolute values */\r
+/* COMPMIN -- the 754 minnum operation */\r
+/* COMPMINMAG -- ditto, comparing absolute values */\r
+/* COMTOTAL -- returns the signum (as a number) giving the */\r
+/* result of a comparison using 754 total ordering */\r
+/* */\r
+/* res is C, the result. C may be A and/or B (e.g., X=X?X) */\r
+/* lhs is A */\r
+/* rhs is B */\r
+/* set is the context */\r
+/* op is the operation flag */\r
+/* status is the usual accumulator */\r
+/* */\r
+/* C must have space for one digit for COMPARE or set->digits for */\r
+/* COMPMAX, COMPMIN, COMPMAXMAG, or COMPMINMAG. */\r
+/* ------------------------------------------------------------------ */\r
+/* The emphasis here is on speed for common cases, and avoiding */\r
+/* coefficient comparison if possible. */\r
+/* ------------------------------------------------------------------ */\r
+decNumber * decCompareOp(decNumber *res, const decNumber *lhs,\r
+ const decNumber *rhs, decContext *set,\r
+ Flag op, uInt *status) {\r
+ #if DECSUBSET\r
+ decNumber *alloclhs=NULL; // non-NULL if rounded lhs allocated\r
+ decNumber *allocrhs=NULL; // .., rhs\r
+ #endif\r
+ Int result=0; // default result value\r
+ uByte merged; // work\r
+\r
+ #if DECCHECK\r
+ if (decCheckOperands(res, lhs, rhs, set)) return res;\r
+ #endif\r
+\r
+ do { // protect allocated storage\r
+ #if DECSUBSET\r
+ if (!set->extended) {\r
+ // reduce operands and set lostDigits status, as needed\r
+ if (lhs->digits>set->digits) {\r
+ alloclhs=decRoundOperand(lhs, set, status);\r
+ if (alloclhs==NULL) {result=BADINT; break;}\r
+ lhs=alloclhs;\r
+ }\r
+ if (rhs->digits>set->digits) {\r
+ allocrhs=decRoundOperand(rhs, set, status);\r
+ if (allocrhs==NULL) {result=BADINT; break;}\r
+ rhs=allocrhs;\r
+ }\r
+ }\r
+ #endif\r
+ // [following code does not require input rounding]\r
+\r
+ // If total ordering then handle differing signs 'up front'\r
+ if (op==COMPTOTAL) { // total ordering\r
+ if (decNumberIsNegative(lhs) & !decNumberIsNegative(rhs)) {\r
+ result=-1;\r
+ break;\r
+ }\r
+ if (!decNumberIsNegative(lhs) & decNumberIsNegative(rhs)) {\r
+ result=+1;\r
+ break;\r
+ }\r
+ }\r
+\r
+ // handle NaNs specially; let infinities drop through\r
+ // This assumes sNaN (even just one) leads to NaN.\r
+ merged=(lhs->bits | rhs->bits) & (DECSNAN | DECNAN);\r
+ if (merged) { // a NaN bit set\r
+ if (op==COMPARE); // result will be NaN\r
+ else if (op==COMPSIG) // treat qNaN as sNaN\r
+ *status|=DEC_Invalid_operation | DEC_sNaN;\r
+ else if (op==COMPTOTAL) { // total ordering, always finite\r
+ // signs are known to be the same; compute the ordering here\r
+ // as if the signs are both positive, then invert for negatives\r
+ if (!decNumberIsNaN(lhs)) result=-1;\r
+ else if (!decNumberIsNaN(rhs)) result=+1;\r
+ // here if both NaNs\r
+ else if (decNumberIsSNaN(lhs) && decNumberIsQNaN(rhs)) result=-1;\r
+ else if (decNumberIsQNaN(lhs) && decNumberIsSNaN(rhs)) result=+1;\r
+ else { // both NaN or both sNaN\r
+ // now it just depends on the payload\r
+ result=decUnitCompare(lhs->lsu, D2U(lhs->digits),\r
+ rhs->lsu, D2U(rhs->digits), 0);\r
+ // [Error not possible, as these are 'aligned']\r
+ } // both same NaNs\r
+ if (decNumberIsNegative(lhs)) result=-result;\r
+ break;\r
+ } // total order\r
+\r
+ else if (merged & DECSNAN); // sNaN -> qNaN\r
+ else { // here if MIN or MAX and one or two quiet NaNs\r
+ // min or max -- 754 rules ignore single NaN\r
+ if (!decNumberIsNaN(lhs) || !decNumberIsNaN(rhs)) {\r
+ // just one NaN; force choice to be the non-NaN operand\r
+ op=COMPMAX;\r
+ if (lhs->bits & DECNAN) result=-1; // pick rhs\r
+ else result=+1; // pick lhs\r
+ break;\r
+ }\r
+ } // max or min\r
+ op=COMPNAN; // use special path\r
+ decNaNs(res, lhs, rhs, set, status); // propagate NaN\r
+ break;\r
+ }\r
+ // have numbers\r
+ if (op==COMPMAXMAG || op==COMPMINMAG) result=decCompare(lhs, rhs, 1);\r
+ else result=decCompare(lhs, rhs, 0); // sign matters\r
+ } while(0); // end protected\r
+\r
+ if (result==BADINT) *status|=DEC_Insufficient_storage; // rare\r
+ else {\r
+ if (op==COMPARE || op==COMPSIG ||op==COMPTOTAL) { // returning signum\r
+ if (op==COMPTOTAL && result==0) {\r
+ // operands are numerically equal or same NaN (and same sign,\r
+ // tested first); if identical, leave result 0\r
+ if (lhs->exponent!=rhs->exponent) {\r
+ if (lhs->exponent<rhs->exponent) result=-1;\r
+ else result=+1;\r
+ if (decNumberIsNegative(lhs)) result=-result;\r
+ } // lexp!=rexp\r
+ } // total-order by exponent\r
+ decNumberZero(res); // [always a valid result]\r
+ if (result!=0) { // must be -1 or +1\r
+ *res->lsu=1;\r
+ if (result<0) res->bits=DECNEG;\r
+ }\r
+ }\r
+ else if (op==COMPNAN); // special, drop through\r
+ else { // MAX or MIN, non-NaN result\r
+ Int residue=0; // rounding accumulator\r
+ // choose the operand for the result\r
+ const decNumber *choice;\r
+ if (result==0) { // operands are numerically equal\r
+ // choose according to sign then exponent (see 754)\r
+ uByte slhs=(lhs->bits & DECNEG);\r
+ uByte srhs=(rhs->bits & DECNEG);\r
+ #if DECSUBSET\r
+ if (!set->extended) { // subset: force left-hand\r
+ op=COMPMAX;\r
+ result=+1;\r
+ }\r
+ else\r
+ #endif\r
+ if (slhs!=srhs) { // signs differ\r
+ if (slhs) result=-1; // rhs is max\r
+ else result=+1; // lhs is max\r
+ }\r
+ else if (slhs && srhs) { // both negative\r
+ if (lhs->exponent<rhs->exponent) result=+1;\r
+ else result=-1;\r
+ // [if equal, use lhs, technically identical]\r
+ }\r
+ else { // both positive\r
+ if (lhs->exponent>rhs->exponent) result=+1;\r
+ else result=-1;\r
+ // [ditto]\r
+ }\r
+ } // numerically equal\r
+ // here result will be non-0; reverse if looking for MIN\r
+ if (op==COMPMIN || op==COMPMINMAG) result=-result;\r
+ choice=(result>0 ? lhs : rhs); // choose\r
+ // copy chosen to result, rounding if need be\r
+ decCopyFit(res, choice, set, &residue, status);\r
+ decFinish(res, set, &residue, status);\r
+ }\r
+ }\r
+ #if DECSUBSET\r
+ if (allocrhs!=NULL) free(allocrhs); // free any storage used\r
+ if (alloclhs!=NULL) free(alloclhs); // ..\r
+ #endif\r
+ return res;\r
+ } // decCompareOp\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decCompare -- compare two decNumbers by numerical value */\r
+/* */\r
+/* This routine compares A ? B without altering them. */\r
+/* */\r
+/* Arg1 is A, a decNumber which is not a NaN */\r
+/* Arg2 is B, a decNumber which is not a NaN */\r
+/* Arg3 is 1 for a sign-independent compare, 0 otherwise */\r
+/* */\r
+/* returns -1, 0, or 1 for A<B, A==B, or A>B, or BADINT if failure */\r
+/* (the only possible failure is an allocation error) */\r
+/* ------------------------------------------------------------------ */\r
+static Int decCompare(const decNumber *lhs, const decNumber *rhs,\r
+ Flag abs) {\r
+ Int result; // result value\r
+ Int sigr; // rhs signum\r
+ Int compare; // work\r
+\r
+ result=1; // assume signum(lhs)\r
+ if (ISZERO(lhs)) result=0;\r
+ if (abs) {\r
+ if (ISZERO(rhs)) return result; // LHS wins or both 0\r
+ // RHS is non-zero\r
+ if (result==0) return -1; // LHS is 0; RHS wins\r
+ // [here, both non-zero, result=1]\r
+ }\r
+ else { // signs matter\r
+ if (result && decNumberIsNegative(lhs)) result=-1;\r
+ sigr=1; // compute signum(rhs)\r
+ if (ISZERO(rhs)) sigr=0;\r
+ else if (decNumberIsNegative(rhs)) sigr=-1;\r
+ if (result > sigr) return +1; // L > R, return 1\r
+ if (result < sigr) return -1; // L < R, return -1\r
+ if (result==0) return 0; // both 0\r
+ }\r
+\r
+ // signums are the same; both are non-zero\r
+ if ((lhs->bits | rhs->bits) & DECINF) { // one or more infinities\r
+ if (decNumberIsInfinite(rhs)) {\r
+ if (decNumberIsInfinite(lhs)) result=0;// both infinite\r
+ else result=-result; // only rhs infinite\r
+ }\r
+ return result;\r
+ }\r
+ // must compare the coefficients, allowing for exponents\r
+ if (lhs->exponent>rhs->exponent) { // LHS exponent larger\r
+ // swap sides, and sign\r
+ const decNumber *temp=lhs;\r
+ lhs=rhs;\r
+ rhs=temp;\r
+ result=-result;\r
+ }\r
+ compare=decUnitCompare(lhs->lsu, D2U(lhs->digits),\r
+ rhs->lsu, D2U(rhs->digits),\r
+ rhs->exponent-lhs->exponent);\r
+ if (compare!=BADINT) compare*=result; // comparison succeeded\r
+ return compare;\r
+ } // decCompare\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decUnitCompare -- compare two >=0 integers in Unit arrays */\r
+/* */\r
+/* This routine compares A ? B*10**E where A and B are unit arrays */\r
+/* A is a plain integer */\r
+/* B has an exponent of E (which must be non-negative) */\r
+/* */\r
+/* Arg1 is A first Unit (lsu) */\r
+/* Arg2 is A length in Units */\r
+/* Arg3 is B first Unit (lsu) */\r
+/* Arg4 is B length in Units */\r
+/* Arg5 is E (0 if the units are aligned) */\r
+/* */\r
+/* returns -1, 0, or 1 for A<B, A==B, or A>B, or BADINT if failure */\r
+/* (the only possible failure is an allocation error, which can */\r
+/* only occur if E!=0) */\r
+/* ------------------------------------------------------------------ */\r
+static Int decUnitCompare(const Unit *a, Int alength,\r
+ const Unit *b, Int blength, Int exp) {\r
+ Unit *acc; // accumulator for result\r
+ Unit accbuff[SD2U(DECBUFFER*2+1)]; // local buffer\r
+ Unit *allocacc=NULL; // -> allocated acc buffer, iff allocated\r
+ Int accunits, need; // units in use or needed for acc\r
+ const Unit *l, *r, *u; // work\r
+ Int expunits, exprem, result; // ..\r
+\r
+ if (exp==0) { // aligned; fastpath\r
+ if (alength>blength) return 1;\r
+ if (alength<blength) return -1;\r
+ // same number of units in both -- need unit-by-unit compare\r
+ l=a+alength-1;\r
+ r=b+alength-1;\r
+ for (;l>=a; l--, r--) {\r
+ if (*l>*r) return 1;\r
+ if (*l<*r) return -1;\r
+ }\r
+ return 0; // all units match\r
+ } // aligned\r
+\r
+ // Unaligned. If one is >1 unit longer than the other, padded\r
+ // approximately, then can return easily\r
+ if (alength>blength+(Int)D2U(exp)) return 1;\r
+ if (alength+1<blength+(Int)D2U(exp)) return -1;\r
+\r
+ // Need to do a real subtract. For this, a result buffer is needed\r
+ // even though only the sign is of interest. Its length needs\r
+ // to be the larger of alength and padded blength, +2\r
+ need=blength+D2U(exp); // maximum real length of B\r
+ if (need<alength) need=alength;\r
+ need+=2;\r
+ acc=accbuff; // assume use local buffer\r
+ if (need*sizeof(Unit)>sizeof(accbuff)) {\r
+ allocacc=(Unit *)malloc(need*sizeof(Unit));\r
+ if (allocacc==NULL) return BADINT; // hopeless -- abandon\r
+ acc=allocacc;\r
+ }\r
+ // Calculate units and remainder from exponent.\r
+ expunits=exp/DECDPUN;\r
+ exprem=exp%DECDPUN;\r
+ // subtract [A+B*(-m)]\r
+ accunits=decUnitAddSub(a, alength, b, blength, expunits, acc,\r
+ -(Int)powers[exprem]);\r
+ // [UnitAddSub result may have leading zeros, even on zero]\r
+ if (accunits<0) result=-1; // negative result\r
+ else { // non-negative result\r
+ // check units of the result before freeing any storage\r
+ for (u=acc; u<acc+accunits-1 && *u==0;) u++;\r
+ result=(*u==0 ? 0 : +1);\r
+ }\r
+ // clean up and return the result\r
+ if (allocacc!=NULL) free(allocacc); // drop any storage used\r
+ return result;\r
+ } // decUnitCompare\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decUnitAddSub -- add or subtract two >=0 integers in Unit arrays */\r
+/* */\r
+/* This routine performs the calculation: */\r
+/* */\r
+/* C=A+(B*M) */\r
+/* */\r
+/* Where M is in the range -DECDPUNMAX through +DECDPUNMAX. */\r
+/* */\r
+/* A may be shorter or longer than B. */\r
+/* */\r
+/* Leading zeros are not removed after a calculation. The result is */\r
+/* either the same length as the longer of A and B (adding any */\r
+/* shift), or one Unit longer than that (if a Unit carry occurred). */\r
+/* */\r
+/* A and B content are not altered unless C is also A or B. */\r
+/* C may be the same array as A or B, but only if no zero padding is */\r
+/* requested (that is, C may be B only if bshift==0). */\r
+/* C is filled from the lsu; only those units necessary to complete */\r
+/* the calculation are referenced. */\r
+/* */\r
+/* Arg1 is A first Unit (lsu) */\r
+/* Arg2 is A length in Units */\r
+/* Arg3 is B first Unit (lsu) */\r
+/* Arg4 is B length in Units */\r
+/* Arg5 is B shift in Units (>=0; pads with 0 units if positive) */\r
+/* Arg6 is C first Unit (lsu) */\r
+/* Arg7 is M, the multiplier */\r
+/* */\r
+/* returns the count of Units written to C, which will be non-zero */\r
+/* and negated if the result is negative. That is, the sign of the */\r
+/* returned Int is the sign of the result (positive for zero) and */\r
+/* the absolute value of the Int is the count of Units. */\r
+/* */\r
+/* It is the caller's responsibility to make sure that C size is */\r
+/* safe, allowing space if necessary for a one-Unit carry. */\r
+/* */\r
+/* This routine is severely performance-critical; *any* change here */\r
+/* must be measured (timed) to assure no performance degradation. */\r
+/* In particular, trickery here tends to be counter-productive, as */\r
+/* increased complexity of code hurts register optimizations on */\r
+/* register-poor architectures. Avoiding divisions is nearly */\r
+/* always a Good Idea, however. */\r
+/* */\r
+/* Special thanks to Rick McGuire (IBM Cambridge, MA) and Dave Clark */\r
+/* (IBM Warwick, UK) for some of the ideas used in this routine. */\r
+/* ------------------------------------------------------------------ */\r
+static Int decUnitAddSub(const Unit *a, Int alength,\r
+ const Unit *b, Int blength, Int bshift,\r
+ Unit *c, Int m) {\r
+ const Unit *alsu=a; // A lsu [need to remember it]\r
+ Unit *clsu=c; // C ditto\r
+ Unit *minC; // low water mark for C\r
+ Unit *maxC; // high water mark for C\r
+ eInt carry=0; // carry integer (could be Long)\r
+ Int add; // work\r
+ #if DECDPUN<=4 // myriadal, millenary, etc.\r
+ Int est; // estimated quotient\r
+ #endif\r
+\r
+ #if DECTRACE\r
+ if (alength<1 || blength<1)\r
+ printf("decUnitAddSub: alen blen m %ld %ld [%ld]\n", alength, blength, m);\r
+ #endif\r
+\r
+ maxC=c+alength; // A is usually the longer\r
+ minC=c+blength; // .. and B the shorter\r
+ if (bshift!=0) { // B is shifted; low As copy across\r
+ minC+=bshift;\r
+ // if in place [common], skip copy unless there's a gap [rare]\r
+ if (a==c && bshift<=alength) {\r
+ c+=bshift;\r
+ a+=bshift;\r
+ }\r
+ else for (; c<clsu+bshift; a++, c++) { // copy needed\r
+ if (a<alsu+alength) *c=*a;\r
+ else *c=0;\r
+ }\r
+ }\r
+ if (minC>maxC) { // swap\r
+ Unit *hold=minC;\r
+ minC=maxC;\r
+ maxC=hold;\r
+ }\r
+\r
+ // For speed, do the addition as two loops; the first where both A\r
+ // and B contribute, and the second (if necessary) where only one or\r
+ // other of the numbers contribute.\r
+ // Carry handling is the same (i.e., duplicated) in each case.\r
+ for (; c<minC; c++) {\r
+ carry+=*a;\r
+ a++;\r
+ carry+=((eInt)*b)*m; // [special-casing m=1/-1\r
+ b++; // here is not a win]\r
+ // here carry is new Unit of digits; it could be +ve or -ve\r
+ if ((ueInt)carry<=DECDPUNMAX) { // fastpath 0-DECDPUNMAX\r
+ *c=(Unit)carry;\r
+ carry=0;\r
+ continue;\r
+ }\r
+ #if DECDPUN==4 // use divide-by-multiply\r
+ if (carry>=0) {\r
+ est=(((ueInt)carry>>11)*53687)>>18;\r
+ *c=(Unit)(carry-est*(DECDPUNMAX+1)); // remainder\r
+ carry=est; // likely quotient [89%]\r
+ if (*c<DECDPUNMAX+1) continue; // estimate was correct\r
+ carry++;\r
+ *c-=DECDPUNMAX+1;\r
+ continue;\r
+ }\r
+ // negative case\r
+ carry=carry+(eInt)(DECDPUNMAX+1)*(DECDPUNMAX+1); // make positive\r
+ est=(((ueInt)carry>>11)*53687)>>18;\r
+ *c=(Unit)(carry-est*(DECDPUNMAX+1));\r
+ carry=est-(DECDPUNMAX+1); // correctly negative\r
+ if (*c<DECDPUNMAX+1) continue; // was OK\r
+ carry++;\r
+ *c-=DECDPUNMAX+1;\r
+ #elif DECDPUN==3\r
+ if (carry>=0) {\r
+ est=(((ueInt)carry>>3)*16777)>>21;\r
+ *c=(Unit)(carry-est*(DECDPUNMAX+1)); // remainder\r
+ carry=est; // likely quotient [99%]\r
+ if (*c<DECDPUNMAX+1) continue; // estimate was correct\r
+ carry++;\r
+ *c-=DECDPUNMAX+1;\r
+ continue;\r
+ }\r
+ // negative case\r
+ carry=carry+(eInt)(DECDPUNMAX+1)*(DECDPUNMAX+1); // make positive\r
+ est=(((ueInt)carry>>3)*16777)>>21;\r
+ *c=(Unit)(carry-est*(DECDPUNMAX+1));\r
+ carry=est-(DECDPUNMAX+1); // correctly negative\r
+ if (*c<DECDPUNMAX+1) continue; // was OK\r
+ carry++;\r
+ *c-=DECDPUNMAX+1;\r
+ #elif DECDPUN<=2\r
+ // Can use QUOT10 as carry <= 4 digits\r
+ if (carry>=0) {\r
+ est=QUOT10(carry, DECDPUN);\r
+ *c=(Unit)(carry-est*(DECDPUNMAX+1)); // remainder\r
+ carry=est; // quotient\r
+ continue;\r
+ }\r
+ // negative case\r
+ carry=carry+(eInt)(DECDPUNMAX+1)*(DECDPUNMAX+1); // make positive\r
+ est=QUOT10(carry, DECDPUN);\r
+ *c=(Unit)(carry-est*(DECDPUNMAX+1));\r
+ carry=est-(DECDPUNMAX+1); // correctly negative\r
+ #else\r
+ // remainder operator is undefined if negative, so must test\r
+ if ((ueInt)carry<(DECDPUNMAX+1)*2) { // fastpath carry +1\r
+ *c=(Unit)(carry-(DECDPUNMAX+1)); // [helps additions]\r
+ carry=1;\r
+ continue;\r
+ }\r
+ if (carry>=0) {\r
+ *c=(Unit)(carry%(DECDPUNMAX+1));\r
+ carry=carry/(DECDPUNMAX+1);\r
+ continue;\r
+ }\r
+ // negative case\r
+ carry=carry+(eInt)(DECDPUNMAX+1)*(DECDPUNMAX+1); // make positive\r
+ *c=(Unit)(carry%(DECDPUNMAX+1));\r
+ carry=carry/(DECDPUNMAX+1)-(DECDPUNMAX+1);\r
+ #endif\r
+ } // c\r
+\r
+ // now may have one or other to complete\r
+ // [pretest to avoid loop setup/shutdown]\r
+ if (c<maxC) for (; c<maxC; c++) {\r
+ if (a<alsu+alength) { // still in A\r
+ carry+=*a;\r
+ a++;\r
+ }\r
+ else { // inside B\r
+ carry+=((eInt)*b)*m;\r
+ b++;\r
+ }\r
+ // here carry is new Unit of digits; it could be +ve or -ve and\r
+ // magnitude up to DECDPUNMAX squared\r
+ if ((ueInt)carry<=DECDPUNMAX) { // fastpath 0-DECDPUNMAX\r
+ *c=(Unit)carry;\r
+ carry=0;\r
+ continue;\r
+ }\r
+ // result for this unit is negative or >DECDPUNMAX\r
+ #if DECDPUN==4 // use divide-by-multiply\r
+ if (carry>=0) {\r
+ est=(((ueInt)carry>>11)*53687)>>18;\r
+ *c=(Unit)(carry-est*(DECDPUNMAX+1)); // remainder\r
+ carry=est; // likely quotient [79.7%]\r
+ if (*c<DECDPUNMAX+1) continue; // estimate was correct\r
+ carry++;\r
+ *c-=DECDPUNMAX+1;\r
+ continue;\r
+ }\r
+ // negative case\r
+ carry=carry+(eInt)(DECDPUNMAX+1)*(DECDPUNMAX+1); // make positive\r
+ est=(((ueInt)carry>>11)*53687)>>18;\r
+ *c=(Unit)(carry-est*(DECDPUNMAX+1));\r
+ carry=est-(DECDPUNMAX+1); // correctly negative\r
+ if (*c<DECDPUNMAX+1) continue; // was OK\r
+ carry++;\r
+ *c-=DECDPUNMAX+1;\r
+ #elif DECDPUN==3\r
+ if (carry>=0) {\r
+ est=(((ueInt)carry>>3)*16777)>>21;\r
+ *c=(Unit)(carry-est*(DECDPUNMAX+1)); // remainder\r
+ carry=est; // likely quotient [99%]\r
+ if (*c<DECDPUNMAX+1) continue; // estimate was correct\r
+ carry++;\r
+ *c-=DECDPUNMAX+1;\r
+ continue;\r
+ }\r
+ // negative case\r
+ carry=carry+(eInt)(DECDPUNMAX+1)*(DECDPUNMAX+1); // make positive\r
+ est=(((ueInt)carry>>3)*16777)>>21;\r
+ *c=(Unit)(carry-est*(DECDPUNMAX+1));\r
+ carry=est-(DECDPUNMAX+1); // correctly negative\r
+ if (*c<DECDPUNMAX+1) continue; // was OK\r
+ carry++;\r
+ *c-=DECDPUNMAX+1;\r
+ #elif DECDPUN<=2\r
+ if (carry>=0) {\r
+ est=QUOT10(carry, DECDPUN);\r
+ *c=(Unit)(carry-est*(DECDPUNMAX+1)); // remainder\r
+ carry=est; // quotient\r
+ continue;\r
+ }\r
+ // negative case\r
+ carry=carry+(eInt)(DECDPUNMAX+1)*(DECDPUNMAX+1); // make positive\r
+ est=QUOT10(carry, DECDPUN);\r
+ *c=(Unit)(carry-est*(DECDPUNMAX+1));\r
+ carry=est-(DECDPUNMAX+1); // correctly negative\r
+ #else\r
+ if ((ueInt)carry<(DECDPUNMAX+1)*2){ // fastpath carry 1\r
+ *c=(Unit)(carry-(DECDPUNMAX+1));\r
+ carry=1;\r
+ continue;\r
+ }\r
+ // remainder operator is undefined if negative, so must test\r
+ if (carry>=0) {\r
+ *c=(Unit)(carry%(DECDPUNMAX+1));\r
+ carry=carry/(DECDPUNMAX+1);\r
+ continue;\r
+ }\r
+ // negative case\r
+ carry=carry+(eInt)(DECDPUNMAX+1)*(DECDPUNMAX+1); // make positive\r
+ *c=(Unit)(carry%(DECDPUNMAX+1));\r
+ carry=carry/(DECDPUNMAX+1)-(DECDPUNMAX+1);\r
+ #endif\r
+ } // c\r
+\r
+ // OK, all A and B processed; might still have carry or borrow\r
+ // return number of Units in the result, negated if a borrow\r
+ if (carry==0) return c-clsu; // no carry, so no more to do\r
+ if (carry>0) { // positive carry\r
+ *c=(Unit)carry; // place as new unit\r
+ c++; // ..\r
+ return c-clsu;\r
+ }\r
+ // -ve carry: it's a borrow; complement needed\r
+ add=1; // temporary carry...\r
+ for (c=clsu; c<maxC; c++) {\r
+ add=DECDPUNMAX+add-*c;\r
+ if (add<=DECDPUNMAX) {\r
+ *c=(Unit)add;\r
+ add=0;\r
+ }\r
+ else {\r
+ *c=0;\r
+ add=1;\r
+ }\r
+ }\r
+ // add an extra unit iff it would be non-zero\r
+ #if DECTRACE\r
+ printf("UAS borrow: add %ld, carry %ld\n", add, carry);\r
+ #endif\r
+ if ((add-carry-1)!=0) {\r
+ *c=(Unit)(add-carry-1);\r
+ c++; // interesting, include it\r
+ }\r
+ return clsu-c; // -ve result indicates borrowed\r
+ } // decUnitAddSub\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decTrim -- trim trailing zeros or normalize */\r
+/* */\r
+/* dn is the number to trim or normalize */\r
+/* set is the context to use to check for clamp */\r
+/* all is 1 to remove all trailing zeros, 0 for just fraction ones */\r
+/* noclamp is 1 to unconditional (unclamped) trim */\r
+/* dropped returns the number of discarded trailing zeros */\r
+/* returns dn */\r
+/* */\r
+/* If clamp is set in the context then the number of zeros trimmed */\r
+/* may be limited if the exponent is high. */\r
+/* All fields are updated as required. This is a utility operation, */\r
+/* so special values are unchanged and no error is possible. */\r
+/* ------------------------------------------------------------------ */\r
+static decNumber * decTrim(decNumber *dn, decContext *set, Flag all,\r
+ Flag noclamp, Int *dropped) {\r
+ Int d, exp; // work\r
+ uInt cut; // ..\r
+ Unit *up; // -> current Unit\r
+\r
+ #if DECCHECK\r
+ if (decCheckOperands(dn, DECUNUSED, DECUNUSED, DECUNCONT)) return dn;\r
+ #endif\r
+\r
+ *dropped=0; // assume no zeros dropped\r
+ if ((dn->bits & DECSPECIAL) // fast exit if special ..\r
+ || (*dn->lsu & 0x01)) return dn; // .. or odd\r
+ if (ISZERO(dn)) { // .. or 0\r
+ dn->exponent=0; // (sign is preserved)\r
+ return dn;\r
+ }\r
+\r
+ // have a finite number which is even\r
+ exp=dn->exponent;\r
+ cut=1; // digit (1-DECDPUN) in Unit\r
+ up=dn->lsu; // -> current Unit\r
+ for (d=0; d<dn->digits-1; d++) { // [don't strip the final digit]\r
+ // slice by powers\r
+ #if DECDPUN<=4\r
+ uInt quot=QUOT10(*up, cut);\r
+ if ((*up-quot*powers[cut])!=0) break; // found non-0 digit\r
+ #else\r
+ if (*up%powers[cut]!=0) break; // found non-0 digit\r
+ #endif\r
+ // have a trailing 0\r
+ if (!all) { // trimming\r
+ // [if exp>0 then all trailing 0s are significant for trim]\r
+ if (exp<=0) { // if digit might be significant\r
+ if (exp==0) break; // then quit\r
+ exp++; // next digit might be significant\r
+ }\r
+ }\r
+ cut++; // next power\r
+ if (cut>DECDPUN) { // need new Unit\r
+ up++;\r
+ cut=1;\r
+ }\r
+ } // d\r
+ if (d==0) return dn; // none to drop\r
+\r
+ // may need to limit drop if clamping\r
+ if (set->clamp && !noclamp) {\r
+ Int maxd=set->emax-set->digits+1-dn->exponent;\r
+ if (maxd<=0) return dn; // nothing possible\r
+ if (d>maxd) d=maxd;\r
+ }\r
+\r
+ // effect the drop\r
+ decShiftToLeast(dn->lsu, D2U(dn->digits), d);\r
+ dn->exponent+=d; // maintain numerical value\r
+ dn->digits-=d; // new length\r
+ *dropped=d; // report the count\r
+ return dn;\r
+ } // decTrim\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decReverse -- reverse a Unit array in place */\r
+/* */\r
+/* ulo is the start of the array */\r
+/* uhi is the end of the array (highest Unit to include) */\r
+/* */\r
+/* The units ulo through uhi are reversed in place (if the number */\r
+/* of units is odd, the middle one is untouched). Note that the */\r
+/* digit(s) in each unit are unaffected. */\r
+/* ------------------------------------------------------------------ */\r
+static void decReverse(Unit *ulo, Unit *uhi) {\r
+ Unit temp;\r
+ for (; ulo<uhi; ulo++, uhi--) {\r
+ temp=*ulo;\r
+ *ulo=*uhi;\r
+ *uhi=temp;\r
+ }\r
+ return;\r
+ } // decReverse\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decShiftToMost -- shift digits in array towards most significant */\r
+/* */\r
+/* uar is the array */\r
+/* digits is the count of digits in use in the array */\r
+/* shift is the number of zeros to pad with (least significant); */\r
+/* it must be zero or positive */\r
+/* */\r
+/* returns the new length of the integer in the array, in digits */\r
+/* */\r
+/* No overflow is permitted (that is, the uar array must be known to */\r
+/* be large enough to hold the result, after shifting). */\r
+/* ------------------------------------------------------------------ */\r
+static Int decShiftToMost(Unit *uar, Int digits, Int shift) {\r
+ Unit *target, *source, *first; // work\r
+ Int cut; // odd 0's to add\r
+ uInt next; // work\r
+\r
+ if (shift==0) return digits; // [fastpath] nothing to do\r
+ if ((digits+shift)<=DECDPUN) { // [fastpath] single-unit case\r
+ *uar=(Unit)(*uar*powers[shift]);\r
+ return digits+shift;\r
+ }\r
+\r
+ next=0; // all paths\r
+ source=uar+D2U(digits)-1; // where msu comes from\r
+ target=source+D2U(shift); // where upper part of first cut goes\r
+ cut=DECDPUN-MSUDIGITS(shift); // where to slice\r
+ if (cut==0) { // unit-boundary case\r
+ for (; source>=uar; source--, target--) *target=*source;\r
+ }\r
+ else {\r
+ first=uar+D2U(digits+shift)-1; // where msu of source will end up\r
+ for (; source>=uar; source--, target--) {\r
+ // split the source Unit and accumulate remainder for next\r
+ #if DECDPUN<=4\r
+ uInt quot=QUOT10(*source, cut);\r
+ uInt rem=*source-quot*powers[cut];\r
+ next+=quot;\r
+ #else\r
+ uInt rem=*source%powers[cut];\r
+ next+=*source/powers[cut];\r
+ #endif\r
+ if (target<=first) *target=(Unit)next; // write to target iff valid\r
+ next=rem*powers[DECDPUN-cut]; // save remainder for next Unit\r
+ }\r
+ } // shift-move\r
+\r
+ // propagate any partial unit to one below and clear the rest\r
+ for (; target>=uar; target--) {\r
+ *target=(Unit)next;\r
+ next=0;\r
+ }\r
+ return digits+shift;\r
+ } // decShiftToMost\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decShiftToLeast -- shift digits in array towards least significant */\r
+/* */\r
+/* uar is the array */\r
+/* units is length of the array, in units */\r
+/* shift is the number of digits to remove from the lsu end; it */\r
+/* must be zero or positive and <= than units*DECDPUN. */\r
+/* */\r
+/* returns the new length of the integer in the array, in units */\r
+/* */\r
+/* Removed digits are discarded (lost). Units not required to hold */\r
+/* the final result are unchanged. */\r
+/* ------------------------------------------------------------------ */\r
+static Int decShiftToLeast(Unit *uar, Int units, Int shift) {\r
+ Unit *target, *up; // work\r
+ Int cut, count; // work\r
+ Int quot, rem; // for division\r
+\r
+ if (shift==0) return units; // [fastpath] nothing to do\r
+ if (shift==units*DECDPUN) { // [fastpath] little to do\r
+ *uar=0; // all digits cleared gives zero\r
+ return 1; // leaves just the one\r
+ }\r
+\r
+ target=uar; // both paths\r
+ cut=MSUDIGITS(shift);\r
+ if (cut==DECDPUN) { // unit-boundary case; easy\r
+ up=uar+D2U(shift);\r
+ for (; up<uar+units; target++, up++) *target=*up;\r
+ return target-uar;\r
+ }\r
+\r
+ // messier\r
+ up=uar+D2U(shift-cut); // source; correct to whole Units\r
+ count=units*DECDPUN-shift; // the maximum new length\r
+ #if DECDPUN<=4\r
+ quot=QUOT10(*up, cut);\r
+ #else\r
+ quot=*up/powers[cut];\r
+ #endif\r
+ for (; ; target++) {\r
+ *target=(Unit)quot;\r
+ count-=(DECDPUN-cut);\r
+ if (count<=0) break;\r
+ up++;\r
+ quot=*up;\r
+ #if DECDPUN<=4\r
+ quot=QUOT10(quot, cut);\r
+ rem=*up-quot*powers[cut];\r
+ #else\r
+ rem=quot%powers[cut];\r
+ quot=quot/powers[cut];\r
+ #endif\r
+ *target=(Unit)(*target+rem*powers[DECDPUN-cut]);\r
+ count-=cut;\r
+ if (count<=0) break;\r
+ }\r
+ return target-uar+1;\r
+ } // decShiftToLeast\r
+\r
+#if DECSUBSET\r
+/* ------------------------------------------------------------------ */\r
+/* decRoundOperand -- round an operand [used for subset only] */\r
+/* */\r
+/* dn is the number to round (dn->digits is > set->digits) */\r
+/* set is the relevant context */\r
+/* status is the status accumulator */\r
+/* */\r
+/* returns an allocated decNumber with the rounded result. */\r
+/* */\r
+/* lostDigits and other status may be set by this. */\r
+/* */\r
+/* Since the input is an operand, it must not be modified. */\r
+/* Instead, return an allocated decNumber, rounded as required. */\r
+/* It is the caller's responsibility to free the allocated storage. */\r
+/* */\r
+/* If no storage is available then the result cannot be used, so NULL */\r
+/* is returned. */\r
+/* ------------------------------------------------------------------ */\r
+static decNumber *decRoundOperand(const decNumber *dn, decContext *set,\r
+ uInt *status) {\r
+ decNumber *res; // result structure\r
+ uInt newstatus=0; // status from round\r
+ Int residue=0; // rounding accumulator\r
+\r
+ // Allocate storage for the returned decNumber, big enough for the\r
+ // length specified by the context\r
+ res=(decNumber *)malloc(sizeof(decNumber)\r
+ +(D2U(set->digits)-1)*sizeof(Unit));\r
+ if (res==NULL) {\r
+ *status|=DEC_Insufficient_storage;\r
+ return NULL;\r
+ }\r
+ decCopyFit(res, dn, set, &residue, &newstatus);\r
+ decApplyRound(res, set, residue, &newstatus);\r
+\r
+ // If that set Inexact then "lost digits" is raised...\r
+ if (newstatus & DEC_Inexact) newstatus|=DEC_Lost_digits;\r
+ *status|=newstatus;\r
+ return res;\r
+ } // decRoundOperand\r
+#endif\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decCopyFit -- copy a number, truncating the coefficient if needed */\r
+/* */\r
+/* dest is the target decNumber */\r
+/* src is the source decNumber */\r
+/* set is the context [used for length (digits) and rounding mode] */\r
+/* residue is the residue accumulator */\r
+/* status contains the current status to be updated */\r
+/* */\r
+/* (dest==src is allowed and will be a no-op if fits) */\r
+/* All fields are updated as required. */\r
+/* ------------------------------------------------------------------ */\r
+static void decCopyFit(decNumber *dest, const decNumber *src,\r
+ decContext *set, Int *residue, uInt *status) {\r
+ dest->bits=src->bits;\r
+ dest->exponent=src->exponent;\r
+ decSetCoeff(dest, set, src->lsu, src->digits, residue, status);\r
+ } // decCopyFit\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decSetCoeff -- set the coefficient of a number */\r
+/* */\r
+/* dn is the number whose coefficient array is to be set. */\r
+/* It must have space for set->digits digits */\r
+/* set is the context [for size] */\r
+/* lsu -> lsu of the source coefficient [may be dn->lsu] */\r
+/* len is digits in the source coefficient [may be dn->digits] */\r
+/* residue is the residue accumulator. This has values as in */\r
+/* decApplyRound, and will be unchanged unless the */\r
+/* target size is less than len. In this case, the */\r
+/* coefficient is truncated and the residue is updated to */\r
+/* reflect the previous residue and the dropped digits. */\r
+/* status is the status accumulator, as usual */\r
+/* */\r
+/* The coefficient may already be in the number, or it can be an */\r
+/* external intermediate array. If it is in the number, lsu must == */\r
+/* dn->lsu and len must == dn->digits. */\r
+/* */\r
+/* Note that the coefficient length (len) may be < set->digits, and */\r
+/* in this case this merely copies the coefficient (or is a no-op */\r
+/* if dn->lsu==lsu). */\r
+/* */\r
+/* Note also that (only internally, from decQuantizeOp and */\r
+/* decSetSubnormal) the value of set->digits may be less than one, */\r
+/* indicating a round to left. This routine handles that case */\r
+/* correctly; caller ensures space. */\r
+/* */\r
+/* dn->digits, dn->lsu (and as required), and dn->exponent are */\r
+/* updated as necessary. dn->bits (sign) is unchanged. */\r
+/* */\r
+/* DEC_Rounded status is set if any digits are discarded. */\r
+/* DEC_Inexact status is set if any non-zero digits are discarded, or */\r
+/* incoming residue was non-0 (implies rounded) */\r
+/* ------------------------------------------------------------------ */\r
+// mapping array: maps 0-9 to canonical residues, so that a residue\r
+// can be adjusted in the range [-1, +1] and achieve correct rounding\r
+// 0 1 2 3 4 5 6 7 8 9\r
+static const uByte resmap[10]={0, 3, 3, 3, 3, 5, 7, 7, 7, 7};\r
+static void decSetCoeff(decNumber *dn, decContext *set, const Unit *lsu,\r
+ Int len, Int *residue, uInt *status) {\r
+ Int discard; // number of digits to discard\r
+ uInt cut; // cut point in Unit\r
+ const Unit *up; // work\r
+ Unit *target; // ..\r
+ Int count; // ..\r
+ #if DECDPUN<=4\r
+ uInt temp; // ..\r
+ #endif\r
+\r
+ discard=len-set->digits; // digits to discard\r
+ if (discard<=0) { // no digits are being discarded\r
+ if (dn->lsu!=lsu) { // copy needed\r
+ // copy the coefficient array to the result number; no shift needed\r
+ count=len; // avoids D2U\r
+ up=lsu;\r
+ for (target=dn->lsu; count>0; target++, up++, count-=DECDPUN)\r
+ *target=*up;\r
+ dn->digits=len; // set the new length\r
+ }\r
+ // dn->exponent and residue are unchanged, record any inexactitude\r
+ if (*residue!=0) *status|=(DEC_Inexact | DEC_Rounded);\r
+ return;\r
+ }\r
+\r
+ // some digits must be discarded ...\r
+ dn->exponent+=discard; // maintain numerical value\r
+ *status|=DEC_Rounded; // accumulate Rounded status\r
+ if (*residue>1) *residue=1; // previous residue now to right, so reduce\r
+\r
+ if (discard>len) { // everything, +1, is being discarded\r
+ // guard digit is 0\r
+ // residue is all the number [NB could be all 0s]\r
+ if (*residue<=0) { // not already positive\r
+ count=len; // avoids D2U\r
+ for (up=lsu; count>0; up++, count-=DECDPUN) if (*up!=0) { // found non-0\r
+ *residue=1;\r
+ break; // no need to check any others\r
+ }\r
+ }\r
+ if (*residue!=0) *status|=DEC_Inexact; // record inexactitude\r
+ *dn->lsu=0; // coefficient will now be 0\r
+ dn->digits=1; // ..\r
+ return;\r
+ } // total discard\r
+\r
+ // partial discard [most common case]\r
+ // here, at least the first (most significant) discarded digit exists\r
+\r
+ // spin up the number, noting residue during the spin, until get to\r
+ // the Unit with the first discarded digit. When reach it, extract\r
+ // it and remember its position\r
+ count=0;\r
+ for (up=lsu;; up++) {\r
+ count+=DECDPUN;\r
+ if (count>=discard) break; // full ones all checked\r
+ if (*up!=0) *residue=1;\r
+ } // up\r
+\r
+ // here up -> Unit with first discarded digit\r
+ cut=discard-(count-DECDPUN)-1;\r
+ if (cut==DECDPUN-1) { // unit-boundary case (fast)\r
+ Unit half=(Unit)powers[DECDPUN]>>1;\r
+ // set residue directly\r
+ if (*up>=half) {\r
+ if (*up>half) *residue=7;\r
+ else *residue+=5; // add sticky bit\r
+ }\r
+ else { // <half\r
+ if (*up!=0) *residue=3; // [else is 0, leave as sticky bit]\r
+ }\r
+ if (set->digits<=0) { // special for Quantize/Subnormal :-(\r
+ *dn->lsu=0; // .. result is 0\r
+ dn->digits=1; // ..\r
+ }\r
+ else { // shift to least\r
+ count=set->digits; // now digits to end up with\r
+ dn->digits=count; // set the new length\r
+ up++; // move to next\r
+ // on unit boundary, so shift-down copy loop is simple\r
+ for (target=dn->lsu; count>0; target++, up++, count-=DECDPUN)\r
+ *target=*up;\r
+ }\r
+ } // unit-boundary case\r
+\r
+ else { // discard digit is in low digit(s), and not top digit\r
+ uInt discard1; // first discarded digit\r
+ uInt quot, rem; // for divisions\r
+ if (cut==0) quot=*up; // is at bottom of unit\r
+ else /* cut>0 */ { // it's not at bottom of unit\r
+ #if DECDPUN<=4\r
+ quot=QUOT10(*up, cut);\r
+ rem=*up-quot*powers[cut];\r
+ #else\r
+ rem=*up%powers[cut];\r
+ quot=*up/powers[cut];\r
+ #endif\r
+ if (rem!=0) *residue=1;\r
+ }\r
+ // discard digit is now at bottom of quot\r
+ #if DECDPUN<=4\r
+ temp=(quot*6554)>>16; // fast /10\r
+ // Vowels algorithm here not a win (9 instructions)\r
+ discard1=quot-X10(temp);\r
+ quot=temp;\r
+ #else\r
+ discard1=quot%10;\r
+ quot=quot/10;\r
+ #endif\r
+ // here, discard1 is the guard digit, and residue is everything\r
+ // else [use mapping array to accumulate residue safely]\r
+ *residue+=resmap[discard1];\r
+ cut++; // update cut\r
+ // here: up -> Unit of the array with bottom digit\r
+ // cut is the division point for each Unit\r
+ // quot holds the uncut high-order digits for the current unit\r
+ if (set->digits<=0) { // special for Quantize/Subnormal :-(\r
+ *dn->lsu=0; // .. result is 0\r
+ dn->digits=1; // ..\r
+ }\r
+ else { // shift to least needed\r
+ count=set->digits; // now digits to end up with\r
+ dn->digits=count; // set the new length\r
+ // shift-copy the coefficient array to the result number\r
+ for (target=dn->lsu; ; target++) {\r
+ *target=(Unit)quot;\r
+ count-=(DECDPUN-cut);\r
+ if (count<=0) break;\r
+ up++;\r
+ quot=*up;\r
+ #if DECDPUN<=4\r
+ quot=QUOT10(quot, cut);\r
+ rem=*up-quot*powers[cut];\r
+ #else\r
+ rem=quot%powers[cut];\r
+ quot=quot/powers[cut];\r
+ #endif\r
+ *target=(Unit)(*target+rem*powers[DECDPUN-cut]);\r
+ count-=cut;\r
+ if (count<=0) break;\r
+ } // shift-copy loop\r
+ } // shift to least\r
+ } // not unit boundary\r
+\r
+ if (*residue!=0) *status|=DEC_Inexact; // record inexactitude\r
+ return;\r
+ } // decSetCoeff\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decApplyRound -- apply pending rounding to a number */\r
+/* */\r
+/* dn is the number, with space for set->digits digits */\r
+/* set is the context [for size and rounding mode] */\r
+/* residue indicates pending rounding, being any accumulated */\r
+/* guard and sticky information. It may be: */\r
+/* 6-9: rounding digit is >5 */\r
+/* 5: rounding digit is exactly half-way */\r
+/* 1-4: rounding digit is <5 and >0 */\r
+/* 0: the coefficient is exact */\r
+/* -1: as 1, but the hidden digits are subtractive, that */\r
+/* is, of the opposite sign to dn. In this case the */\r
+/* coefficient must be non-0. This case occurs when */\r
+/* subtracting a small number (which can be reduced to */\r
+/* a sticky bit); see decAddOp. */\r
+/* status is the status accumulator, as usual */\r
+/* */\r
+/* This routine applies rounding while keeping the length of the */\r
+/* coefficient constant. The exponent and status are unchanged */\r
+/* except if: */\r
+/* */\r
+/* -- the coefficient was increased and is all nines (in which */\r
+/* case Overflow could occur, and is handled directly here so */\r
+/* the caller does not need to re-test for overflow) */\r
+/* */\r
+/* -- the coefficient was decreased and becomes all nines (in which */\r
+/* case Underflow could occur, and is also handled directly). */\r
+/* */\r
+/* All fields in dn are updated as required. */\r
+/* */\r
+/* ------------------------------------------------------------------ */\r
+static void decApplyRound(decNumber *dn, decContext *set, Int residue,\r
+ uInt *status) {\r
+ Int bump; // 1 if coefficient needs to be incremented\r
+ // -1 if coefficient needs to be decremented\r
+\r
+ if (residue==0) return; // nothing to apply\r
+\r
+ bump=0; // assume a smooth ride\r
+\r
+ // now decide whether, and how, to round, depending on mode\r
+ switch (set->round) {\r
+ case DEC_ROUND_05UP: { // round zero or five up (for reround)\r
+ // This is the same as DEC_ROUND_DOWN unless there is a\r
+ // positive residue and the lsd of dn is 0 or 5, in which case\r
+ // it is bumped; when residue is <0, the number is therefore\r
+ // bumped down unless the final digit was 1 or 6 (in which\r
+ // case it is bumped down and then up -- a no-op)\r
+ Int lsd5=*dn->lsu%5; // get lsd and quintate\r
+ if (residue<0 && lsd5!=1) bump=-1;\r
+ else if (residue>0 && lsd5==0) bump=1;\r
+ // [bump==1 could be applied directly; use common path for clarity]\r
+ break;} // r-05\r
+\r
+ case DEC_ROUND_DOWN: {\r
+ // no change, except if negative residue\r
+ if (residue<0) bump=-1;\r
+ break;} // r-d\r
+\r
+ case DEC_ROUND_HALF_DOWN: {\r
+ if (residue>5) bump=1;\r
+ break;} // r-h-d\r
+\r
+ case DEC_ROUND_HALF_EVEN: {\r
+ if (residue>5) bump=1; // >0.5 goes up\r
+ else if (residue==5) { // exactly 0.5000...\r
+ // 0.5 goes up iff [new] lsd is odd\r
+ if (*dn->lsu & 0x01) bump=1;\r
+ }\r
+ break;} // r-h-e\r
+\r
+ case DEC_ROUND_HALF_UP: {\r
+ if (residue>=5) bump=1;\r
+ break;} // r-h-u\r
+\r
+ case DEC_ROUND_UP: {\r
+ if (residue>0) bump=1;\r
+ break;} // r-u\r
+\r
+ case DEC_ROUND_CEILING: {\r
+ // same as _UP for positive numbers, and as _DOWN for negatives\r
+ // [negative residue cannot occur on 0]\r
+ if (decNumberIsNegative(dn)) {\r
+ if (residue<0) bump=-1;\r
+ }\r
+ else {\r
+ if (residue>0) bump=1;\r
+ }\r
+ break;} // r-c\r
+\r
+ case DEC_ROUND_FLOOR: {\r
+ // same as _UP for negative numbers, and as _DOWN for positive\r
+ // [negative residue cannot occur on 0]\r
+ if (!decNumberIsNegative(dn)) {\r
+ if (residue<0) bump=-1;\r
+ }\r
+ else {\r
+ if (residue>0) bump=1;\r
+ }\r
+ break;} // r-f\r
+\r
+ default: { // e.g., DEC_ROUND_MAX\r
+ *status|=DEC_Invalid_context;\r
+ #if DECTRACE || (DECCHECK && DECVERB)\r
+ printf("Unknown rounding mode: %d\n", set->round);\r
+ #endif\r
+ break;}\r
+ } // switch\r
+\r
+ // now bump the number, up or down, if need be\r
+ if (bump==0) return; // no action required\r
+\r
+ // Simply use decUnitAddSub unless bumping up and the number is\r
+ // all nines. In this special case set to 100... explicitly\r
+ // and adjust the exponent by one (as otherwise could overflow\r
+ // the array)\r
+ // Similarly handle all-nines result if bumping down.\r
+ if (bump>0) {\r
+ Unit *up; // work\r
+ uInt count=dn->digits; // digits to be checked\r
+ for (up=dn->lsu; ; up++) {\r
+ if (count<=DECDPUN) {\r
+ // this is the last Unit (the msu)\r
+ if (*up!=powers[count]-1) break; // not still 9s\r
+ // here if it, too, is all nines\r
+ *up=(Unit)powers[count-1]; // here 999 -> 100 etc.\r
+ for (up=up-1; up>=dn->lsu; up--) *up=0; // others all to 0\r
+ dn->exponent++; // and bump exponent\r
+ // [which, very rarely, could cause Overflow...]\r
+ if ((dn->exponent+dn->digits)>set->emax+1) {\r
+ decSetOverflow(dn, set, status);\r
+ }\r
+ return; // done\r
+ }\r
+ // a full unit to check, with more to come\r
+ if (*up!=DECDPUNMAX) break; // not still 9s\r
+ count-=DECDPUN;\r
+ } // up\r
+ } // bump>0\r
+ else { // -1\r
+ // here checking for a pre-bump of 1000... (leading 1, all\r
+ // other digits zero)\r
+ Unit *up, *sup; // work\r
+ uInt count=dn->digits; // digits to be checked\r
+ for (up=dn->lsu; ; up++) {\r
+ if (count<=DECDPUN) {\r
+ // this is the last Unit (the msu)\r
+ if (*up!=powers[count-1]) break; // not 100..\r
+ // here if have the 1000... case\r
+ sup=up; // save msu pointer\r
+ *up=(Unit)powers[count]-1; // here 100 in msu -> 999\r
+ // others all to all-nines, too\r
+ for (up=up-1; up>=dn->lsu; up--) *up=(Unit)powers[DECDPUN]-1;\r
+ dn->exponent--; // and bump exponent\r
+\r
+ // iff the number was at the subnormal boundary (exponent=etiny)\r
+ // then the exponent is now out of range, so it will in fact get\r
+ // clamped to etiny and the final 9 dropped.\r
+ // printf(">> emin=%d exp=%d sdig=%d\n", set->emin,\r
+ // dn->exponent, set->digits);\r
+ if (dn->exponent+1==set->emin-set->digits+1) {\r
+ if (count==1 && dn->digits==1) *sup=0; // here 9 -> 0[.9]\r
+ else {\r
+ *sup=(Unit)powers[count-1]-1; // here 999.. in msu -> 99..\r
+ dn->digits--;\r
+ }\r
+ dn->exponent++;\r
+ *status|=DEC_Underflow | DEC_Subnormal | DEC_Inexact | DEC_Rounded;\r
+ }\r
+ return; // done\r
+ }\r
+\r
+ // a full unit to check, with more to come\r
+ if (*up!=0) break; // not still 0s\r
+ count-=DECDPUN;\r
+ } // up\r
+\r
+ } // bump<0\r
+\r
+ // Actual bump needed. Do it.\r
+ decUnitAddSub(dn->lsu, D2U(dn->digits), uarrone, 1, 0, dn->lsu, bump);\r
+ } // decApplyRound\r
+\r
+#if DECSUBSET\r
+/* ------------------------------------------------------------------ */\r
+/* decFinish -- finish processing a number */\r
+/* */\r
+/* dn is the number */\r
+/* set is the context */\r
+/* residue is the rounding accumulator (as in decApplyRound) */\r
+/* status is the accumulator */\r
+/* */\r
+/* This finishes off the current number by: */\r
+/* 1. If not extended: */\r
+/* a. Converting a zero result to clean '0' */\r
+/* b. Reducing positive exponents to 0, if would fit in digits */\r
+/* 2. Checking for overflow and subnormals (always) */\r
+/* Note this is just Finalize when no subset arithmetic. */\r
+/* All fields are updated as required. */\r
+/* ------------------------------------------------------------------ */\r
+static void decFinish(decNumber *dn, decContext *set, Int *residue,\r
+ uInt *status) {\r
+ if (!set->extended) {\r
+ if ISZERO(dn) { // value is zero\r
+ dn->exponent=0; // clean exponent ..\r
+ dn->bits=0; // .. and sign\r
+ return; // no error possible\r
+ }\r
+ if (dn->exponent>=0) { // non-negative exponent\r
+ // >0; reduce to integer if possible\r
+ if (set->digits >= (dn->exponent+dn->digits)) {\r
+ dn->digits=decShiftToMost(dn->lsu, dn->digits, dn->exponent);\r
+ dn->exponent=0;\r
+ }\r
+ }\r
+ } // !extended\r
+\r
+ decFinalize(dn, set, residue, status);\r
+ } // decFinish\r
+#endif\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decFinalize -- final check, clamp, and round of a number */\r
+/* */\r
+/* dn is the number */\r
+/* set is the context */\r
+/* residue is the rounding accumulator (as in decApplyRound) */\r
+/* status is the status accumulator */\r
+/* */\r
+/* This finishes off the current number by checking for subnormal */\r
+/* results, applying any pending rounding, checking for overflow, */\r
+/* and applying any clamping. */\r
+/* Underflow and overflow conditions are raised as appropriate. */\r
+/* All fields are updated as required. */\r
+/* ------------------------------------------------------------------ */\r
+static void decFinalize(decNumber *dn, decContext *set, Int *residue,\r
+ uInt *status) {\r
+ Int shift; // shift needed if clamping\r
+ Int tinyexp=set->emin-dn->digits+1; // precalculate subnormal boundary\r
+\r
+ // Must be careful, here, when checking the exponent as the\r
+ // adjusted exponent could overflow 31 bits [because it may already\r
+ // be up to twice the expected].\r
+\r
+ // First test for subnormal. This must be done before any final\r
+ // round as the result could be rounded to Nmin or 0.\r
+ if (dn->exponent<=tinyexp) { // prefilter\r
+ Int comp;\r
+ decNumber nmin;\r
+ // A very nasty case here is dn == Nmin and residue<0\r
+ if (dn->exponent<tinyexp) {\r
+ // Go handle subnormals; this will apply round if needed.\r
+ decSetSubnormal(dn, set, residue, status);\r
+ return;\r
+ }\r
+ // Equals case: only subnormal if dn=Nmin and negative residue\r
+ decNumberZero(&nmin);\r
+ nmin.lsu[0]=1;\r
+ nmin.exponent=set->emin;\r
+ comp=decCompare(dn, &nmin, 1); // (signless compare)\r
+ if (comp==BADINT) { // oops\r
+ *status|=DEC_Insufficient_storage; // abandon...\r
+ return;\r
+ }\r
+ if (*residue<0 && comp==0) { // neg residue and dn==Nmin\r
+ decApplyRound(dn, set, *residue, status); // might force down\r
+ decSetSubnormal(dn, set, residue, status);\r
+ return;\r
+ }\r
+ }\r
+\r
+ // now apply any pending round (this could raise overflow).\r
+ if (*residue!=0) decApplyRound(dn, set, *residue, status);\r
+\r
+ // Check for overflow [redundant in the 'rare' case] or clamp\r
+ if (dn->exponent<=set->emax-set->digits+1) return; // neither needed\r
+\r
+\r
+ // here when might have an overflow or clamp to do\r
+ if (dn->exponent>set->emax-dn->digits+1) { // too big\r
+ decSetOverflow(dn, set, status);\r
+ return;\r
+ }\r
+ // here when the result is normal but in clamp range\r
+ if (!set->clamp) return;\r
+\r
+ // here when need to apply the IEEE exponent clamp (fold-down)\r
+ shift=dn->exponent-(set->emax-set->digits+1);\r
+\r
+ // shift coefficient (if non-zero)\r
+ if (!ISZERO(dn)) {\r
+ dn->digits=decShiftToMost(dn->lsu, dn->digits, shift);\r
+ }\r
+ dn->exponent-=shift; // adjust the exponent to match\r
+ *status|=DEC_Clamped; // and record the dirty deed\r
+ return;\r
+ } // decFinalize\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decSetOverflow -- set number to proper overflow value */\r
+/* */\r
+/* dn is the number (used for sign [only] and result) */\r
+/* set is the context [used for the rounding mode, etc.] */\r
+/* status contains the current status to be updated */\r
+/* */\r
+/* This sets the sign of a number and sets its value to either */\r
+/* Infinity or the maximum finite value, depending on the sign of */\r
+/* dn and the rounding mode, following IEEE 754 rules. */\r
+/* ------------------------------------------------------------------ */\r
+static void decSetOverflow(decNumber *dn, decContext *set, uInt *status) {\r
+ Flag needmax=0; // result is maximum finite value\r
+ uByte sign=dn->bits&DECNEG; // clean and save sign bit\r
+\r
+ if (ISZERO(dn)) { // zero does not overflow magnitude\r
+ Int emax=set->emax; // limit value\r
+ if (set->clamp) emax-=set->digits-1; // lower if clamping\r
+ if (dn->exponent>emax) { // clamp required\r
+ dn->exponent=emax;\r
+ *status|=DEC_Clamped;\r
+ }\r
+ return;\r
+ }\r
+\r
+ decNumberZero(dn);\r
+ switch (set->round) {\r
+ case DEC_ROUND_DOWN: {\r
+ needmax=1; // never Infinity\r
+ break;} // r-d\r
+ case DEC_ROUND_05UP: {\r
+ needmax=1; // never Infinity\r
+ break;} // r-05\r
+ case DEC_ROUND_CEILING: {\r
+ if (sign) needmax=1; // Infinity if non-negative\r
+ break;} // r-c\r
+ case DEC_ROUND_FLOOR: {\r
+ if (!sign) needmax=1; // Infinity if negative\r
+ break;} // r-f\r
+ default: break; // Infinity in all other cases\r
+ }\r
+ if (needmax) {\r
+ decSetMaxValue(dn, set);\r
+ dn->bits=sign; // set sign\r
+ }\r
+ else dn->bits=sign|DECINF; // Value is +/-Infinity\r
+ *status|=DEC_Overflow | DEC_Inexact | DEC_Rounded;\r
+ } // decSetOverflow\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decSetMaxValue -- set number to +Nmax (maximum normal value) */\r
+/* */\r
+/* dn is the number to set */\r
+/* set is the context [used for digits and emax] */\r
+/* */\r
+/* This sets the number to the maximum positive value. */\r
+/* ------------------------------------------------------------------ */\r
+static void decSetMaxValue(decNumber *dn, decContext *set) {\r
+ Unit *up; // work\r
+ Int count=set->digits; // nines to add\r
+ dn->digits=count;\r
+ // fill in all nines to set maximum value\r
+ for (up=dn->lsu; ; up++) {\r
+ if (count>DECDPUN) *up=DECDPUNMAX; // unit full o'nines\r
+ else { // this is the msu\r
+ *up=(Unit)(powers[count]-1);\r
+ break;\r
+ }\r
+ count-=DECDPUN; // filled those digits\r
+ } // up\r
+ dn->bits=0; // + sign\r
+ dn->exponent=set->emax-set->digits+1;\r
+ } // decSetMaxValue\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decSetSubnormal -- process value whose exponent is <Emin */\r
+/* */\r
+/* dn is the number (used as input as well as output; it may have */\r
+/* an allowed subnormal value, which may need to be rounded) */\r
+/* set is the context [used for the rounding mode] */\r
+/* residue is any pending residue */\r
+/* status contains the current status to be updated */\r
+/* */\r
+/* If subset mode, set result to zero and set Underflow flags. */\r
+/* */\r
+/* Value may be zero with a low exponent; this does not set Subnormal */\r
+/* but the exponent will be clamped to Etiny. */\r
+/* */\r
+/* Otherwise ensure exponent is not out of range, and round as */\r
+/* necessary. Underflow is set if the result is Inexact. */\r
+/* ------------------------------------------------------------------ */\r
+static void decSetSubnormal(decNumber *dn, decContext *set, Int *residue,\r
+ uInt *status) {\r
+ decContext workset; // work\r
+ Int etiny, adjust; // ..\r
+\r
+ #if DECSUBSET\r
+ // simple set to zero and 'hard underflow' for subset\r
+ if (!set->extended) {\r
+ decNumberZero(dn);\r
+ // always full overflow\r
+ *status|=DEC_Underflow | DEC_Subnormal | DEC_Inexact | DEC_Rounded;\r
+ return;\r
+ }\r
+ #endif\r
+\r
+ // Full arithmetic -- allow subnormals, rounded to minimum exponent\r
+ // (Etiny) if needed\r
+ etiny=set->emin-(set->digits-1); // smallest allowed exponent\r
+\r
+ if ISZERO(dn) { // value is zero\r
+ // residue can never be non-zero here\r
+ #if DECCHECK\r
+ if (*residue!=0) {\r
+ printf("++ Subnormal 0 residue %ld\n", (LI)*residue);\r
+ *status|=DEC_Invalid_operation;\r
+ }\r
+ #endif\r
+ if (dn->exponent<etiny) { // clamp required\r
+ dn->exponent=etiny;\r
+ *status|=DEC_Clamped;\r
+ }\r
+ return;\r
+ }\r
+\r
+ *status|=DEC_Subnormal; // have a non-zero subnormal\r
+ adjust=etiny-dn->exponent; // calculate digits to remove\r
+ if (adjust<=0) { // not out of range; unrounded\r
+ // residue can never be non-zero here, except in the Nmin-residue\r
+ // case (which is a subnormal result), so can take fast-path here\r
+ // it may already be inexact (from setting the coefficient)\r
+ if (*status&DEC_Inexact) *status|=DEC_Underflow;\r
+ return;\r
+ }\r
+\r
+ // adjust>0, so need to rescale the result so exponent becomes Etiny\r
+ // [this code is similar to that in rescale]\r
+ workset=*set; // clone rounding, etc.\r
+ workset.digits=dn->digits-adjust; // set requested length\r
+ workset.emin-=adjust; // and adjust emin to match\r
+ // [note that the latter can be <1, here, similar to Rescale case]\r
+ decSetCoeff(dn, &workset, dn->lsu, dn->digits, residue, status);\r
+ decApplyRound(dn, &workset, *residue, status);\r
+\r
+ // Use 754 default rule: Underflow is set iff Inexact\r
+ // [independent of whether trapped]\r
+ if (*status&DEC_Inexact) *status|=DEC_Underflow;\r
+\r
+ // if rounded up a 999s case, exponent will be off by one; adjust\r
+ // back if so [it will fit, because it was shortened earlier]\r
+ if (dn->exponent>etiny) {\r
+ dn->digits=decShiftToMost(dn->lsu, dn->digits, 1);\r
+ dn->exponent--; // (re)adjust the exponent.\r
+ }\r
+\r
+ // if rounded to zero, it is by definition clamped...\r
+ if (ISZERO(dn)) *status|=DEC_Clamped;\r
+ } // decSetSubnormal\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decCheckMath - check entry conditions for a math function */\r
+/* */\r
+/* This checks the context and the operand */\r
+/* */\r
+/* rhs is the operand to check */\r
+/* set is the context to check */\r
+/* status is unchanged if both are good */\r
+/* */\r
+/* returns non-zero if status is changed, 0 otherwise */\r
+/* */\r
+/* Restrictions enforced: */\r
+/* */\r
+/* digits, emax, and -emin in the context must be less than */\r
+/* DEC_MAX_MATH (999999), and A must be within these bounds if */\r
+/* non-zero. Invalid_operation is set in the status if a */\r
+/* restriction is violated. */\r
+/* ------------------------------------------------------------------ */\r
+static uInt decCheckMath(const decNumber *rhs, decContext *set,\r
+ uInt *status) {\r
+ uInt save=*status; // record\r
+ if (set->digits>DEC_MAX_MATH\r
+ || set->emax>DEC_MAX_MATH\r
+ || -set->emin>DEC_MAX_MATH) *status|=DEC_Invalid_context;\r
+ else if ((rhs->digits>DEC_MAX_MATH\r
+ || rhs->exponent+rhs->digits>DEC_MAX_MATH+1\r
+ || rhs->exponent+rhs->digits<2*(1-DEC_MAX_MATH))\r
+ && !ISZERO(rhs)) *status|=DEC_Invalid_operation;\r
+ return (*status!=save);\r
+ } // decCheckMath\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decGetInt -- get integer from a number */\r
+/* */\r
+/* dn is the number [which will not be altered] */\r
+/* */\r
+/* returns one of: */\r
+/* BADINT if there is a non-zero fraction */\r
+/* the converted integer */\r
+/* BIGEVEN if the integer is even and magnitude > 2*10**9 */\r
+/* BIGODD if the integer is odd and magnitude > 2*10**9 */\r
+/* */\r
+/* This checks and gets a whole number from the input decNumber. */\r
+/* The sign can be determined from dn by the caller when BIGEVEN or */\r
+/* BIGODD is returned. */\r
+/* ------------------------------------------------------------------ */\r
+static Int decGetInt(const decNumber *dn) {\r
+ Int theInt; // result accumulator\r
+ const Unit *up; // work\r
+ Int got; // digits (real or not) processed\r
+ Int ilength=dn->digits+dn->exponent; // integral length\r
+ Flag neg=decNumberIsNegative(dn); // 1 if -ve\r
+\r
+ // The number must be an integer that fits in 10 digits\r
+ // Assert, here, that 10 is enough for any rescale Etiny\r
+ #if DEC_MAX_EMAX > 999999999\r
+ #error GetInt may need updating [for Emax]\r
+ #endif\r
+ #if DEC_MIN_EMIN < -999999999\r
+ #error GetInt may need updating [for Emin]\r
+ #endif\r
+ if (ISZERO(dn)) return 0; // zeros are OK, with any exponent\r
+\r
+ up=dn->lsu; // ready for lsu\r
+ theInt=0; // ready to accumulate\r
+ if (dn->exponent>=0) { // relatively easy\r
+ // no fractional part [usual]; allow for positive exponent\r
+ got=dn->exponent;\r
+ }\r
+ else { // -ve exponent; some fractional part to check and discard\r
+ Int count=-dn->exponent; // digits to discard\r
+ // spin up whole units until reach the Unit with the unit digit\r
+ for (; count>=DECDPUN; up++) {\r
+ if (*up!=0) return BADINT; // non-zero Unit to discard\r
+ count-=DECDPUN;\r
+ }\r
+ if (count==0) got=0; // [a multiple of DECDPUN]\r
+ else { // [not multiple of DECDPUN]\r
+ Int rem; // work\r
+ // slice off fraction digits and check for non-zero\r
+ #if DECDPUN<=4\r
+ theInt=QUOT10(*up, count);\r
+ rem=*up-theInt*powers[count];\r
+ #else\r
+ rem=*up%powers[count]; // slice off discards\r
+ theInt=*up/powers[count];\r
+ #endif\r
+ if (rem!=0) return BADINT; // non-zero fraction\r
+ // it looks good\r
+ got=DECDPUN-count; // number of digits so far\r
+ up++; // ready for next\r
+ }\r
+ }\r
+ // now it's known there's no fractional part\r
+\r
+ // tricky code now, to accumulate up to 9.3 digits\r
+ if (got==0) {theInt=*up; got+=DECDPUN; up++;} // ensure lsu is there\r
+\r
+ if (ilength<11) {\r
+ Int save=theInt;\r
+ // collect any remaining unit(s)\r
+ for (; got<ilength; up++) {\r
+ theInt+=*up*powers[got];\r
+ got+=DECDPUN;\r
+ }\r
+ if (ilength==10) { // need to check for wrap\r
+ if (theInt/(Int)powers[got-DECDPUN]!=(Int)*(up-1)) ilength=11;\r
+ // [that test also disallows the BADINT result case]\r
+ else if (neg && theInt>1999999997) ilength=11;\r
+ else if (!neg && theInt>999999999) ilength=11;\r
+ if (ilength==11) theInt=save; // restore correct low bit\r
+ }\r
+ }\r
+\r
+ if (ilength>10) { // too big\r
+ if (theInt&1) return BIGODD; // bottom bit 1\r
+ return BIGEVEN; // bottom bit 0\r
+ }\r
+\r
+ if (neg) theInt=-theInt; // apply sign\r
+ return theInt;\r
+ } // decGetInt\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decDecap -- decapitate the coefficient of a number */\r
+/* */\r
+/* dn is the number to be decapitated */\r
+/* drop is the number of digits to be removed from the left of dn; */\r
+/* this must be <= dn->digits (if equal, the coefficient is */\r
+/* set to 0) */\r
+/* */\r
+/* Returns dn; dn->digits will be <= the initial digits less drop */\r
+/* (after removing drop digits there may be leading zero digits */\r
+/* which will also be removed). Only dn->lsu and dn->digits change. */\r
+/* ------------------------------------------------------------------ */\r
+static decNumber *decDecap(decNumber *dn, Int drop) {\r
+ Unit *msu; // -> target cut point\r
+ Int cut; // work\r
+ if (drop>=dn->digits) { // losing the whole thing\r
+ #if DECCHECK\r
+ if (drop>dn->digits)\r
+ printf("decDecap called with drop>digits [%ld>%ld]\n",\r
+ (LI)drop, (LI)dn->digits);\r
+ #endif\r
+ dn->lsu[0]=0;\r
+ dn->digits=1;\r
+ return dn;\r
+ }\r
+ msu=dn->lsu+D2U(dn->digits-drop)-1; // -> likely msu\r
+ cut=MSUDIGITS(dn->digits-drop); // digits to be in use in msu\r
+ if (cut!=DECDPUN) *msu%=powers[cut]; // clear left digits\r
+ // that may have left leading zero digits, so do a proper count...\r
+ dn->digits=decGetDigits(dn->lsu, msu-dn->lsu+1);\r
+ return dn;\r
+ } // decDecap\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decBiStr -- compare string with pairwise options */\r
+/* */\r
+/* targ is the string to compare */\r
+/* str1 is one of the strings to compare against (length may be 0) */\r
+/* str2 is the other; it must be the same length as str1 */\r
+/* */\r
+/* returns 1 if strings compare equal, (that is, it is the same */\r
+/* length as str1 and str2, and each character of targ is in either */\r
+/* str1 or str2 in the corresponding position), or 0 otherwise */\r
+/* */\r
+/* This is used for generic caseless compare, including the awkward */\r
+/* case of the Turkish dotted and dotless Is. Use as (for example): */\r
+/* if (decBiStr(test, "mike", "MIKE")) ... */\r
+/* ------------------------------------------------------------------ */\r
+static Flag decBiStr(const char *targ, const char *str1, const char *str2) {\r
+ for (;;targ++, str1++, str2++) {\r
+ if (*targ!=*str1 && *targ!=*str2) return 0;\r
+ // *targ has a match in one (or both, if terminator)\r
+ if (*targ=='\0') break;\r
+ } // forever\r
+ return 1;\r
+ } // decBiStr\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decNaNs -- handle NaN operand or operands */\r
+/* */\r
+/* res is the result number */\r
+/* lhs is the first operand */\r
+/* rhs is the second operand, or NULL if none */\r
+/* context is used to limit payload length */\r
+/* status contains the current status */\r
+/* returns res in case convenient */\r
+/* */\r
+/* Called when one or both operands is a NaN, and propagates the */\r
+/* appropriate result to res. When an sNaN is found, it is changed */\r
+/* to a qNaN and Invalid operation is set. */\r
+/* ------------------------------------------------------------------ */\r
+static decNumber * decNaNs(decNumber *res, const decNumber *lhs,\r
+ const decNumber *rhs, decContext *set,\r
+ uInt *status) {\r
+ // This decision tree ends up with LHS being the source pointer,\r
+ // and status updated if need be\r
+ if (lhs->bits & DECSNAN)\r
+ *status|=DEC_Invalid_operation | DEC_sNaN;\r
+ else if (rhs==NULL);\r
+ else if (rhs->bits & DECSNAN) {\r
+ lhs=rhs;\r
+ *status|=DEC_Invalid_operation | DEC_sNaN;\r
+ }\r
+ else if (lhs->bits & DECNAN);\r
+ else lhs=rhs;\r
+\r
+ // propagate the payload\r
+ if (lhs->digits<=set->digits) decNumberCopy(res, lhs); // easy\r
+ else { // too long\r
+ const Unit *ul;\r
+ Unit *ur, *uresp1;\r
+ // copy safe number of units, then decapitate\r
+ res->bits=lhs->bits; // need sign etc.\r
+ uresp1=res->lsu+D2U(set->digits);\r
+ for (ur=res->lsu, ul=lhs->lsu; ur<uresp1; ur++, ul++) *ur=*ul;\r
+ res->digits=D2U(set->digits)*DECDPUN;\r
+ // maybe still too long\r
+ if (res->digits>set->digits) decDecap(res, res->digits-set->digits);\r
+ }\r
+\r
+ res->bits&=~DECSNAN; // convert any sNaN to NaN, while\r
+ res->bits|=DECNAN; // .. preserving sign\r
+ res->exponent=0; // clean exponent\r
+ // [coefficient was copied/decapitated]\r
+ return res;\r
+ } // decNaNs\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decStatus -- apply non-zero status */\r
+/* */\r
+/* dn is the number to set if error */\r
+/* status contains the current status (not yet in context) */\r
+/* set is the context */\r
+/* */\r
+/* If the status is an error status, the number is set to a NaN, */\r
+/* unless the error was an overflow, divide-by-zero, or underflow, */\r
+/* in which case the number will have already been set. */\r
+/* */\r
+/* The context status is then updated with the new status. Note that */\r
+/* this may raise a signal, so control may never return from this */\r
+/* routine (hence resources must be recovered before it is called). */\r
+/* ------------------------------------------------------------------ */\r
+static void decStatus(decNumber *dn, uInt status, decContext *set) {\r
+ if (status & DEC_NaNs) { // error status -> NaN\r
+ // if cause was an sNaN, clear and propagate [NaN is already set up]\r
+ if (status & DEC_sNaN) status&=~DEC_sNaN;\r
+ else {\r
+ decNumberZero(dn); // other error: clean throughout\r
+ dn->bits=DECNAN; // and make a quiet NaN\r
+ }\r
+ }\r
+ decContextSetStatus(set, status); // [may not return]\r
+ return;\r
+ } // decStatus\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decGetDigits -- count digits in a Units array */\r
+/* */\r
+/* uar is the Unit array holding the number (this is often an */\r
+/* accumulator of some sort) */\r
+/* len is the length of the array in units [>=1] */\r
+/* */\r
+/* returns the number of (significant) digits in the array */\r
+/* */\r
+/* All leading zeros are excluded, except the last if the array has */\r
+/* only zero Units. */\r
+/* ------------------------------------------------------------------ */\r
+// This may be called twice during some operations.\r
+static Int decGetDigits(Unit *uar, Int len) {\r
+ Unit *up=uar+(len-1); // -> msu\r
+ Int digits=(len-1)*DECDPUN+1; // possible digits excluding msu\r
+ #if DECDPUN>4\r
+ uInt const *pow; // work\r
+ #endif\r
+ // (at least 1 in final msu)\r
+ #if DECCHECK\r
+ if (len<1) printf("decGetDigits called with len<1 [%ld]\n", (LI)len);\r
+ #endif\r
+\r
+ for (; up>=uar; up--) {\r
+ if (*up==0) { // unit is all 0s\r
+ if (digits==1) break; // a zero has one digit\r
+ digits-=DECDPUN; // adjust for 0 unit\r
+ continue;}\r
+ // found the first (most significant) non-zero Unit\r
+ #if DECDPUN>1 // not done yet\r
+ if (*up<10) break; // is 1-9\r
+ digits++;\r
+ #if DECDPUN>2 // not done yet\r
+ if (*up<100) break; // is 10-99\r
+ digits++;\r
+ #if DECDPUN>3 // not done yet\r
+ if (*up<1000) break; // is 100-999\r
+ digits++;\r
+ #if DECDPUN>4 // count the rest ...\r
+ for (pow=&powers[4]; *up>=*pow; pow++) digits++;\r
+ #endif\r
+ #endif\r
+ #endif\r
+ #endif\r
+ break;\r
+ } // up\r
+ return digits;\r
+ } // decGetDigits\r
+\r
+#if DECTRACE | DECCHECK\r
+/* ------------------------------------------------------------------ */\r
+/* decNumberShow -- display a number [debug aid] */\r
+/* dn is the number to show */\r
+/* */\r
+/* Shows: sign, exponent, coefficient (msu first), digits */\r
+/* or: sign, special-value */\r
+/* ------------------------------------------------------------------ */\r
+// this is public so other modules can use it\r
+void decNumberShow(const decNumber *dn) {\r
+ const Unit *up; // work\r
+ uInt u, d; // ..\r
+ Int cut; // ..\r
+ char isign='+'; // main sign\r
+ if (dn==NULL) {\r
+ printf("NULL\n");\r
+ return;}\r
+ if (decNumberIsNegative(dn)) isign='-';\r
+ printf(" >> %c ", isign);\r
+ if (dn->bits&DECSPECIAL) { // Is a special value\r
+ if (decNumberIsInfinite(dn)) printf("Infinity");\r
+ else { // a NaN\r
+ if (dn->bits&DECSNAN) printf("sNaN"); // signalling NaN\r
+ else printf("NaN");\r
+ }\r
+ // if coefficient and exponent are 0, no more to do\r
+ if (dn->exponent==0 && dn->digits==1 && *dn->lsu==0) {\r
+ printf("\n");\r
+ return;}\r
+ // drop through to report other information\r
+ printf(" ");\r
+ }\r
+\r
+ // now carefully display the coefficient\r
+ up=dn->lsu+D2U(dn->digits)-1; // msu\r
+ printf("%ld", (LI)*up);\r
+ for (up=up-1; up>=dn->lsu; up--) {\r
+ u=*up;\r
+ printf(":");\r
+ for (cut=DECDPUN-1; cut>=0; cut--) {\r
+ d=u/powers[cut];\r
+ u-=d*powers[cut];\r
+ printf("%ld", (LI)d);\r
+ } // cut\r
+ } // up\r
+ if (dn->exponent!=0) {\r
+ char esign='+';\r
+ if (dn->exponent<0) esign='-';\r
+ printf(" E%c%ld", esign, (LI)abs(dn->exponent));\r
+ }\r
+ printf(" [%ld]\n", (LI)dn->digits);\r
+ } // decNumberShow\r
+#endif\r
+\r
+#if DECTRACE || DECCHECK\r
+/* ------------------------------------------------------------------ */\r
+/* decDumpAr -- display a unit array [debug/check aid] */\r
+/* name is a single-character tag name */\r
+/* ar is the array to display */\r
+/* len is the length of the array in Units */\r
+/* ------------------------------------------------------------------ */\r
+static void decDumpAr(char name, const Unit *ar, Int len) {\r
+ Int i;\r
+ const char *spec;\r
+ #if DECDPUN==9\r
+ spec="%09d ";\r
+ #elif DECDPUN==8\r
+ spec="%08d ";\r
+ #elif DECDPUN==7\r
+ spec="%07d ";\r
+ #elif DECDPUN==6\r
+ spec="%06d ";\r
+ #elif DECDPUN==5\r
+ spec="%05d ";\r
+ #elif DECDPUN==4\r
+ spec="%04d ";\r
+ #elif DECDPUN==3\r
+ spec="%03d ";\r
+ #elif DECDPUN==2\r
+ spec="%02d ";\r
+ #else\r
+ spec="%d ";\r
+ #endif\r
+ printf(" :%c: ", name);\r
+ for (i=len-1; i>=0; i--) {\r
+ if (i==len-1) printf("%ld ", (LI)ar[i]);\r
+ else printf(spec, ar[i]);\r
+ }\r
+ printf("\n");\r
+ return;}\r
+#endif\r
+\r
+#if DECCHECK\r
+/* ------------------------------------------------------------------ */\r
+/* decCheckOperands -- check operand(s) to a routine */\r
+/* res is the result structure (not checked; it will be set to */\r
+/* quiet NaN if error found (and it is not NULL)) */\r
+/* lhs is the first operand (may be DECUNRESU) */\r
+/* rhs is the second (may be DECUNUSED) */\r
+/* set is the context (may be DECUNCONT) */\r
+/* returns 0 if both operands, and the context are clean, or 1 */\r
+/* otherwise (in which case the context will show an error, */\r
+/* unless NULL). Note that res is not cleaned; caller should */\r
+/* handle this so res=NULL case is safe. */\r
+/* The caller is expected to abandon immediately if 1 is returned. */\r
+/* ------------------------------------------------------------------ */\r
+static Flag decCheckOperands(decNumber *res, const decNumber *lhs,\r
+ const decNumber *rhs, decContext *set) {\r
+ Flag bad=0;\r
+ if (set==NULL) { // oops; hopeless\r
+ #if DECTRACE || DECVERB\r
+ printf("Reference to context is NULL.\n");\r
+ #endif\r
+ bad=1;\r
+ return 1;}\r
+ else if (set!=DECUNCONT\r
+ && (set->digits<1 || set->round>=DEC_ROUND_MAX)) {\r
+ bad=1;\r
+ #if DECTRACE || DECVERB\r
+ printf("Bad context [digits=%ld round=%ld].\n",\r
+ (LI)set->digits, (LI)set->round);\r
+ #endif\r
+ }\r
+ else {\r
+ if (res==NULL) {\r
+ bad=1;\r
+ #if DECTRACE\r
+ // this one not DECVERB as standard tests include NULL\r
+ printf("Reference to result is NULL.\n");\r
+ #endif\r
+ }\r
+ if (!bad && lhs!=DECUNUSED) bad=(decCheckNumber(lhs));\r
+ if (!bad && rhs!=DECUNUSED) bad=(decCheckNumber(rhs));\r
+ }\r
+ if (bad) {\r
+ if (set!=DECUNCONT) decContextSetStatus(set, DEC_Invalid_operation);\r
+ if (res!=DECUNRESU && res!=NULL) {\r
+ decNumberZero(res);\r
+ res->bits=DECNAN; // qNaN\r
+ }\r
+ }\r
+ return bad;\r
+ } // decCheckOperands\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decCheckNumber -- check a number */\r
+/* dn is the number to check */\r
+/* returns 0 if the number is clean, or 1 otherwise */\r
+/* */\r
+/* The number is considered valid if it could be a result from some */\r
+/* operation in some valid context. */\r
+/* ------------------------------------------------------------------ */\r
+static Flag decCheckNumber(const decNumber *dn) {\r
+ const Unit *up; // work\r
+ uInt maxuint; // ..\r
+ Int ae, d, digits; // ..\r
+ Int emin, emax; // ..\r
+\r
+ if (dn==NULL) { // hopeless\r
+ #if DECTRACE\r
+ // this one not DECVERB as standard tests include NULL\r
+ printf("Reference to decNumber is NULL.\n");\r
+ #endif\r
+ return 1;}\r
+\r
+ // check special values\r
+ if (dn->bits & DECSPECIAL) {\r
+ if (dn->exponent!=0) {\r
+ #if DECTRACE || DECVERB\r
+ printf("Exponent %ld (not 0) for a special value [%02x].\n",\r
+ (LI)dn->exponent, dn->bits);\r
+ #endif\r
+ return 1;}\r
+\r
+ // 2003.09.08: NaNs may now have coefficients, so next tests Inf only\r
+ if (decNumberIsInfinite(dn)) {\r
+ if (dn->digits!=1) {\r
+ #if DECTRACE || DECVERB\r
+ printf("Digits %ld (not 1) for an infinity.\n", (LI)dn->digits);\r
+ #endif\r
+ return 1;}\r
+ if (*dn->lsu!=0) {\r
+ #if DECTRACE || DECVERB\r
+ printf("LSU %ld (not 0) for an infinity.\n", (LI)*dn->lsu);\r
+ #endif\r
+ decDumpAr('I', dn->lsu, D2U(dn->digits));\r
+ return 1;}\r
+ } // Inf\r
+ // 2002.12.26: negative NaNs can now appear through proposed IEEE\r
+ // concrete formats (decimal64, etc.).\r
+ return 0;\r
+ }\r
+\r
+ // check the coefficient\r
+ if (dn->digits<1 || dn->digits>DECNUMMAXP) {\r
+ #if DECTRACE || DECVERB\r
+ printf("Digits %ld in number.\n", (LI)dn->digits);\r
+ #endif\r
+ return 1;}\r
+\r
+ d=dn->digits;\r
+\r
+ for (up=dn->lsu; d>0; up++) {\r
+ if (d>DECDPUN) maxuint=DECDPUNMAX;\r
+ else { // reached the msu\r
+ maxuint=powers[d]-1;\r
+ if (dn->digits>1 && *up<powers[d-1]) {\r
+ #if DECTRACE || DECVERB\r
+ printf("Leading 0 in number.\n");\r
+ decNumberShow(dn);\r
+ #endif\r
+ return 1;}\r
+ }\r
+ if (*up>maxuint) {\r
+ #if DECTRACE || DECVERB\r
+ printf("Bad Unit [%08lx] in %ld-digit number at offset %ld [maxuint %ld].\n",\r
+ (LI)*up, (LI)dn->digits, (LI)(up-dn->lsu), (LI)maxuint);\r
+ #endif\r
+ return 1;}\r
+ d-=DECDPUN;\r
+ }\r
+\r
+ // check the exponent. Note that input operands can have exponents\r
+ // which are out of the set->emin/set->emax and set->digits range\r
+ // (just as they can have more digits than set->digits).\r
+ ae=dn->exponent+dn->digits-1; // adjusted exponent\r
+ emax=DECNUMMAXE;\r
+ emin=DECNUMMINE;\r
+ digits=DECNUMMAXP;\r
+ if (ae<emin-(digits-1)) {\r
+ #if DECTRACE || DECVERB\r
+ printf("Adjusted exponent underflow [%ld].\n", (LI)ae);\r
+ decNumberShow(dn);\r
+ #endif\r
+ return 1;}\r
+ if (ae>+emax) {\r
+ #if DECTRACE || DECVERB\r
+ printf("Adjusted exponent overflow [%ld].\n", (LI)ae);\r
+ decNumberShow(dn);\r
+ #endif\r
+ return 1;}\r
+\r
+ return 0; // it's OK\r
+ } // decCheckNumber\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decCheckInexact -- check a normal finite inexact result has digits */\r
+/* dn is the number to check */\r
+/* set is the context (for status and precision) */\r
+/* sets Invalid operation, etc., if some digits are missing */\r
+/* [this check is not made for DECSUBSET compilation or when */\r
+/* subnormal is not set] */\r
+/* ------------------------------------------------------------------ */\r
+static void decCheckInexact(const decNumber *dn, decContext *set) {\r
+ #if !DECSUBSET && DECEXTFLAG\r
+ if ((set->status & (DEC_Inexact|DEC_Subnormal))==DEC_Inexact\r
+ && (set->digits!=dn->digits) && !(dn->bits & DECSPECIAL)) {\r
+ #if DECTRACE || DECVERB\r
+ printf("Insufficient digits [%ld] on normal Inexact result.\n",\r
+ (LI)dn->digits);\r
+ decNumberShow(dn);\r
+ #endif\r
+ decContextSetStatus(set, DEC_Invalid_operation);\r
+ }\r
+ #else\r
+ // next is a noop for quiet compiler\r
+ if (dn!=NULL && dn->digits==0) set->status|=DEC_Invalid_operation;\r
+ #endif\r
+ return;\r
+ } // decCheckInexact\r
+#endif\r
+\r
+#if DECALLOC\r
+#undef malloc\r
+#undef free\r
+/* ------------------------------------------------------------------ */\r
+/* decMalloc -- accountable allocation routine */\r
+/* n is the number of bytes to allocate */\r
+/* */\r
+/* Semantics is the same as the stdlib malloc routine, but bytes */\r
+/* allocated are accounted for globally, and corruption fences are */\r
+/* added before and after the 'actual' storage. */\r
+/* ------------------------------------------------------------------ */\r
+/* This routine allocates storage with an extra twelve bytes; 8 are */\r
+/* at the start and hold: */\r
+/* 0-3 the original length requested */\r
+/* 4-7 buffer corruption detection fence (DECFENCE, x4) */\r
+/* The 4 bytes at the end also hold a corruption fence (DECFENCE, x4) */\r
+/* ------------------------------------------------------------------ */\r
+static void *decMalloc(size_t n) {\r
+ uInt size=n+12; // true size\r
+ void *alloc; // -> allocated storage\r
+ uByte *b, *b0; // work\r
+ uInt uiwork; // for macros\r
+\r
+ alloc=malloc(size); // -> allocated storage\r
+ if (alloc==NULL) return NULL; // out of strorage\r
+ b0=(uByte *)alloc; // as bytes\r
+ decAllocBytes+=n; // account for storage\r
+ UBFROMUI(alloc, n); // save n\r
+ // printf(" alloc ++ dAB: %ld (%ld)\n", (LI)decAllocBytes, (LI)n);\r
+ for (b=b0+4; b<b0+8; b++) *b=DECFENCE;\r
+ for (b=b0+n+8; b<b0+n+12; b++) *b=DECFENCE;\r
+ return b0+8; // -> play area\r
+ } // decMalloc\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decFree -- accountable free routine */\r
+/* alloc is the storage to free */\r
+/* */\r
+/* Semantics is the same as the stdlib malloc routine, except that */\r
+/* the global storage accounting is updated and the fences are */\r
+/* checked to ensure that no routine has written 'out of bounds'. */\r
+/* ------------------------------------------------------------------ */\r
+/* This routine first checks that the fences have not been corrupted. */\r
+/* It then frees the storage using the 'truw' storage address (that */\r
+/* is, offset by 8). */\r
+/* ------------------------------------------------------------------ */\r
+static void decFree(void *alloc) {\r
+ uInt n; // original length\r
+ uByte *b, *b0; // work\r
+ uInt uiwork; // for macros\r
+\r
+ if (alloc==NULL) return; // allowed; it's a nop\r
+ b0=(uByte *)alloc; // as bytes\r
+ b0-=8; // -> true start of storage\r
+ n=UBTOUI(b0); // lift length\r
+ for (b=b0+4; b<b0+8; b++) if (*b!=DECFENCE)\r
+ printf("=== Corrupt byte [%02x] at offset %d from %ld ===\n", *b,\r
+ b-b0-8, (LI)b0);\r
+ for (b=b0+n+8; b<b0+n+12; b++) if (*b!=DECFENCE)\r
+ printf("=== Corrupt byte [%02x] at offset +%d from %ld, n=%ld ===\n", *b,\r
+ b-b0-8, (LI)b0, (LI)n);\r
+ free(b0); // drop the storage\r
+ decAllocBytes-=n; // account for storage\r
+ // printf(" free -- dAB: %d (%d)\n", decAllocBytes, -n);\r
+ } // decFree\r
+#define malloc(a) decMalloc(a)\r
+#define free(a) decFree(a)\r
+#endif\r
--- /dev/null
+/* ------------------------------------------------------------------ */\r
+/* Decimal Number arithmetic module header */\r
+/* ------------------------------------------------------------------ */\r
+/* Copyright (c) IBM Corporation, 2000, 2010. All rights reserved. */\r
+/* */\r
+/* This software is made available under the terms of the */\r
+/* ICU License -- ICU 1.8.1 and later. */\r
+/* */\r
+/* The description and User's Guide ("The decNumber C Library") for */\r
+/* this software is called decNumber.pdf. This document is */\r
+/* available, together with arithmetic and format specifications, */\r
+/* testcases, and Web links, on the General Decimal Arithmetic page. */\r
+/* */\r
+/* Please send comments, suggestions, and corrections to the author: */\r
+/* mfc@uk.ibm.com */\r
+/* Mike Cowlishaw, IBM Fellow */\r
+/* IBM UK, PO Box 31, Birmingham Road, Warwick CV34 5JL, UK */\r
+/* ------------------------------------------------------------------ */\r
+\r
+#if !defined(DECNUMBER)\r
+ #define DECNUMBER\r
+ #define DECNAME "decNumber" /* Short name */\r
+ #define DECFULLNAME "Decimal Number Module" /* Verbose name */\r
+ #define DECAUTHOR "Mike Cowlishaw" /* Who to blame */\r
+\r
+ #if !defined(DECCONTEXT)\r
+ #include "decContext.h"\r
+ #endif\r
+\r
+ /* Bit settings for decNumber.bits */\r
+ #define DECNEG 0x80 /* Sign; 1=negative, 0=positive or zero */\r
+ #define DECINF 0x40 /* 1=Infinity */\r
+ #define DECNAN 0x20 /* 1=NaN */\r
+ #define DECSNAN 0x10 /* 1=sNaN */\r
+ /* The remaining bits are reserved; they must be 0 */\r
+ #define DECSPECIAL (DECINF|DECNAN|DECSNAN) /* any special value */\r
+\r
+ /* Define the decNumber data structure. The size and shape of the */\r
+ /* units array in the structure is determined by the following */\r
+ /* constant. This must not be changed without recompiling the */\r
+ /* decNumber library modules. */\r
+\r
+ //#define DECDPUN 3 /* DECimal Digits Per UNit [must be >0 */\r
+ #define DECDPUN 8 /* DECimal Digits Per UNit [must be >0 */\r
+ /* and <10; 3 or powers of 2 are best]. */\r
+\r
+ /* DECNUMDIGITS is the default number of digits that can be held in */\r
+ /* the structure. If undefined, 1 is assumed and it is assumed */\r
+ /* that the structure will be immediately followed by extra space, */\r
+ /* as required. DECNUMDIGITS is always >0. */\r
+ #if !defined(DECNUMDIGITS)\r
+ #define DECNUMDIGITS 1\r
+ #endif\r
+\r
+ /* The size (integer data type) of each unit is determined by the */\r
+ /* number of digits it will hold. */\r
+ #if DECDPUN<=2\r
+ #define decNumberUnit uint8_t\r
+ #elif DECDPUN<=4\r
+ #define decNumberUnit uint16_t\r
+ #else\r
+ #define decNumberUnit uint32_t\r
+ #endif\r
+ /* The number of units needed is ceil(DECNUMDIGITS/DECDPUN) */\r
+ #define DECNUMUNITS ((DECNUMDIGITS+DECDPUN-1)/DECDPUN)\r
+\r
+ /* The data structure... */\r
+ typedef struct {\r
+ int32_t digits; /* Count of digits in the coefficient; >0 */\r
+ int32_t exponent; /* Unadjusted exponent, unbiased, in */\r
+ /* range: -1999999997 through 999999999 */\r
+ uint8_t bits; /* Indicator bits (see above) */\r
+ /* Coefficient, from least significant unit */\r
+ decNumberUnit lsu[DECNUMUNITS];\r
+ } decNumber;\r
+\r
+ /* Notes: */\r
+ /* 1. If digits is > DECDPUN then there will one or more */\r
+ /* decNumberUnits immediately following the first element of lsu.*/\r
+ /* These contain the remaining (more significant) digits of the */\r
+ /* number, and may be in the lsu array, or may be guaranteed by */\r
+ /* some other mechanism (such as being contained in another */\r
+ /* structure, or being overlaid on dynamically allocated */\r
+ /* storage). */\r
+ /* */\r
+ /* Each integer of the coefficient (except potentially the last) */\r
+ /* contains DECDPUN digits (e.g., a value in the range 0 through */\r
+ /* 99999999 if DECDPUN is 8, or 0 through 999 if DECDPUN is 3). */\r
+ /* */\r
+ /* 2. A decNumber converted to a string may need up to digits+14 */\r
+ /* characters. The worst cases (non-exponential and exponential */\r
+ /* formats) are -0.00000{9...}# and -9.{9...}E+999999999# */\r
+ /* (where # is '\0') */\r
+\r
+\r
+ /* ---------------------------------------------------------------- */\r
+ /* decNumber public functions and macros */\r
+ /* ---------------------------------------------------------------- */\r
+ /* Conversions */\r
+ decNumber * decNumberFromInt32(decNumber *, int32_t);\r
+ decNumber * decNumberFromUInt32(decNumber *, uint32_t);\r
+ decNumber * decNumberFromString(decNumber *, const char *, decContext *);\r
+ char * decNumberToString(const decNumber *, char *);\r
+ char * decNumberToEngString(const decNumber *, char *);\r
+ uint32_t decNumberToUInt32(const decNumber *, decContext *);\r
+ int32_t decNumberToInt32(const decNumber *, decContext *);\r
+ uint8_t * decNumberGetBCD(const decNumber *, uint8_t *);\r
+ decNumber * decNumberSetBCD(decNumber *, const uint8_t *, uint32_t);\r
+\r
+ /* Operators and elementary functions */\r
+ decNumber * decNumberAbs(decNumber *, const decNumber *, decContext *);\r
+ decNumber * decNumberAdd(decNumber *, const decNumber *, const decNumber *, decContext *);\r
+ decNumber * decNumberAnd(decNumber *, const decNumber *, const decNumber *, decContext *);\r
+ decNumber * decNumberCompare(decNumber *, const decNumber *, const decNumber *, decContext *);\r
+ decNumber * decNumberCompareSignal(decNumber *, const decNumber *, const decNumber *, decContext *);\r
+ decNumber * decNumberCompareTotal(decNumber *, const decNumber *, const decNumber *, decContext *);\r
+ decNumber * decNumberCompareTotalMag(decNumber *, const decNumber *, const decNumber *, decContext *);\r
+ decNumber * decNumberDivide(decNumber *, const decNumber *, const decNumber *, decContext *);\r
+ decNumber * decNumberDivideInteger(decNumber *, const decNumber *, const decNumber *, decContext *);\r
+ decNumber * decNumberExp(decNumber *, const decNumber *, decContext *);\r
+ decNumber * decNumberFMA(decNumber *, const decNumber *, const decNumber *, const decNumber *, decContext *);\r
+ decNumber * decNumberInvert(decNumber *, const decNumber *, decContext *);\r
+ decNumber * decNumberLn(decNumber *, const decNumber *, decContext *);\r
+ decNumber * decNumberLogB(decNumber *, const decNumber *, decContext *);\r
+ decNumber * decNumberLog10(decNumber *, const decNumber *, decContext *);\r
+ decNumber * decNumberMax(decNumber *, const decNumber *, const decNumber *, decContext *);\r
+ decNumber * decNumberMaxMag(decNumber *, const decNumber *, const decNumber *, decContext *);\r
+ decNumber * decNumberMin(decNumber *, const decNumber *, const decNumber *, decContext *);\r
+ decNumber * decNumberMinMag(decNumber *, const decNumber *, const decNumber *, decContext *);\r
+ decNumber * decNumberMinus(decNumber *, const decNumber *, decContext *);\r
+ decNumber * decNumberMultiply(decNumber *, const decNumber *, const decNumber *, decContext *);\r
+ decNumber * decNumberNormalize(decNumber *, const decNumber *, decContext *);\r
+ decNumber * decNumberOr(decNumber *, const decNumber *, const decNumber *, decContext *);\r
+ decNumber * decNumberPlus(decNumber *, const decNumber *, decContext *);\r
+ decNumber * decNumberPower(decNumber *, const decNumber *, const decNumber *, decContext *);\r
+ decNumber * decNumberQuantize(decNumber *, const decNumber *, const decNumber *, decContext *);\r
+ decNumber * decNumberReduce(decNumber *, const decNumber *, decContext *);\r
+ decNumber * decNumberRemainder(decNumber *, const decNumber *, const decNumber *, decContext *);\r
+ decNumber * decNumberRemainderNear(decNumber *, const decNumber *, const decNumber *, decContext *);\r
+ decNumber * decNumberRescale(decNumber *, const decNumber *, const decNumber *, decContext *);\r
+ decNumber * decNumberRotate(decNumber *, const decNumber *, const decNumber *, decContext *);\r
+ decNumber * decNumberSameQuantum(decNumber *, const decNumber *, const decNumber *);\r
+ decNumber * decNumberScaleB(decNumber *, const decNumber *, const decNumber *, decContext *);\r
+ decNumber * decNumberShift(decNumber *, const decNumber *, const decNumber *, decContext *);\r
+ decNumber * decNumberSquareRoot(decNumber *, const decNumber *, decContext *);\r
+ decNumber * decNumberSubtract(decNumber *, const decNumber *, const decNumber *, decContext *);\r
+ decNumber * decNumberToIntegralExact(decNumber *, const decNumber *, decContext *);\r
+ decNumber * decNumberToIntegralValue(decNumber *, const decNumber *, decContext *);\r
+ decNumber * decNumberXor(decNumber *, const decNumber *, const decNumber *, decContext *);\r
+\r
+ /* Utilities */\r
+ enum decClass decNumberClass(const decNumber *, decContext *);\r
+ const char * decNumberClassToString(enum decClass);\r
+ decNumber * decNumberCopy(decNumber *, const decNumber *);\r
+ decNumber * decNumberCopyAbs(decNumber *, const decNumber *);\r
+ decNumber * decNumberCopyNegate(decNumber *, const decNumber *);\r
+ decNumber * decNumberCopySign(decNumber *, const decNumber *, const decNumber *);\r
+ decNumber * decNumberNextMinus(decNumber *, const decNumber *, decContext *);\r
+ decNumber * decNumberNextPlus(decNumber *, const decNumber *, decContext *);\r
+ decNumber * decNumberNextToward(decNumber *, const decNumber *, const decNumber *, decContext *);\r
+ decNumber * decNumberTrim(decNumber *);\r
+ const char * decNumberVersion(void);\r
+ decNumber * decNumberZero(decNumber *);\r
+\r
+ /* Functions for testing decNumbers (normality depends on context) */\r
+ int32_t decNumberIsNormal(const decNumber *, decContext *);\r
+ int32_t decNumberIsSubnormal(const decNumber *, decContext *);\r
+\r
+ /* Macros for testing decNumber *dn */\r
+ #define decNumberIsCanonical(dn) (1) /* All decNumbers are saintly */\r
+ #define decNumberIsFinite(dn) (((dn)->bits&DECSPECIAL)==0)\r
+ #define decNumberIsInfinite(dn) (((dn)->bits&DECINF)!=0)\r
+ #define decNumberIsNaN(dn) (((dn)->bits&(DECNAN|DECSNAN))!=0)\r
+ #define decNumberIsNegative(dn) (((dn)->bits&DECNEG)!=0)\r
+ #define decNumberIsQNaN(dn) (((dn)->bits&(DECNAN))!=0)\r
+ #define decNumberIsSNaN(dn) (((dn)->bits&(DECSNAN))!=0)\r
+ #define decNumberIsSpecial(dn) (((dn)->bits&DECSPECIAL)!=0)\r
+ #define decNumberIsZero(dn) (*(dn)->lsu==0 \\r
+ && (dn)->digits==1 \\r
+ && (((dn)->bits&DECSPECIAL)==0))\r
+ #define decNumberRadix(dn) (10)\r
+\r
+#endif\r
--- /dev/null
+/* ------------------------------------------------------------------ */\r
+/* decNumber package local type, tuning, and macro definitions */\r
+/* ------------------------------------------------------------------ */\r
+/* Copyright (c) IBM Corporation, 2000, 2010. All rights reserved. */\r
+/* */\r
+/* This software is made available under the terms of the */\r
+/* ICU License -- ICU 1.8.1 and later. */\r
+/* */\r
+/* The description and User's Guide ("The decNumber C Library") for */\r
+/* this software is called decNumber.pdf. This document is */\r
+/* available, together with arithmetic and format specifications, */\r
+/* testcases, and Web links, on the General Decimal Arithmetic page. */\r
+/* */\r
+/* Please send comments, suggestions, and corrections to the author: */\r
+/* mfc@uk.ibm.com */\r
+/* Mike Cowlishaw, IBM Fellow */\r
+/* IBM UK, PO Box 31, Birmingham Road, Warwick CV34 5JL, UK */\r
+/* ------------------------------------------------------------------ */\r
+/* This header file is included by all modules in the decNumber */\r
+/* library, and contains local type definitions, tuning parameters, */\r
+/* etc. It should not need to be used by application programs. */\r
+/* decNumber.h or one of decDouble (etc.) must be included first. */\r
+/* ------------------------------------------------------------------ */\r
+\r
+#if !defined(DECNUMBERLOC)\r
+ #define DECNUMBERLOC\r
+ #define DECVERSION "decNumber 3.68" /* Package Version [16 max.] */\r
+ #define DECNLAUTHOR "Mike Cowlishaw" /* Who to blame */\r
+\r
+ #include <stdlib.h> /* for abs */\r
+ #include <string.h> /* for memset, strcpy */\r
+\r
+ /* Conditional code flag -- set this to match hardware platform */\r
+ #if !defined(DECLITEND)\r
+ #define DECLITEND 1 /* 1=little-endian, 0=big-endian */\r
+ #endif\r
+\r
+ /* Conditional code flag -- set this to 1 for best performance */\r
+ #if !defined(DECUSE64)\r
+ #define DECUSE64 1 /* 1=use int64s, 0=int32 & smaller only */\r
+ #endif\r
+\r
+ /* Conditional code flag -- set this to 0 to exclude printf calls */\r
+ #if !defined(DECPRINT)\r
+ #define DECPRINT 1 /* 1=allow printf calls; 0=no printf */\r
+ #endif\r
+\r
+ /* Conditional check flags -- set these to 0 for best performance */\r
+ #if !defined(DECCHECK)\r
+ #define DECCHECK 0 /* 1 to enable robust checking */\r
+ #endif\r
+ #if !defined(DECALLOC)\r
+ #define DECALLOC 0 /* 1 to enable memory accounting */\r
+ #endif\r
+ #if !defined(DECTRACE)\r
+ #define DECTRACE 0 /* 1 to trace certain internals, etc. */\r
+ #endif\r
+\r
+ /* Tuning parameter for decNumber (arbitrary precision) module */\r
+ #if !defined(DECBUFFER)\r
+ #define DECBUFFER 36 /* Size basis for local buffers. This */\r
+ /* should be a common maximum precision */\r
+ /* rounded up to a multiple of 4; must */\r
+ /* be zero or positive. */\r
+ #endif\r
+\r
+\r
+ /* ---------------------------------------------------------------- */\r
+ /* Check parameter dependencies */\r
+ /* ---------------------------------------------------------------- */\r
+ #if DECCHECK & !DECPRINT\r
+ #error DECCHECK needs DECPRINT to be useful\r
+ #endif\r
+ #if DECALLOC & !DECPRINT\r
+ #error DECALLOC needs DECPRINT to be useful\r
+ #endif\r
+ #if DECTRACE & !DECPRINT\r
+ #error DECTRACE needs DECPRINT to be useful\r
+ #endif\r
+\r
+ /* ---------------------------------------------------------------- */\r
+ /* Definitions for all modules (general-purpose) */\r
+ /* ---------------------------------------------------------------- */\r
+\r
+ /* Local names for common types -- for safety, decNumber modules do */\r
+ /* not use int or long directly. */\r
+ #define Flag uint8_t\r
+ #define Byte int8_t\r
+ #define uByte uint8_t\r
+ #define Short int16_t\r
+ #define uShort uint16_t\r
+ #define Int int32_t\r
+ #define uInt uint32_t\r
+ #define Unit decNumberUnit\r
+ #if DECUSE64\r
+ #define Long int64_t\r
+ #define uLong uint64_t\r
+ #endif\r
+\r
+ /* Development-use definitions */\r
+ typedef long int LI; /* for printf arguments only */\r
+ #define DECNOINT 0 /* 1 to check no internal use of 'int' */\r
+ /* or stdint types */\r
+ #if DECNOINT\r
+ /* if these interfere with your C includes, do not set DECNOINT */\r
+ #define int ? /* enable to ensure that plain C 'int' */\r
+ #define long ?? /* .. or 'long' types are not used */\r
+ #endif\r
+\r
+ /* Shared lookup tables */\r
+ extern const uByte DECSTICKYTAB[10]; /* re-round digits if sticky */\r
+ extern const uInt DECPOWERS[10]; /* powers of ten table */\r
+ /* The following are included from decDPD.h */\r
+ extern const uShort DPD2BIN[1024]; /* DPD -> 0-999 */\r
+ extern const uShort BIN2DPD[1000]; /* 0-999 -> DPD */\r
+ extern const uInt DPD2BINK[1024]; /* DPD -> 0-999000 */\r
+ extern const uInt DPD2BINM[1024]; /* DPD -> 0-999000000 */\r
+ extern const uByte DPD2BCD8[4096]; /* DPD -> ddd + len */\r
+ extern const uByte BIN2BCD8[4000]; /* 0-999 -> ddd + len */\r
+ extern const uShort BCD2DPD[2458]; /* 0-0x999 -> DPD (0x999=2457)*/\r
+\r
+ /* LONGMUL32HI -- set w=(u*v)>>32, where w, u, and v are uInts */\r
+ /* (that is, sets w to be the high-order word of the 64-bit result; */\r
+ /* the low-order word is simply u*v.) */\r
+ /* This version is derived from Knuth via Hacker's Delight; */\r
+ /* it seems to optimize better than some others tried */\r
+ #define LONGMUL32HI(w, u, v) { \\r
+ uInt u0, u1, v0, v1, w0, w1, w2, t; \\r
+ u0=u & 0xffff; u1=u>>16; \\r
+ v0=v & 0xffff; v1=v>>16; \\r
+ w0=u0*v0; \\r
+ t=u1*v0 + (w0>>16); \\r
+ w1=t & 0xffff; w2=t>>16; \\r
+ w1=u0*v1 + w1; \\r
+ (w)=u1*v1 + w2 + (w1>>16);}\r
+\r
+ /* ROUNDUP -- round an integer up to a multiple of n */\r
+ #define ROUNDUP(i, n) ((((i)+(n)-1)/n)*n)\r
+ #define ROUNDUP4(i) (((i)+3)&~3) /* special for n=4 */\r
+\r
+ /* ROUNDDOWN -- round an integer down to a multiple of n */\r
+ #define ROUNDDOWN(i, n) (((i)/n)*n)\r
+ #define ROUNDDOWN4(i) ((i)&~3) /* special for n=4 */\r
+\r
+ /* References to multi-byte sequences under different sizes; these */\r
+ /* require locally declared variables, but do not violate strict */\r
+ /* aliasing or alignment (as did the UINTAT simple cast to uInt). */\r
+ /* Variables needed are uswork, uiwork, etc. [so do not use at same */\r
+ /* level in an expression, e.g., UBTOUI(x)==UBTOUI(y) may fail]. */\r
+\r
+ /* Return a uInt, etc., from bytes starting at a char* or uByte* */\r
+ #define UBTOUS(b) (memcpy((void *)&uswork, b, 2), uswork)\r
+ #define UBTOUI(b) (memcpy((void *)&uiwork, b, 4), uiwork)\r
+\r
+ /* Store a uInt, etc., into bytes starting at a char* or uByte*. */\r
+ /* Returns i, evaluated, for convenience; has to use uiwork because */\r
+ /* i may be an expression. */\r
+ #define UBFROMUS(b, i) (uswork=(i), memcpy(b, (void *)&uswork, 2), uswork)\r
+ #define UBFROMUI(b, i) (uiwork=(i), memcpy(b, (void *)&uiwork, 4), uiwork)\r
+\r
+ /* X10 and X100 -- multiply integer i by 10 or 100 */\r
+ /* [shifts are usually faster than multiply; could be conditional] */\r
+ #define X10(i) (((i)<<1)+((i)<<3))\r
+ #define X100(i) (((i)<<2)+((i)<<5)+((i)<<6))\r
+\r
+ /* MAXI and MINI -- general max & min (not in ANSI) for integers */\r
+ #define MAXI(x,y) ((x)<(y)?(y):(x))\r
+ #define MINI(x,y) ((x)>(y)?(y):(x))\r
+\r
+ /* Useful constants */\r
+ #define BILLION 1000000000 /* 10**9 */\r
+ /* CHARMASK: 0x30303030 for ASCII/UTF8; 0xF0F0F0F0 for EBCDIC */\r
+ #define CHARMASK ((((((((uInt)'0')<<8)+'0')<<8)+'0')<<8)+'0')\r
+\r
+\r
+ /* ---------------------------------------------------------------- */\r
+ /* Definitions for arbitary-precision modules (only valid after */\r
+ /* decNumber.h has been included) */\r
+ /* ---------------------------------------------------------------- */\r
+\r
+ /* Limits and constants */\r
+ #define DECNUMMAXP 999999999 /* maximum precision code can handle */\r
+ #define DECNUMMAXE 999999999 /* maximum adjusted exponent ditto */\r
+ #define DECNUMMINE -999999999 /* minimum adjusted exponent ditto */\r
+ #if (DECNUMMAXP != DEC_MAX_DIGITS)\r
+ #error Maximum digits mismatch\r
+ #endif\r
+ #if (DECNUMMAXE != DEC_MAX_EMAX)\r
+ #error Maximum exponent mismatch\r
+ #endif\r
+ #if (DECNUMMINE != DEC_MIN_EMIN)\r
+ #error Minimum exponent mismatch\r
+ #endif\r
+\r
+ /* Set DECDPUNMAX -- the maximum integer that fits in DECDPUN */\r
+ /* digits, and D2UTABLE -- the initializer for the D2U table */\r
+ #if DECDPUN==1\r
+ #define DECDPUNMAX 9\r
+ #define D2UTABLE {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17, \\r
+ 18,19,20,21,22,23,24,25,26,27,28,29,30,31,32, \\r
+ 33,34,35,36,37,38,39,40,41,42,43,44,45,46,47, \\r
+ 48,49}\r
+ #elif DECDPUN==2\r
+ #define DECDPUNMAX 99\r
+ #define D2UTABLE {0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10, \\r
+ 11,11,12,12,13,13,14,14,15,15,16,16,17,17,18, \\r
+ 18,19,19,20,20,21,21,22,22,23,23,24,24,25}\r
+ #elif DECDPUN==3\r
+ #define DECDPUNMAX 999\r
+ #define D2UTABLE {0,1,1,1,2,2,2,3,3,3,4,4,4,5,5,5,6,6,6,7,7,7, \\r
+ 8,8,8,9,9,9,10,10,10,11,11,11,12,12,12,13,13, \\r
+ 13,14,14,14,15,15,15,16,16,16,17}\r
+ #elif DECDPUN==4\r
+ #define DECDPUNMAX 9999\r
+ #define D2UTABLE {0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,6, \\r
+ 6,6,6,7,7,7,7,8,8,8,8,9,9,9,9,10,10,10,10,11, \\r
+ 11,11,11,12,12,12,12,13}\r
+ #elif DECDPUN==5\r
+ #define DECDPUNMAX 99999\r
+ #define D2UTABLE {0,1,1,1,1,1,2,2,2,2,2,3,3,3,3,3,4,4,4,4,4,5, \\r
+ 5,5,5,5,6,6,6,6,6,7,7,7,7,7,8,8,8,8,8,9,9,9, \\r
+ 9,9,10,10,10,10}\r
+ #elif DECDPUN==6\r
+ #define DECDPUNMAX 999999\r
+ #define D2UTABLE {0,1,1,1,1,1,1,2,2,2,2,2,2,3,3,3,3,3,3,4,4,4, \\r
+ 4,4,4,5,5,5,5,5,5,6,6,6,6,6,6,7,7,7,7,7,7,8, \\r
+ 8,8,8,8,8,9}\r
+ #elif DECDPUN==7\r
+ #define DECDPUNMAX 9999999\r
+ #define D2UTABLE {0,1,1,1,1,1,1,1,2,2,2,2,2,2,2,3,3,3,3,3,3,3, \\r
+ 4,4,4,4,4,4,4,5,5,5,5,5,5,5,6,6,6,6,6,6,6,7, \\r
+ 7,7,7,7,7,7}\r
+ #elif DECDPUN==8\r
+ #define DECDPUNMAX 99999999\r
+ #define D2UTABLE {0,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,3,3,3,3,3, \\r
+ 3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,6,6,6, \\r
+ 6,6,6,6,6,7}\r
+ #elif DECDPUN==9\r
+ #define DECDPUNMAX 999999999\r
+ #define D2UTABLE {0,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,3,3,3, \\r
+ 3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5, \\r
+ 5,5,6,6,6,6}\r
+ #elif defined(DECDPUN)\r
+ #error DECDPUN must be in the range 1-9\r
+ #endif\r
+\r
+ /* ----- Shared data (in decNumber.c) ----- */\r
+ /* Public lookup table used by the D2U macro (see below) */\r
+ #define DECMAXD2U 49\r
+ extern const uByte d2utable[DECMAXD2U+1];\r
+\r
+ /* ----- Macros ----- */\r
+ /* ISZERO -- return true if decNumber dn is a zero */\r
+ /* [performance-critical in some situations] */\r
+ #define ISZERO(dn) decNumberIsZero(dn) /* now just a local name */\r
+\r
+ /* D2U -- return the number of Units needed to hold d digits */\r
+ /* (runtime version, with table lookaside for small d) */\r
+ #if DECDPUN==8\r
+ #define D2U(d) ((unsigned)((d)<=DECMAXD2U?d2utable[d]:((d)+7)>>3))\r
+ #elif DECDPUN==4\r
+ #define D2U(d) ((unsigned)((d)<=DECMAXD2U?d2utable[d]:((d)+3)>>2))\r
+ #else\r
+ #define D2U(d) ((d)<=DECMAXD2U?d2utable[d]:((d)+DECDPUN-1)/DECDPUN)\r
+ #endif\r
+ /* SD2U -- static D2U macro (for compile-time calculation) */\r
+ #define SD2U(d) (((d)+DECDPUN-1)/DECDPUN)\r
+\r
+ /* MSUDIGITS -- returns digits in msu, from digits, calculated */\r
+ /* using D2U */\r
+ #define MSUDIGITS(d) ((d)-(D2U(d)-1)*DECDPUN)\r
+\r
+ /* D2N -- return the number of decNumber structs that would be */\r
+ /* needed to contain that number of digits (and the initial */\r
+ /* decNumber struct) safely. Note that one Unit is included in the */\r
+ /* initial structure. Used for allocating space that is aligned on */\r
+ /* a decNumber struct boundary. */\r
+ #define D2N(d) \\r
+ ((((SD2U(d)-1)*sizeof(Unit))+sizeof(decNumber)*2-1)/sizeof(decNumber))\r
+\r
+ /* TODIGIT -- macro to remove the leading digit from the unsigned */\r
+ /* integer u at column cut (counting from the right, LSD=0) and */\r
+ /* place it as an ASCII character into the character pointed to by */\r
+ /* c. Note that cut must be <= 9, and the maximum value for u is */\r
+ /* 2,000,000,000 (as is needed for negative exponents of */\r
+ /* subnormals). The unsigned integer pow is used as a temporary */\r
+ /* variable. */\r
+ #define TODIGIT(u, cut, c, pow) { \\r
+ *(c)='0'; \\r
+ pow=DECPOWERS[cut]*2; \\r
+ if ((u)>pow) { \\r
+ pow*=4; \\r
+ if ((u)>=pow) {(u)-=pow; *(c)+=8;} \\r
+ pow/=2; \\r
+ if ((u)>=pow) {(u)-=pow; *(c)+=4;} \\r
+ pow/=2; \\r
+ } \\r
+ if ((u)>=pow) {(u)-=pow; *(c)+=2;} \\r
+ pow/=2; \\r
+ if ((u)>=pow) {(u)-=pow; *(c)+=1;} \\r
+ }\r
+\r
+ /* ---------------------------------------------------------------- */\r
+ /* Definitions for fixed-precision modules (only valid after */\r
+ /* decSingle.h, decDouble.h, or decQuad.h has been included) */\r
+ /* ---------------------------------------------------------------- */\r
+\r
+ /* bcdnum -- a structure describing a format-independent finite */\r
+ /* number, whose coefficient is a string of bcd8 uBytes */\r
+ typedef struct {\r
+ uByte *msd; /* -> most significant digit */\r
+ uByte *lsd; /* -> least ditto */\r
+ uInt sign; /* 0=positive, DECFLOAT_Sign=negative */\r
+ Int exponent; /* Unadjusted signed exponent (q), or */\r
+ /* DECFLOAT_NaN etc. for a special */\r
+ } bcdnum;\r
+\r
+ /* Test if exponent or bcdnum exponent must be a special, etc. */\r
+ #define EXPISSPECIAL(exp) ((exp)>=DECFLOAT_MinSp)\r
+ #define EXPISINF(exp) (exp==DECFLOAT_Inf)\r
+ #define EXPISNAN(exp) (exp==DECFLOAT_qNaN || exp==DECFLOAT_sNaN)\r
+ #define NUMISSPECIAL(num) (EXPISSPECIAL((num)->exponent))\r
+\r
+ /* Refer to a 32-bit word or byte in a decFloat (df) by big-endian */\r
+ /* (array) notation (the 0 word or byte contains the sign bit), */\r
+ /* automatically adjusting for endianness; similarly address a word */\r
+ /* in the next-wider format (decFloatWider, or dfw) */\r
+ #define DECWORDS (DECBYTES/4)\r
+ #define DECWWORDS (DECWBYTES/4)\r
+ #if DECLITEND\r
+ #define DFBYTE(df, off) ((df)->bytes[DECBYTES-1-(off)])\r
+ #define DFWORD(df, off) ((df)->words[DECWORDS-1-(off)])\r
+ #define DFWWORD(dfw, off) ((dfw)->words[DECWWORDS-1-(off)])\r
+ #else\r
+ #define DFBYTE(df, off) ((df)->bytes[off])\r
+ #define DFWORD(df, off) ((df)->words[off])\r
+ #define DFWWORD(dfw, off) ((dfw)->words[off])\r
+ #endif\r
+\r
+ /* Tests for sign or specials, directly on DECFLOATs */\r
+ #define DFISSIGNED(df) ((DFWORD(df, 0)&0x80000000)!=0)\r
+ #define DFISSPECIAL(df) ((DFWORD(df, 0)&0x78000000)==0x78000000)\r
+ #define DFISINF(df) ((DFWORD(df, 0)&0x7c000000)==0x78000000)\r
+ #define DFISNAN(df) ((DFWORD(df, 0)&0x7c000000)==0x7c000000)\r
+ #define DFISQNAN(df) ((DFWORD(df, 0)&0x7e000000)==0x7c000000)\r
+ #define DFISSNAN(df) ((DFWORD(df, 0)&0x7e000000)==0x7e000000)\r
+\r
+ /* Shared lookup tables */\r
+ extern const uInt DECCOMBMSD[64]; /* Combination field -> MSD */\r
+ extern const uInt DECCOMBFROM[48]; /* exp+msd -> Combination */\r
+\r
+ /* Private generic (utility) routine */\r
+ #if DECCHECK || DECTRACE\r
+ extern void decShowNum(const bcdnum *, const char *);\r
+ #endif\r
+\r
+ /* Format-dependent macros and constants */\r
+ #if defined(DECPMAX)\r
+\r
+ /* Useful constants */\r
+ #define DECPMAX9 (ROUNDUP(DECPMAX, 9)/9) /* 'Pmax' in 10**9s */\r
+ /* Top words for a zero */\r
+ #define SINGLEZERO 0x22500000\r
+ #define DOUBLEZERO 0x22380000\r
+ #define QUADZERO 0x22080000\r
+ /* [ZEROWORD is defined to be one of these in the DFISZERO macro] */\r
+\r
+ /* Format-dependent common tests: */\r
+ /* DFISZERO -- test for (any) zero */\r
+ /* DFISCCZERO -- test for coefficient continuation being zero */\r
+ /* DFISCC01 -- test for coefficient contains only 0s and 1s */\r
+ /* DFISINT -- test for finite and exponent q=0 */\r
+ /* DFISUINT01 -- test for sign=0, finite, exponent q=0, and */\r
+ /* MSD=0 or 1 */\r
+ /* ZEROWORD is also defined here. */\r
+ /* */\r
+ /* In DFISZERO the first test checks the least-significant word */\r
+ /* (most likely to be non-zero); the penultimate tests MSD and */\r
+ /* DPDs in the signword, and the final test excludes specials and */\r
+ /* MSD>7. DFISINT similarly has to allow for the two forms of */\r
+ /* MSD codes. DFISUINT01 only has to allow for one form of MSD */\r
+ /* code. */\r
+ #if DECPMAX==7\r
+ #define ZEROWORD SINGLEZERO\r
+ /* [test macros not needed except for Zero] */\r
+ #define DFISZERO(df) ((DFWORD(df, 0)&0x1c0fffff)==0 \\r
+ && (DFWORD(df, 0)&0x60000000)!=0x60000000)\r
+ #elif DECPMAX==16\r
+ #define ZEROWORD DOUBLEZERO\r
+ #define DFISZERO(df) ((DFWORD(df, 1)==0 \\r
+ && (DFWORD(df, 0)&0x1c03ffff)==0 \\r
+ && (DFWORD(df, 0)&0x60000000)!=0x60000000))\r
+ #define DFISINT(df) ((DFWORD(df, 0)&0x63fc0000)==0x22380000 \\r
+ ||(DFWORD(df, 0)&0x7bfc0000)==0x6a380000)\r
+ #define DFISUINT01(df) ((DFWORD(df, 0)&0xfbfc0000)==0x22380000)\r
+ #define DFISCCZERO(df) (DFWORD(df, 1)==0 \\r
+ && (DFWORD(df, 0)&0x0003ffff)==0)\r
+ #define DFISCC01(df) ((DFWORD(df, 0)&~0xfffc9124)==0 \\r
+ && (DFWORD(df, 1)&~0x49124491)==0)\r
+ #elif DECPMAX==34\r
+ #define ZEROWORD QUADZERO\r
+ #define DFISZERO(df) ((DFWORD(df, 3)==0 \\r
+ && DFWORD(df, 2)==0 \\r
+ && DFWORD(df, 1)==0 \\r
+ && (DFWORD(df, 0)&0x1c003fff)==0 \\r
+ && (DFWORD(df, 0)&0x60000000)!=0x60000000))\r
+ #define DFISINT(df) ((DFWORD(df, 0)&0x63ffc000)==0x22080000 \\r
+ ||(DFWORD(df, 0)&0x7bffc000)==0x6a080000)\r
+ #define DFISUINT01(df) ((DFWORD(df, 0)&0xfbffc000)==0x22080000)\r
+ #define DFISCCZERO(df) (DFWORD(df, 3)==0 \\r
+ && DFWORD(df, 2)==0 \\r
+ && DFWORD(df, 1)==0 \\r
+ && (DFWORD(df, 0)&0x00003fff)==0)\r
+\r
+ #define DFISCC01(df) ((DFWORD(df, 0)&~0xffffc912)==0 \\r
+ && (DFWORD(df, 1)&~0x44912449)==0 \\r
+ && (DFWORD(df, 2)&~0x12449124)==0 \\r
+ && (DFWORD(df, 3)&~0x49124491)==0)\r
+ #endif\r
+\r
+ /* Macros to test if a certain 10 bits of a uInt or pair of uInts */\r
+ /* are a canonical declet [higher or lower bits are ignored]. */\r
+ /* declet is at offset 0 (from the right) in a uInt: */\r
+ #define CANONDPD(dpd) (((dpd)&0x300)==0 || ((dpd)&0x6e)!=0x6e)\r
+ /* declet is at offset k (a multiple of 2) in a uInt: */\r
+ #define CANONDPDOFF(dpd, k) (((dpd)&(0x300<<(k)))==0 \\r
+ || ((dpd)&(((uInt)0x6e)<<(k)))!=(((uInt)0x6e)<<(k)))\r
+ /* declet is at offset k (a multiple of 2) in a pair of uInts: */\r
+ /* [the top 2 bits will always be in the more-significant uInt] */\r
+ #define CANONDPDTWO(hi, lo, k) (((hi)&(0x300>>(32-(k))))==0 \\r
+ || ((hi)&(0x6e>>(32-(k))))!=(0x6e>>(32-(k))) \\r
+ || ((lo)&(((uInt)0x6e)<<(k)))!=(((uInt)0x6e)<<(k)))\r
+\r
+ /* Macro to test whether a full-length (length DECPMAX) BCD8 */\r
+ /* coefficient, starting at uByte u, is all zeros */\r
+ /* Test just the LSWord first, then the remainder as a sequence */\r
+ /* of tests in order to avoid same-level use of UBTOUI */\r
+ #if DECPMAX==7\r
+ #define ISCOEFFZERO(u) ( \\r
+ UBTOUI((u)+DECPMAX-4)==0 \\r
+ && UBTOUS((u)+DECPMAX-6)==0 \\r
+ && *(u)==0)\r
+ #elif DECPMAX==16\r
+ #define ISCOEFFZERO(u) ( \\r
+ UBTOUI((u)+DECPMAX-4)==0 \\r
+ && UBTOUI((u)+DECPMAX-8)==0 \\r
+ && UBTOUI((u)+DECPMAX-12)==0 \\r
+ && UBTOUI(u)==0)\r
+ #elif DECPMAX==34\r
+ #define ISCOEFFZERO(u) ( \\r
+ UBTOUI((u)+DECPMAX-4)==0 \\r
+ && UBTOUI((u)+DECPMAX-8)==0 \\r
+ && UBTOUI((u)+DECPMAX-12)==0 \\r
+ && UBTOUI((u)+DECPMAX-16)==0 \\r
+ && UBTOUI((u)+DECPMAX-20)==0 \\r
+ && UBTOUI((u)+DECPMAX-24)==0 \\r
+ && UBTOUI((u)+DECPMAX-28)==0 \\r
+ && UBTOUI((u)+DECPMAX-32)==0 \\r
+ && UBTOUS(u)==0)\r
+ #endif\r
+\r
+ /* Macros and masks for the sign, exponent continuation, and MSD */\r
+ /* Get the sign as DECFLOAT_Sign or 0 */\r
+ #define GETSIGN(df) (DFWORD(df, 0)&0x80000000)\r
+ /* Get the exponent continuation from a decFloat *df as an Int */\r
+ #define GETECON(df) ((Int)((DFWORD((df), 0)&0x03ffffff)>>(32-6-DECECONL)))\r
+ /* Ditto, from the next-wider format */\r
+ #define GETWECON(df) ((Int)((DFWWORD((df), 0)&0x03ffffff)>>(32-6-DECWECONL)))\r
+ /* Get the biased exponent similarly */\r
+ #define GETEXP(df) ((Int)(DECCOMBEXP[DFWORD((df), 0)>>26]+GETECON(df)))\r
+ /* Get the unbiased exponent similarly */\r
+ #define GETEXPUN(df) ((Int)GETEXP(df)-DECBIAS)\r
+ /* Get the MSD similarly (as uInt) */\r
+ #define GETMSD(df) (DECCOMBMSD[DFWORD((df), 0)>>26])\r
+\r
+ /* Compile-time computes of the exponent continuation field masks */\r
+ /* full exponent continuation field: */\r
+ #define ECONMASK ((0x03ffffff>>(32-6-DECECONL))<<(32-6-DECECONL))\r
+ /* same, not including its first digit (the qNaN/sNaN selector): */\r
+ #define ECONNANMASK ((0x01ffffff>>(32-6-DECECONL))<<(32-6-DECECONL))\r
+\r
+ /* Macros to decode the coefficient in a finite decFloat *df into */\r
+ /* a BCD string (uByte *bcdin) of length DECPMAX uBytes. */\r
+\r
+ /* In-line sequence to convert least significant 10 bits of uInt */\r
+ /* dpd to three BCD8 digits starting at uByte u. Note that an */\r
+ /* extra byte is written to the right of the three digits because */\r
+ /* four bytes are moved at a time for speed; the alternative */\r
+ /* macro moves exactly three bytes (usually slower). */\r
+ #define dpd2bcd8(u, dpd) memcpy(u, &DPD2BCD8[((dpd)&0x3ff)*4], 4)\r
+ #define dpd2bcd83(u, dpd) memcpy(u, &DPD2BCD8[((dpd)&0x3ff)*4], 3)\r
+\r
+ /* Decode the declets. After extracting each one, it is decoded */\r
+ /* to BCD8 using a table lookup (also used for variable-length */\r
+ /* decode). Each DPD decode is 3 bytes BCD8 plus a one-byte */\r
+ /* length which is not used, here). Fixed-length 4-byte moves */\r
+ /* are fast, however, almost everywhere, and so are used except */\r
+ /* for the final three bytes (to avoid overrun). The code below */\r
+ /* is 36 instructions for Doubles and about 70 for Quads, even */\r
+ /* on IA32. */\r
+\r
+ /* Two macros are defined for each format: */\r
+ /* GETCOEFF extracts the coefficient of the current format */\r
+ /* GETWCOEFF extracts the coefficient of the next-wider format. */\r
+ /* The latter is a copy of the next-wider GETCOEFF using DFWWORD. */\r
+\r
+ #if DECPMAX==7\r
+ #define GETCOEFF(df, bcd) { \\r
+ uInt sourhi=DFWORD(df, 0); \\r
+ *(bcd)=(uByte)DECCOMBMSD[sourhi>>26]; \\r
+ dpd2bcd8(bcd+1, sourhi>>10); \\r
+ dpd2bcd83(bcd+4, sourhi);}\r
+ #define GETWCOEFF(df, bcd) { \\r
+ uInt sourhi=DFWWORD(df, 0); \\r
+ uInt sourlo=DFWWORD(df, 1); \\r
+ *(bcd)=(uByte)DECCOMBMSD[sourhi>>26]; \\r
+ dpd2bcd8(bcd+1, sourhi>>8); \\r
+ dpd2bcd8(bcd+4, (sourhi<<2) | (sourlo>>30)); \\r
+ dpd2bcd8(bcd+7, sourlo>>20); \\r
+ dpd2bcd8(bcd+10, sourlo>>10); \\r
+ dpd2bcd83(bcd+13, sourlo);}\r
+\r
+ #elif DECPMAX==16\r
+ #define GETCOEFF(df, bcd) { \\r
+ uInt sourhi=DFWORD(df, 0); \\r
+ uInt sourlo=DFWORD(df, 1); \\r
+ *(bcd)=(uByte)DECCOMBMSD[sourhi>>26]; \\r
+ dpd2bcd8(bcd+1, sourhi>>8); \\r
+ dpd2bcd8(bcd+4, (sourhi<<2) | (sourlo>>30)); \\r
+ dpd2bcd8(bcd+7, sourlo>>20); \\r
+ dpd2bcd8(bcd+10, sourlo>>10); \\r
+ dpd2bcd83(bcd+13, sourlo);}\r
+ #define GETWCOEFF(df, bcd) { \\r
+ uInt sourhi=DFWWORD(df, 0); \\r
+ uInt sourmh=DFWWORD(df, 1); \\r
+ uInt sourml=DFWWORD(df, 2); \\r
+ uInt sourlo=DFWWORD(df, 3); \\r
+ *(bcd)=(uByte)DECCOMBMSD[sourhi>>26]; \\r
+ dpd2bcd8(bcd+1, sourhi>>4); \\r
+ dpd2bcd8(bcd+4, ((sourhi)<<6) | (sourmh>>26)); \\r
+ dpd2bcd8(bcd+7, sourmh>>16); \\r
+ dpd2bcd8(bcd+10, sourmh>>6); \\r
+ dpd2bcd8(bcd+13, ((sourmh)<<4) | (sourml>>28)); \\r
+ dpd2bcd8(bcd+16, sourml>>18); \\r
+ dpd2bcd8(bcd+19, sourml>>8); \\r
+ dpd2bcd8(bcd+22, ((sourml)<<2) | (sourlo>>30)); \\r
+ dpd2bcd8(bcd+25, sourlo>>20); \\r
+ dpd2bcd8(bcd+28, sourlo>>10); \\r
+ dpd2bcd83(bcd+31, sourlo);}\r
+\r
+ #elif DECPMAX==34\r
+ #define GETCOEFF(df, bcd) { \\r
+ uInt sourhi=DFWORD(df, 0); \\r
+ uInt sourmh=DFWORD(df, 1); \\r
+ uInt sourml=DFWORD(df, 2); \\r
+ uInt sourlo=DFWORD(df, 3); \\r
+ *(bcd)=(uByte)DECCOMBMSD[sourhi>>26]; \\r
+ dpd2bcd8(bcd+1, sourhi>>4); \\r
+ dpd2bcd8(bcd+4, ((sourhi)<<6) | (sourmh>>26)); \\r
+ dpd2bcd8(bcd+7, sourmh>>16); \\r
+ dpd2bcd8(bcd+10, sourmh>>6); \\r
+ dpd2bcd8(bcd+13, ((sourmh)<<4) | (sourml>>28)); \\r
+ dpd2bcd8(bcd+16, sourml>>18); \\r
+ dpd2bcd8(bcd+19, sourml>>8); \\r
+ dpd2bcd8(bcd+22, ((sourml)<<2) | (sourlo>>30)); \\r
+ dpd2bcd8(bcd+25, sourlo>>20); \\r
+ dpd2bcd8(bcd+28, sourlo>>10); \\r
+ dpd2bcd83(bcd+31, sourlo);}\r
+\r
+ #define GETWCOEFF(df, bcd) {??} /* [should never be used] */\r
+ #endif\r
+\r
+ /* Macros to decode the coefficient in a finite decFloat *df into */\r
+ /* a base-billion uInt array, with the least-significant */\r
+ /* 0-999999999 'digit' at offset 0. */\r
+\r
+ /* Decode the declets. After extracting each one, it is decoded */\r
+ /* to binary using a table lookup. Three tables are used; one */\r
+ /* the usual DPD to binary, the other two pre-multiplied by 1000 */\r
+ /* and 1000000 to avoid multiplication during decode. These */\r
+ /* tables can also be used for multiplying up the MSD as the DPD */\r
+ /* code for 0 through 9 is the identity. */\r
+ #define DPD2BIN0 DPD2BIN /* for prettier code */\r
+\r
+ #if DECPMAX==7\r
+ #define GETCOEFFBILL(df, buf) { \\r
+ uInt sourhi=DFWORD(df, 0); \\r
+ (buf)[0]=DPD2BIN0[sourhi&0x3ff] \\r
+ +DPD2BINK[(sourhi>>10)&0x3ff] \\r
+ +DPD2BINM[DECCOMBMSD[sourhi>>26]];}\r
+\r
+ #elif DECPMAX==16\r
+ #define GETCOEFFBILL(df, buf) { \\r
+ uInt sourhi, sourlo; \\r
+ sourlo=DFWORD(df, 1); \\r
+ (buf)[0]=DPD2BIN0[sourlo&0x3ff] \\r
+ +DPD2BINK[(sourlo>>10)&0x3ff] \\r
+ +DPD2BINM[(sourlo>>20)&0x3ff]; \\r
+ sourhi=DFWORD(df, 0); \\r
+ (buf)[1]=DPD2BIN0[((sourhi<<2) | (sourlo>>30))&0x3ff] \\r
+ +DPD2BINK[(sourhi>>8)&0x3ff] \\r
+ +DPD2BINM[DECCOMBMSD[sourhi>>26]];}\r
+\r
+ #elif DECPMAX==34\r
+ #define GETCOEFFBILL(df, buf) { \\r
+ uInt sourhi, sourmh, sourml, sourlo; \\r
+ sourlo=DFWORD(df, 3); \\r
+ (buf)[0]=DPD2BIN0[sourlo&0x3ff] \\r
+ +DPD2BINK[(sourlo>>10)&0x3ff] \\r
+ +DPD2BINM[(sourlo>>20)&0x3ff]; \\r
+ sourml=DFWORD(df, 2); \\r
+ (buf)[1]=DPD2BIN0[((sourml<<2) | (sourlo>>30))&0x3ff] \\r
+ +DPD2BINK[(sourml>>8)&0x3ff] \\r
+ +DPD2BINM[(sourml>>18)&0x3ff]; \\r
+ sourmh=DFWORD(df, 1); \\r
+ (buf)[2]=DPD2BIN0[((sourmh<<4) | (sourml>>28))&0x3ff] \\r
+ +DPD2BINK[(sourmh>>6)&0x3ff] \\r
+ +DPD2BINM[(sourmh>>16)&0x3ff]; \\r
+ sourhi=DFWORD(df, 0); \\r
+ (buf)[3]=DPD2BIN0[((sourhi<<6) | (sourmh>>26))&0x3ff] \\r
+ +DPD2BINK[(sourhi>>4)&0x3ff] \\r
+ +DPD2BINM[DECCOMBMSD[sourhi>>26]];}\r
+\r
+ #endif\r
+\r
+ /* Macros to decode the coefficient in a finite decFloat *df into */\r
+ /* a base-thousand uInt array (of size DECLETS+1, to allow for */\r
+ /* the MSD), with the least-significant 0-999 'digit' at offset 0.*/\r
+\r
+ /* Decode the declets. After extracting each one, it is decoded */\r
+ /* to binary using a table lookup. */\r
+ #if DECPMAX==7\r
+ #define GETCOEFFTHOU(df, buf) { \\r
+ uInt sourhi=DFWORD(df, 0); \\r
+ (buf)[0]=DPD2BIN[sourhi&0x3ff]; \\r
+ (buf)[1]=DPD2BIN[(sourhi>>10)&0x3ff]; \\r
+ (buf)[2]=DECCOMBMSD[sourhi>>26];}\r
+\r
+ #elif DECPMAX==16\r
+ #define GETCOEFFTHOU(df, buf) { \\r
+ uInt sourhi, sourlo; \\r
+ sourlo=DFWORD(df, 1); \\r
+ (buf)[0]=DPD2BIN[sourlo&0x3ff]; \\r
+ (buf)[1]=DPD2BIN[(sourlo>>10)&0x3ff]; \\r
+ (buf)[2]=DPD2BIN[(sourlo>>20)&0x3ff]; \\r
+ sourhi=DFWORD(df, 0); \\r
+ (buf)[3]=DPD2BIN[((sourhi<<2) | (sourlo>>30))&0x3ff]; \\r
+ (buf)[4]=DPD2BIN[(sourhi>>8)&0x3ff]; \\r
+ (buf)[5]=DECCOMBMSD[sourhi>>26];}\r
+\r
+ #elif DECPMAX==34\r
+ #define GETCOEFFTHOU(df, buf) { \\r
+ uInt sourhi, sourmh, sourml, sourlo; \\r
+ sourlo=DFWORD(df, 3); \\r
+ (buf)[0]=DPD2BIN[sourlo&0x3ff]; \\r
+ (buf)[1]=DPD2BIN[(sourlo>>10)&0x3ff]; \\r
+ (buf)[2]=DPD2BIN[(sourlo>>20)&0x3ff]; \\r
+ sourml=DFWORD(df, 2); \\r
+ (buf)[3]=DPD2BIN[((sourml<<2) | (sourlo>>30))&0x3ff]; \\r
+ (buf)[4]=DPD2BIN[(sourml>>8)&0x3ff]; \\r
+ (buf)[5]=DPD2BIN[(sourml>>18)&0x3ff]; \\r
+ sourmh=DFWORD(df, 1); \\r
+ (buf)[6]=DPD2BIN[((sourmh<<4) | (sourml>>28))&0x3ff]; \\r
+ (buf)[7]=DPD2BIN[(sourmh>>6)&0x3ff]; \\r
+ (buf)[8]=DPD2BIN[(sourmh>>16)&0x3ff]; \\r
+ sourhi=DFWORD(df, 0); \\r
+ (buf)[9]=DPD2BIN[((sourhi<<6) | (sourmh>>26))&0x3ff]; \\r
+ (buf)[10]=DPD2BIN[(sourhi>>4)&0x3ff]; \\r
+ (buf)[11]=DECCOMBMSD[sourhi>>26];}\r
+ #endif\r
+\r
+\r
+ /* Macros to decode the coefficient in a finite decFloat *df and */\r
+ /* add to a base-thousand uInt array (as for GETCOEFFTHOU). */\r
+ /* After the addition then most significant 'digit' in the array */\r
+ /* might have a value larger then 10 (with a maximum of 19). */\r
+ #if DECPMAX==7\r
+ #define ADDCOEFFTHOU(df, buf) { \\r
+ uInt sourhi=DFWORD(df, 0); \\r
+ (buf)[0]+=DPD2BIN[sourhi&0x3ff]; \\r
+ if (buf[0]>999) {buf[0]-=1000; buf[1]++;} \\r
+ (buf)[1]+=DPD2BIN[(sourhi>>10)&0x3ff]; \\r
+ if (buf[1]>999) {buf[1]-=1000; buf[2]++;} \\r
+ (buf)[2]+=DECCOMBMSD[sourhi>>26];}\r
+\r
+ #elif DECPMAX==16\r
+ #define ADDCOEFFTHOU(df, buf) { \\r
+ uInt sourhi, sourlo; \\r
+ sourlo=DFWORD(df, 1); \\r
+ (buf)[0]+=DPD2BIN[sourlo&0x3ff]; \\r
+ if (buf[0]>999) {buf[0]-=1000; buf[1]++;} \\r
+ (buf)[1]+=DPD2BIN[(sourlo>>10)&0x3ff]; \\r
+ if (buf[1]>999) {buf[1]-=1000; buf[2]++;} \\r
+ (buf)[2]+=DPD2BIN[(sourlo>>20)&0x3ff]; \\r
+ if (buf[2]>999) {buf[2]-=1000; buf[3]++;} \\r
+ sourhi=DFWORD(df, 0); \\r
+ (buf)[3]+=DPD2BIN[((sourhi<<2) | (sourlo>>30))&0x3ff]; \\r
+ if (buf[3]>999) {buf[3]-=1000; buf[4]++;} \\r
+ (buf)[4]+=DPD2BIN[(sourhi>>8)&0x3ff]; \\r
+ if (buf[4]>999) {buf[4]-=1000; buf[5]++;} \\r
+ (buf)[5]+=DECCOMBMSD[sourhi>>26];}\r
+\r
+ #elif DECPMAX==34\r
+ #define ADDCOEFFTHOU(df, buf) { \\r
+ uInt sourhi, sourmh, sourml, sourlo; \\r
+ sourlo=DFWORD(df, 3); \\r
+ (buf)[0]+=DPD2BIN[sourlo&0x3ff]; \\r
+ if (buf[0]>999) {buf[0]-=1000; buf[1]++;} \\r
+ (buf)[1]+=DPD2BIN[(sourlo>>10)&0x3ff]; \\r
+ if (buf[1]>999) {buf[1]-=1000; buf[2]++;} \\r
+ (buf)[2]+=DPD2BIN[(sourlo>>20)&0x3ff]; \\r
+ if (buf[2]>999) {buf[2]-=1000; buf[3]++;} \\r
+ sourml=DFWORD(df, 2); \\r
+ (buf)[3]+=DPD2BIN[((sourml<<2) | (sourlo>>30))&0x3ff]; \\r
+ if (buf[3]>999) {buf[3]-=1000; buf[4]++;} \\r
+ (buf)[4]+=DPD2BIN[(sourml>>8)&0x3ff]; \\r
+ if (buf[4]>999) {buf[4]-=1000; buf[5]++;} \\r
+ (buf)[5]+=DPD2BIN[(sourml>>18)&0x3ff]; \\r
+ if (buf[5]>999) {buf[5]-=1000; buf[6]++;} \\r
+ sourmh=DFWORD(df, 1); \\r
+ (buf)[6]+=DPD2BIN[((sourmh<<4) | (sourml>>28))&0x3ff]; \\r
+ if (buf[6]>999) {buf[6]-=1000; buf[7]++;} \\r
+ (buf)[7]+=DPD2BIN[(sourmh>>6)&0x3ff]; \\r
+ if (buf[7]>999) {buf[7]-=1000; buf[8]++;} \\r
+ (buf)[8]+=DPD2BIN[(sourmh>>16)&0x3ff]; \\r
+ if (buf[8]>999) {buf[8]-=1000; buf[9]++;} \\r
+ sourhi=DFWORD(df, 0); \\r
+ (buf)[9]+=DPD2BIN[((sourhi<<6) | (sourmh>>26))&0x3ff]; \\r
+ if (buf[9]>999) {buf[9]-=1000; buf[10]++;} \\r
+ (buf)[10]+=DPD2BIN[(sourhi>>4)&0x3ff]; \\r
+ if (buf[10]>999) {buf[10]-=1000; buf[11]++;} \\r
+ (buf)[11]+=DECCOMBMSD[sourhi>>26];}\r
+ #endif\r
+\r
+\r
+ /* Set a decFloat to the maximum positive finite number (Nmax) */\r
+ #if DECPMAX==7\r
+ #define DFSETNMAX(df) \\r
+ {DFWORD(df, 0)=0x77f3fcff;}\r
+ #elif DECPMAX==16\r
+ #define DFSETNMAX(df) \\r
+ {DFWORD(df, 0)=0x77fcff3f; \\r
+ DFWORD(df, 1)=0xcff3fcff;}\r
+ #elif DECPMAX==34\r
+ #define DFSETNMAX(df) \\r
+ {DFWORD(df, 0)=0x77ffcff3; \\r
+ DFWORD(df, 1)=0xfcff3fcf; \\r
+ DFWORD(df, 2)=0xf3fcff3f; \\r
+ DFWORD(df, 3)=0xcff3fcff;}\r
+ #endif\r
+\r
+ /* [end of format-dependent macros and constants] */\r
+ #endif\r
+\r
+#else\r
+ #error decNumberLocal included more than once\r
+#endif\r
--- /dev/null
+/* ------------------------------------------------------------------ */\r
+/* Packed Decimal conversion module */\r
+/* ------------------------------------------------------------------ */\r
+/* Copyright (c) IBM Corporation, 2000, 2002. All rights reserved. */\r
+/* */\r
+/* This software is made available under the terms of the */\r
+/* ICU License -- ICU 1.8.1 and later. */\r
+/* */\r
+/* The description and User's Guide ("The decNumber C Library") for */\r
+/* this software is called decNumber.pdf. This document is */\r
+/* available, together with arithmetic and format specifications, */\r
+/* testcases, and Web links, on the General Decimal Arithmetic page. */\r
+/* */\r
+/* Please send comments, suggestions, and corrections to the author: */\r
+/* mfc@uk.ibm.com */\r
+/* Mike Cowlishaw, IBM Fellow */\r
+/* IBM UK, PO Box 31, Birmingham Road, Warwick CV34 5JL, UK */\r
+/* ------------------------------------------------------------------ */\r
+/* This module comprises the routines for Packed Decimal format */\r
+/* numbers. Conversions are supplied to and from decNumber, which in */\r
+/* turn supports: */\r
+/* conversions to and from string */\r
+/* arithmetic routines */\r
+/* utilities. */\r
+/* Conversions from decNumber to and from densely packed decimal */\r
+/* formats are provided by the decimal32 through decimal128 modules. */\r
+/* ------------------------------------------------------------------ */\r
+\r
+#include <string.h> // for NULL\r
+#include "decNumber.h" // base number library\r
+#include "decPacked.h" // packed decimal\r
+#include "decNumberLocal.h" // decNumber local types, etc.\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decPackedFromNumber -- convert decNumber to BCD Packed Decimal */\r
+/* */\r
+/* bcd is the BCD bytes */\r
+/* length is the length of the BCD array */\r
+/* scale is the scale result */\r
+/* dn is the decNumber */\r
+/* returns bcd, or NULL if error */\r
+/* */\r
+/* The number is converted to a BCD packed decimal byte array, */\r
+/* right aligned in the bcd array, whose length is indicated by the */\r
+/* second parameter. The final 4-bit nibble in the array will be a */\r
+/* sign nibble, C (1100) for + and D (1101) for -. Unused bytes and */\r
+/* nibbles to the left of the number are set to 0. */\r
+/* */\r
+/* scale is set to the scale of the number (this is the exponent, */\r
+/* negated). To force the number to a specified scale, first use the */\r
+/* decNumberRescale routine, which will round and change the exponent */\r
+/* as necessary. */\r
+/* */\r
+/* If there is an error (that is, the decNumber has too many digits */\r
+/* to fit in length bytes, or it is a NaN or Infinity), NULL is */\r
+/* returned and the bcd and scale results are unchanged. Otherwise */\r
+/* bcd is returned. */\r
+/* ------------------------------------------------------------------ */\r
+uByte * decPackedFromNumber(uByte *bcd, Int length, Int *scale,\r
+ const decNumber *dn) {\r
+ const Unit *up=dn->lsu; // Unit array pointer\r
+ uByte obyte, *out; // current output byte, and where it goes\r
+ Int indigs=dn->digits; // digits processed\r
+ uInt cut=DECDPUN; // downcounter per Unit\r
+ uInt u=*up; // work\r
+ uInt nib; // ..\r
+ #if DECDPUN<=4\r
+ uInt temp; // ..\r
+ #endif\r
+\r
+ if (dn->digits>length*2-1 // too long ..\r
+ ||(dn->bits & DECSPECIAL)) return NULL; // .. or special -- hopeless\r
+\r
+ if (dn->bits&DECNEG) obyte=DECPMINUS; // set the sign ..\r
+ else obyte=DECPPLUS;\r
+ *scale=-dn->exponent; // .. and scale\r
+\r
+ // loop from lowest (rightmost) byte\r
+ out=bcd+length-1; // -> final byte\r
+ for (; out>=bcd; out--) {\r
+ if (indigs>0) {\r
+ if (cut==0) {\r
+ up++;\r
+ u=*up;\r
+ cut=DECDPUN;\r
+ }\r
+ #if DECDPUN<=4\r
+ temp=(u*6554)>>16; // fast /10\r
+ nib=u-X10(temp);\r
+ u=temp;\r
+ #else\r
+ nib=u%10; // cannot use *6554 trick :-(\r
+ u=u/10;\r
+ #endif\r
+ obyte|=(nib<<4);\r
+ indigs--;\r
+ cut--;\r
+ }\r
+ *out=obyte;\r
+ obyte=0; // assume 0\r
+ if (indigs>0) {\r
+ if (cut==0) {\r
+ up++;\r
+ u=*up;\r
+ cut=DECDPUN;\r
+ }\r
+ #if DECDPUN<=4\r
+ temp=(u*6554)>>16; // as above\r
+ obyte=(uByte)(u-X10(temp));\r
+ u=temp;\r
+ #else\r
+ obyte=(uByte)(u%10);\r
+ u=u/10;\r
+ #endif\r
+ indigs--;\r
+ cut--;\r
+ }\r
+ } // loop\r
+\r
+ return bcd;\r
+ } // decPackedFromNumber\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decPackedToNumber -- convert BCD Packed Decimal to a decNumber */\r
+/* */\r
+/* bcd is the BCD bytes */\r
+/* length is the length of the BCD array */\r
+/* scale is the scale associated with the BCD integer */\r
+/* dn is the decNumber [with space for length*2 digits] */\r
+/* returns dn, or NULL if error */\r
+/* */\r
+/* The BCD packed decimal byte array, together with an associated */\r
+/* scale, is converted to a decNumber. The BCD array is assumed full */\r
+/* of digits, and must be ended by a 4-bit sign nibble in the least */\r
+/* significant four bits of the final byte. */\r
+/* */\r
+/* The scale is used (negated) as the exponent of the decNumber. */\r
+/* Note that zeros may have a sign and/or a scale. */\r
+/* */\r
+/* The decNumber structure is assumed to have sufficient space to */\r
+/* hold the converted number (that is, up to length*2-1 digits), so */\r
+/* no error is possible unless the adjusted exponent is out of range, */\r
+/* no sign nibble was found, or a sign nibble was found before the */\r
+/* final nibble. In these error cases, NULL is returned and the */\r
+/* decNumber will be 0. */\r
+/* ------------------------------------------------------------------ */\r
+decNumber * decPackedToNumber(const uByte *bcd, Int length,\r
+ const Int *scale, decNumber *dn) {\r
+ const uByte *last=bcd+length-1; // -> last byte\r
+ const uByte *first; // -> first non-zero byte\r
+ uInt nib; // work nibble\r
+ Unit *up=dn->lsu; // output pointer\r
+ Int digits; // digits count\r
+ Int cut=0; // phase of output\r
+\r
+ decNumberZero(dn); // default result\r
+ last=&bcd[length-1];\r
+ nib=*last & 0x0f; // get the sign\r
+ if (nib==DECPMINUS || nib==DECPMINUSALT) dn->bits=DECNEG;\r
+ else if (nib<=9) return NULL; // not a sign nibble\r
+\r
+ // skip leading zero bytes [final byte is always non-zero, due to sign]\r
+ for (first=bcd; *first==0;) first++;\r
+ digits=(last-first)*2+1; // calculate digits ..\r
+ if ((*first & 0xf0)==0) digits--; // adjust for leading zero nibble\r
+ if (digits!=0) dn->digits=digits; // count of actual digits [if 0,\r
+ // leave as 1]\r
+\r
+ // check the adjusted exponent; note that scale could be unbounded\r
+ dn->exponent=-*scale; // set the exponent\r
+ if (*scale>=0) { // usual case\r
+ if ((dn->digits-*scale-1)<-DECNUMMAXE) { // underflow\r
+ decNumberZero(dn);\r
+ return NULL;}\r
+ }\r
+ else { // -ve scale; +ve exponent\r
+ // need to be careful to avoid wrap, here, also BADINT case\r
+ if ((*scale<-DECNUMMAXE) // overflow even without digits\r
+ || ((dn->digits-*scale-1)>DECNUMMAXE)) { // overflow\r
+ decNumberZero(dn);\r
+ return NULL;}\r
+ }\r
+ if (digits==0) return dn; // result was zero\r
+\r
+ // copy the digits to the number's units, starting at the lsu\r
+ // [unrolled]\r
+ for (;;) { // forever\r
+ // left nibble first\r
+ nib=(unsigned)(*last & 0xf0)>>4;\r
+ // got a digit, in nib\r
+ if (nib>9) {decNumberZero(dn); return NULL;}\r
+\r
+ if (cut==0) *up=(Unit)nib;\r
+ else *up=(Unit)(*up+nib*DECPOWERS[cut]);\r
+ digits--;\r
+ if (digits==0) break; // got them all\r
+ cut++;\r
+ if (cut==DECDPUN) {\r
+ up++;\r
+ cut=0;\r
+ }\r
+ last--; // ready for next\r
+ nib=*last & 0x0f; // get right nibble\r
+ if (nib>9) {decNumberZero(dn); return NULL;}\r
+\r
+ // got a digit, in nib\r
+ if (cut==0) *up=(Unit)nib;\r
+ else *up=(Unit)(*up+nib*DECPOWERS[cut]);\r
+ digits--;\r
+ if (digits==0) break; // got them all\r
+ cut++;\r
+ if (cut==DECDPUN) {\r
+ up++;\r
+ cut=0;\r
+ }\r
+ } // forever\r
+\r
+ return dn;\r
+ } // decPackedToNumber\r
+\r
--- /dev/null
+/* ------------------------------------------------------------------ */\r
+/* Packed Decimal conversion module header */\r
+/* ------------------------------------------------------------------ */\r
+/* Copyright (c) IBM Corporation, 2000, 2005. All rights reserved. */\r
+/* */\r
+/* This software is made available under the terms of the */\r
+/* ICU License -- ICU 1.8.1 and later. */\r
+/* */\r
+/* The description and User's Guide ("The decNumber C Library") for */\r
+/* this software is called decNumber.pdf. This document is */\r
+/* available, together with arithmetic and format specifications, */\r
+/* testcases, and Web links, on the General Decimal Arithmetic page. */\r
+/* */\r
+/* Please send comments, suggestions, and corrections to the author: */\r
+/* mfc@uk.ibm.com */\r
+/* Mike Cowlishaw, IBM Fellow */\r
+/* IBM UK, PO Box 31, Birmingham Road, Warwick CV34 5JL, UK */\r
+/* ------------------------------------------------------------------ */\r
+\r
+#if !defined(DECPACKED)\r
+ #define DECPACKED\r
+ #define DECPNAME "decPacked" /* Short name */\r
+ #define DECPFULLNAME "Packed Decimal conversions" /* Verbose name */\r
+ #define DECPAUTHOR "Mike Cowlishaw" /* Who to blame */\r
+\r
+ #define DECPACKED_DefP 32 /* default precision */\r
+\r
+ #ifndef DECNUMDIGITS\r
+ #define DECNUMDIGITS DECPACKED_DefP /* size if not already defined*/\r
+ #endif\r
+ #include "decNumber.h" /* context and number library */\r
+\r
+ /* Sign nibble constants */\r
+ #if !defined(DECPPLUSALT)\r
+ #define DECPPLUSALT 0x0A /* alternate plus nibble */\r
+ #define DECPMINUSALT 0x0B /* alternate minus nibble */\r
+ #define DECPPLUS 0x0C /* preferred plus nibble */\r
+ #define DECPMINUS 0x0D /* preferred minus nibble */\r
+ #define DECPPLUSALT2 0x0E /* alternate plus nibble */\r
+ #define DECPUNSIGNED 0x0F /* alternate plus nibble (unsigned) */\r
+ #endif\r
+\r
+ /* ---------------------------------------------------------------- */\r
+ /* decPacked public routines */\r
+ /* ---------------------------------------------------------------- */\r
+ /* Conversions */\r
+ uint8_t * decPackedFromNumber(uint8_t *, int32_t, int32_t *,\r
+ const decNumber *);\r
+ decNumber * decPackedToNumber(const uint8_t *, int32_t, const int32_t *,\r
+ decNumber *);\r
+\r
+#endif\r
--- /dev/null
+/* ------------------------------------------------------------------ */\r
+/* decQuad.c -- decQuad operations module */\r
+/* ------------------------------------------------------------------ */\r
+/* Copyright (c) IBM Corporation, 2000, 2010. All rights reserved. */\r
+/* */\r
+/* This software is made available under the terms of the */\r
+/* ICU License -- ICU 1.8.1 and later. */\r
+/* */\r
+/* The description and User's Guide ("The decNumber C Library") for */\r
+/* this software is included in the package as decNumber.pdf. This */\r
+/* document is also available in HTML, together with specifications, */\r
+/* testcases, and Web links, on the General Decimal Arithmetic page. */\r
+/* */\r
+/* Please send comments, suggestions, and corrections to the author: */\r
+/* mfc@uk.ibm.com */\r
+/* Mike Cowlishaw, IBM Fellow */\r
+/* IBM UK, PO Box 31, Birmingham Road, Warwick CV34 5JL, UK */\r
+/* ------------------------------------------------------------------ */\r
+/* This module comprises decQuad operations (including conversions) */\r
+/* ------------------------------------------------------------------ */\r
+\r
+\r
+/* Constant mappings for shared code */\r
+#define DECPMAX DECQUAD_Pmax\r
+#define DECEMIN DECQUAD_Emin\r
+#define DECEMAX DECQUAD_Emax\r
+#define DECEMAXD DECQUAD_EmaxD\r
+#define DECBYTES DECQUAD_Bytes\r
+#define DECSTRING DECQUAD_String\r
+#define DECECONL DECQUAD_EconL\r
+#define DECBIAS DECQUAD_Bias\r
+#define DECLETS DECQUAD_Declets\r
+#define DECQTINY (-DECQUAD_Bias)\r
+\r
+/* Type and function mappings for shared code */\r
+#define decFloat decQuad // Type name\r
+\r
+// Utilities and conversions (binary results, extractors, etc.)\r
+#define decFloatFromBCD decQuadFromBCD\r
+#define decFloatFromInt32 decQuadFromInt32\r
+#define decFloatFromPacked decQuadFromPacked\r
+#define decFloatFromPackedChecked decQuadFromPackedChecked\r
+#define decFloatFromString decQuadFromString\r
+#define decFloatFromUInt32 decQuadFromUInt32\r
+#define decFloatFromWider decQuadFromWider\r
+#define decFloatGetCoefficient decQuadGetCoefficient\r
+#define decFloatGetExponent decQuadGetExponent\r
+#define decFloatSetCoefficient decQuadSetCoefficient\r
+#define decFloatSetExponent decQuadSetExponent\r
+#define decFloatShow decQuadShow\r
+#define decFloatToBCD decQuadToBCD\r
+#define decFloatToEngString decQuadToEngString\r
+#define decFloatToInt32 decQuadToInt32\r
+#define decFloatToInt32Exact decQuadToInt32Exact\r
+#define decFloatToPacked decQuadToPacked\r
+#define decFloatToString decQuadToString\r
+#define decFloatToUInt32 decQuadToUInt32\r
+#define decFloatToUInt32Exact decQuadToUInt32Exact\r
+#define decFloatToWider decQuadToWider\r
+#define decFloatZero decQuadZero\r
+\r
+// Computational (result is a decFloat)\r
+#define decFloatAbs decQuadAbs\r
+#define decFloatAdd decQuadAdd\r
+#define decFloatAnd decQuadAnd\r
+#define decFloatDivide decQuadDivide\r
+#define decFloatDivideInteger decQuadDivideInteger\r
+#define decFloatFMA decQuadFMA\r
+#define decFloatInvert decQuadInvert\r
+#define decFloatLogB decQuadLogB\r
+#define decFloatMax decQuadMax\r
+#define decFloatMaxMag decQuadMaxMag\r
+#define decFloatMin decQuadMin\r
+#define decFloatMinMag decQuadMinMag\r
+#define decFloatMinus decQuadMinus\r
+#define decFloatMultiply decQuadMultiply\r
+#define decFloatNextMinus decQuadNextMinus\r
+#define decFloatNextPlus decQuadNextPlus\r
+#define decFloatNextToward decQuadNextToward\r
+#define decFloatOr decQuadOr\r
+#define decFloatPlus decQuadPlus\r
+#define decFloatQuantize decQuadQuantize\r
+#define decFloatReduce decQuadReduce\r
+#define decFloatRemainder decQuadRemainder\r
+#define decFloatRemainderNear decQuadRemainderNear\r
+#define decFloatRotate decQuadRotate\r
+#define decFloatScaleB decQuadScaleB\r
+#define decFloatShift decQuadShift\r
+#define decFloatSubtract decQuadSubtract\r
+#define decFloatToIntegralValue decQuadToIntegralValue\r
+#define decFloatToIntegralExact decQuadToIntegralExact\r
+#define decFloatXor decQuadXor\r
+\r
+// Comparisons\r
+#define decFloatCompare decQuadCompare\r
+#define decFloatCompareSignal decQuadCompareSignal\r
+#define decFloatCompareTotal decQuadCompareTotal\r
+#define decFloatCompareTotalMag decQuadCompareTotalMag\r
+\r
+// Copies\r
+#define decFloatCanonical decQuadCanonical\r
+#define decFloatCopy decQuadCopy\r
+#define decFloatCopyAbs decQuadCopyAbs\r
+#define decFloatCopyNegate decQuadCopyNegate\r
+#define decFloatCopySign decQuadCopySign\r
+\r
+// Non-computational\r
+#define decFloatClass decQuadClass\r
+#define decFloatClassString decQuadClassString\r
+#define decFloatDigits decQuadDigits\r
+#define decFloatIsCanonical decQuadIsCanonical\r
+#define decFloatIsFinite decQuadIsFinite\r
+#define decFloatIsInfinite decQuadIsInfinite\r
+#define decFloatIsInteger decQuadIsInteger\r
+#define decFloatIsLogical decQuadIsLogical\r
+#define decFloatIsNaN decQuadIsNaN\r
+#define decFloatIsNegative decQuadIsNegative\r
+#define decFloatIsNormal decQuadIsNormal\r
+#define decFloatIsPositive decQuadIsPositive\r
+#define decFloatIsSignaling decQuadIsSignaling\r
+#define decFloatIsSignalling decQuadIsSignalling\r
+#define decFloatIsSigned decQuadIsSigned\r
+#define decFloatIsSubnormal decQuadIsSubnormal\r
+#define decFloatIsZero decQuadIsZero\r
+#define decFloatRadix decQuadRadix\r
+#define decFloatSameQuantum decQuadSameQuantum\r
+#define decFloatVersion decQuadVersion\r
+\r
+/* And now the code itself */\r
+#include "decContext.h" // public includes\r
+#include "decQuad.h" // ..\r
+#include "decNumberLocal.h" // local includes (need DECPMAX)\r
+#include "decCommon.inc" // non-arithmetic decFloat routines\r
+#include "decBasic.inc" // basic formats routines\r
+\r
--- /dev/null
+/* ------------------------------------------------------------------ */\r
+/* decQuad.h -- Decimal 128-bit format module header */\r
+/* ------------------------------------------------------------------ */\r
+/* Copyright (c) IBM Corporation, 2000, 2010. All rights reserved. */\r
+/* */\r
+/* This software is made available under the terms of the */\r
+/* ICU License -- ICU 1.8.1 and later. */\r
+/* */\r
+/* The description and User's Guide ("The decNumber C Library") for */\r
+/* this software is included in the package as decNumber.pdf. This */\r
+/* document is also available in HTML, together with specifications, */\r
+/* testcases, and Web links, on the General Decimal Arithmetic page. */\r
+/* */\r
+/* Please send comments, suggestions, and corrections to the author: */\r
+/* mfc@uk.ibm.com */\r
+/* Mike Cowlishaw, IBM Fellow */\r
+/* IBM UK, PO Box 31, Birmingham Road, Warwick CV34 5JL, UK */\r
+/* ------------------------------------------------------------------ */\r
+/* This include file is always included by decSingle and decDouble, */\r
+/* and therefore also holds useful constants used by all three. */\r
+\r
+#if !defined(DECQUAD)\r
+ #define DECQUAD\r
+\r
+ #define DECQUADNAME "decimalQuad" /* Short name */\r
+ #define DECQUADTITLE "Decimal 128-bit datum" /* Verbose name */\r
+ #define DECQUADAUTHOR "Mike Cowlishaw" /* Who to blame */\r
+\r
+ /* parameters for decQuads */\r
+ #define DECQUAD_Bytes 16 /* length */\r
+ #define DECQUAD_Pmax 34 /* maximum precision (digits) */\r
+ #define DECQUAD_Emin -6143 /* minimum adjusted exponent */\r
+ #define DECQUAD_Emax 6144 /* maximum adjusted exponent */\r
+ #define DECQUAD_EmaxD 4 /* maximum exponent digits */\r
+ #define DECQUAD_Bias 6176 /* bias for the exponent */\r
+ #define DECQUAD_String 43 /* maximum string length, +1 */\r
+ #define DECQUAD_EconL 12 /* exponent continuation length */\r
+ #define DECQUAD_Declets 11 /* count of declets */\r
+ /* highest biased exponent (Elimit-1) */\r
+ #define DECQUAD_Ehigh (DECQUAD_Emax + DECQUAD_Bias - (DECQUAD_Pmax-1))\r
+\r
+ /* Required include */\r
+ #include "decContext.h"\r
+\r
+ /* The decQuad decimal 128-bit type, accessible by all sizes */\r
+ typedef union {\r
+ uint8_t bytes[DECQUAD_Bytes]; /* fields: 1, 5, 12, 110 bits */\r
+ uint16_t shorts[DECQUAD_Bytes/2];\r
+ uint32_t words[DECQUAD_Bytes/4];\r
+ #if DECUSE64\r
+ uint64_t longs[DECQUAD_Bytes/8];\r
+ #endif\r
+ } decQuad;\r
+\r
+ /* ---------------------------------------------------------------- */\r
+ /* Shared constants */\r
+ /* ---------------------------------------------------------------- */\r
+\r
+ /* sign and special values [top 32-bits; last two bits are don't-care\r
+ for Infinity on input, last bit don't-care for NaNs] */\r
+ #define DECFLOAT_Sign 0x80000000 /* 1 00000 00 Sign */\r
+ #define DECFLOAT_NaN 0x7c000000 /* 0 11111 00 NaN generic */\r
+ #define DECFLOAT_qNaN 0x7c000000 /* 0 11111 00 qNaN */\r
+ #define DECFLOAT_sNaN 0x7e000000 /* 0 11111 10 sNaN */\r
+ #define DECFLOAT_Inf 0x78000000 /* 0 11110 00 Infinity */\r
+ #define DECFLOAT_MinSp 0x78000000 /* minimum special value */\r
+ /* [specials are all >=MinSp] */\r
+ /* Sign nibble constants */\r
+ #if !defined(DECPPLUSALT)\r
+ #define DECPPLUSALT 0x0A /* alternate plus nibble */\r
+ #define DECPMINUSALT 0x0B /* alternate minus nibble */\r
+ #define DECPPLUS 0x0C /* preferred plus nibble */\r
+ #define DECPMINUS 0x0D /* preferred minus nibble */\r
+ #define DECPPLUSALT2 0x0E /* alternate plus nibble */\r
+ #define DECPUNSIGNED 0x0F /* alternate plus nibble (unsigned) */\r
+ #endif\r
+\r
+ /* ---------------------------------------------------------------- */\r
+ /* Routines -- implemented as decFloat routines in common files */\r
+ /* ---------------------------------------------------------------- */\r
+\r
+ /* Utilities and conversions, extractors, etc.) */\r
+ extern decQuad * decQuadFromBCD(decQuad *, int32_t, const uint8_t *, int32_t);\r
+ extern decQuad * decQuadFromInt32(decQuad *, int32_t);\r
+ extern decQuad * decQuadFromPacked(decQuad *, int32_t, const uint8_t *);\r
+ extern decQuad * decQuadFromPackedChecked(decQuad *, int32_t, const uint8_t *);\r
+ extern decQuad * decQuadFromString(decQuad *, const char *, decContext *);\r
+ extern decQuad * decQuadFromUInt32(decQuad *, uint32_t);\r
+ extern int32_t decQuadGetCoefficient(const decQuad *, uint8_t *);\r
+ extern int32_t decQuadGetExponent(const decQuad *);\r
+ extern decQuad * decQuadSetCoefficient(decQuad *, const uint8_t *, int32_t);\r
+ extern decQuad * decQuadSetExponent(decQuad *, decContext *, int32_t);\r
+ extern void decQuadShow(const decQuad *, const char *);\r
+ extern int32_t decQuadToBCD(const decQuad *, int32_t *, uint8_t *);\r
+ extern char * decQuadToEngString(const decQuad *, char *);\r
+ extern int32_t decQuadToInt32(const decQuad *, decContext *, enum rounding);\r
+ extern int32_t decQuadToInt32Exact(const decQuad *, decContext *, enum rounding);\r
+ extern int32_t decQuadToPacked(const decQuad *, int32_t *, uint8_t *);\r
+ extern char * decQuadToString(const decQuad *, char *);\r
+ extern uint32_t decQuadToUInt32(const decQuad *, decContext *, enum rounding);\r
+ extern uint32_t decQuadToUInt32Exact(const decQuad *, decContext *, enum rounding);\r
+ extern decQuad * decQuadZero(decQuad *);\r
+\r
+ /* Computational (result is a decQuad) */\r
+ extern decQuad * decQuadAbs(decQuad *, const decQuad *, decContext *);\r
+ extern decQuad * decQuadAdd(decQuad *, const decQuad *, const decQuad *, decContext *);\r
+ extern decQuad * decQuadAnd(decQuad *, const decQuad *, const decQuad *, decContext *);\r
+ extern decQuad * decQuadDivide(decQuad *, const decQuad *, const decQuad *, decContext *);\r
+ extern decQuad * decQuadDivideInteger(decQuad *, const decQuad *, const decQuad *, decContext *);\r
+ extern decQuad * decQuadFMA(decQuad *, const decQuad *, const decQuad *, const decQuad *, decContext *);\r
+ extern decQuad * decQuadInvert(decQuad *, const decQuad *, decContext *);\r
+ extern decQuad * decQuadLogB(decQuad *, const decQuad *, decContext *);\r
+ extern decQuad * decQuadMax(decQuad *, const decQuad *, const decQuad *, decContext *);\r
+ extern decQuad * decQuadMaxMag(decQuad *, const decQuad *, const decQuad *, decContext *);\r
+ extern decQuad * decQuadMin(decQuad *, const decQuad *, const decQuad *, decContext *);\r
+ extern decQuad * decQuadMinMag(decQuad *, const decQuad *, const decQuad *, decContext *);\r
+ extern decQuad * decQuadMinus(decQuad *, const decQuad *, decContext *);\r
+ extern decQuad * decQuadMultiply(decQuad *, const decQuad *, const decQuad *, decContext *);\r
+ extern decQuad * decQuadNextMinus(decQuad *, const decQuad *, decContext *);\r
+ extern decQuad * decQuadNextPlus(decQuad *, const decQuad *, decContext *);\r
+ extern decQuad * decQuadNextToward(decQuad *, const decQuad *, const decQuad *, decContext *);\r
+ extern decQuad * decQuadOr(decQuad *, const decQuad *, const decQuad *, decContext *);\r
+ extern decQuad * decQuadPlus(decQuad *, const decQuad *, decContext *);\r
+ extern decQuad * decQuadQuantize(decQuad *, const decQuad *, const decQuad *, decContext *);\r
+ extern decQuad * decQuadReduce(decQuad *, const decQuad *, decContext *);\r
+ extern decQuad * decQuadRemainder(decQuad *, const decQuad *, const decQuad *, decContext *);\r
+ extern decQuad * decQuadRemainderNear(decQuad *, const decQuad *, const decQuad *, decContext *);\r
+ extern decQuad * decQuadRotate(decQuad *, const decQuad *, const decQuad *, decContext *);\r
+ extern decQuad * decQuadScaleB(decQuad *, const decQuad *, const decQuad *, decContext *);\r
+ extern decQuad * decQuadShift(decQuad *, const decQuad *, const decQuad *, decContext *);\r
+ extern decQuad * decQuadSubtract(decQuad *, const decQuad *, const decQuad *, decContext *);\r
+ extern decQuad * decQuadToIntegralValue(decQuad *, const decQuad *, decContext *, enum rounding);\r
+ extern decQuad * decQuadToIntegralExact(decQuad *, const decQuad *, decContext *);\r
+ extern decQuad * decQuadXor(decQuad *, const decQuad *, const decQuad *, decContext *);\r
+\r
+ /* Comparisons */\r
+ extern decQuad * decQuadCompare(decQuad *, const decQuad *, const decQuad *, decContext *);\r
+ extern decQuad * decQuadCompareSignal(decQuad *, const decQuad *, const decQuad *, decContext *);\r
+ extern decQuad * decQuadCompareTotal(decQuad *, const decQuad *, const decQuad *);\r
+ extern decQuad * decQuadCompareTotalMag(decQuad *, const decQuad *, const decQuad *);\r
+\r
+ /* Copies */\r
+ extern decQuad * decQuadCanonical(decQuad *, const decQuad *);\r
+ extern decQuad * decQuadCopy(decQuad *, const decQuad *);\r
+ extern decQuad * decQuadCopyAbs(decQuad *, const decQuad *);\r
+ extern decQuad * decQuadCopyNegate(decQuad *, const decQuad *);\r
+ extern decQuad * decQuadCopySign(decQuad *, const decQuad *, const decQuad *);\r
+\r
+ /* Non-computational */\r
+ extern enum decClass decQuadClass(const decQuad *);\r
+ extern const char * decQuadClassString(const decQuad *);\r
+ extern uint32_t decQuadDigits(const decQuad *);\r
+ extern uint32_t decQuadIsCanonical(const decQuad *);\r
+ extern uint32_t decQuadIsFinite(const decQuad *);\r
+ extern uint32_t decQuadIsInteger(const decQuad *);\r
+ extern uint32_t decQuadIsLogical(const decQuad *);\r
+ extern uint32_t decQuadIsInfinite(const decQuad *);\r
+ extern uint32_t decQuadIsNaN(const decQuad *);\r
+ extern uint32_t decQuadIsNegative(const decQuad *);\r
+ extern uint32_t decQuadIsNormal(const decQuad *);\r
+ extern uint32_t decQuadIsPositive(const decQuad *);\r
+ extern uint32_t decQuadIsSignaling(const decQuad *);\r
+ extern uint32_t decQuadIsSignalling(const decQuad *);\r
+ extern uint32_t decQuadIsSigned(const decQuad *);\r
+ extern uint32_t decQuadIsSubnormal(const decQuad *);\r
+ extern uint32_t decQuadIsZero(const decQuad *);\r
+ extern uint32_t decQuadRadix(const decQuad *);\r
+ extern uint32_t decQuadSameQuantum(const decQuad *, const decQuad *);\r
+ extern const char * decQuadVersion(void);\r
+\r
+ /* decNumber conversions; these are implemented as macros so as not */\r
+ /* to force a dependency on decimal128 and decNumber in decQuad. */\r
+ /* decQuadFromNumber returns a decimal128 * to avoid warnings. */\r
+ #define decQuadToNumber(dq, dn) decimal128ToNumber((decimal128 *)(dq), dn)\r
+ #define decQuadFromNumber(dq, dn, set) decimal128FromNumber((decimal128 *)(dq), dn, set)\r
+\r
+#endif\r
--- /dev/null
+/* ------------------------------------------------------------------ */\r
+/* decSingle.c -- decSingle operations module */\r
+/* ------------------------------------------------------------------ */\r
+/* Copyright (c) IBM Corporation, 2000, 2008. All rights reserved. */\r
+/* */\r
+/* This software is made available under the terms of the */\r
+/* ICU License -- ICU 1.8.1 and later. */\r
+/* */\r
+/* The description and User's Guide ("The decNumber C Library") for */\r
+/* this software is included in the package as decNumber.pdf. This */\r
+/* document is also available in HTML, together with specifications, */\r
+/* testcases, and Web links, on the General Decimal Arithmetic page. */\r
+/* */\r
+/* Please send comments, suggestions, and corrections to the author: */\r
+/* mfc@uk.ibm.com */\r
+/* Mike Cowlishaw, IBM Fellow */\r
+/* IBM UK, PO Box 31, Birmingham Road, Warwick CV34 5JL, UK */\r
+/* ------------------------------------------------------------------ */\r
+/* This module comprises decSingle operations (including conversions) */\r
+/* ------------------------------------------------------------------ */\r
+\r
+#include "decContext.h" // public includes\r
+#include "decSingle.h" // public includes\r
+\r
+/* Constant mappings for shared code */\r
+#define DECPMAX DECSINGLE_Pmax\r
+#define DECEMIN DECSINGLE_Emin\r
+#define DECEMAX DECSINGLE_Emax\r
+#define DECEMAXD DECSINGLE_EmaxD\r
+#define DECBYTES DECSINGLE_Bytes\r
+#define DECSTRING DECSINGLE_String\r
+#define DECECONL DECSINGLE_EconL\r
+#define DECBIAS DECSINGLE_Bias\r
+#define DECLETS DECSINGLE_Declets\r
+#define DECQTINY (-DECSINGLE_Bias)\r
+// parameters of next-wider format\r
+#define DECWBYTES DECDOUBLE_Bytes\r
+#define DECWPMAX DECDOUBLE_Pmax\r
+#define DECWECONL DECDOUBLE_EconL\r
+#define DECWBIAS DECDOUBLE_Bias\r
+\r
+/* Type and function mappings for shared code */\r
+#define decFloat decSingle // Type name\r
+#define decFloatWider decDouble // Type name\r
+\r
+// Utility (binary results, extractors, etc.)\r
+#define decFloatFromBCD decSingleFromBCD\r
+#define decFloatFromPacked decSingleFromPacked\r
+#define decFloatFromPackedChecked decSingleFromPackedChecked\r
+#define decFloatFromString decSingleFromString\r
+#define decFloatFromWider decSingleFromWider\r
+#define decFloatGetCoefficient decSingleGetCoefficient\r
+#define decFloatGetExponent decSingleGetExponent\r
+#define decFloatSetCoefficient decSingleSetCoefficient\r
+#define decFloatSetExponent decSingleSetExponent\r
+#define decFloatShow decSingleShow\r
+#define decFloatToBCD decSingleToBCD\r
+#define decFloatToEngString decSingleToEngString\r
+#define decFloatToPacked decSingleToPacked\r
+#define decFloatToString decSingleToString\r
+#define decFloatToWider decSingleToWider\r
+#define decFloatZero decSingleZero\r
+\r
+// Non-computational\r
+#define decFloatRadix decSingleRadix\r
+#define decFloatVersion decSingleVersion\r
+\r
+#include "decNumberLocal.h" // local includes (need DECPMAX)\r
+#include "decCommon.inc" // non-basic decFloat routines\r
+// [Do not include decBasic.inc for decimal32]\r
+\r
--- /dev/null
+/* ------------------------------------------------------------------ */\r
+/* decSingle.h -- Decimal 32-bit format module header */\r
+/* ------------------------------------------------------------------ */\r
+/* Copyright (c) IBM Corporation, 2000, 2008. All rights reserved. */\r
+/* */\r
+/* This software is made available under the terms of the */\r
+/* ICU License -- ICU 1.8.1 and later. */\r
+/* */\r
+/* The description and User's Guide ("The decNumber C Library") for */\r
+/* this software is included in the package as decNumber.pdf. This */\r
+/* document is also available in HTML, together with specifications, */\r
+/* testcases, and Web links, on the General Decimal Arithmetic page. */\r
+/* */\r
+/* Please send comments, suggestions, and corrections to the author: */\r
+/* mfc@uk.ibm.com */\r
+/* Mike Cowlishaw, IBM Fellow */\r
+/* IBM UK, PO Box 31, Birmingham Road, Warwick CV34 5JL, UK */\r
+/* ------------------------------------------------------------------ */\r
+\r
+#if !defined(DECSINGLE)\r
+ #define DECSINGLE\r
+\r
+ #define DECSINGLENAME "decSingle" /* Short name */\r
+ #define DECSINGLETITLE "Decimal 32-bit datum" /* Verbose name */\r
+ #define DECSINGLEAUTHOR "Mike Cowlishaw" /* Who to blame */\r
+\r
+ /* parameters for decSingles */\r
+ #define DECSINGLE_Bytes 4 /* length */\r
+ #define DECSINGLE_Pmax 7 /* maximum precision (digits) */\r
+ #define DECSINGLE_Emin -95 /* minimum adjusted exponent */\r
+ #define DECSINGLE_Emax 96 /* maximum adjusted exponent */\r
+ #define DECSINGLE_EmaxD 3 /* maximum exponent digits */\r
+ #define DECSINGLE_Bias 101 /* bias for the exponent */\r
+ #define DECSINGLE_String 16 /* maximum string length, +1 */\r
+ #define DECSINGLE_EconL 6 /* exponent continuation length */\r
+ #define DECSINGLE_Declets 2 /* count of declets */\r
+ /* highest biased exponent (Elimit-1) */\r
+ #define DECSINGLE_Ehigh (DECSINGLE_Emax + DECSINGLE_Bias - (DECSINGLE_Pmax-1))\r
+\r
+ /* Required includes */\r
+ #include "decContext.h"\r
+ #include "decQuad.h"\r
+ #include "decDouble.h"\r
+\r
+ /* The decSingle decimal 32-bit type, accessible by all sizes */\r
+ typedef union {\r
+ uint8_t bytes[DECSINGLE_Bytes]; /* fields: 1, 5, 6, 20 bits */\r
+ uint16_t shorts[DECSINGLE_Bytes/2];\r
+ uint32_t words[DECSINGLE_Bytes/4];\r
+ } decSingle;\r
+\r
+ /* ---------------------------------------------------------------- */\r
+ /* Routines -- implemented as decFloat routines in common files */\r
+ /* ---------------------------------------------------------------- */\r
+\r
+ /* Utilities (binary argument(s) or result, extractors, etc.) */\r
+ extern decSingle * decSingleFromBCD(decSingle *, int32_t, const uint8_t *, int32_t);\r
+ extern decSingle * decSingleFromPacked(decSingle *, int32_t, const uint8_t *);\r
+ extern decSingle * decSingleFromPackedChecked(decSingle *, int32_t, const uint8_t *);\r
+ extern decSingle * decSingleFromString(decSingle *, const char *, decContext *);\r
+ extern decSingle * decSingleFromWider(decSingle *, const decDouble *, decContext *);\r
+ extern int32_t decSingleGetCoefficient(const decSingle *, uint8_t *);\r
+ extern int32_t decSingleGetExponent(const decSingle *);\r
+ extern decSingle * decSingleSetCoefficient(decSingle *, const uint8_t *, int32_t);\r
+ extern decSingle * decSingleSetExponent(decSingle *, decContext *, int32_t);\r
+ extern void decSingleShow(const decSingle *, const char *);\r
+ extern int32_t decSingleToBCD(const decSingle *, int32_t *, uint8_t *);\r
+ extern char * decSingleToEngString(const decSingle *, char *);\r
+ extern int32_t decSingleToPacked(const decSingle *, int32_t *, uint8_t *);\r
+ extern char * decSingleToString(const decSingle *, char *);\r
+ extern decDouble * decSingleToWider(const decSingle *, decDouble *);\r
+ extern decSingle * decSingleZero(decSingle *);\r
+\r
+ /* (No Arithmetic routines for decSingle) */\r
+\r
+ /* Non-computational */\r
+ extern uint32_t decSingleRadix(const decSingle *);\r
+ extern const char * decSingleVersion(void);\r
+\r
+ /* decNumber conversions; these are implemented as macros so as not */\r
+ /* to force a dependency on decimal32 and decNumber in decSingle. */\r
+ /* decSingleFromNumber returns a decimal32 * to avoid warnings. */\r
+ #define decSingleToNumber(dq, dn) decimal32ToNumber((decimal32 *)(dq), dn)\r
+ #define decSingleFromNumber(dq, dn, set) decimal32FromNumber((decimal32 *)(dq), dn, set)\r
+\r
+#endif\r
--- /dev/null
+/* ------------------------------------------------------------------ */\r
+/* Decimal 128-bit format module */\r
+/* ------------------------------------------------------------------ */\r
+/* Copyright (c) IBM Corporation, 2000, 2008. All rights reserved. */\r
+/* */\r
+/* This software is made available under the terms of the */\r
+/* ICU License -- ICU 1.8.1 and later. */\r
+/* */\r
+/* The description and User's Guide ("The decNumber C Library") for */\r
+/* this software is called decNumber.pdf. This document is */\r
+/* available, together with arithmetic and format specifications, */\r
+/* testcases, and Web links, on the General Decimal Arithmetic page. */\r
+/* */\r
+/* Please send comments, suggestions, and corrections to the author: */\r
+/* mfc@uk.ibm.com */\r
+/* Mike Cowlishaw, IBM Fellow */\r
+/* IBM UK, PO Box 31, Birmingham Road, Warwick CV34 5JL, UK */\r
+/* ------------------------------------------------------------------ */\r
+/* This module comprises the routines for decimal128 format numbers. */\r
+/* Conversions are supplied to and from decNumber and String. */\r
+/* */\r
+/* This is used when decNumber provides operations, either for all */\r
+/* operations or as a proxy between decNumber and decSingle. */\r
+/* */\r
+/* Error handling is the same as decNumber (qv.). */\r
+/* ------------------------------------------------------------------ */\r
+#include <string.h> // [for memset/memcpy]\r
+#include <stdio.h> // [for printf]\r
+\r
+#define DECNUMDIGITS 34 // make decNumbers with space for 34\r
+#include "decNumber.h" // base number library\r
+#include "decNumberLocal.h" // decNumber local types, etc.\r
+#include "decimal128.h" // our primary include\r
+\r
+/* Utility routines and tables [in decimal64.c] */\r
+// DPD2BIN and the reverse are renamed to prevent link-time conflict\r
+// if decQuad is also built in the same executable\r
+#define DPD2BIN DPD2BINx\r
+#define BIN2DPD BIN2DPDx\r
+extern const uInt COMBEXP[32], COMBMSD[32];\r
+extern const uShort DPD2BIN[1024];\r
+extern const uShort BIN2DPD[1000]; // [not used]\r
+extern const uByte BIN2CHAR[4001];\r
+\r
+extern void decDigitsFromDPD(decNumber *, const uInt *, Int);\r
+extern void decDigitsToDPD(const decNumber *, uInt *, Int);\r
+\r
+#if DECTRACE || DECCHECK\r
+void decimal128Show(const decimal128 *); // for debug\r
+extern void decNumberShow(const decNumber *); // ..\r
+#endif\r
+\r
+/* Useful macro */\r
+// Clear a structure (e.g., a decNumber)\r
+#define DEC_clear(d) memset(d, 0, sizeof(*d))\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decimal128FromNumber -- convert decNumber to decimal128 */\r
+/* */\r
+/* ds is the target decimal128 */\r
+/* dn is the source number (assumed valid) */\r
+/* set is the context, used only for reporting errors */\r
+/* */\r
+/* The set argument is used only for status reporting and for the */\r
+/* rounding mode (used if the coefficient is more than DECIMAL128_Pmax*/\r
+/* digits or an overflow is detected). If the exponent is out of the */\r
+/* valid range then Overflow or Underflow will be raised. */\r
+/* After Underflow a subnormal result is possible. */\r
+/* */\r
+/* DEC_Clamped is set if the number has to be 'folded down' to fit, */\r
+/* by reducing its exponent and multiplying the coefficient by a */\r
+/* power of ten, or if the exponent on a zero had to be clamped. */\r
+/* ------------------------------------------------------------------ */\r
+decimal128 * decimal128FromNumber(decimal128 *d128, const decNumber *dn,\r
+ decContext *set) {\r
+ uInt status=0; // status accumulator\r
+ Int ae; // adjusted exponent\r
+ decNumber dw; // work\r
+ decContext dc; // ..\r
+ uInt comb, exp; // ..\r
+ uInt uiwork; // for macros\r
+ uInt targar[4]={0,0,0,0}; // target 128-bit\r
+ #define targhi targar[3] // name the word with the sign\r
+ #define targmh targar[2] // name the words\r
+ #define targml targar[1] // ..\r
+ #define targlo targar[0] // ..\r
+\r
+ // If the number has too many digits, or the exponent could be\r
+ // out of range then reduce the number under the appropriate\r
+ // constraints. This could push the number to Infinity or zero,\r
+ // so this check and rounding must be done before generating the\r
+ // decimal128]\r
+ ae=dn->exponent+dn->digits-1; // [0 if special]\r
+ if (dn->digits>DECIMAL128_Pmax // too many digits\r
+ || ae>DECIMAL128_Emax // likely overflow\r
+ || ae<DECIMAL128_Emin) { // likely underflow\r
+ decContextDefault(&dc, DEC_INIT_DECIMAL128); // [no traps]\r
+ dc.round=set->round; // use supplied rounding\r
+ decNumberPlus(&dw, dn, &dc); // (round and check)\r
+ // [this changes -0 to 0, so enforce the sign...]\r
+ dw.bits|=dn->bits&DECNEG;\r
+ status=dc.status; // save status\r
+ dn=&dw; // use the work number\r
+ } // maybe out of range\r
+\r
+ if (dn->bits&DECSPECIAL) { // a special value\r
+ if (dn->bits&DECINF) targhi=DECIMAL_Inf<<24;\r
+ else { // sNaN or qNaN\r
+ if ((*dn->lsu!=0 || dn->digits>1) // non-zero coefficient\r
+ && (dn->digits<DECIMAL128_Pmax)) { // coefficient fits\r
+ decDigitsToDPD(dn, targar, 0);\r
+ }\r
+ if (dn->bits&DECNAN) targhi|=DECIMAL_NaN<<24;\r
+ else targhi|=DECIMAL_sNaN<<24;\r
+ } // a NaN\r
+ } // special\r
+\r
+ else { // is finite\r
+ if (decNumberIsZero(dn)) { // is a zero\r
+ // set and clamp exponent\r
+ if (dn->exponent<-DECIMAL128_Bias) {\r
+ exp=0; // low clamp\r
+ status|=DEC_Clamped;\r
+ }\r
+ else {\r
+ exp=dn->exponent+DECIMAL128_Bias; // bias exponent\r
+ if (exp>DECIMAL128_Ehigh) { // top clamp\r
+ exp=DECIMAL128_Ehigh;\r
+ status|=DEC_Clamped;\r
+ }\r
+ }\r
+ comb=(exp>>9) & 0x18; // msd=0, exp top 2 bits ..\r
+ }\r
+ else { // non-zero finite number\r
+ uInt msd; // work\r
+ Int pad=0; // coefficient pad digits\r
+\r
+ // the dn is known to fit, but it may need to be padded\r
+ exp=(uInt)(dn->exponent+DECIMAL128_Bias); // bias exponent\r
+ if (exp>DECIMAL128_Ehigh) { // fold-down case\r
+ pad=exp-DECIMAL128_Ehigh;\r
+ exp=DECIMAL128_Ehigh; // [to maximum]\r
+ status|=DEC_Clamped;\r
+ }\r
+\r
+ // [fastpath for common case is not a win, here]\r
+ decDigitsToDPD(dn, targar, pad);\r
+ // save and clear the top digit\r
+ msd=targhi>>14;\r
+ targhi&=0x00003fff;\r
+\r
+ // create the combination field\r
+ if (msd>=8) comb=0x18 | ((exp>>11) & 0x06) | (msd & 0x01);\r
+ else comb=((exp>>9) & 0x18) | msd;\r
+ }\r
+ targhi|=comb<<26; // add combination field ..\r
+ targhi|=(exp&0xfff)<<14; // .. and exponent continuation\r
+ } // finite\r
+\r
+ if (dn->bits&DECNEG) targhi|=0x80000000; // add sign bit\r
+\r
+ // now write to storage; this is endian\r
+ if (DECLITEND) {\r
+ // lo -> hi\r
+ UBFROMUI(d128->bytes, targlo);\r
+ UBFROMUI(d128->bytes+4, targml);\r
+ UBFROMUI(d128->bytes+8, targmh);\r
+ UBFROMUI(d128->bytes+12, targhi);\r
+ }\r
+ else {\r
+ // hi -> lo\r
+ UBFROMUI(d128->bytes, targhi);\r
+ UBFROMUI(d128->bytes+4, targmh);\r
+ UBFROMUI(d128->bytes+8, targml);\r
+ UBFROMUI(d128->bytes+12, targlo);\r
+ }\r
+\r
+ if (status!=0) decContextSetStatus(set, status); // pass on status\r
+ // decimal128Show(d128);\r
+ return d128;\r
+ } // decimal128FromNumber\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decimal128ToNumber -- convert decimal128 to decNumber */\r
+/* d128 is the source decimal128 */\r
+/* dn is the target number, with appropriate space */\r
+/* No error is possible. */\r
+/* ------------------------------------------------------------------ */\r
+decNumber * decimal128ToNumber(const decimal128 *d128, decNumber *dn) {\r
+ uInt msd; // coefficient MSD\r
+ uInt exp; // exponent top two bits\r
+ uInt comb; // combination field\r
+ Int need; // work\r
+ uInt uiwork; // for macros\r
+ uInt sourar[4]; // source 128-bit\r
+ #define sourhi sourar[3] // name the word with the sign\r
+ #define sourmh sourar[2] // and the mid-high word\r
+ #define sourml sourar[1] // and the mod-low word\r
+ #define sourlo sourar[0] // and the lowest word\r
+\r
+ // load source from storage; this is endian\r
+ if (DECLITEND) {\r
+ sourlo=UBTOUI(d128->bytes ); // directly load the low int\r
+ sourml=UBTOUI(d128->bytes+4 ); // then the mid-low\r
+ sourmh=UBTOUI(d128->bytes+8 ); // then the mid-high\r
+ sourhi=UBTOUI(d128->bytes+12); // then the high int\r
+ }\r
+ else {\r
+ sourhi=UBTOUI(d128->bytes ); // directly load the high int\r
+ sourmh=UBTOUI(d128->bytes+4 ); // then the mid-high\r
+ sourml=UBTOUI(d128->bytes+8 ); // then the mid-low\r
+ sourlo=UBTOUI(d128->bytes+12); // then the low int\r
+ }\r
+\r
+ comb=(sourhi>>26)&0x1f; // combination field\r
+\r
+ decNumberZero(dn); // clean number\r
+ if (sourhi&0x80000000) dn->bits=DECNEG; // set sign if negative\r
+\r
+ msd=COMBMSD[comb]; // decode the combination field\r
+ exp=COMBEXP[comb]; // ..\r
+\r
+ if (exp==3) { // is a special\r
+ if (msd==0) {\r
+ dn->bits|=DECINF;\r
+ return dn; // no coefficient needed\r
+ }\r
+ else if (sourhi&0x02000000) dn->bits|=DECSNAN;\r
+ else dn->bits|=DECNAN;\r
+ msd=0; // no top digit\r
+ }\r
+ else { // is a finite number\r
+ dn->exponent=(exp<<12)+((sourhi>>14)&0xfff)-DECIMAL128_Bias; // unbiased\r
+ }\r
+\r
+ // get the coefficient\r
+ sourhi&=0x00003fff; // clean coefficient continuation\r
+ if (msd) { // non-zero msd\r
+ sourhi|=msd<<14; // prefix to coefficient\r
+ need=12; // process 12 declets\r
+ }\r
+ else { // msd=0\r
+ if (sourhi) need=11; // declets to process\r
+ else if (sourmh) need=10;\r
+ else if (sourml) need=7;\r
+ else if (sourlo) need=4;\r
+ else return dn; // easy: coefficient is 0\r
+ } //msd=0\r
+\r
+ decDigitsFromDPD(dn, sourar, need); // process declets\r
+ // decNumberShow(dn);\r
+ return dn;\r
+ } // decimal128ToNumber\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* to-scientific-string -- conversion to numeric string */\r
+/* to-engineering-string -- conversion to numeric string */\r
+/* */\r
+/* decimal128ToString(d128, string); */\r
+/* decimal128ToEngString(d128, string); */\r
+/* */\r
+/* d128 is the decimal128 format number to convert */\r
+/* string is the string where the result will be laid out */\r
+/* */\r
+/* string must be at least 24 characters */\r
+/* */\r
+/* No error is possible, and no status can be set. */\r
+/* ------------------------------------------------------------------ */\r
+char * decimal128ToEngString(const decimal128 *d128, char *string){\r
+ decNumber dn; // work\r
+ decimal128ToNumber(d128, &dn);\r
+ decNumberToEngString(&dn, string);\r
+ return string;\r
+ } // decimal128ToEngString\r
+\r
+char * decimal128ToString(const decimal128 *d128, char *string){\r
+ uInt msd; // coefficient MSD\r
+ Int exp; // exponent top two bits or full\r
+ uInt comb; // combination field\r
+ char *cstart; // coefficient start\r
+ char *c; // output pointer in string\r
+ const uByte *u; // work\r
+ char *s, *t; // .. (source, target)\r
+ Int dpd; // ..\r
+ Int pre, e; // ..\r
+ uInt uiwork; // for macros\r
+\r
+ uInt sourar[4]; // source 128-bit\r
+ #define sourhi sourar[3] // name the word with the sign\r
+ #define sourmh sourar[2] // and the mid-high word\r
+ #define sourml sourar[1] // and the mod-low word\r
+ #define sourlo sourar[0] // and the lowest word\r
+\r
+ // load source from storage; this is endian\r
+ if (DECLITEND) {\r
+ sourlo=UBTOUI(d128->bytes ); // directly load the low int\r
+ sourml=UBTOUI(d128->bytes+4 ); // then the mid-low\r
+ sourmh=UBTOUI(d128->bytes+8 ); // then the mid-high\r
+ sourhi=UBTOUI(d128->bytes+12); // then the high int\r
+ }\r
+ else {\r
+ sourhi=UBTOUI(d128->bytes ); // directly load the high int\r
+ sourmh=UBTOUI(d128->bytes+4 ); // then the mid-high\r
+ sourml=UBTOUI(d128->bytes+8 ); // then the mid-low\r
+ sourlo=UBTOUI(d128->bytes+12); // then the low int\r
+ }\r
+\r
+ c=string; // where result will go\r
+ if (((Int)sourhi)<0) *c++='-'; // handle sign\r
+\r
+ comb=(sourhi>>26)&0x1f; // combination field\r
+ msd=COMBMSD[comb]; // decode the combination field\r
+ exp=COMBEXP[comb]; // ..\r
+\r
+ if (exp==3) {\r
+ if (msd==0) { // infinity\r
+ strcpy(c, "Inf");\r
+ strcpy(c+3, "inity");\r
+ return string; // easy\r
+ }\r
+ if (sourhi&0x02000000) *c++='s'; // sNaN\r
+ strcpy(c, "NaN"); // complete word\r
+ c+=3; // step past\r
+ if (sourlo==0 && sourml==0 && sourmh==0\r
+ && (sourhi&0x0003ffff)==0) return string; // zero payload\r
+ // otherwise drop through to add integer; set correct exp\r
+ exp=0; msd=0; // setup for following code\r
+ }\r
+ else exp=(exp<<12)+((sourhi>>14)&0xfff)-DECIMAL128_Bias; // unbiased\r
+\r
+ // convert 34 digits of significand to characters\r
+ cstart=c; // save start of coefficient\r
+ if (msd) *c++='0'+(char)msd; // non-zero most significant digit\r
+\r
+ // Now decode the declets. After extracting each one, it is\r
+ // decoded to binary and then to a 4-char sequence by table lookup;\r
+ // the 4-chars are a 1-char length (significant digits, except 000\r
+ // has length 0). This allows us to left-align the first declet\r
+ // with non-zero content, then remaining ones are full 3-char\r
+ // length. We use fixed-length memcpys because variable-length\r
+ // causes a subroutine call in GCC. (These are length 4 for speed\r
+ // and are safe because the array has an extra terminator byte.)\r
+ #define dpd2char u=&BIN2CHAR[DPD2BIN[dpd]*4]; \\r
+ if (c!=cstart) {memcpy(c, u+1, 4); c+=3;} \\r
+ else if (*u) {memcpy(c, u+4-*u, 4); c+=*u;}\r
+ dpd=(sourhi>>4)&0x3ff; // declet 1\r
+ dpd2char;\r
+ dpd=((sourhi&0xf)<<6) | (sourmh>>26); // declet 2\r
+ dpd2char;\r
+ dpd=(sourmh>>16)&0x3ff; // declet 3\r
+ dpd2char;\r
+ dpd=(sourmh>>6)&0x3ff; // declet 4\r
+ dpd2char;\r
+ dpd=((sourmh&0x3f)<<4) | (sourml>>28); // declet 5\r
+ dpd2char;\r
+ dpd=(sourml>>18)&0x3ff; // declet 6\r
+ dpd2char;\r
+ dpd=(sourml>>8)&0x3ff; // declet 7\r
+ dpd2char;\r
+ dpd=((sourml&0xff)<<2) | (sourlo>>30); // declet 8\r
+ dpd2char;\r
+ dpd=(sourlo>>20)&0x3ff; // declet 9\r
+ dpd2char;\r
+ dpd=(sourlo>>10)&0x3ff; // declet 10\r
+ dpd2char;\r
+ dpd=(sourlo)&0x3ff; // declet 11\r
+ dpd2char;\r
+\r
+ if (c==cstart) *c++='0'; // all zeros -- make 0\r
+\r
+ if (exp==0) { // integer or NaN case -- easy\r
+ *c='\0'; // terminate\r
+ return string;\r
+ }\r
+\r
+ /* non-0 exponent */\r
+ e=0; // assume no E\r
+ pre=c-cstart+exp;\r
+ // [here, pre-exp is the digits count (==1 for zero)]\r
+ if (exp>0 || pre<-5) { // need exponential form\r
+ e=pre-1; // calculate E value\r
+ pre=1; // assume one digit before '.'\r
+ } // exponential form\r
+\r
+ /* modify the coefficient, adding 0s, '.', and E+nn as needed */\r
+ s=c-1; // source (LSD)\r
+ if (pre>0) { // ddd.ddd (plain), perhaps with E\r
+ char *dotat=cstart+pre;\r
+ if (dotat<c) { // if embedded dot needed...\r
+ t=c; // target\r
+ for (; s>=dotat; s--, t--) *t=*s; // open the gap; leave t at gap\r
+ *t='.'; // insert the dot\r
+ c++; // length increased by one\r
+ }\r
+\r
+ // finally add the E-part, if needed; it will never be 0, and has\r
+ // a maximum length of 4 digits\r
+ if (e!=0) {\r
+ *c++='E'; // starts with E\r
+ *c++='+'; // assume positive\r
+ if (e<0) {\r
+ *(c-1)='-'; // oops, need '-'\r
+ e=-e; // uInt, please\r
+ }\r
+ if (e<1000) { // 3 (or fewer) digits case\r
+ u=&BIN2CHAR[e*4]; // -> length byte\r
+ memcpy(c, u+4-*u, 4); // copy fixed 4 characters [is safe]\r
+ c+=*u; // bump pointer appropriately\r
+ }\r
+ else { // 4-digits\r
+ Int thou=((e>>3)*1049)>>17; // e/1000\r
+ Int rem=e-(1000*thou); // e%1000\r
+ *c++='0'+(char)thou;\r
+ u=&BIN2CHAR[rem*4]; // -> length byte\r
+ memcpy(c, u+1, 4); // copy fixed 3+1 characters [is safe]\r
+ c+=3; // bump pointer, always 3 digits\r
+ }\r
+ }\r
+ *c='\0'; // add terminator\r
+ //printf("res %s\n", string);\r
+ return string;\r
+ } // pre>0\r
+\r
+ /* -5<=pre<=0: here for plain 0.ddd or 0.000ddd forms (can never have E) */\r
+ t=c+1-pre;\r
+ *(t+1)='\0'; // can add terminator now\r
+ for (; s>=cstart; s--, t--) *t=*s; // shift whole coefficient right\r
+ c=cstart;\r
+ *c++='0'; // always starts with 0.\r
+ *c++='.';\r
+ for (; pre<0; pre++) *c++='0'; // add any 0's after '.'\r
+ //printf("res %s\n", string);\r
+ return string;\r
+ } // decimal128ToString\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* to-number -- conversion from numeric string */\r
+/* */\r
+/* decimal128FromString(result, string, set); */\r
+/* */\r
+/* result is the decimal128 format number which gets the result of */\r
+/* the conversion */\r
+/* *string is the character string which should contain a valid */\r
+/* number (which may be a special value) */\r
+/* set is the context */\r
+/* */\r
+/* The context is supplied to this routine is used for error handling */\r
+/* (setting of status and traps) and for the rounding mode, only. */\r
+/* If an error occurs, the result will be a valid decimal128 NaN. */\r
+/* ------------------------------------------------------------------ */\r
+decimal128 * decimal128FromString(decimal128 *result, const char *string,\r
+ decContext *set) {\r
+ decContext dc; // work\r
+ decNumber dn; // ..\r
+\r
+ decContextDefault(&dc, DEC_INIT_DECIMAL128); // no traps, please\r
+ dc.round=set->round; // use supplied rounding\r
+\r
+ decNumberFromString(&dn, string, &dc); // will round if needed\r
+ decimal128FromNumber(result, &dn, &dc);\r
+ if (dc.status!=0) { // something happened\r
+ decContextSetStatus(set, dc.status); // .. pass it on\r
+ }\r
+ return result;\r
+ } // decimal128FromString\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decimal128IsCanonical -- test whether encoding is canonical */\r
+/* d128 is the source decimal128 */\r
+/* returns 1 if the encoding of d128 is canonical, 0 otherwise */\r
+/* No error is possible. */\r
+/* ------------------------------------------------------------------ */\r
+uInt decimal128IsCanonical(const decimal128 *d128) {\r
+ decNumber dn; // work\r
+ decimal128 canon; // ..\r
+ decContext dc; // ..\r
+ decContextDefault(&dc, DEC_INIT_DECIMAL128);\r
+ decimal128ToNumber(d128, &dn);\r
+ decimal128FromNumber(&canon, &dn, &dc);// canon will now be canonical\r
+ return memcmp(d128, &canon, DECIMAL128_Bytes)==0;\r
+ } // decimal128IsCanonical\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decimal128Canonical -- copy an encoding, ensuring it is canonical */\r
+/* d128 is the source decimal128 */\r
+/* result is the target (may be the same decimal128) */\r
+/* returns result */\r
+/* No error is possible. */\r
+/* ------------------------------------------------------------------ */\r
+decimal128 * decimal128Canonical(decimal128 *result, const decimal128 *d128) {\r
+ decNumber dn; // work\r
+ decContext dc; // ..\r
+ decContextDefault(&dc, DEC_INIT_DECIMAL128);\r
+ decimal128ToNumber(d128, &dn);\r
+ decimal128FromNumber(result, &dn, &dc);// result will now be canonical\r
+ return result;\r
+ } // decimal128Canonical\r
+\r
+#if DECTRACE || DECCHECK\r
+/* Macros for accessing decimal128 fields. These assume the argument\r
+ is a reference (pointer) to the decimal128 structure, and the\r
+ decimal128 is in network byte order (big-endian) */\r
+// Get sign\r
+#define decimal128Sign(d) ((unsigned)(d)->bytes[0]>>7)\r
+\r
+// Get combination field\r
+#define decimal128Comb(d) (((d)->bytes[0] & 0x7c)>>2)\r
+\r
+// Get exponent continuation [does not remove bias]\r
+#define decimal128ExpCon(d) ((((d)->bytes[0] & 0x03)<<10) \\r
+ | ((unsigned)(d)->bytes[1]<<2) \\r
+ | ((unsigned)(d)->bytes[2]>>6))\r
+\r
+// Set sign [this assumes sign previously 0]\r
+#define decimal128SetSign(d, b) { \\r
+ (d)->bytes[0]|=((unsigned)(b)<<7);}\r
+\r
+// Set exponent continuation [does not apply bias]\r
+// This assumes range has been checked and exponent previously 0;\r
+// type of exponent must be unsigned\r
+#define decimal128SetExpCon(d, e) { \\r
+ (d)->bytes[0]|=(uByte)((e)>>10); \\r
+ (d)->bytes[1] =(uByte)(((e)&0x3fc)>>2); \\r
+ (d)->bytes[2]|=(uByte)(((e)&0x03)<<6);}\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decimal128Show -- display a decimal128 in hexadecimal [debug aid] */\r
+/* d128 -- the number to show */\r
+/* ------------------------------------------------------------------ */\r
+// Also shows sign/cob/expconfields extracted\r
+void decimal128Show(const decimal128 *d128) {\r
+ char buf[DECIMAL128_Bytes*2+1];\r
+ Int i, j=0;\r
+\r
+ if (DECLITEND) {\r
+ for (i=0; i<DECIMAL128_Bytes; i++, j+=2) {\r
+ sprintf(&buf[j], "%02x", d128->bytes[15-i]);\r
+ }\r
+ printf(" D128> %s [S:%d Cb:%02x Ec:%02x] LittleEndian\n", buf,\r
+ d128->bytes[15]>>7, (d128->bytes[15]>>2)&0x1f,\r
+ ((d128->bytes[15]&0x3)<<10)|(d128->bytes[14]<<2)|\r
+ (d128->bytes[13]>>6));\r
+ }\r
+ else {\r
+ for (i=0; i<DECIMAL128_Bytes; i++, j+=2) {\r
+ sprintf(&buf[j], "%02x", d128->bytes[i]);\r
+ }\r
+ printf(" D128> %s [S:%d Cb:%02x Ec:%02x] BigEndian\n", buf,\r
+ decimal128Sign(d128), decimal128Comb(d128),\r
+ decimal128ExpCon(d128));\r
+ }\r
+ } // decimal128Show\r
+#endif\r
--- /dev/null
+/* ------------------------------------------------------------------ */\r
+/* Decimal 128-bit format module header */\r
+/* ------------------------------------------------------------------ */\r
+/* Copyright (c) IBM Corporation, 2000, 2005. All rights reserved. */\r
+/* */\r
+/* This software is made available under the terms of the */\r
+/* ICU License -- ICU 1.8.1 and later. */\r
+/* */\r
+/* The description and User's Guide ("The decNumber C Library") for */\r
+/* this software is called decNumber.pdf. This document is */\r
+/* available, together with arithmetic and format specifications, */\r
+/* testcases, and Web links, on the General Decimal Arithmetic page. */\r
+/* */\r
+/* Please send comments, suggestions, and corrections to the author: */\r
+/* mfc@uk.ibm.com */\r
+/* Mike Cowlishaw, IBM Fellow */\r
+/* IBM UK, PO Box 31, Birmingham Road, Warwick CV34 5JL, UK */\r
+/* ------------------------------------------------------------------ */\r
+\r
+#if !defined(DECIMAL128)\r
+ #define DECIMAL128\r
+ #define DEC128NAME "decimal128" /* Short name */\r
+ #define DEC128FULLNAME "Decimal 128-bit Number" /* Verbose name */\r
+ #define DEC128AUTHOR "Mike Cowlishaw" /* Who to blame */\r
+\r
+ /* parameters for decimal128s */\r
+ #define DECIMAL128_Bytes 16 /* length */\r
+ #define DECIMAL128_Pmax 34 /* maximum precision (digits) */\r
+ #define DECIMAL128_Emax 6144 /* maximum adjusted exponent */\r
+ #define DECIMAL128_Emin -6143 /* minimum adjusted exponent */\r
+ #define DECIMAL128_Bias 6176 /* bias for the exponent */\r
+ #define DECIMAL128_String 43 /* maximum string length, +1 */\r
+ #define DECIMAL128_EconL 12 /* exp. continuation length */\r
+ /* highest biased exponent (Elimit-1) */\r
+ #define DECIMAL128_Ehigh (DECIMAL128_Emax+DECIMAL128_Bias-DECIMAL128_Pmax+1)\r
+\r
+ /* check enough digits, if pre-defined */\r
+ #if defined(DECNUMDIGITS)\r
+ #if (DECNUMDIGITS<DECIMAL128_Pmax)\r
+ #error decimal128.h needs pre-defined DECNUMDIGITS>=34 for safe use\r
+ #endif\r
+ #endif\r
+\r
+ #ifndef DECNUMDIGITS\r
+ #define DECNUMDIGITS DECIMAL128_Pmax /* size if not already defined*/\r
+ #endif\r
+ #ifndef DECNUMBER\r
+ #include "decNumber.h" /* context and number library */\r
+ #endif\r
+\r
+ /* Decimal 128-bit type, accessible by bytes */\r
+ typedef struct {\r
+ uint8_t bytes[DECIMAL128_Bytes]; /* decimal128: 1, 5, 12, 110 bits*/\r
+ } decimal128;\r
+\r
+ /* special values [top byte excluding sign bit; last two bits are */\r
+ /* don't-care for Infinity on input, last bit don't-care for NaN] */\r
+ #if !defined(DECIMAL_NaN)\r
+ #define DECIMAL_NaN 0x7c /* 0 11111 00 NaN */\r
+ #define DECIMAL_sNaN 0x7e /* 0 11111 10 sNaN */\r
+ #define DECIMAL_Inf 0x78 /* 0 11110 00 Infinity */\r
+ #endif\r
+\r
+ /* ---------------------------------------------------------------- */\r
+ /* Routines */\r
+ /* ---------------------------------------------------------------- */\r
+ /* String conversions */\r
+ decimal128 * decimal128FromString(decimal128 *, const char *, decContext *);\r
+ char * decimal128ToString(const decimal128 *, char *);\r
+ char * decimal128ToEngString(const decimal128 *, char *);\r
+\r
+ /* decNumber conversions */\r
+ decimal128 * decimal128FromNumber(decimal128 *, const decNumber *,\r
+ decContext *);\r
+ decNumber * decimal128ToNumber(const decimal128 *, decNumber *);\r
+\r
+ /* Format-dependent utilities */\r
+ uint32_t decimal128IsCanonical(const decimal128 *);\r
+ decimal128 * decimal128Canonical(decimal128 *, const decimal128 *);\r
+\r
+#endif\r
--- /dev/null
+/* ------------------------------------------------------------------ */\r
+/* Decimal 32-bit format module */\r
+/* ------------------------------------------------------------------ */\r
+/* Copyright (c) IBM Corporation, 2000, 2008. All rights reserved. */\r
+/* */\r
+/* This software is made available under the terms of the */\r
+/* ICU License -- ICU 1.8.1 and later. */\r
+/* */\r
+/* The description and User's Guide ("The decNumber C Library") for */\r
+/* this software is called decNumber.pdf. This document is */\r
+/* available, together with arithmetic and format specifications, */\r
+/* testcases, and Web links, on the General Decimal Arithmetic page. */\r
+/* */\r
+/* Please send comments, suggestions, and corrections to the author: */\r
+/* mfc@uk.ibm.com */\r
+/* Mike Cowlishaw, IBM Fellow */\r
+/* IBM UK, PO Box 31, Birmingham Road, Warwick CV34 5JL, UK */\r
+/* ------------------------------------------------------------------ */\r
+/* This module comprises the routines for decimal32 format numbers. */\r
+/* Conversions are supplied to and from decNumber and String. */\r
+/* */\r
+/* This is used when decNumber provides operations, either for all */\r
+/* operations or as a proxy between decNumber and decSingle. */\r
+/* */\r
+/* Error handling is the same as decNumber (qv.). */\r
+/* ------------------------------------------------------------------ */\r
+#include <string.h> // [for memset/memcpy]\r
+#include <stdio.h> // [for printf]\r
+\r
+#define DECNUMDIGITS 7 // make decNumbers with space for 7\r
+#include "decNumber.h" // base number library\r
+#include "decNumberLocal.h" // decNumber local types, etc.\r
+#include "decimal32.h" // our primary include\r
+\r
+/* Utility tables and routines [in decimal64.c] */\r
+// DPD2BIN and the reverse are renamed to prevent link-time conflict\r
+// if decQuad is also built in the same executable\r
+#define DPD2BIN DPD2BINx\r
+#define BIN2DPD BIN2DPDx\r
+extern const uInt COMBEXP[32], COMBMSD[32];\r
+extern const uShort DPD2BIN[1024];\r
+extern const uShort BIN2DPD[1000];\r
+extern const uByte BIN2CHAR[4001];\r
+\r
+extern void decDigitsToDPD(const decNumber *, uInt *, Int);\r
+extern void decDigitsFromDPD(decNumber *, const uInt *, Int);\r
+\r
+#if DECTRACE || DECCHECK\r
+void decimal32Show(const decimal32 *); // for debug\r
+extern void decNumberShow(const decNumber *); // ..\r
+#endif\r
+\r
+/* Useful macro */\r
+// Clear a structure (e.g., a decNumber)\r
+#define DEC_clear(d) memset(d, 0, sizeof(*d))\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decimal32FromNumber -- convert decNumber to decimal32 */\r
+/* */\r
+/* ds is the target decimal32 */\r
+/* dn is the source number (assumed valid) */\r
+/* set is the context, used only for reporting errors */\r
+/* */\r
+/* The set argument is used only for status reporting and for the */\r
+/* rounding mode (used if the coefficient is more than DECIMAL32_Pmax */\r
+/* digits or an overflow is detected). If the exponent is out of the */\r
+/* valid range then Overflow or Underflow will be raised. */\r
+/* After Underflow a subnormal result is possible. */\r
+/* */\r
+/* DEC_Clamped is set if the number has to be 'folded down' to fit, */\r
+/* by reducing its exponent and multiplying the coefficient by a */\r
+/* power of ten, or if the exponent on a zero had to be clamped. */\r
+/* ------------------------------------------------------------------ */\r
+decimal32 * decimal32FromNumber(decimal32 *d32, const decNumber *dn,\r
+ decContext *set) {\r
+ uInt status=0; // status accumulator\r
+ Int ae; // adjusted exponent\r
+ decNumber dw; // work\r
+ decContext dc; // ..\r
+ uInt comb, exp; // ..\r
+ uInt uiwork; // for macros\r
+ uInt targ=0; // target 32-bit\r
+\r
+ // If the number has too many digits, or the exponent could be\r
+ // out of range then reduce the number under the appropriate\r
+ // constraints. This could push the number to Infinity or zero,\r
+ // so this check and rounding must be done before generating the\r
+ // decimal32]\r
+ ae=dn->exponent+dn->digits-1; // [0 if special]\r
+ if (dn->digits>DECIMAL32_Pmax // too many digits\r
+ || ae>DECIMAL32_Emax // likely overflow\r
+ || ae<DECIMAL32_Emin) { // likely underflow\r
+ decContextDefault(&dc, DEC_INIT_DECIMAL32); // [no traps]\r
+ dc.round=set->round; // use supplied rounding\r
+ decNumberPlus(&dw, dn, &dc); // (round and check)\r
+ // [this changes -0 to 0, so enforce the sign...]\r
+ dw.bits|=dn->bits&DECNEG;\r
+ status=dc.status; // save status\r
+ dn=&dw; // use the work number\r
+ } // maybe out of range\r
+\r
+ if (dn->bits&DECSPECIAL) { // a special value\r
+ if (dn->bits&DECINF) targ=DECIMAL_Inf<<24;\r
+ else { // sNaN or qNaN\r
+ if ((*dn->lsu!=0 || dn->digits>1) // non-zero coefficient\r
+ && (dn->digits<DECIMAL32_Pmax)) { // coefficient fits\r
+ decDigitsToDPD(dn, &targ, 0);\r
+ }\r
+ if (dn->bits&DECNAN) targ|=DECIMAL_NaN<<24;\r
+ else targ|=DECIMAL_sNaN<<24;\r
+ } // a NaN\r
+ } // special\r
+\r
+ else { // is finite\r
+ if (decNumberIsZero(dn)) { // is a zero\r
+ // set and clamp exponent\r
+ if (dn->exponent<-DECIMAL32_Bias) {\r
+ exp=0; // low clamp\r
+ status|=DEC_Clamped;\r
+ }\r
+ else {\r
+ exp=dn->exponent+DECIMAL32_Bias; // bias exponent\r
+ if (exp>DECIMAL32_Ehigh) { // top clamp\r
+ exp=DECIMAL32_Ehigh;\r
+ status|=DEC_Clamped;\r
+ }\r
+ }\r
+ comb=(exp>>3) & 0x18; // msd=0, exp top 2 bits ..\r
+ }\r
+ else { // non-zero finite number\r
+ uInt msd; // work\r
+ Int pad=0; // coefficient pad digits\r
+\r
+ // the dn is known to fit, but it may need to be padded\r
+ exp=(uInt)(dn->exponent+DECIMAL32_Bias); // bias exponent\r
+ if (exp>DECIMAL32_Ehigh) { // fold-down case\r
+ pad=exp-DECIMAL32_Ehigh;\r
+ exp=DECIMAL32_Ehigh; // [to maximum]\r
+ status|=DEC_Clamped;\r
+ }\r
+\r
+ // fastpath common case\r
+ if (DECDPUN==3 && pad==0) {\r
+ targ=BIN2DPD[dn->lsu[0]];\r
+ if (dn->digits>3) targ|=(uInt)(BIN2DPD[dn->lsu[1]])<<10;\r
+ msd=(dn->digits==7 ? dn->lsu[2] : 0);\r
+ }\r
+ else { // general case\r
+ decDigitsToDPD(dn, &targ, pad);\r
+ // save and clear the top digit\r
+ msd=targ>>20;\r
+ targ&=0x000fffff;\r
+ }\r
+\r
+ // create the combination field\r
+ if (msd>=8) comb=0x18 | ((exp>>5) & 0x06) | (msd & 0x01);\r
+ else comb=((exp>>3) & 0x18) | msd;\r
+ }\r
+ targ|=comb<<26; // add combination field ..\r
+ targ|=(exp&0x3f)<<20; // .. and exponent continuation\r
+ } // finite\r
+\r
+ if (dn->bits&DECNEG) targ|=0x80000000; // add sign bit\r
+\r
+ // now write to storage; this is endian\r
+ UBFROMUI(d32->bytes, targ); // directly store the int\r
+\r
+ if (status!=0) decContextSetStatus(set, status); // pass on status\r
+ // decimal32Show(d32);\r
+ return d32;\r
+ } // decimal32FromNumber\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decimal32ToNumber -- convert decimal32 to decNumber */\r
+/* d32 is the source decimal32 */\r
+/* dn is the target number, with appropriate space */\r
+/* No error is possible. */\r
+/* ------------------------------------------------------------------ */\r
+decNumber * decimal32ToNumber(const decimal32 *d32, decNumber *dn) {\r
+ uInt msd; // coefficient MSD\r
+ uInt exp; // exponent top two bits\r
+ uInt comb; // combination field\r
+ uInt sour; // source 32-bit\r
+ uInt uiwork; // for macros\r
+\r
+ // load source from storage; this is endian\r
+ sour=UBTOUI(d32->bytes); // directly load the int\r
+\r
+ comb=(sour>>26)&0x1f; // combination field\r
+\r
+ decNumberZero(dn); // clean number\r
+ if (sour&0x80000000) dn->bits=DECNEG; // set sign if negative\r
+\r
+ msd=COMBMSD[comb]; // decode the combination field\r
+ exp=COMBEXP[comb]; // ..\r
+\r
+ if (exp==3) { // is a special\r
+ if (msd==0) {\r
+ dn->bits|=DECINF;\r
+ return dn; // no coefficient needed\r
+ }\r
+ else if (sour&0x02000000) dn->bits|=DECSNAN;\r
+ else dn->bits|=DECNAN;\r
+ msd=0; // no top digit\r
+ }\r
+ else { // is a finite number\r
+ dn->exponent=(exp<<6)+((sour>>20)&0x3f)-DECIMAL32_Bias; // unbiased\r
+ }\r
+\r
+ // get the coefficient\r
+ sour&=0x000fffff; // clean coefficient continuation\r
+ if (msd) { // non-zero msd\r
+ sour|=msd<<20; // prefix to coefficient\r
+ decDigitsFromDPD(dn, &sour, 3); // process 3 declets\r
+ return dn;\r
+ }\r
+ // msd=0\r
+ if (!sour) return dn; // easy: coefficient is 0\r
+ if (sour&0x000ffc00) // need 2 declets?\r
+ decDigitsFromDPD(dn, &sour, 2); // process 2 declets\r
+ else\r
+ decDigitsFromDPD(dn, &sour, 1); // process 1 declet\r
+ return dn;\r
+ } // decimal32ToNumber\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* to-scientific-string -- conversion to numeric string */\r
+/* to-engineering-string -- conversion to numeric string */\r
+/* */\r
+/* decimal32ToString(d32, string); */\r
+/* decimal32ToEngString(d32, string); */\r
+/* */\r
+/* d32 is the decimal32 format number to convert */\r
+/* string is the string where the result will be laid out */\r
+/* */\r
+/* string must be at least 24 characters */\r
+/* */\r
+/* No error is possible, and no status can be set. */\r
+/* ------------------------------------------------------------------ */\r
+char * decimal32ToEngString(const decimal32 *d32, char *string){\r
+ decNumber dn; // work\r
+ decimal32ToNumber(d32, &dn);\r
+ decNumberToEngString(&dn, string);\r
+ return string;\r
+ } // decimal32ToEngString\r
+\r
+char * decimal32ToString(const decimal32 *d32, char *string){\r
+ uInt msd; // coefficient MSD\r
+ Int exp; // exponent top two bits or full\r
+ uInt comb; // combination field\r
+ char *cstart; // coefficient start\r
+ char *c; // output pointer in string\r
+ const uByte *u; // work\r
+ char *s, *t; // .. (source, target)\r
+ Int dpd; // ..\r
+ Int pre, e; // ..\r
+ uInt uiwork; // for macros\r
+ uInt sour; // source 32-bit\r
+\r
+ // load source from storage; this is endian\r
+ sour=UBTOUI(d32->bytes); // directly load the int\r
+\r
+ c=string; // where result will go\r
+ if (((Int)sour)<0) *c++='-'; // handle sign\r
+\r
+ comb=(sour>>26)&0x1f; // combination field\r
+ msd=COMBMSD[comb]; // decode the combination field\r
+ exp=COMBEXP[comb]; // ..\r
+\r
+ if (exp==3) {\r
+ if (msd==0) { // infinity\r
+ strcpy(c, "Inf");\r
+ strcpy(c+3, "inity");\r
+ return string; // easy\r
+ }\r
+ if (sour&0x02000000) *c++='s'; // sNaN\r
+ strcpy(c, "NaN"); // complete word\r
+ c+=3; // step past\r
+ if ((sour&0x000fffff)==0) return string; // zero payload\r
+ // otherwise drop through to add integer; set correct exp\r
+ exp=0; msd=0; // setup for following code\r
+ }\r
+ else exp=(exp<<6)+((sour>>20)&0x3f)-DECIMAL32_Bias; // unbiased\r
+\r
+ // convert 7 digits of significand to characters\r
+ cstart=c; // save start of coefficient\r
+ if (msd) *c++='0'+(char)msd; // non-zero most significant digit\r
+\r
+ // Now decode the declets. After extracting each one, it is\r
+ // decoded to binary and then to a 4-char sequence by table lookup;\r
+ // the 4-chars are a 1-char length (significant digits, except 000\r
+ // has length 0). This allows us to left-align the first declet\r
+ // with non-zero content, then remaining ones are full 3-char\r
+ // length. We use fixed-length memcpys because variable-length\r
+ // causes a subroutine call in GCC. (These are length 4 for speed\r
+ // and are safe because the array has an extra terminator byte.)\r
+ #define dpd2char u=&BIN2CHAR[DPD2BIN[dpd]*4]; \\r
+ if (c!=cstart) {memcpy(c, u+1, 4); c+=3;} \\r
+ else if (*u) {memcpy(c, u+4-*u, 4); c+=*u;}\r
+\r
+ dpd=(sour>>10)&0x3ff; // declet 1\r
+ dpd2char;\r
+ dpd=(sour)&0x3ff; // declet 2\r
+ dpd2char;\r
+\r
+ if (c==cstart) *c++='0'; // all zeros -- make 0\r
+\r
+ if (exp==0) { // integer or NaN case -- easy\r
+ *c='\0'; // terminate\r
+ return string;\r
+ }\r
+\r
+ /* non-0 exponent */\r
+ e=0; // assume no E\r
+ pre=c-cstart+exp;\r
+ // [here, pre-exp is the digits count (==1 for zero)]\r
+ if (exp>0 || pre<-5) { // need exponential form\r
+ e=pre-1; // calculate E value\r
+ pre=1; // assume one digit before '.'\r
+ } // exponential form\r
+\r
+ /* modify the coefficient, adding 0s, '.', and E+nn as needed */\r
+ s=c-1; // source (LSD)\r
+ if (pre>0) { // ddd.ddd (plain), perhaps with E\r
+ char *dotat=cstart+pre;\r
+ if (dotat<c) { // if embedded dot needed...\r
+ t=c; // target\r
+ for (; s>=dotat; s--, t--) *t=*s; // open the gap; leave t at gap\r
+ *t='.'; // insert the dot\r
+ c++; // length increased by one\r
+ }\r
+\r
+ // finally add the E-part, if needed; it will never be 0, and has\r
+ // a maximum length of 3 digits (E-101 case)\r
+ if (e!=0) {\r
+ *c++='E'; // starts with E\r
+ *c++='+'; // assume positive\r
+ if (e<0) {\r
+ *(c-1)='-'; // oops, need '-'\r
+ e=-e; // uInt, please\r
+ }\r
+ u=&BIN2CHAR[e*4]; // -> length byte\r
+ memcpy(c, u+4-*u, 4); // copy fixed 4 characters [is safe]\r
+ c+=*u; // bump pointer appropriately\r
+ }\r
+ *c='\0'; // add terminator\r
+ //printf("res %s\n", string);\r
+ return string;\r
+ } // pre>0\r
+\r
+ /* -5<=pre<=0: here for plain 0.ddd or 0.000ddd forms (can never have E) */\r
+ t=c+1-pre;\r
+ *(t+1)='\0'; // can add terminator now\r
+ for (; s>=cstart; s--, t--) *t=*s; // shift whole coefficient right\r
+ c=cstart;\r
+ *c++='0'; // always starts with 0.\r
+ *c++='.';\r
+ for (; pre<0; pre++) *c++='0'; // add any 0's after '.'\r
+ //printf("res %s\n", string);\r
+ return string;\r
+ } // decimal32ToString\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* to-number -- conversion from numeric string */\r
+/* */\r
+/* decimal32FromString(result, string, set); */\r
+/* */\r
+/* result is the decimal32 format number which gets the result of */\r
+/* the conversion */\r
+/* *string is the character string which should contain a valid */\r
+/* number (which may be a special value) */\r
+/* set is the context */\r
+/* */\r
+/* The context is supplied to this routine is used for error handling */\r
+/* (setting of status and traps) and for the rounding mode, only. */\r
+/* If an error occurs, the result will be a valid decimal32 NaN. */\r
+/* ------------------------------------------------------------------ */\r
+decimal32 * decimal32FromString(decimal32 *result, const char *string,\r
+ decContext *set) {\r
+ decContext dc; // work\r
+ decNumber dn; // ..\r
+\r
+ decContextDefault(&dc, DEC_INIT_DECIMAL32); // no traps, please\r
+ dc.round=set->round; // use supplied rounding\r
+\r
+ decNumberFromString(&dn, string, &dc); // will round if needed\r
+ decimal32FromNumber(result, &dn, &dc);\r
+ if (dc.status!=0) { // something happened\r
+ decContextSetStatus(set, dc.status); // .. pass it on\r
+ }\r
+ return result;\r
+ } // decimal32FromString\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decimal32IsCanonical -- test whether encoding is canonical */\r
+/* d32 is the source decimal32 */\r
+/* returns 1 if the encoding of d32 is canonical, 0 otherwise */\r
+/* No error is possible. */\r
+/* ------------------------------------------------------------------ */\r
+uInt decimal32IsCanonical(const decimal32 *d32) {\r
+ decNumber dn; // work\r
+ decimal32 canon; // ..\r
+ decContext dc; // ..\r
+ decContextDefault(&dc, DEC_INIT_DECIMAL32);\r
+ decimal32ToNumber(d32, &dn);\r
+ decimal32FromNumber(&canon, &dn, &dc);// canon will now be canonical\r
+ return memcmp(d32, &canon, DECIMAL32_Bytes)==0;\r
+ } // decimal32IsCanonical\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decimal32Canonical -- copy an encoding, ensuring it is canonical */\r
+/* d32 is the source decimal32 */\r
+/* result is the target (may be the same decimal32) */\r
+/* returns result */\r
+/* No error is possible. */\r
+/* ------------------------------------------------------------------ */\r
+decimal32 * decimal32Canonical(decimal32 *result, const decimal32 *d32) {\r
+ decNumber dn; // work\r
+ decContext dc; // ..\r
+ decContextDefault(&dc, DEC_INIT_DECIMAL32);\r
+ decimal32ToNumber(d32, &dn);\r
+ decimal32FromNumber(result, &dn, &dc);// result will now be canonical\r
+ return result;\r
+ } // decimal32Canonical\r
+\r
+#if DECTRACE || DECCHECK\r
+/* Macros for accessing decimal32 fields. These assume the argument\r
+ is a reference (pointer) to the decimal32 structure, and the\r
+ decimal32 is in network byte order (big-endian) */\r
+// Get sign\r
+#define decimal32Sign(d) ((unsigned)(d)->bytes[0]>>7)\r
+\r
+// Get combination field\r
+#define decimal32Comb(d) (((d)->bytes[0] & 0x7c)>>2)\r
+\r
+// Get exponent continuation [does not remove bias]\r
+#define decimal32ExpCon(d) ((((d)->bytes[0] & 0x03)<<4) \\r
+ | ((unsigned)(d)->bytes[1]>>4))\r
+\r
+// Set sign [this assumes sign previously 0]\r
+#define decimal32SetSign(d, b) { \\r
+ (d)->bytes[0]|=((unsigned)(b)<<7);}\r
+\r
+// Set exponent continuation [does not apply bias]\r
+// This assumes range has been checked and exponent previously 0;\r
+// type of exponent must be unsigned\r
+#define decimal32SetExpCon(d, e) { \\r
+ (d)->bytes[0]|=(uByte)((e)>>4); \\r
+ (d)->bytes[1]|=(uByte)(((e)&0x0F)<<4);}\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decimal32Show -- display a decimal32 in hexadecimal [debug aid] */\r
+/* d32 -- the number to show */\r
+/* ------------------------------------------------------------------ */\r
+// Also shows sign/cob/expconfields extracted - valid bigendian only\r
+void decimal32Show(const decimal32 *d32) {\r
+ char buf[DECIMAL32_Bytes*2+1];\r
+ Int i, j=0;\r
+\r
+ if (DECLITEND) {\r
+ for (i=0; i<DECIMAL32_Bytes; i++, j+=2) {\r
+ sprintf(&buf[j], "%02x", d32->bytes[3-i]);\r
+ }\r
+ printf(" D32> %s [S:%d Cb:%02x Ec:%02x] LittleEndian\n", buf,\r
+ d32->bytes[3]>>7, (d32->bytes[3]>>2)&0x1f,\r
+ ((d32->bytes[3]&0x3)<<4)| (d32->bytes[2]>>4));\r
+ }\r
+ else {\r
+ for (i=0; i<DECIMAL32_Bytes; i++, j+=2) {\r
+ sprintf(&buf[j], "%02x", d32->bytes[i]);\r
+ }\r
+ printf(" D32> %s [S:%d Cb:%02x Ec:%02x] BigEndian\n", buf,\r
+ decimal32Sign(d32), decimal32Comb(d32), decimal32ExpCon(d32));\r
+ }\r
+ } // decimal32Show\r
+#endif\r
--- /dev/null
+/* ------------------------------------------------------------------ */\r
+/* Decimal 32-bit format module header */\r
+/* ------------------------------------------------------------------ */\r
+/* Copyright (c) IBM Corporation, 2000, 2006. All rights reserved. */\r
+/* */\r
+/* This software is made available under the terms of the */\r
+/* ICU License -- ICU 1.8.1 and later. */\r
+/* */\r
+/* The description and User's Guide ("The decNumber C Library") for */\r
+/* this software is called decNumber.pdf. This document is */\r
+/* available, together with arithmetic and format specifications, */\r
+/* testcases, and Web links, on the General Decimal Arithmetic page. */\r
+/* */\r
+/* Please send comments, suggestions, and corrections to the author: */\r
+/* mfc@uk.ibm.com */\r
+/* Mike Cowlishaw, IBM Fellow */\r
+/* IBM UK, PO Box 31, Birmingham Road, Warwick CV34 5JL, UK */\r
+/* ------------------------------------------------------------------ */\r
+\r
+#if !defined(DECIMAL32)\r
+ #define DECIMAL32\r
+ #define DEC32NAME "decimal32" /* Short name */\r
+ #define DEC32FULLNAME "Decimal 32-bit Number" /* Verbose name */\r
+ #define DEC32AUTHOR "Mike Cowlishaw" /* Who to blame */\r
+\r
+ /* parameters for decimal32s */\r
+ #define DECIMAL32_Bytes 4 /* length */\r
+ #define DECIMAL32_Pmax 7 /* maximum precision (digits) */\r
+ #define DECIMAL32_Emax 96 /* maximum adjusted exponent */\r
+ #define DECIMAL32_Emin -95 /* minimum adjusted exponent */\r
+ #define DECIMAL32_Bias 101 /* bias for the exponent */\r
+ #define DECIMAL32_String 15 /* maximum string length, +1 */\r
+ #define DECIMAL32_EconL 6 /* exp. continuation length */\r
+ /* highest biased exponent (Elimit-1) */\r
+ #define DECIMAL32_Ehigh (DECIMAL32_Emax+DECIMAL32_Bias-DECIMAL32_Pmax+1)\r
+\r
+ /* check enough digits, if pre-defined */\r
+ #if defined(DECNUMDIGITS)\r
+ #if (DECNUMDIGITS<DECIMAL32_Pmax)\r
+ #error decimal32.h needs pre-defined DECNUMDIGITS>=7 for safe use\r
+ #endif\r
+ #endif\r
+\r
+ #ifndef DECNUMDIGITS\r
+ #define DECNUMDIGITS DECIMAL32_Pmax /* size if not already defined*/\r
+ #endif\r
+ #ifndef DECNUMBER\r
+ #include "decNumber.h" /* context and number library */\r
+ #endif\r
+\r
+ /* Decimal 32-bit type, accessible by bytes */\r
+ typedef struct {\r
+ uint8_t bytes[DECIMAL32_Bytes]; /* decimal32: 1, 5, 6, 20 bits*/\r
+ } decimal32;\r
+\r
+ /* special values [top byte excluding sign bit; last two bits are */\r
+ /* don't-care for Infinity on input, last bit don't-care for NaN] */\r
+ #if !defined(DECIMAL_NaN)\r
+ #define DECIMAL_NaN 0x7c /* 0 11111 00 NaN */\r
+ #define DECIMAL_sNaN 0x7e /* 0 11111 10 sNaN */\r
+ #define DECIMAL_Inf 0x78 /* 0 11110 00 Infinity */\r
+ #endif\r
+\r
+ /* ---------------------------------------------------------------- */\r
+ /* Routines */\r
+ /* ---------------------------------------------------------------- */\r
+ /* String conversions */\r
+ decimal32 * decimal32FromString(decimal32 *, const char *, decContext *);\r
+ char * decimal32ToString(const decimal32 *, char *);\r
+ char * decimal32ToEngString(const decimal32 *, char *);\r
+\r
+ /* decNumber conversions */\r
+ decimal32 * decimal32FromNumber(decimal32 *, const decNumber *,\r
+ decContext *);\r
+ decNumber * decimal32ToNumber(const decimal32 *, decNumber *);\r
+\r
+ /* Format-dependent utilities */\r
+ uint32_t decimal32IsCanonical(const decimal32 *);\r
+ decimal32 * decimal32Canonical(decimal32 *, const decimal32 *);\r
+\r
+#endif\r
--- /dev/null
+/* ------------------------------------------------------------------ */\r
+/* Decimal 64-bit format module */\r
+/* ------------------------------------------------------------------ */\r
+/* Copyright (c) IBM Corporation, 2000, 2009. All rights reserved. */\r
+/* */\r
+/* This software is made available under the terms of the */\r
+/* ICU License -- ICU 1.8.1 and later. */\r
+/* */\r
+/* The description and User's Guide ("The decNumber C Library") for */\r
+/* this software is called decNumber.pdf. This document is */\r
+/* available, together with arithmetic and format specifications, */\r
+/* testcases, and Web links, on the General Decimal Arithmetic page. */\r
+/* */\r
+/* Please send comments, suggestions, and corrections to the author: */\r
+/* mfc@uk.ibm.com */\r
+/* Mike Cowlishaw, IBM Fellow */\r
+/* IBM UK, PO Box 31, Birmingham Road, Warwick CV34 5JL, UK */\r
+/* ------------------------------------------------------------------ */\r
+/* This module comprises the routines for decimal64 format numbers. */\r
+/* Conversions are supplied to and from decNumber and String. */\r
+/* */\r
+/* This is used when decNumber provides operations, either for all */\r
+/* operations or as a proxy between decNumber and decSingle. */\r
+/* */\r
+/* Error handling is the same as decNumber (qv.). */\r
+/* ------------------------------------------------------------------ */\r
+#include <string.h> // [for memset/memcpy]\r
+#include <stdio.h> // [for printf]\r
+\r
+#define DECNUMDIGITS 16 // make decNumbers with space for 16\r
+#include "decNumber.h" // base number library\r
+#include "decNumberLocal.h" // decNumber local types, etc.\r
+#include "decimal64.h" // our primary include\r
+\r
+/* Utility routines and tables [in decimal64.c]; externs for C++ */\r
+// DPD2BIN and the reverse are renamed to prevent link-time conflict\r
+// if decQuad is also built in the same executable\r
+#define DPD2BIN DPD2BINx\r
+#define BIN2DPD BIN2DPDx\r
+extern const uInt COMBEXP[32], COMBMSD[32];\r
+extern const uShort DPD2BIN[1024];\r
+extern const uShort BIN2DPD[1000];\r
+extern const uByte BIN2CHAR[4001];\r
+\r
+extern void decDigitsFromDPD(decNumber *, const uInt *, Int);\r
+extern void decDigitsToDPD(const decNumber *, uInt *, Int);\r
+\r
+#if DECTRACE || DECCHECK\r
+void decimal64Show(const decimal64 *); // for debug\r
+extern void decNumberShow(const decNumber *); // ..\r
+#endif\r
+\r
+/* Useful macro */\r
+// Clear a structure (e.g., a decNumber)\r
+#define DEC_clear(d) memset(d, 0, sizeof(*d))\r
+\r
+/* define and include the tables to use for conversions */\r
+#define DEC_BIN2CHAR 1\r
+#define DEC_DPD2BIN 1\r
+#define DEC_BIN2DPD 1 // used for all sizes\r
+#include "decDPD.h" // lookup tables\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decimal64FromNumber -- convert decNumber to decimal64 */\r
+/* */\r
+/* ds is the target decimal64 */\r
+/* dn is the source number (assumed valid) */\r
+/* set is the context, used only for reporting errors */\r
+/* */\r
+/* The set argument is used only for status reporting and for the */\r
+/* rounding mode (used if the coefficient is more than DECIMAL64_Pmax */\r
+/* digits or an overflow is detected). If the exponent is out of the */\r
+/* valid range then Overflow or Underflow will be raised. */\r
+/* After Underflow a subnormal result is possible. */\r
+/* */\r
+/* DEC_Clamped is set if the number has to be 'folded down' to fit, */\r
+/* by reducing its exponent and multiplying the coefficient by a */\r
+/* power of ten, or if the exponent on a zero had to be clamped. */\r
+/* ------------------------------------------------------------------ */\r
+decimal64 * decimal64FromNumber(decimal64 *d64, const decNumber *dn,\r
+ decContext *set) {\r
+ uInt status=0; // status accumulator\r
+ Int ae; // adjusted exponent\r
+ decNumber dw; // work\r
+ decContext dc; // ..\r
+ uInt comb, exp; // ..\r
+ uInt uiwork; // for macros\r
+ uInt targar[2]={0, 0}; // target 64-bit\r
+ #define targhi targar[1] // name the word with the sign\r
+ #define targlo targar[0] // and the other\r
+\r
+ // If the number has too many digits, or the exponent could be\r
+ // out of range then reduce the number under the appropriate\r
+ // constraints. This could push the number to Infinity or zero,\r
+ // so this check and rounding must be done before generating the\r
+ // decimal64]\r
+ ae=dn->exponent+dn->digits-1; // [0 if special]\r
+ if (dn->digits>DECIMAL64_Pmax // too many digits\r
+ || ae>DECIMAL64_Emax // likely overflow\r
+ || ae<DECIMAL64_Emin) { // likely underflow\r
+ decContextDefault(&dc, DEC_INIT_DECIMAL64); // [no traps]\r
+ dc.round=set->round; // use supplied rounding\r
+ decNumberPlus(&dw, dn, &dc); // (round and check)\r
+ // [this changes -0 to 0, so enforce the sign...]\r
+ dw.bits|=dn->bits&DECNEG;\r
+ status=dc.status; // save status\r
+ dn=&dw; // use the work number\r
+ } // maybe out of range\r
+\r
+ if (dn->bits&DECSPECIAL) { // a special value\r
+ if (dn->bits&DECINF) targhi=DECIMAL_Inf<<24;\r
+ else { // sNaN or qNaN\r
+ if ((*dn->lsu!=0 || dn->digits>1) // non-zero coefficient\r
+ && (dn->digits<DECIMAL64_Pmax)) { // coefficient fits\r
+ decDigitsToDPD(dn, targar, 0);\r
+ }\r
+ if (dn->bits&DECNAN) targhi|=DECIMAL_NaN<<24;\r
+ else targhi|=DECIMAL_sNaN<<24;\r
+ } // a NaN\r
+ } // special\r
+\r
+ else { // is finite\r
+ if (decNumberIsZero(dn)) { // is a zero\r
+ // set and clamp exponent\r
+ if (dn->exponent<-DECIMAL64_Bias) {\r
+ exp=0; // low clamp\r
+ status|=DEC_Clamped;\r
+ }\r
+ else {\r
+ exp=dn->exponent+DECIMAL64_Bias; // bias exponent\r
+ if (exp>DECIMAL64_Ehigh) { // top clamp\r
+ exp=DECIMAL64_Ehigh;\r
+ status|=DEC_Clamped;\r
+ }\r
+ }\r
+ comb=(exp>>5) & 0x18; // msd=0, exp top 2 bits ..\r
+ }\r
+ else { // non-zero finite number\r
+ uInt msd; // work\r
+ Int pad=0; // coefficient pad digits\r
+\r
+ // the dn is known to fit, but it may need to be padded\r
+ exp=(uInt)(dn->exponent+DECIMAL64_Bias); // bias exponent\r
+ if (exp>DECIMAL64_Ehigh) { // fold-down case\r
+ pad=exp-DECIMAL64_Ehigh;\r
+ exp=DECIMAL64_Ehigh; // [to maximum]\r
+ status|=DEC_Clamped;\r
+ }\r
+\r
+ // fastpath common case\r
+ if (DECDPUN==3 && pad==0) {\r
+ uInt dpd[6]={0,0,0,0,0,0};\r
+ uInt i;\r
+ Int d=dn->digits;\r
+ for (i=0; d>0; i++, d-=3) dpd[i]=BIN2DPD[dn->lsu[i]];\r
+ targlo =dpd[0];\r
+ targlo|=dpd[1]<<10;\r
+ targlo|=dpd[2]<<20;\r
+ if (dn->digits>6) {\r
+ targlo|=dpd[3]<<30;\r
+ targhi =dpd[3]>>2;\r
+ targhi|=dpd[4]<<8;\r
+ }\r
+ msd=dpd[5]; // [did not really need conversion]\r
+ }\r
+ else { // general case\r
+ decDigitsToDPD(dn, targar, pad);\r
+ // save and clear the top digit\r
+ msd=targhi>>18;\r
+ targhi&=0x0003ffff;\r
+ }\r
+\r
+ // create the combination field\r
+ if (msd>=8) comb=0x18 | ((exp>>7) & 0x06) | (msd & 0x01);\r
+ else comb=((exp>>5) & 0x18) | msd;\r
+ }\r
+ targhi|=comb<<26; // add combination field ..\r
+ targhi|=(exp&0xff)<<18; // .. and exponent continuation\r
+ } // finite\r
+\r
+ if (dn->bits&DECNEG) targhi|=0x80000000; // add sign bit\r
+\r
+ // now write to storage; this is now always endian\r
+ if (DECLITEND) {\r
+ // lo int then hi\r
+ UBFROMUI(d64->bytes, targar[0]);\r
+ UBFROMUI(d64->bytes+4, targar[1]);\r
+ }\r
+ else {\r
+ // hi int then lo\r
+ UBFROMUI(d64->bytes, targar[1]);\r
+ UBFROMUI(d64->bytes+4, targar[0]);\r
+ }\r
+\r
+ if (status!=0) decContextSetStatus(set, status); // pass on status\r
+ // decimal64Show(d64);\r
+ return d64;\r
+ } // decimal64FromNumber\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decimal64ToNumber -- convert decimal64 to decNumber */\r
+/* d64 is the source decimal64 */\r
+/* dn is the target number, with appropriate space */\r
+/* No error is possible. */\r
+/* ------------------------------------------------------------------ */\r
+decNumber * decimal64ToNumber(const decimal64 *d64, decNumber *dn) {\r
+ uInt msd; // coefficient MSD\r
+ uInt exp; // exponent top two bits\r
+ uInt comb; // combination field\r
+ Int need; // work\r
+ uInt uiwork; // for macros\r
+ uInt sourar[2]; // source 64-bit\r
+ #define sourhi sourar[1] // name the word with the sign\r
+ #define sourlo sourar[0] // and the lower word\r
+\r
+ // load source from storage; this is endian\r
+ if (DECLITEND) {\r
+ sourlo=UBTOUI(d64->bytes ); // directly load the low int\r
+ sourhi=UBTOUI(d64->bytes+4); // then the high int\r
+ }\r
+ else {\r
+ sourhi=UBTOUI(d64->bytes ); // directly load the high int\r
+ sourlo=UBTOUI(d64->bytes+4); // then the low int\r
+ }\r
+\r
+ comb=(sourhi>>26)&0x1f; // combination field\r
+\r
+ decNumberZero(dn); // clean number\r
+ if (sourhi&0x80000000) dn->bits=DECNEG; // set sign if negative\r
+\r
+ msd=COMBMSD[comb]; // decode the combination field\r
+ exp=COMBEXP[comb]; // ..\r
+\r
+ if (exp==3) { // is a special\r
+ if (msd==0) {\r
+ dn->bits|=DECINF;\r
+ return dn; // no coefficient needed\r
+ }\r
+ else if (sourhi&0x02000000) dn->bits|=DECSNAN;\r
+ else dn->bits|=DECNAN;\r
+ msd=0; // no top digit\r
+ }\r
+ else { // is a finite number\r
+ dn->exponent=(exp<<8)+((sourhi>>18)&0xff)-DECIMAL64_Bias; // unbiased\r
+ }\r
+\r
+ // get the coefficient\r
+ sourhi&=0x0003ffff; // clean coefficient continuation\r
+ if (msd) { // non-zero msd\r
+ sourhi|=msd<<18; // prefix to coefficient\r
+ need=6; // process 6 declets\r
+ }\r
+ else { // msd=0\r
+ if (!sourhi) { // top word 0\r
+ if (!sourlo) return dn; // easy: coefficient is 0\r
+ need=3; // process at least 3 declets\r
+ if (sourlo&0xc0000000) need++; // process 4 declets\r
+ // [could reduce some more, here]\r
+ }\r
+ else { // some bits in top word, msd=0\r
+ need=4; // process at least 4 declets\r
+ if (sourhi&0x0003ff00) need++; // top declet!=0, process 5\r
+ }\r
+ } //msd=0\r
+\r
+ decDigitsFromDPD(dn, sourar, need); // process declets\r
+ return dn;\r
+ } // decimal64ToNumber\r
+\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* to-scientific-string -- conversion to numeric string */\r
+/* to-engineering-string -- conversion to numeric string */\r
+/* */\r
+/* decimal64ToString(d64, string); */\r
+/* decimal64ToEngString(d64, string); */\r
+/* */\r
+/* d64 is the decimal64 format number to convert */\r
+/* string is the string where the result will be laid out */\r
+/* */\r
+/* string must be at least 24 characters */\r
+/* */\r
+/* No error is possible, and no status can be set. */\r
+/* ------------------------------------------------------------------ */\r
+char * decimal64ToEngString(const decimal64 *d64, char *string){\r
+ decNumber dn; // work\r
+ decimal64ToNumber(d64, &dn);\r
+ decNumberToEngString(&dn, string);\r
+ return string;\r
+ } // decimal64ToEngString\r
+\r
+char * decimal64ToString(const decimal64 *d64, char *string){\r
+ uInt msd; // coefficient MSD\r
+ Int exp; // exponent top two bits or full\r
+ uInt comb; // combination field\r
+ char *cstart; // coefficient start\r
+ char *c; // output pointer in string\r
+ const uByte *u; // work\r
+ char *s, *t; // .. (source, target)\r
+ Int dpd; // ..\r
+ Int pre, e; // ..\r
+ uInt uiwork; // for macros\r
+\r
+ uInt sourar[2]; // source 64-bit\r
+ #define sourhi sourar[1] // name the word with the sign\r
+ #define sourlo sourar[0] // and the lower word\r
+\r
+ // load source from storage; this is endian\r
+ if (DECLITEND) {\r
+ sourlo=UBTOUI(d64->bytes ); // directly load the low int\r
+ sourhi=UBTOUI(d64->bytes+4); // then the high int\r
+ }\r
+ else {\r
+ sourhi=UBTOUI(d64->bytes ); // directly load the high int\r
+ sourlo=UBTOUI(d64->bytes+4); // then the low int\r
+ }\r
+\r
+ c=string; // where result will go\r
+ if (((Int)sourhi)<0) *c++='-'; // handle sign\r
+\r
+ comb=(sourhi>>26)&0x1f; // combination field\r
+ msd=COMBMSD[comb]; // decode the combination field\r
+ exp=COMBEXP[comb]; // ..\r
+\r
+ if (exp==3) {\r
+ if (msd==0) { // infinity\r
+ strcpy(c, "Inf");\r
+ strcpy(c+3, "inity");\r
+ return string; // easy\r
+ }\r
+ if (sourhi&0x02000000) *c++='s'; // sNaN\r
+ strcpy(c, "NaN"); // complete word\r
+ c+=3; // step past\r
+ if (sourlo==0 && (sourhi&0x0003ffff)==0) return string; // zero payload\r
+ // otherwise drop through to add integer; set correct exp\r
+ exp=0; msd=0; // setup for following code\r
+ }\r
+ else exp=(exp<<8)+((sourhi>>18)&0xff)-DECIMAL64_Bias;\r
+\r
+ // convert 16 digits of significand to characters\r
+ cstart=c; // save start of coefficient\r
+ if (msd) *c++='0'+(char)msd; // non-zero most significant digit\r
+\r
+ // Now decode the declets. After extracting each one, it is\r
+ // decoded to binary and then to a 4-char sequence by table lookup;\r
+ // the 4-chars are a 1-char length (significant digits, except 000\r
+ // has length 0). This allows us to left-align the first declet\r
+ // with non-zero content, then remaining ones are full 3-char\r
+ // length. We use fixed-length memcpys because variable-length\r
+ // causes a subroutine call in GCC. (These are length 4 for speed\r
+ // and are safe because the array has an extra terminator byte.)\r
+ #define dpd2char u=&BIN2CHAR[DPD2BIN[dpd]*4]; \\r
+ if (c!=cstart) {memcpy(c, u+1, 4); c+=3;} \\r
+ else if (*u) {memcpy(c, u+4-*u, 4); c+=*u;}\r
+\r
+ dpd=(sourhi>>8)&0x3ff; // declet 1\r
+ dpd2char;\r
+ dpd=((sourhi&0xff)<<2) | (sourlo>>30); // declet 2\r
+ dpd2char;\r
+ dpd=(sourlo>>20)&0x3ff; // declet 3\r
+ dpd2char;\r
+ dpd=(sourlo>>10)&0x3ff; // declet 4\r
+ dpd2char;\r
+ dpd=(sourlo)&0x3ff; // declet 5\r
+ dpd2char;\r
+\r
+ if (c==cstart) *c++='0'; // all zeros -- make 0\r
+\r
+ if (exp==0) { // integer or NaN case -- easy\r
+ *c='\0'; // terminate\r
+ return string;\r
+ }\r
+\r
+ /* non-0 exponent */\r
+ e=0; // assume no E\r
+ pre=c-cstart+exp;\r
+ // [here, pre-exp is the digits count (==1 for zero)]\r
+ if (exp>0 || pre<-5) { // need exponential form\r
+ e=pre-1; // calculate E value\r
+ pre=1; // assume one digit before '.'\r
+ } // exponential form\r
+\r
+ /* modify the coefficient, adding 0s, '.', and E+nn as needed */\r
+ s=c-1; // source (LSD)\r
+ if (pre>0) { // ddd.ddd (plain), perhaps with E\r
+ char *dotat=cstart+pre;\r
+ if (dotat<c) { // if embedded dot needed...\r
+ t=c; // target\r
+ for (; s>=dotat; s--, t--) *t=*s; // open the gap; leave t at gap\r
+ *t='.'; // insert the dot\r
+ c++; // length increased by one\r
+ }\r
+\r
+ // finally add the E-part, if needed; it will never be 0, and has\r
+ // a maximum length of 3 digits\r
+ if (e!=0) {\r
+ *c++='E'; // starts with E\r
+ *c++='+'; // assume positive\r
+ if (e<0) {\r
+ *(c-1)='-'; // oops, need '-'\r
+ e=-e; // uInt, please\r
+ }\r
+ u=&BIN2CHAR[e*4]; // -> length byte\r
+ memcpy(c, u+4-*u, 4); // copy fixed 4 characters [is safe]\r
+ c+=*u; // bump pointer appropriately\r
+ }\r
+ *c='\0'; // add terminator\r
+ //printf("res %s\n", string);\r
+ return string;\r
+ } // pre>0\r
+\r
+ /* -5<=pre<=0: here for plain 0.ddd or 0.000ddd forms (can never have E) */\r
+ t=c+1-pre;\r
+ *(t+1)='\0'; // can add terminator now\r
+ for (; s>=cstart; s--, t--) *t=*s; // shift whole coefficient right\r
+ c=cstart;\r
+ *c++='0'; // always starts with 0.\r
+ *c++='.';\r
+ for (; pre<0; pre++) *c++='0'; // add any 0's after '.'\r
+ //printf("res %s\n", string);\r
+ return string;\r
+ } // decimal64ToString\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* to-number -- conversion from numeric string */\r
+/* */\r
+/* decimal64FromString(result, string, set); */\r
+/* */\r
+/* result is the decimal64 format number which gets the result of */\r
+/* the conversion */\r
+/* *string is the character string which should contain a valid */\r
+/* number (which may be a special value) */\r
+/* set is the context */\r
+/* */\r
+/* The context is supplied to this routine is used for error handling */\r
+/* (setting of status and traps) and for the rounding mode, only. */\r
+/* If an error occurs, the result will be a valid decimal64 NaN. */\r
+/* ------------------------------------------------------------------ */\r
+decimal64 * decimal64FromString(decimal64 *result, const char *string,\r
+ decContext *set) {\r
+ decContext dc; // work\r
+ decNumber dn; // ..\r
+\r
+ decContextDefault(&dc, DEC_INIT_DECIMAL64); // no traps, please\r
+ dc.round=set->round; // use supplied rounding\r
+\r
+ decNumberFromString(&dn, string, &dc); // will round if needed\r
+\r
+ decimal64FromNumber(result, &dn, &dc);\r
+ if (dc.status!=0) { // something happened\r
+ decContextSetStatus(set, dc.status); // .. pass it on\r
+ }\r
+ return result;\r
+ } // decimal64FromString\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decimal64IsCanonical -- test whether encoding is canonical */\r
+/* d64 is the source decimal64 */\r
+/* returns 1 if the encoding of d64 is canonical, 0 otherwise */\r
+/* No error is possible. */\r
+/* ------------------------------------------------------------------ */\r
+uInt decimal64IsCanonical(const decimal64 *d64) {\r
+ decNumber dn; // work\r
+ decimal64 canon; // ..\r
+ decContext dc; // ..\r
+ decContextDefault(&dc, DEC_INIT_DECIMAL64);\r
+ decimal64ToNumber(d64, &dn);\r
+ decimal64FromNumber(&canon, &dn, &dc);// canon will now be canonical\r
+ return memcmp(d64, &canon, DECIMAL64_Bytes)==0;\r
+ } // decimal64IsCanonical\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decimal64Canonical -- copy an encoding, ensuring it is canonical */\r
+/* d64 is the source decimal64 */\r
+/* result is the target (may be the same decimal64) */\r
+/* returns result */\r
+/* No error is possible. */\r
+/* ------------------------------------------------------------------ */\r
+decimal64 * decimal64Canonical(decimal64 *result, const decimal64 *d64) {\r
+ decNumber dn; // work\r
+ decContext dc; // ..\r
+ decContextDefault(&dc, DEC_INIT_DECIMAL64);\r
+ decimal64ToNumber(d64, &dn);\r
+ decimal64FromNumber(result, &dn, &dc);// result will now be canonical\r
+ return result;\r
+ } // decimal64Canonical\r
+\r
+#if DECTRACE || DECCHECK\r
+/* Macros for accessing decimal64 fields. These assume the\r
+ argument is a reference (pointer) to the decimal64 structure,\r
+ and the decimal64 is in network byte order (big-endian) */\r
+// Get sign\r
+#define decimal64Sign(d) ((unsigned)(d)->bytes[0]>>7)\r
+\r
+// Get combination field\r
+#define decimal64Comb(d) (((d)->bytes[0] & 0x7c)>>2)\r
+\r
+// Get exponent continuation [does not remove bias]\r
+#define decimal64ExpCon(d) ((((d)->bytes[0] & 0x03)<<6) \\r
+ | ((unsigned)(d)->bytes[1]>>2))\r
+\r
+// Set sign [this assumes sign previously 0]\r
+#define decimal64SetSign(d, b) { \\r
+ (d)->bytes[0]|=((unsigned)(b)<<7);}\r
+\r
+// Set exponent continuation [does not apply bias]\r
+// This assumes range has been checked and exponent previously 0;\r
+// type of exponent must be unsigned\r
+#define decimal64SetExpCon(d, e) { \\r
+ (d)->bytes[0]|=(uByte)((e)>>6); \\r
+ (d)->bytes[1]|=(uByte)(((e)&0x3F)<<2);}\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decimal64Show -- display a decimal64 in hexadecimal [debug aid] */\r
+/* d64 -- the number to show */\r
+/* ------------------------------------------------------------------ */\r
+// Also shows sign/cob/expconfields extracted\r
+void decimal64Show(const decimal64 *d64) {\r
+ char buf[DECIMAL64_Bytes*2+1];\r
+ Int i, j=0;\r
+\r
+ if (DECLITEND) {\r
+ for (i=0; i<DECIMAL64_Bytes; i++, j+=2) {\r
+ sprintf(&buf[j], "%02x", d64->bytes[7-i]);\r
+ }\r
+ printf(" D64> %s [S:%d Cb:%02x Ec:%02x] LittleEndian\n", buf,\r
+ d64->bytes[7]>>7, (d64->bytes[7]>>2)&0x1f,\r
+ ((d64->bytes[7]&0x3)<<6)| (d64->bytes[6]>>2));\r
+ }\r
+ else { // big-endian\r
+ for (i=0; i<DECIMAL64_Bytes; i++, j+=2) {\r
+ sprintf(&buf[j], "%02x", d64->bytes[i]);\r
+ }\r
+ printf(" D64> %s [S:%d Cb:%02x Ec:%02x] BigEndian\n", buf,\r
+ decimal64Sign(d64), decimal64Comb(d64), decimal64ExpCon(d64));\r
+ }\r
+ } // decimal64Show\r
+#endif\r
+\r
+/* ================================================================== */\r
+/* Shared utility routines and tables */\r
+/* ================================================================== */\r
+// define and include the conversion tables to use for shared code\r
+#if DECDPUN==3\r
+ #define DEC_DPD2BIN 1\r
+#else\r
+ #define DEC_DPD2BCD 1\r
+#endif\r
+#include "decDPD.h" // lookup tables\r
+\r
+// The maximum number of decNumberUnits needed for a working copy of\r
+// the units array is the ceiling of digits/DECDPUN, where digits is\r
+// the maximum number of digits in any of the formats for which this\r
+// is used. decimal128.h must not be included in this module, so, as\r
+// a very special case, that number is defined as a literal here.\r
+#define DECMAX754 34\r
+#define DECMAXUNITS ((DECMAX754+DECDPUN-1)/DECDPUN)\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* Combination field lookup tables (uInts to save measurable work) */\r
+/* */\r
+/* COMBEXP - 2-bit most-significant-bits of exponent */\r
+/* [11 if an Infinity or NaN] */\r
+/* COMBMSD - 4-bit most-significant-digit */\r
+/* [0=Infinity, 1=NaN if COMBEXP=11] */\r
+/* */\r
+/* Both are indexed by the 5-bit combination field (0-31) */\r
+/* ------------------------------------------------------------------ */\r
+const uInt COMBEXP[32]={0, 0, 0, 0, 0, 0, 0, 0,\r
+ 1, 1, 1, 1, 1, 1, 1, 1,\r
+ 2, 2, 2, 2, 2, 2, 2, 2,\r
+ 0, 0, 1, 1, 2, 2, 3, 3};\r
+const uInt COMBMSD[32]={0, 1, 2, 3, 4, 5, 6, 7,\r
+ 0, 1, 2, 3, 4, 5, 6, 7,\r
+ 0, 1, 2, 3, 4, 5, 6, 7,\r
+ 8, 9, 8, 9, 8, 9, 0, 1};\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decDigitsToDPD -- pack coefficient into DPD form */\r
+/* */\r
+/* dn is the source number (assumed valid, max DECMAX754 digits) */\r
+/* targ is 1, 2, or 4-element uInt array, which the caller must */\r
+/* have cleared to zeros */\r
+/* shift is the number of 0 digits to add on the right (normally 0) */\r
+/* */\r
+/* The coefficient must be known small enough to fit. The full */\r
+/* coefficient is copied, including the leading 'odd' digit. This */\r
+/* digit is retrieved and packed into the combination field by the */\r
+/* caller. */\r
+/* */\r
+/* The target uInts are altered only as necessary to receive the */\r
+/* digits of the decNumber. When more than one uInt is needed, they */\r
+/* are filled from left to right (that is, the uInt at offset 0 will */\r
+/* end up with the least-significant digits). */\r
+/* */\r
+/* shift is used for 'fold-down' padding. */\r
+/* */\r
+/* No error is possible. */\r
+/* ------------------------------------------------------------------ */\r
+#if DECDPUN<=4\r
+// Constant multipliers for divide-by-power-of five using reciprocal\r
+// multiply, after removing powers of 2 by shifting, and final shift\r
+// of 17 [we only need up to **4]\r
+static const uInt multies[]={131073, 26215, 5243, 1049, 210};\r
+// QUOT10 -- macro to return the quotient of unit u divided by 10**n\r
+#define QUOT10(u, n) ((((uInt)(u)>>(n))*multies[n])>>17)\r
+#endif\r
+void decDigitsToDPD(const decNumber *dn, uInt *targ, Int shift) {\r
+ Int cut; // work\r
+ Int n; // output bunch counter\r
+ Int digits=dn->digits; // digit countdown\r
+ uInt dpd; // densely packed decimal value\r
+ uInt bin; // binary value 0-999\r
+ uInt *uout=targ; // -> current output uInt\r
+ uInt uoff=0; // -> current output offset [from right]\r
+ const Unit *inu=dn->lsu; // -> current input unit\r
+ Unit uar[DECMAXUNITS]; // working copy of units, iff shifted\r
+ #if DECDPUN!=3 // not fast path\r
+ Unit in; // current unit\r
+ #endif\r
+\r
+ if (shift!=0) { // shift towards most significant required\r
+ // shift the units array to the left by pad digits and copy\r
+ // [this code is a special case of decShiftToMost, which could\r
+ // be used instead if exposed and the array were copied first]\r
+ const Unit *source; // ..\r
+ Unit *target, *first; // ..\r
+ uInt next=0; // work\r
+\r
+ source=dn->lsu+D2U(digits)-1; // where msu comes from\r
+ target=uar+D2U(digits)-1+D2U(shift);// where upper part of first cut goes\r
+ cut=DECDPUN-MSUDIGITS(shift); // where to slice\r
+ if (cut==0) { // unit-boundary case\r
+ for (; source>=dn->lsu; source--, target--) *target=*source;\r
+ }\r
+ else {\r
+ first=uar+D2U(digits+shift)-1; // where msu will end up\r
+ for (; source>=dn->lsu; source--, target--) {\r
+ // split the source Unit and accumulate remainder for next\r
+ #if DECDPUN<=4\r
+ uInt quot=QUOT10(*source, cut);\r
+ uInt rem=*source-quot*DECPOWERS[cut];\r
+ next+=quot;\r
+ #else\r
+ uInt rem=*source%DECPOWERS[cut];\r
+ next+=*source/DECPOWERS[cut];\r
+ #endif\r
+ if (target<=first) *target=(Unit)next; // write to target iff valid\r
+ next=rem*DECPOWERS[DECDPUN-cut]; // save remainder for next Unit\r
+ }\r
+ } // shift-move\r
+ // propagate remainder to one below and clear the rest\r
+ for (; target>=uar; target--) {\r
+ *target=(Unit)next;\r
+ next=0;\r
+ }\r
+ digits+=shift; // add count (shift) of zeros added\r
+ inu=uar; // use units in working array\r
+ }\r
+\r
+ /* now densely pack the coefficient into DPD declets */\r
+\r
+ #if DECDPUN!=3 // not fast path\r
+ in=*inu; // current unit\r
+ cut=0; // at lowest digit\r
+ bin=0; // [keep compiler quiet]\r
+ #endif\r
+\r
+ for(n=0; digits>0; n++) { // each output bunch\r
+ #if DECDPUN==3 // fast path, 3-at-a-time\r
+ bin=*inu; // 3 digits ready for convert\r
+ digits-=3; // [may go negative]\r
+ inu++; // may need another\r
+\r
+ #else // must collect digit-by-digit\r
+ Unit dig; // current digit\r
+ Int j; // digit-in-declet count\r
+ for (j=0; j<3; j++) {\r
+ #if DECDPUN<=4\r
+ Unit temp=(Unit)((uInt)(in*6554)>>16);\r
+ dig=(Unit)(in-X10(temp));\r
+ in=temp;\r
+ #else\r
+ dig=in%10;\r
+ in=in/10;\r
+ #endif\r
+ if (j==0) bin=dig;\r
+ else if (j==1) bin+=X10(dig);\r
+ else /* j==2 */ bin+=X100(dig);\r
+ digits--;\r
+ if (digits==0) break; // [also protects *inu below]\r
+ cut++;\r
+ if (cut==DECDPUN) {inu++; in=*inu; cut=0;}\r
+ }\r
+ #endif\r
+ // here there are 3 digits in bin, or have used all input digits\r
+\r
+ dpd=BIN2DPD[bin];\r
+\r
+ // write declet to uInt array\r
+ *uout|=dpd<<uoff;\r
+ uoff+=10;\r
+ if (uoff<32) continue; // no uInt boundary cross\r
+ uout++;\r
+ uoff-=32;\r
+ *uout|=dpd>>(10-uoff); // collect top bits\r
+ } // n declets\r
+ return;\r
+ } // decDigitsToDPD\r
+\r
+/* ------------------------------------------------------------------ */\r
+/* decDigitsFromDPD -- unpack a format's coefficient */\r
+/* */\r
+/* dn is the target number, with 7, 16, or 34-digit space. */\r
+/* sour is a 1, 2, or 4-element uInt array containing only declets */\r
+/* declets is the number of (right-aligned) declets in sour to */\r
+/* be processed. This may be 1 more than the obvious number in */\r
+/* a format, as any top digit is prefixed to the coefficient */\r
+/* continuation field. It also may be as small as 1, as the */\r
+/* caller may pre-process leading zero declets. */\r
+/* */\r
+/* When doing the 'extra declet' case care is taken to avoid writing */\r
+/* extra digits when there are leading zeros, as these could overflow */\r
+/* the units array when DECDPUN is not 3. */\r
+/* */\r
+/* The target uInts are used only as necessary to process declets */\r
+/* declets into the decNumber. When more than one uInt is needed, */\r
+/* they are used from left to right (that is, the uInt at offset 0 */\r
+/* provides the least-significant digits). */\r
+/* */\r
+/* dn->digits is set, but not the sign or exponent. */\r
+/* No error is possible [the redundant 888 codes are allowed]. */\r
+/* ------------------------------------------------------------------ */\r
+void decDigitsFromDPD(decNumber *dn, const uInt *sour, Int declets) {\r
+\r
+ uInt dpd; // collector for 10 bits\r
+ Int n; // counter\r
+ Unit *uout=dn->lsu; // -> current output unit\r
+ Unit *last=uout; // will be unit containing msd\r
+ const uInt *uin=sour; // -> current input uInt\r
+ uInt uoff=0; // -> current input offset [from right]\r
+\r
+ #if DECDPUN!=3\r
+ uInt bcd; // BCD result\r
+ uInt nibble; // work\r
+ Unit out=0; // accumulator\r
+ Int cut=0; // power of ten in current unit\r
+ #endif\r
+ #if DECDPUN>4\r
+ uInt const *pow; // work\r
+ #endif\r
+\r
+ // Expand the densely-packed integer, right to left\r
+ for (n=declets-1; n>=0; n--) { // count down declets of 10 bits\r
+ dpd=*uin>>uoff;\r
+ uoff+=10;\r
+ if (uoff>32) { // crossed uInt boundary\r
+ uin++;\r
+ uoff-=32; // [if using this code for wider, check this]\r
+ dpd|=*uin<<(10-uoff); // get waiting bits\r
+ }\r
+ dpd&=0x3ff; // clear uninteresting bits\r
+\r
+ #if DECDPUN==3\r
+ if (dpd==0) *uout=0;\r
+ else {\r
+ *uout=DPD2BIN[dpd]; // convert 10 bits to binary 0-999\r
+ last=uout; // record most significant unit\r
+ }\r
+ uout++;\r
+ } // n\r
+\r
+ #else // DECDPUN!=3\r
+ if (dpd==0) { // fastpath [e.g., leading zeros]\r
+ // write out three 0 digits (nibbles); out may have digit(s)\r
+ cut++;\r
+ if (cut==DECDPUN) {*uout=out; if (out) {last=uout; out=0;} uout++; cut=0;}\r
+ if (n==0) break; // [as below, works even if MSD=0]\r
+ cut++;\r
+ if (cut==DECDPUN) {*uout=out; if (out) {last=uout; out=0;} uout++; cut=0;}\r
+ cut++;\r
+ if (cut==DECDPUN) {*uout=out; if (out) {last=uout; out=0;} uout++; cut=0;}\r
+ continue;\r
+ }\r
+\r
+ bcd=DPD2BCD[dpd]; // convert 10 bits to 12 bits BCD\r
+\r
+ // now accumulate the 3 BCD nibbles into units\r
+ nibble=bcd & 0x00f;\r
+ if (nibble) out=(Unit)(out+nibble*DECPOWERS[cut]);\r
+ cut++;\r
+ if (cut==DECDPUN) {*uout=out; if (out) {last=uout; out=0;} uout++; cut=0;}\r
+ bcd>>=4;\r
+\r
+ // if this is the last declet and the remaining nibbles in bcd\r
+ // are 00 then process no more nibbles, because this could be\r
+ // the 'odd' MSD declet and writing any more Units would then\r
+ // overflow the unit array\r
+ if (n==0 && !bcd) break;\r
+\r
+ nibble=bcd & 0x00f;\r
+ if (nibble) out=(Unit)(out+nibble*DECPOWERS[cut]);\r
+ cut++;\r
+ if (cut==DECDPUN) {*uout=out; if (out) {last=uout; out=0;} uout++; cut=0;}\r
+ bcd>>=4;\r
+\r
+ nibble=bcd & 0x00f;\r
+ if (nibble) out=(Unit)(out+nibble*DECPOWERS[cut]);\r
+ cut++;\r
+ if (cut==DECDPUN) {*uout=out; if (out) {last=uout; out=0;} uout++; cut=0;}\r
+ } // n\r
+ if (cut!=0) { // some more left over\r
+ *uout=out; // write out final unit\r
+ if (out) last=uout; // and note if non-zero\r
+ }\r
+ #endif\r
+\r
+ // here, last points to the most significant unit with digits;\r
+ // inspect it to get the final digits count -- this is essentially\r
+ // the same code as decGetDigits in decNumber.c\r
+ dn->digits=(last-dn->lsu)*DECDPUN+1; // floor of digits, plus\r
+ // must be at least 1 digit\r
+ #if DECDPUN>1\r
+ if (*last<10) return; // common odd digit or 0\r
+ dn->digits++; // must be 2 at least\r
+ #if DECDPUN>2\r
+ if (*last<100) return; // 10-99\r
+ dn->digits++; // must be 3 at least\r
+ #if DECDPUN>3\r
+ if (*last<1000) return; // 100-999\r
+ dn->digits++; // must be 4 at least\r
+ #if DECDPUN>4\r
+ for (pow=&DECPOWERS[4]; *last>=*pow; pow++) dn->digits++;\r
+ #endif\r
+ #endif\r
+ #endif\r
+ #endif\r
+ return;\r
+ } //decDigitsFromDPD\r
--- /dev/null
+/* ------------------------------------------------------------------ */\r
+/* Decimal 64-bit format module header */\r
+/* ------------------------------------------------------------------ */\r
+/* Copyright (c) IBM Corporation, 2000, 2005. All rights reserved. */\r
+/* */\r
+/* This software is made available under the terms of the */\r
+/* ICU License -- ICU 1.8.1 and later. */\r
+/* */\r
+/* The description and User's Guide ("The decNumber C Library") for */\r
+/* this software is called decNumber.pdf. This document is */\r
+/* available, together with arithmetic and format specifications, */\r
+/* testcases, and Web links, on the General Decimal Arithmetic page. */\r
+/* */\r
+/* Please send comments, suggestions, and corrections to the author: */\r
+/* mfc@uk.ibm.com */\r
+/* Mike Cowlishaw, IBM Fellow */\r
+/* IBM UK, PO Box 31, Birmingham Road, Warwick CV34 5JL, UK */\r
+/* ------------------------------------------------------------------ */\r
+\r
+#if !defined(DECIMAL64)\r
+ #define DECIMAL64\r
+ #define DEC64NAME "decimal64" /* Short name */\r
+ #define DEC64FULLNAME "Decimal 64-bit Number" /* Verbose name */\r
+ #define DEC64AUTHOR "Mike Cowlishaw" /* Who to blame */\r
+\r
+\r
+ /* parameters for decimal64s */\r
+ #define DECIMAL64_Bytes 8 /* length */\r
+ #define DECIMAL64_Pmax 16 /* maximum precision (digits) */\r
+ #define DECIMAL64_Emax 384 /* maximum adjusted exponent */\r
+ #define DECIMAL64_Emin -383 /* minimum adjusted exponent */\r
+ #define DECIMAL64_Bias 398 /* bias for the exponent */\r
+ #define DECIMAL64_String 24 /* maximum string length, +1 */\r
+ #define DECIMAL64_EconL 8 /* exp. continuation length */\r
+ /* highest biased exponent (Elimit-1) */\r
+ #define DECIMAL64_Ehigh (DECIMAL64_Emax+DECIMAL64_Bias-DECIMAL64_Pmax+1)\r
+\r
+ /* check enough digits, if pre-defined */\r
+ #if defined(DECNUMDIGITS)\r
+ #if (DECNUMDIGITS<DECIMAL64_Pmax)\r
+ #error decimal64.h needs pre-defined DECNUMDIGITS>=16 for safe use\r
+ #endif\r
+ #endif\r
+\r
+\r
+ #ifndef DECNUMDIGITS\r
+ #define DECNUMDIGITS DECIMAL64_Pmax /* size if not already defined*/\r
+ #endif\r
+ #ifndef DECNUMBER\r
+ #include "decNumber.h" /* context and number library */\r
+ #endif\r
+\r
+ /* Decimal 64-bit type, accessible by bytes */\r
+ typedef struct {\r
+ uint8_t bytes[DECIMAL64_Bytes]; /* decimal64: 1, 5, 8, 50 bits*/\r
+ } decimal64;\r
+\r
+ /* special values [top byte excluding sign bit; last two bits are */\r
+ /* don't-care for Infinity on input, last bit don't-care for NaN] */\r
+ #if !defined(DECIMAL_NaN)\r
+ #define DECIMAL_NaN 0x7c /* 0 11111 00 NaN */\r
+ #define DECIMAL_sNaN 0x7e /* 0 11111 10 sNaN */\r
+ #define DECIMAL_Inf 0x78 /* 0 11110 00 Infinity */\r
+ #endif\r
+\r
+ /* ---------------------------------------------------------------- */\r
+ /* Routines */\r
+ /* ---------------------------------------------------------------- */\r
+ /* String conversions */\r
+ decimal64 * decimal64FromString(decimal64 *, const char *, decContext *);\r
+ char * decimal64ToString(const decimal64 *, char *);\r
+ char * decimal64ToEngString(const decimal64 *, char *);\r
+\r
+ /* decNumber conversions */\r
+ decimal64 * decimal64FromNumber(decimal64 *, const decNumber *,\r
+ decContext *);\r
+ decNumber * decimal64ToNumber(const decimal64 *, decNumber *);\r
+\r
+ /* Format-dependent utilities */\r
+ uint32_t decimal64IsCanonical(const decimal64 *);\r
+ decimal64 * decimal64Canonical(decimal64 *, const decimal64 *);\r
+\r
+#endif\r
--- /dev/null
+#ifndef _DEFINITION_H
+#define _DEFINITION_H 1
+
+// see definition.incl.pl1
+
+// dcl 1 definition aligned based,
+struct definition {
+ // 2 forward unal bit(18), /* offset of next def */
+ // 2 backward unal bit(18), /* offset of previous def */
+ uint64_t backward : 18;
+ uint64_t forward : 18;
+ uint64_t dummy0 : 28;
+
+ // 2 value unal bit(18),
+ // 2 flags unal,
+ // 3 new bit(1),
+ // 3 ignore bit(1),
+ // 3 entry bit(1),
+ // 3 retain bit(1),
+ // 3 argcount bit(1),
+ // 3 descriptors bit(1),
+ // 3 indirect bit(1),
+ // 3 unused bit(8),
+ // 2 class unal bit(3),
+ uint64_t class : 3;
+ uint64_t flags_unused : 8;
+ uint64_t flags_indirect : 1;
+ uint64_t flags_descriptors : 1;
+ uint64_t flags_argcount : 1;
+ uint64_t flags_retain : 1;
+ uint64_t flags_entry : 1;
+ uint64_t flags_ignore : 1;
+ uint64_t flags_new : 1;
+ uint64_t value : 18;
+ uint64_t dummy1 : 28;
+
+ // 2 symbol unal bit(18), /* offset of ACC for symbol */
+ // 2 segname unal bit(18); /* offset of segname def */
+ uint64_t segname : 18;
+ uint64_t symbol : 18;
+};
+
+#endif
--- /dev/null
+/*
+ Copyright 2012-2016 by Harry Reed
+ Copyright 2013-2018 by Charles Anthony
+ Copyright 2016 by Jean-Michel Merliot
+
+ All rights reserved.
+
+ This software is made available under the terms of the
+ ICU License -- ICU 1.8.1 and later.
+ See the LICENSE file at the top-level directory of this distribution and
+ at https://sourceforge.net/p/dps8m/code/ci/master/tree/LICENSE
+ */
+
+/**
+ * \file dps8.h
+ * \project dps8
+ * \author Harry Reed
+ * \date 9/17/12
+ * \copyright Copyright (c) 2012 Harry Reed. All rights reserved.
+*/
+
+#ifndef DPS8_H
+#define DPS8_H
+
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <inttypes.h>
+
+#include <sys/time.h>
+
+#include <setjmp.h> // for setjmp/longjmp used by interrupts & faults
+
+#if (defined(__APPLE__) && defined(__MACH__)) || defined(__ANDROID__)
+#include <libgen.h> // needed for OS/X and Android
+#endif
+
+//#define EMULATOR_ONLY 1
+
+// Define to emulate Level 68 instead of DPS8M
+
+//#define L68
+
+#ifndef L68
+#ifndef DPS8M
+#define DPS8M
+#endif
+#endif
+
+#ifndef USE_INT64
+#define USE_INT64
+#endif
+
+#ifdef NEED_128
+typedef struct { uint64_t h; uint64_t l; } __uint128_t;
+typedef struct { int64_t h; uint64_t l; } __int128_t;
+
+#define construct_128(h, l) ((uint128) { (h), (l) })
+#define construct_s128(h, l) ((int128) { (h), (l) })
+
+#endif
+
+// Quiet compiler unused warnings
+#define QUIET_UNUSED
+
+// Enable M[] as shared memory segment
+//#define M_SHARED
+//LDFLAGS += -lrt
+
+#ifdef TESTING
+#else
+// Enable speed over debuggibility
+#define SPEED
+#endif
+
+// Enable WAM
+//#define WAM
+
+// Enable panel support
+//#define PANEL
+
+// Enable history debugger
+// NB. This has an large impact on ARM architectures, probably due to cache
+// misses.
+//#define HDBG
+
+// XXX FixMe
+// history debugger wont build under XCode just yet.
+#ifdef __APPLE__
+#undef HDBG
+#endif
+
+// Enable round-robin multi-CPU
+//#define ROUND_ROBIN
+
+// Legacy ATTN_HACK support
+// #define ATTN_HACK
+
+// Enable ISOLTS support
+//#define ISOLTS
+
+// Experimential dial_out line disconnect delay
+// FNP polled ~100Hz; 2 secs. is 200 polls
+#define DISC_DELAY 200
+
+// Enable simh 'launch' command
+// #define LAUNCH
+
+
+//
+// Dependencies
+//
+
+// ISOLTS requires multiple CPU support
+#ifdef ISOLTS
+#if !defined(ROUND_ROBIN) && !defined(LOCKLESS)
+#define ROUND_ROBIN
+#endif
+#endif
+
+// PANEL only works on L68
+#ifdef PANEL
+#ifdef DPS8M
+#error "PANEL works with L68, not DPS8M"
+#endif
+#ifndef L68
+#define L68
+#endif
+#endif
+
+#ifdef PANEL
+#define PNL(x) x
+#else
+#define PNL(x)
+#endif
+
+#ifdef L68
+#define L68_(x) x
+#else
+#define L68_(x)
+#endif
+
+#ifdef DPS8M
+#define DPS8M_(x) x
+#else
+#define DPS8M_(x)
+#endif
+
+// debugging tool
+#ifdef ISOLTS
+#define IF1 if (current_running_cpu_idx && sim_deb)
+#else
+#define IF1 if (0)
+#endif
+
+//#define OSCAR
+
+// DPS8-M support Hex Mode Floating Point
+#ifdef DPS8M
+#define HEX_MODE
+#endif
+
+// Instruction profiler
+// #define MATRIX
+
+// Run TR on work done, not wall clock.
+// Define one of these; tied to memory access (MEM) or to instruction
+// execution (EXEC)
+
+//#define TR_WORK_MEM
+#define TR_WORK_EXEC
+
+// Multi-threading may require 'volatile' in some place; make it easy
+// to support both configurations
+
+#if defined(THREADZ) || defined(LOCKLESS)
+#define CWO
+#define vol volatile
+#else
+#define vol
+#endif
+
+// Fix glibc incompatibility with new simh code.
+
+#if __WORDSIZE == 64
+#undef PRIu64
+#define PRIu64 "llu"
+#undef PRId64
+#define PRId64 "lld"
+#undef PRIo64
+#define PRIo64 "llo"
+#endif
+#include "sim_defs.h" /* simulator defns */
+
+//#include "sim_tape.h"
+
+// patch supplied by Dave Jordan (jordandave@gmail.com) 29 Nov 2012
+#ifdef __MINGW32__
+#include <stdint.h>
+typedef t_uint64 u_int64_t;
+#endif
+typedef t_uint64 uint64;
+typedef t_int64 int64;
+
+/* Data types */
+
+typedef uint8 word1;
+typedef uint8 word2;
+typedef uint8 word3;
+typedef uint8 word4;
+typedef uint8 word5;
+typedef uint8 word6;
+typedef uint8 word7;
+typedef uint8 word8;
+typedef int8 word8s; // signed 8-bit quantity
+typedef uint16 word9;
+typedef uint16 word10;
+typedef uint16 word11;
+typedef uint16 word12;
+typedef uint16 word13;
+typedef uint16 word14;
+typedef uint16 word15;
+typedef uint16 word16;
+typedef uint32 word17;
+typedef uint32 word18;
+typedef uint32 word19;
+typedef int32 word18s;
+typedef uint32 word20;
+typedef uint32 word21;
+typedef uint32 word22;
+typedef uint32 word23;
+typedef uint32 word24;
+typedef uint32 word27;
+typedef uint32 word28;
+typedef uint32 word32;
+typedef uint64 word34;
+typedef uint64 word36;
+typedef uint64 word37;
+typedef uint64 word38;
+typedef int64 word36s;
+typedef __uint128_t word72;
+typedef __int128_t word72s;
+typedef __uint128_t word73;
+typedef __uint128_t word74;
+
+typedef __uint128_t uint128;
+typedef __int128_t int128;
+
+typedef word36 float36; // single precision float
+typedef word72 float72; // double precision float
+
+typedef unsigned int uint; // efficient unsigned int, at least 32 bits
+
+#include "dps8_simh.h"
+#include "dps8_math128.h"
+#include "dps8_hw_consts.h"
+#include "dps8_em_consts.h"
+
+
+
+#define SETF(flags, x) flags = ((flags) | (x))
+#define CLRF(flags, x) flags = ((flags) & ~(x))
+#define TSTF(flags, x) (((flags) & (x)) ? 1 : 0)
+#define SCF(cond, flags, x) { if (cond) SETF((flags), x); else CLRF((flags), x); }
+
+#define SETBIT(dst, bitno) ((dst) | (1LLU << (bitno)))
+#define CLRBIT(dst, bitno) ((dst) & ~(1LLU << (bitno)))
+#define TSTBIT(dst, bitno) (((dst) & (1LLU << (bitno))) ? 1: 0)
+
+typedef enum
+ {
+ UNKNOWN_CYCLE = 0,
+ OPERAND_STORE,
+ OPERAND_READ,
+ INDIRECT_WORD_FETCH,
+ RTCD_OPERAND_FETCH,
+ INSTRUCTION_FETCH,
+ APU_DATA_READ,
+ APU_DATA_STORE,
+ ABSA_CYCLE,
+#ifdef LOCKLESS
+ OPERAND_RMW,
+ APU_DATA_RMW,
+#endif
+ } processor_cycle_type;
+
+#ifndef LOCKLESS
+#define OPERAND_RMW OPERAND_READ
+#define APU_DATA_RMW APU_DATA_READ
+#endif
+
+#ifndef EIS_PTR4
+// some breakpoint stuff ...
+typedef enum
+ {
+ UnknownMAT = 0,
+ OperandRead,
+ OperandWrite,
+ viaPR
+ } MemoryAccessType;
+#endif
+
+// get 6-bit char @ pos
+#define GETCHAR(src, pos) (word6)(((word36)src >> (word36)((5 - pos) * 6)) & 077)
+// get 9-bit byte @ pos
+#define GETBYTE(src, pos) (word9)(((word36)src >> (word36)((3 - pos) * 9)) & 0777)
+
+#ifdef NEED_128
+#define YPAIRTO72(ypair) construct_128 ((ypair[0] >> 28) & MASK8, \
+ ((ypair[0] & MASK28) << 36) | \
+ (ypair[1] & MASK36));
+#else
+#define YPAIRTO72(ypair) (((((word72)(ypair[0] & DMASK)) << 36) | (ypair[1] & DMASK)) & MASK72)
+#endif
+
+
+#define GET_TALLY(src) (((src) >> 6) & MASK12) // 12-bits
+#define GET_DELTA(src) ((src) & MASK6) // 6-bits
+
+#ifndef max
+#define max(a,b) max2((a),(b))
+#endif
+#define max2(a,b) ((a) > (b) ? (a) : (b))
+#define max3(a,b,c) max((a), max((b),(c)))
+
+#ifndef min
+#define min(a,b) min2((a),(b))
+#endif
+#define min2(a,b) ((a) < (b) ? (a) : (b))
+#define min3(a,b,c) min((a), min((b),(c)))
+
+// opcode metadata (flag) ...
+typedef enum
+ {
+ READ_OPERAND = (1U << 0), // fetches/reads operand (CA) from memory
+ STORE_OPERAND = (1U << 1), // stores/writes operand to memory (its a STR-OP)
+#define RMW (READ_OPERAND | STORE_OPERAND) // a Read-Modify-Write instruction
+ READ_YPAIR = (1U << 2), // fetches/reads Y-pair operand (CA) from memory
+ STORE_YPAIR = (1U << 3), // stores/writes Y-pair operand to memory
+ READ_YBLOCK8 = (1U << 4), // fetches/reads Y-block8 operand (CA) from memory
+ NO_RPT = (1U << 5), // Repeat instructions not allowed
+//#define NO_RPD (1U << 6)
+ NO_RPL = (1U << 7),
+//#define NO_RPX (NO_RPT | NO_RPD | NO_RPL)
+ READ_YBLOCK16 = (1U << 8), // fetches/reads Y-block16 operands from memory
+ STORE_YBLOCK16 = (1U << 9), // fetches/reads Y-block16 operands from memory
+ TRANSFER_INS = (1U << 10), // a transfer instruction
+ TSPN_INS = (1U << 11), // a TSPn instruction
+ CALL6_INS = (1U << 12), // a call6 instruction
+ PREPARE_CA = (1U << 13), // prepare TPR.CA for instruction
+ STORE_YBLOCK8 = (1U << 14), // stores/writes Y-block8 operand to memory
+ IGN_B29 = (1U << 15), // Bit-29 has an instruction specific meaning. Ignore.
+ NO_TAG = (1U << 16), // tag is interpreted differently and for addressing purposes is effectively 0
+ PRIV_INS = (1U << 17), // priveleged instruction
+ NO_BAR = (1U << 18), // not allowed in BAR mode
+// NO_XEC = (1U << 19), // can't be executed via xec/xed
+ NO_XED = (1U << 20), // No execution via XEC/XED instruction
+
+// EIS operand types
+
+#define EOP_ALPHA 1U
+
+// bits 21, 22
+ EOP1_ALPHA = (EOP_ALPHA << 21),
+ EOP1_MASK = (3U << 21),
+#define EOP1_SHIFT 21
+
+// bits 23, 24
+ EOP2_ALPHA = (EOP_ALPHA << 23),
+ EOP2_MASK = (3U << 23),
+#define EOP2_SHIFT 23
+
+// bits 25, 26
+ EOP3_ALPHA = (EOP_ALPHA << 25),
+ EOP3_MASK = (3U << 25),
+#define EOP3_SHIFT 25
+
+ READ_YBLOCK32 = (1U << 27), // fetches/reads Y-block16 operands from memory
+ STORE_YBLOCK32 = (1U << 28), // fetches/reads Y-block16 operands from memory
+ } opc_flag;
+
+
+// opcode metadata (disallowed) modifications
+typedef enum opc_mod
+ {
+ NO_DU = (1U << 0), // No DU modification allowed (Can these 2 be combined into 1?)
+ NO_DL = (1U << 1), // No DL modification allowed
+#define NO_DUDL (NO_DU | NO_DL)
+
+ NO_CI = (1U << 2), // No character indirect modification (can these next 3 be combined?_
+ NO_SC = (1U << 3), // No sequence character modification
+ NO_SCR = (1U << 4), // No sequence character reverse modification
+#define NO_CSS (NO_CI | NO_SC | NO_SCR)
+
+#define NO_DLCSS (NO_DU | NO_CSS)
+#define NO_DDCSS (NO_DUDL | NO_CSS)
+
+ ONLY_AU_QU_AL_QL_XN = (1U << 5) // None except au, qu, al, ql, xn
+ } opc_mod;
+
+// None except au, qu, al, ql, xn for MF1 and REG
+// None except du, au, qu, al, ql, xn for MF2
+// None except au, qu, al, ql, xn for MF1, MF2, and MF3
+
+
+#define IS_NONE(tag) (!(tag))
+/*! non-tally: du or dl */
+#define IS_DD(tag) ((_TM(tag) != 040U) && \
+ ((_TD(tag) == 003U) || (_TD(tag) == 007U)))
+/*! tally: ci, sc, or scr */
+#define IS_CSS(tag) ((_TM(tag) == 040U) && \
+ ((_TD(tag) == 050U) || (_TD(tag) == 052U) || \
+ (_TD(tag) == 045U)))
+#define IS_DDCSS(tag) (IS_DD(tag) || IS_CSS(tag))
+/*! just dl or css */
+#define IS_DCSS(tag) (((_TM(tag) != 040U) && (_TD(tag) == 007U)) || IS_CSS(tag))
+
+// !%WRD ~0200000 017
+// !%9 ~0100000 027
+// !%6 ~0040000 033
+// !%4 ~0020000 035
+// !%1 ~0010000 036
+enum reg_use { is_WRD = 0174000,
+ is_9 = 0274000,
+ is_6 = 0334000,
+ is_4 = 0354000,
+ is_1 = 0364000,
+ is_DU = 04000,
+ is_OU = 02000,
+ ru_A = 02000 | 01000,
+ ru_Q = 02000 | 0400,
+ ru_X0 = 02000 | 0200,
+ ru_X1 = 02000 | 0100,
+ ru_X2 = 02000 | 040,
+ ru_X3 = 02000 | 020,
+ ru_X4 = 02000 | 010,
+ ru_X5 = 02000 | 04,
+ ru_X6 = 02000 | 02,
+ ru_X7 = 02000 | 01,
+ ru_none = 02000 | 0 };
+//, ru_notou = 1024 };
+
+#define ru_AQ (ru_A | ru_Q)
+#define ru_Xn(n) (1 << (7 - (n)))
+
+// Basic + EIS opcodes .....
+struct opcode_s {
+ const char *mne; // mnemonic
+ opc_flag flags; // various and sundry flags
+ opc_mod mods; // disallowed addr mods
+ uint ndes; // number of operand descriptor words for instruction (mw EIS)
+ enum reg_use reg_use; // register usage
+};
+
+// operations stuff
+
+/*! Y of instruc word */
+#define Y(i) (i & MASKHI18)
+/*! X from opcodes in instruc word */
+#define OPSX(i) ((i & 0007000LLU) >> 9)
+/*! X from OP_* enum, and X from */
+#define X(i) (i & 07U)
+
+enum { OP_1 = 00001U,
+ OP_E = 00002U,
+ OP_BAR = 00003U,
+ OP_IC = 00004U,
+ OP_A = 00005U,
+ OP_Q = 00006U,
+ OP_AQ = 00007U,
+ OP_IR = 00010U,
+ OP_TR = 00011U,
+ OP_REGS = 00012U,
+
+ /* 645/6180 */
+ OP_CPR = 00021U,
+ OP_DBR = 00022U,
+ OP_PTP = 00023U,
+ OP_PTR = 00024U,
+ OP_RA = 00025U,
+ OP_SDP = 00026U,
+ OP_SDR = 00027U,
+
+ OP_X = 01000U
+};
+
+
+enum eCAFoper {
+ unknown = 0,
+ readCY,
+ writeCY,
+ rmwCY, // Read-Modify-Write
+// readCYpair,
+// writeCYpair,
+// readCYblock8,
+// writeCYblock8,
+// readCYblock16,
+// writeCYblock16,
+
+ prepareCA,
+};
+typedef enum eCAFoper eCAFoper;
+
+#define READOP(i) ((bool) (i->info->flags & \
+ (READ_OPERAND | \
+ READ_YPAIR | \
+ READ_YBLOCK8 | \
+ READ_YBLOCK16 | \
+ READ_YBLOCK32)))
+
+#define WRITEOP(i) ((bool) (i->info->flags & \
+ (STORE_OPERAND | \
+ STORE_YPAIR | \
+ STORE_YBLOCK8 | \
+ STORE_YBLOCK16 | \
+ STORE_YBLOCK32)) )
+
+// if it's both read and write it's a RMW
+#define RMWOP(i) ((bool) READOP(i) && WRITEOP(i))
+
+#define TRANSOP(i) ((bool) (i->info->flags & (TRANSFER_INS) ))
+
+//
+// EIS stuff ...
+//
+
+// Numeric operand descriptors
+
+
+// AL39 Table 4-3. Alphanumeric Data Type (TA) Codes
+enum
+ {
+ CTA9 = 0U, // 9-bit bytes
+ CTA6 = 1U, // 6-bit characters
+ CTA4 = 2U, // 4-bit decimal
+ CTAILL = 3U // Illegal
+ };
+
+// TN - Type Numeric AL39 Table 4-3. Alphanumeric Data Type (TN) Codes
+enum
+ {
+ CTN9 = 0U, // 9-bit
+ CTN4 = 1U // 4-bit
+ };
+
+// S - Sign and Decimal Type (AL39 Table 4-4. Sign and Decimal Type (S) Codes)
+
+enum
+ {
+ CSFL = 0U, // Floating-point, leading sign
+ CSLS = 1U, // Scaled fixed-point, leading sign
+ CSTS = 2U, // Scaled fixed-point, trailing sign
+ CSNS = 3U // Scaled fixed-point, unsigned
+ };
+
+
+enum
+ {
+ // Address register flag. This flag controls interpretation of the ADDRESS
+ // field of the operand descriptor just as the "A" flag controls
+ // interpretation of the ADDRESS field of the basic and EIS single-word
+ // instructions.
+ MFkAR = 0x40U,
+ // Register length control. If RL = 0, then the length (N) field of the
+ // operand descriptor contains the length of the operand. If RL = 1, then
+ // the length (N) field of the operand descriptor contains a selector value
+ // specifying a register holding the operand length. Operand length is
+ // interpreted as units of the data size (1-, 4-, 6-, or 9-bit) given in
+ // the associated operand descriptor.
+ MFkRL = 0x20U,
+ // Indirect descriptor control. If ID = 1 for Mfk, then the kth word
+ // following the instruction word is an indirect pointer to the operand
+ // descriptor for the kth operand; otherwise, that word is the operand
+ // descriptor.
+ MFkID = 0x10U,
+
+ MFkREGMASK = 0xfU
+ };
+
+
+// EIS instruction take on a life of their own. Need to take into account
+// RNR/SNR/BAR etc.
+typedef enum
+ {
+ eisUnknown = 0, // uninitialized
+ eisTA = 1, // type alphanumeric
+ eisTN = 2, // type numeric
+ eisBIT = 3 // bit string
+ } eisDataType;
+
+typedef enum
+ {
+ eRWreadBit = 0,
+ eRWwriteBit
+ } eRW;
+
+// Misc constants and macros
+
+#define ARRAY_SIZE(a) ( sizeof(a) / sizeof((a)[0]) )
+
+#ifdef __GNUC__
+#define NO_RETURN __attribute__ ((noreturn))
+#define UNUSED __attribute__ ((unused))
+#elif defined (__MINGW64__)
+#define NO_RETURN __attribute__ ((noreturn))
+#define UNUSED __attribute__ ((unused))
+#else
+#define NO_RETURN
+#define UNUSED
+#endif
+
+#define MAX_DEV_NAME_LEN 64
+
+#ifdef TEST_OLIN
+extern int64_t cmpxchg_data;
+inline void cmpxchg (void)
+ {
+ __sync_val_compare_and_swap (& cmpxchg_data, 0, 1);
+ cmpxchg_data = 0;
+ }
+#endif
+
+#ifdef TEST_FENCE
+#include <pthread.h>
+extern pthread_mutex_t fenceLock;
+inline void fence (void)
+ {
+ pthread_mutex_lock (& fenceLock);
+ pthread_mutex_unlock (& fenceLock);
+ }
+#endif
+
+#endif // ifdef DPS8_H
--- /dev/null
+/*
+ Copyright (c) 2007-2013 Michael Mondy
+ Copyright 2012-2016 by Harry Reed
+ Copyright 2013-2018 by Charles Anthony
+
+ All rights reserved.
+
+ This software is made available under the terms of the
+ ICU License -- ICU 1.8.1 and later.
+ See the LICENSE file at the top-level directory of this distribution and
+ at https://sourceforge.net/p/dps8m/code/ci/master/tree/LICENSE
+ */
+
+/**
+ * \file dps8_addrmods.c
+ * \project dps8
+ * \author Harry Reed
+ * \date 9/25/12
+ * \copyright Copyright (c) 2012 Harry Reed. All rights reserved.
+ */
+
+#include <stdio.h>
+#include "dps8.h"
+#include "dps8_addrmods.h"
+#include "dps8_sys.h"
+#include "dps8_faults.h"
+//#include "dps8_scu.h"
+//#include "dps8_iom.h"
+//#include "dps8_cable.h"
+#include "dps8_cpu.h"
+#include "dps8_append.h"
+#include "dps8_ins.h"
+#include "dps8_iefp.h"
+#include "dps8_opcodetable.h"
+#include "dps8_utils.h"
+#if defined(THREADZ) || defined(LOCKLESS)
+//#include "threadz.h"
+#endif
+
+#define DBG_CTR cpu.cycleCnt
+
+// Computed Address Formation Flowcharts
+
+//
+// return contents of register indicated by Td
+//
+
+static word18 get_Cr (word4 Tdes)
+ {
+ cpu.ou.directOperandFlag = false;
+
+ if (Tdes == 0)
+ return 0;
+
+ if (Tdes & 010) // Xn
+ return cpu.rX [X (Tdes)];
+
+ switch (Tdes)
+ {
+ case TD_N: // rY = address from opcode
+ return 0;
+
+ case TD_AU: // rY + C(A)0,17
+ return GETHI (cpu.rA);
+
+ case TD_QU: // rY + C(Q)0,17
+ return GETHI (cpu.rQ);
+
+ case TD_DU: // none; operand has the form y || (00...0)18
+ cpu.ou.directOperand = 0;
+ SETHI (cpu.ou.directOperand, cpu.rY);
+ cpu.ou.directOperandFlag = true;
+
+ sim_debug (DBG_ADDRMOD, & cpu_dev,
+ "%s(TD_DU): rY=%06o directOperand=%012"PRIo64"\n",
+ __func__, cpu.rY, cpu.ou.directOperand);
+
+ return 0;
+
+ case TD_IC: // rY + C (PPR.IC)
+ return cpu.PPR.IC;
+
+ case TD_AL: // rY + C(A)18,35
+ return GETLO (cpu.rA);
+
+ case TD_QL: // rY + C(Q)18,35
+ return GETLO (cpu.rQ);
+
+ case TD_DL: // none; operand has the form (00...0)18 || y
+ cpu.ou.directOperand = 0;
+ SETLO (cpu.ou.directOperand, cpu.rY);
+ cpu.ou.directOperandFlag = true;
+
+ sim_debug (DBG_ADDRMOD, & cpu_dev,
+ "%s(TD_DL): rY=%06o directOperand=%012"PRIo64"\n",
+ __func__, cpu.rY, cpu.ou.directOperand);
+
+ return 0;
+ }
+ return 0;
+ }
+
+static char * op_desc_str (char * temp)
+ {
+ DCDstruct * i = & cpu.currentInstruction;
+
+ strcpy (temp, "");
+
+ if (READOP (i))
+ {
+ switch (operand_size ())
+ {
+ case 1:
+ strcat (temp, "readCY");
+ break;
+
+ case 2:
+ strcat (temp, "readCYpair2");
+ break;
+
+ case 8:
+ strcat (temp, "readCYblock8");
+ break;
+
+ case 16:
+ strcat (temp, "readCYblock16");
+ break;
+
+ case 32:
+ strcat (temp, "readCYblock32");
+ break;
+ }
+ }
+
+ if (WRITEOP (i))
+ {
+ if (strlen (temp))
+ strcat (temp, "/");
+
+ switch (operand_size ())
+ {
+ case 1:
+ strcat (temp, "writeCY");
+ break;
+
+ case 2:
+ strcat (temp, "writeCYpair2");
+ break;
+
+ case 8:
+ strcat (temp, "writeCYblock8");
+ break;
+
+ case 16:
+ strcat (temp, "writeCYblock16");
+ break;
+
+ case 32:
+ strcat (temp, "writeCYblock32");
+ break;
+ }
+ }
+
+ if (TRANSOP (i))
+ {
+ if (strlen (temp))
+ strcat (temp, "/");
+
+ strcat (temp, "prepareCA (TRA)");
+ }
+
+ if (! READOP (i) && ! WRITEOP (i) && ! TRANSOP (i) &&
+ i -> info -> flags & PREPARE_CA)
+ {
+ if (strlen (temp))
+ strcat (temp, "/");
+
+ strcat (temp, "prepareCA");
+ }
+
+ return temp; //"op_desc_str (???)";
+ }
+
+static void do_ITP (void)
+ {
+ sim_debug (DBG_APPENDING, & cpu_dev,
+ "ITP Pair: PRNUM=%o BITNO=%o WORDNO=%o MOD=%o\n",
+ GET_ITP_PRNUM (cpu.itxPair), GET_ITP_WORDNO (cpu.itxPair),
+ GET_ITP_BITNO (cpu.itxPair), GET_ITP_MOD (cpu.itxPair));
+ //
+ // For n = C(ITP.PRNUM):
+ // C(PRn.SNR) -> C(TPR.TSR)
+ // maximum of ( C(PRn.RNR), C(SDW.R1), C(TPR.TRR) ) -> C(TPR.TRR)
+ // C(ITP.BITNO) -> C(TPR.TBR)
+ // C(PRn.WORDNO) + C(ITP.WORDNO) + C(r) -> C(TPR.CA)
+
+ // Notes:
+ // 1. r = C(CT-HOLD) if the instruction word or preceding indirect word
+ // specified indirect then register modification, or
+ // 2. r = C(ITP.MOD.Td) if the instruction word or preceding indirect word
+ // specified register then indirect modification and ITP.MOD.Tm specifies
+ // either register or register then indirect modification.
+ // 3. SDW.R1 is the upper limit of the read/write ring bracket for the
+ // segment C(TPR.TSR) (see Section 8).
+ //
+
+ word3 n = GET_ITP_PRNUM (cpu.itxPair);
+ CPTUR (cptUsePRn + n);
+ cpu.TPR.TSR = cpu.PR[n].SNR;
+ cpu.TPR.TRR = max3 (cpu.PR[n].RNR, cpu.RSDWH_R1, cpu.TPR.TRR);
+ cpu.TPR.TBR = GET_ITP_BITNO (cpu.itxPair);
+ cpu.TPR.CA = cpu.PAR[n].WORDNO + GET_ITP_WORDNO (cpu.itxPair);
+ cpu.TPR.CA &= AMASK;
+ cpu.rY = cpu.TPR.CA;
+
+ cpu.rTAG = GET_ITP_MOD (cpu.itxPair);
+
+ cpu.cu.itp = 1;
+ cpu.cu.TSN_PRNO[0] = n;
+ cpu.cu.TSN_VALID[0] = 1;
+
+ return;
+ }
+
+static void do_ITS (void)
+ {
+ sim_debug (DBG_APPENDING, & cpu_dev,
+ "ITS Pair: SEGNO=%o RN=%o WORDNO=%o BITNO=%o MOD=%o\n",
+ GET_ITS_SEGNO (cpu.itxPair), GET_ITS_RN (cpu.itxPair),
+ GET_ITS_WORDNO (cpu.itxPair), GET_ITS_BITNO (cpu.itxPair),
+ GET_ITS_MOD (cpu.itxPair));
+ // C(ITS.SEGNO) -> C(TPR.TSR)
+ // maximum of ( C(ITS.RN), C(SDW.R1), C(TPR.TRR) ) -> C(TPR.TRR)
+ // C(ITS.BITNO) -> C(TPR.TBR)
+ // C(ITS.WORDNO) + C(r) -> C(TPR.CA)
+ //
+ // 1. r = C(CT-HOLD) if the instruction word or preceding indirect word
+ // specified indirect then register modification, or
+ // 2. r = C(ITS.MOD.Td) if the instruction word or preceding indirect word
+ // specified register then indirect modification and ITS.MOD.Tm specifies
+ // either register or register then indirect modification.
+ // 3. SDW.R1 is the upper limit of the read/write ring bracket for the
+ // segment C(TPR.TSR) (see Section 8).
+
+ cpu.TPR.TSR = GET_ITS_SEGNO (cpu.itxPair);
+
+ sim_debug (DBG_APPENDING, & cpu_dev,
+ "ITS Pair Ring: RN %o RSDWH_R1 %o TRR %o max %o\n",
+ GET_ITS_RN (cpu.itxPair), cpu.RSDWH_R1, cpu.TPR.TRR,
+ max3 (GET_ITS_RN (cpu.itxPair), cpu.RSDWH_R1, cpu.TPR.TRR));
+
+ cpu.TPR.TRR = max3 (GET_ITS_RN (cpu.itxPair), cpu.RSDWH_R1, cpu.TPR.TRR);
+ cpu.TPR.TBR = GET_ITS_BITNO (cpu.itxPair);
+ cpu.TPR.CA = GET_ITS_WORDNO (cpu.itxPair);
+ cpu.TPR.CA &= AMASK;
+
+ cpu.rY = cpu.TPR.CA;
+
+ cpu.rTAG = GET_ITS_MOD (cpu.itxPair);
+
+ cpu.cu.its = 1;
+
+ return;
+ }
+
+
+// CANFAULT
+static void do_ITS_ITP (word6 Tag, word6 * newtag)
+ {
+ word6 ind_tag = GET_TAG (cpu.itxPair [0]);
+
+ sim_debug (DBG_APPENDING, & cpu_dev,
+ "do_ITS/ITP: %012"PRIo64" %012"PRIo64" Tag:%o\n",
+ cpu.itxPair[0], cpu.itxPair[1], Tag);
+
+ if (! ((GET_TM (Tag) == TM_IR || GET_TM (Tag) == TM_RI) &&
+ (ISITP (cpu.itxPair[0]) || ISITS (cpu.itxPair[0]))))
+ {
+ sim_debug (DBG_APPENDING, & cpu_dev, "do_ITS_ITP: faulting\n");
+ doFault (FAULT_IPR, fst_ill_mod, "Incorrect address modifier");
+ }
+
+ // Whenever the processor is forming a virtual address two special address
+ // modifiers may be specified and are effective under certain restrictive
+ // conditions. The special address modifiers are shown in Table 6-4 and
+ // discussed in the paragraphs below.
+ //
+ // The conditions for which the special address modifiers are effective
+ // are as follows:
+ //
+ // 1. The instruction word (or preceding indirect word) must specify
+ // indirect then register or register then indirect modification.
+ //
+ // 2. The computed address for the indirect word must be even.
+ //
+ // If these conditions are satisfied, the processor examines the indirect
+ // word TAG field for the special address modifiers.
+ //
+ // If either condition is violated, the indirect word TAG field is
+ // interpreted as a normal address modifier and the presence of a special
+ // address modifier will cause an illegal procedure, illegal modifier,
+ // fault.
+
+
+ if (ISITS (ind_tag))
+ do_ITS ();
+ else
+ do_ITP ();
+
+ * newtag = GET_TAG (cpu.itxPair [1]);
+ //set_went_appending ();
+ cpu.cu.XSF = 1;
+ sim_debug (DBG_APPENDING, & cpu_dev, "do_ITS_ITP sets XSF to 1\n");
+ }
+
+
+void updateIWB (word18 addr, word6 tag)
+ {
+ word36 * wb;
+ if (USE_IRODD)
+ wb = & cpu.cu.IRODD;
+ else
+ wb = & cpu.cu.IWB;
+ sim_debug (DBG_ADDRMOD, & cpu_dev,
+ "updateIWB: IWB was %012"PRIo64" %06o %s\n",
+ * wb, GET_ADDR (* wb),
+ extMods [GET_TAG (* wb)].mod);
+
+ putbits36_18 (wb, 0, addr);
+ putbits36_6 (wb, 30, tag);
+ putbits36_1 (wb, 29, 0);
+
+ sim_debug (DBG_ADDRMOD, & cpu_dev,
+ "updateIWB: IWB now %012"PRIo64" %06o %s\n",
+ * wb, GET_ADDR (* wb),
+ extMods [GET_TAG (* wb)].mod);
+
+ decode_instruction (IWB_IRODD, & cpu.currentInstruction);
+ }
+
+//
+// Input:
+// cu.IWB
+// currentInstruction
+// TPR.TSR
+// TPR.TRR
+// TPR.TBR // XXX check to see if this is initialized
+// TPR.CA
+//
+// Output:
+// TPR.CA
+// directOperandFlag
+//
+// CANFAULT
+
+void do_caf (void)
+ {
+//#ifdef CA_REWORK
+ if (cpu.currentInstruction.b29 == 0)
+ {
+ cpu.TPR.CA = GET_ADDR (IWB_IRODD);
+ }
+ else
+ {
+ word3 n = GET_PRN(IWB_IRODD); // get PRn
+ word15 offset = GET_OFFSET(IWB_IRODD);
+ cpu.TPR.CA = (cpu.PAR[n].WORDNO + SIGNEXT15_18 (offset))
+ & MASK18;
+ }
+//#endif
+ char buf [256];
+ sim_debug (DBG_ADDRMOD, & cpu_dev,
+ "%s(Entry): operType:%s TPR.CA=%06o\n",
+ __func__, op_desc_str (buf), cpu.TPR.CA);
+ sim_debug (DBG_ADDRMOD, & cpu_dev,
+ "%s(Entry): CT_HOLD %o\n",
+ __func__, cpu.cu.CT_HOLD);
+
+ DCDstruct * i = & cpu.currentInstruction;
+
+ word6 Tm = 0;
+ word6 Td = 0;
+
+ word6 iTAG; // tag of word preceeding an indirect fetch
+
+ cpu.ou.directOperandFlag = false;
+
+ if (i -> info -> flags & NO_TAG) // for instructions line STCA/STCQ
+ cpu.rTAG = 0;
+ else
+ cpu.rTAG = GET_TAG (IWB_IRODD);
+
+ int lockupCnt = 0;
+#define lockupLimit 4096 // approx. 2 ms
+
+startCA:;
+
+ if (++ lockupCnt > lockupLimit)
+ {
+ doFault (FAULT_LUF, fst_zero, "Lockup in addrmod");
+ }
+
+ Td = GET_TD (cpu.rTAG);
+ Tm = GET_TM (cpu.rTAG);
+
+ // CT_HOLD is set to 0 on instruction setup; if it is non-zero here,
+ // we must have faulted during an indirect word fetch. Restore
+ // state and restart the fetch.
+ if (cpu.cu.CT_HOLD)
+ {
+ sim_debug (DBG_ADDRMOD, & cpu_dev,
+ "%s(startCA): restart; CT_HOLD %02o\n",
+ __func__, cpu.cu.CT_HOLD);
+ word6 save_rTAG = cpu.rTAG;
+ if (Tm == TM_IR)
+ {
+ cpu.rTAG = cpu.cu.CT_HOLD;
+ Td = GET_TD (cpu.rTAG);
+ Tm = GET_TM (cpu.rTAG);
+ cpu.cu.CT_HOLD = save_rTAG;
+ }
+#ifdef ISOLTS
+ else if (GET_TM(cpu.cu.CT_HOLD) == TM_IT && GET_TD (cpu.cu.CT_HOLD) == IT_DIC &&
+ cpu.cu.pot == 1 && GET_ADDR (IWB_IRODD) == cpu.TPR.CA)
+ {
+ cpu.TPR.CA--;
+ sim_warn ("%s: correct CA\n", __func__);
+ }
+#endif
+ else if (Tm == TM_IT && (Td == IT_IDC || Td == IT_DIC))
+ {
+ cpu.cu.pot = 1;
+ }
+ }
+ else
+ {
+ // not CT_HOLD
+ if (Tm == TM_IR || (Tm == TM_IT && (Td == IT_IDC || Td == IT_DIC)))
+ cpu.cu.CT_HOLD = cpu.rTAG;
+ cpu.cu.its = 0;
+ cpu.cu.itp = 0;
+ cpu.cu.pot = 0;
+ }
+ sim_debug (DBG_ADDRMOD, & cpu_dev,
+ "%s(startCA): TAG=%02o(%s) Tm=%o Td=%o CT_HOLD %02o\n",
+ __func__, cpu.rTAG, get_mod_string (buf, cpu.rTAG), Tm, Td, cpu.cu.CT_HOLD);
+
+ switch (Tm)
+ {
+ case TM_R:
+ goto R_MOD;
+ case TM_RI:
+ goto RI_MOD;
+ case TM_IT:
+ goto IT_MOD;
+ case TM_IR:
+ goto IR_MOD;
+ default:
+ break;
+ }
+
+ sim_printf ("%s(startCA): unknown Tm??? %o\n",
+ __func__, GET_TM (cpu.rTAG));
+ sim_warn ("(startCA): unknown Tmi; can't happen!\n");
+ return;
+
+
+ // Register modification. Fig 6-3
+
+ R_MOD:;
+ {
+ if (Td == 0) // TPR.CA = address from opcode
+ {
+ //updateIWB (identity) // known that Td is 0.
+ return;
+ }
+
+ word18 Cr = get_Cr (Td);
+
+ sim_debug (DBG_ADDRMOD, & cpu_dev, "R_MOD: Cr=%06o\n", Cr);
+
+ if (cpu.ou.directOperandFlag)
+ {
+ sim_debug (DBG_ADDRMOD, & cpu_dev,
+ "R_MOD: directOperand = %012"PRIo64"\n",
+ cpu.ou.directOperand);
+ return;
+ }
+
+ // For the case of RPT/RPD, the instruction decoder has
+ // verified that Tm is R or RI, and Td is X1..X7.
+ if (cpu.cu.rpt || cpu.cu.rd | cpu.cu.rl)
+ {
+ if (cpu.currentInstruction.b29)
+ {
+ word3 PRn = GET_PRN(IWB_IRODD);
+ CPTUR (cptUsePRn + PRn);
+ cpu.TPR.CA = (Cr & MASK15) + cpu.PR [PRn].WORDNO;
+ cpu.TPR.CA &= AMASK;
+ }
+ else
+ {
+ cpu.TPR.CA = Cr;
+ }
+ }
+ else
+ {
+ cpu.TPR.CA += Cr;
+ cpu.TPR.CA &= MASK18; // keep to 18-bits
+ }
+ sim_debug (DBG_ADDRMOD, & cpu_dev, "R_MOD: TPR.CA=%06o\n",
+ cpu.TPR.CA);
+
+ return;
+ } // R_MOD
+
+
+ // Figure 6-4. Register Then Indirect Modification Flowchart
+
+ RI_MOD:;
+ {
+ sim_debug (DBG_ADDRMOD, & cpu_dev, "RI_MOD: Td=%o\n", Td);
+
+ if (Td == TD_DU || Td == TD_DL)
+ doFault (FAULT_IPR, fst_ill_mod,
+ "RI_MOD: Td == TD_DU || Td == TD_DL");
+
+ if (Td != 0)
+ {
+ word18 Cr = get_Cr (Td); // C(r)
+
+ // We don''t need to worry about direct operand here, since du
+ // and dl are disallowed above
+
+ sim_debug (DBG_ADDRMOD, & cpu_dev,
+ "RI_MOD: Cr=%06o CA(Before)=%06o\n", Cr, cpu.TPR.CA);
+
+ if (cpu.cu.rpt || cpu.cu.rd || cpu.cu.rl)
+ {
+ word6 Td_ = GET_TD (i -> tag);
+ uint Xn = X (Td_); // Get Xn of next instruction
+ cpu.TPR.CA = cpu.rX [Xn];
+ }
+ else
+ {
+ cpu.TPR.CA += Cr;
+ cpu.TPR.CA &= MASK18; // keep to 18-bits
+ }
+ sim_debug (DBG_ADDRMOD, & cpu_dev,
+ "RI_MOD: CA(After)=%06o\n", cpu.TPR.CA);
+ }
+
+ // If the indirect word faults, on restart the CA will be the post
+ // register modification value, so we want to prevent it from
+ // happening again on restart
+
+ // in case it turns out to be a ITS/ITP
+ iTAG = cpu.rTAG;
+
+ word18 saveCA = cpu.TPR.CA;
+ ReadIndirect ();
+
+ // "In the case of RI modification, only one indirect reference is made
+ // per repeated execution. The TAG field of the indirect word is not
+ // interpreted. The indirect word is treated as though it had R
+ // modification with R = N."
+
+ if (cpu.cu.rpt || cpu.cu.rd || cpu.cu.rl)
+ {
+ cpu.itxPair[0] &= ~ INST_M_TAG;
+ cpu.itxPair[0] |= TM_R | GET_TD (iTAG);
+ }
+
+ // (Closed) Ticket 15: Check for fault causing tags before updating
+ // the IWB, so the instruction restart will reload the offending
+ // indirect word.
+
+ if (GET_TM (GET_TAG (cpu.itxPair[0])) == TM_IT)
+ {
+ if (GET_TD (GET_TAG (cpu.itxPair[0])) == IT_F2)
+ {
+ doFault (FAULT_F2, fst_zero, "RI_MOD: IT_F2 (0)");
+ }
+ if (GET_TD (GET_TAG (cpu.itxPair[0])) == IT_F3)
+ {
+ doFault (FAULT_F3, fst_zero, "RI_MOD: IT_F3");
+ }
+ }
+
+ if ((saveCA & 1) == 1 && (ISITP (cpu.itxPair[0]) || ISITS (cpu.itxPair[0])))
+ {
+ sim_warn ("%s: itp/its at odd address\n", __func__);
+#ifdef TESTING
+ traceInstruction (0);
+#endif
+ }
+
+ if ((saveCA & 1) == 0 && (ISITP (cpu.itxPair[0]) || ISITS (cpu.itxPair[0])))
+ {
+ do_ITS_ITP (iTAG, & cpu.rTAG);
+ }
+ else
+ {
+ cpu.TPR.CA = GETHI (cpu.itxPair[0]);
+ cpu.rTAG = GET_TAG (cpu.itxPair[0]);
+ cpu.rY = cpu.TPR.CA;
+ }
+
+ sim_debug (DBG_ADDRMOD, & cpu_dev,
+ "RI_MOD: cpu.itxPair[0]=%012"PRIo64
+ " TPR.CA=%06o rTAG=%02o\n",
+ cpu.itxPair[0], cpu.TPR.CA, cpu.rTAG);
+ // If repeat, the indirection chain is limited, so it is not needed
+ // to clear the tag; the delta code later on needs the tag to know
+ // which X register to update
+ if (cpu.cu.rpt || cpu.cu.rd || cpu.cu.rl)
+ return;
+
+ updateIWB (cpu.TPR.CA, cpu.rTAG);
+ goto startCA;
+ } // RI_MOD
+
+ // Figure 6-5. Indirect Then Register Modification Flowchart
+ IR_MOD:;
+ {
+ sim_debug (DBG_ADDRMOD, & cpu_dev,
+ "IR_MOD: CT_HOLD=%o %o\n", cpu.cu.CT_HOLD, Td);
+
+ IR_MOD_1:;
+
+ if (++ lockupCnt > lockupLimit)
+ {
+ doFault (FAULT_LUF, fst_zero, "Lockup in addrmod IR mode");
+ }
+
+ sim_debug (DBG_ADDRMOD, & cpu_dev,
+ "IR_MOD: fetching indirect word from %06o\n",
+ cpu.TPR.CA);
+
+ // in case it turns out to be a ITS/ITP
+ iTAG = cpu.rTAG;
+
+ word18 saveCA = cpu.TPR.CA;
+ ReadIndirect ();
+
+ if ((saveCA & 1) == 1 && (ISITP (cpu.itxPair[0]) || ISITS (cpu.itxPair[0])))
+ {
+ sim_warn ("%s: itp/its at odd address\n", __func__);
+#ifdef TESTING
+ traceInstruction (0);
+#endif
+ }
+
+ if ((saveCA & 1) == 0 && (ISITP (cpu.itxPair[0]) || ISITS (cpu.itxPair[0])))
+ {
+ do_ITS_ITP (iTAG, & cpu.rTAG);
+ }
+ else
+ {
+ cpu.TPR.CA = GETHI (cpu.itxPair[0]);
+ cpu.rY = cpu.TPR.CA;
+ cpu.rTAG = GET_TAG (cpu.itxPair[0]);
+ }
+
+ sim_debug (DBG_ADDRMOD, & cpu_dev,
+ "IR_MOD: CT_HOLD=%o\n", cpu.cu.CT_HOLD);
+ Td = GET_TD (cpu.rTAG);
+ Tm = GET_TM (cpu.rTAG);
+
+ // IR_MOD_2:;
+
+ sim_debug (DBG_ADDRMOD, & cpu_dev,
+ "IR_MOD1: cpu.itxPair[0]=%012"PRIo64
+ " TPR.CA=%06o Tm=%o Td=%02o (%s)\n",
+ cpu.itxPair[0], cpu.TPR.CA, Tm, Td,
+ get_mod_string (buf, GET_TAG (cpu.itxPair[0])));
+
+ switch (Tm)
+ {
+ case TM_IT:
+ {
+ sim_debug (DBG_ADDRMOD, & cpu_dev,
+ "IR_MOD(TM_IT): Td=%02o => %02o\n",
+ Td, cpu.cu.CT_HOLD);
+
+ if (Td == IT_F2 || Td == IT_F3)
+ {
+ // Abort. FT2 or 3
+ switch (Td)
+ {
+ case IT_F2:
+ cpu.TPR.CA = saveCA;
+ doFault (FAULT_F2, fst_zero, "TM_IT: IT_F2 (1)");
+
+ case IT_F3:
+ cpu.TPR.CA = saveCA;
+ doFault (FAULT_F3, fst_zero, "TM_IT: IT_F3");
+ }
+ }
+ // fall through to TM_R
+ } // TM_IT
+
+ case TM_R:
+ {
+ word18 Cr = get_Cr (GET_TD (cpu.cu.CT_HOLD));
+
+ sim_debug (DBG_ADDRMOD, & cpu_dev,
+ "IR_MOD(TM_R): CT_HOLD %o Cr=%06o\n",
+ GET_TD (cpu.cu.CT_HOLD), Cr);
+
+ if (cpu.ou.directOperandFlag)
+ {
+ sim_debug (DBG_ADDRMOD, & cpu_dev,
+ "IR_MOD(TM_R): CT_HOLD DO %012"PRIo64"\n",
+ cpu.ou.directOperand);
+ // CT_HOLD has *DU or *DL; convert to DU or DL
+ word6 tag = TM_R | GET_TD (cpu.cu.CT_HOLD);
+ updateIWB (cpu.TPR.CA, tag); // Known to be DL or DU
+ }
+ else
+ {
+ cpu.TPR.CA += Cr;
+ cpu.TPR.CA &= MASK18; // keep to 18-bits
+
+ sim_debug (DBG_ADDRMOD, & cpu_dev,
+ "IR_MOD(TM_R): TPR.CA=%06o\n", cpu.TPR.CA);
+
+ updateIWB (cpu.TPR.CA, 0);
+ }
+ return;
+ } // TM_R
+
+ case TM_RI:
+ {
+ word18 Cr = get_Cr (Td);
+
+ sim_debug (DBG_ADDRMOD, & cpu_dev,
+ "IR_MOD(TM_RI): Td=%o Cr=%06o TPR.CA(Before)=%06o\n",
+ Td, Cr, cpu.TPR.CA);
+
+ if (cpu.ou.directOperandFlag)
+ {
+ // keep to 18-bits
+ cpu.TPR.CA = (word18) cpu.ou.directOperand & MASK18;
+
+ sim_debug (DBG_ADDRMOD, & cpu_dev,
+ "IR_MOD(TM_RI): DO TPR.CA=%06o\n",
+ cpu.TPR.CA);
+ }
+ else
+ {
+ cpu.TPR.CA += Cr;
+ cpu.TPR.CA &= MASK18; // keep to 18-bits
+
+ sim_debug (DBG_ADDRMOD, & cpu_dev,
+ "IR_MOD(TM_RI): TPR.CA=%06o\n", cpu.TPR.CA);
+
+ }
+
+ sim_debug (DBG_ADDRMOD, & cpu_dev,
+ "IR_MOD(TM_RI): TPR.CA(After)=%06o\n",
+ cpu.TPR.CA);
+
+ updateIWB (cpu.TPR.CA, cpu.rTAG); // XXX guessing here...
+ goto IR_MOD_1;
+ } // TM_RI
+
+ case TM_IR:
+ {
+ updateIWB (cpu.TPR.CA, cpu.rTAG); // XXX guessing here...
+ goto IR_MOD_1;
+ } // TM_IR
+ } // Tm
+
+ sim_printf ("%s(IR_MOD): unknown Tm??? %o\n",
+ __func__, GET_TM (cpu.rTAG));
+ return;
+ } // IR_MOD
+
+ IT_MOD:;
+ {
+ // IT_SD = 004,
+ // IT_SCR = 005,
+ // IT_CI = 010,
+ // IT_I = 011,
+ // IT_SC = 012,
+ // IT_AD = 013,
+ // IT_DI = 014,
+ // IT_DIC = 015,
+ // IT_ID = 016,
+ // IT_IDC = 017
+ word6 idwtag, delta;
+ word24 Yi = (word24) -1;
+
+ switch (Td)
+ {
+ // XXX this is probably wrong. ITS/ITP are not standard addr mods
+ case SPEC_ITP:
+ case SPEC_ITS:
+ {
+ doFault(FAULT_IPR, fst_ill_mod, "ITx in IT_MOD)");
+ }
+
+ case 2:
+ {
+ sim_debug (DBG_ADDRMOD, & cpu_dev,
+ "IT_MOD(): illegal procedure, illegal modifier, "
+ "fault Td=%o\n", Td);
+ doFault (FAULT_IPR, fst_ill_mod,
+ "IT_MOD(): illegal procedure, illegal modifier, "
+ "fault");
+ }
+
+ case IT_F1:
+ {
+ doFault(FAULT_F1, fst_zero, "IT_MOD: IT_F1");
+ }
+
+ case IT_F2:
+ {
+ doFault(FAULT_F2, fst_zero, "IT_MOD: IT_F2 (2)");
+ }
+
+ case IT_F3:
+ {
+ doFault(FAULT_F3, fst_zero, "IT_MOD: IT_F3");
+ }
+
+
+ case IT_CI: // Character indirect (Td = 10)
+ case IT_SC: // Sequence character (Td = 12)
+ case IT_SCR: // Sequence character reverse (Td = 5)
+ {
+ // There is complexity with managing page faults and tracking
+ // the indirect word address and the operand address.
+ //
+ // To address this, we force those pages in during PREPARE_CA,
+ // so that they won't fault during operand read/write.
+
+ sim_debug (DBG_ADDRMOD, & cpu_dev,
+ "IT_MOD CI/SC/SCR reading indirect word from %06o\n",
+ cpu.TPR.CA);
+
+ //
+ // Get the indirect word
+ //
+
+ word36 indword;
+ word18 indaddr = cpu.TPR.CA;
+ Read (indaddr, & indword, APU_DATA_READ);
+#ifdef LOCKLESS
+ word24 phys_address = cpu.iefpFinalAddress;
+#endif
+
+ sim_debug (DBG_ADDRMOD, & cpu_dev,
+ "IT_MOD CI/SC/SCR indword=%012"PRIo64"\n", indword);
+
+ //
+ // Parse and validate the indirect word
+ //
+
+ Yi = GET_ADDR (indword);
+ word6 sz = GET_TB (GET_TAG (indword));
+ word3 os = GET_CF (GET_TAG (indword));
+ word12 tally = GET_TALLY (indword);
+
+ sim_debug (DBG_ADDRMOD, & cpu_dev,
+ "IT_MOD CI/SC/SCR size=%o offset=%o Yi=%06o\n",
+ sz, os, Yi);
+
+ if (sz == TB6 && os > 5)
+ // generate an illegal procedure, illegal modifier fault
+ doFault (FAULT_IPR, fst_ill_mod,
+ "co size == TB6 && offset > 5");
+
+ if (sz == TB9 && os > 3)
+ // generate an illegal procedure, illegal modifier fault
+ doFault (FAULT_IPR, fst_ill_mod,
+ "co size == TB9 && offset > 3");
+
+ // Save data in OU registers for readOperands/writeOperands
+
+ cpu.TPR.CA = Yi;
+ cpu.ou.character_address = Yi;
+ cpu.ou.characterOperandSize = sz;
+ cpu.ou.characterOperandOffset = os;
+
+ // CI uses the address, and SC uses the pre-increment address;
+ // but SCR use the post-decrement address
+ if (Td == IT_SCR)
+ {
+ // For each reference to the indirect word, the character
+ // counter, cf, is reduced by 1 and the TALLY field is
+ // increased by 1 before the computed address is formed.
+ //
+ // Character count arithmetic is modulo 6 for 6-bit
+ // characters and modulo 4 for 9-bit bytes. If the
+ // character count, cf, underflows to -1, it is reset to 5
+ // for 6-bit characters or to 3 for 9-bit bytes and ADDRESS
+ // is reduced by 1. ADDRESS arithmetic is modulo 2^18.
+ // TALLY arithmetic is modulo 4096.
+ //
+ // If the TALLY field overflows to 0, the tally runout
+ // indicator is set ON, otherwise it is set OFF. The
+ // computed address is the (possibly) decremented value of
+ // the ADDRESS field of the indirect word. The effective
+ // character/byte number is the decremented value of the
+ // character position count, cf, field of the indirect
+ // word.
+
+ if (os == 0)
+ {
+ if (sz == TB6)
+ os = 5;
+ else
+ os = 3;
+ Yi -= 1;
+ Yi &= MASK18;
+ }
+ else
+ {
+ os -= 1;
+ }
+
+ CPT (cpt2L, 5); // Update IT Tally; SCR
+ tally ++;
+ tally &= 07777; // keep to 12-bits
+
+ // Update saved values
+
+ cpu.TPR.CA = Yi;
+ cpu.ou.character_address = Yi;
+ cpu.ou.characterOperandSize = sz;
+ cpu.ou.characterOperandOffset = os;
+ }
+
+
+// What if readOperands and/of writeOperands fault? On restart, doCAF will be
+// called again and the indirect word would incorrectly be updated a second
+// time.
+//
+// We don't care about read/write access violations; in general, they are not
+// restarted.
+//
+// We can avoid page faults by preemptively fetching the data word.
+
+ //
+ // Get the data word
+ //
+
+ cpu.cu.pot = 1;
+
+#ifdef LOCKLESSXXX
+ // gives warnings as another lock is aquired in between
+ Read (cpu.TPR.CA, & cpu.ou.character_data, (i->info->flags & RMW) == STORE_OPERAND ? OPERAND_RMW : OPERAND_READ);
+#else
+ Read (cpu.TPR.CA, & cpu.ou.character_data, OPERAND_READ);
+#endif
+#ifdef LOCKLESS
+ cpu.char_word_address = cpu.iefpFinalAddress;
+#endif
+
+ sim_debug (DBG_ADDRMOD, & cpu_dev,
+ "IT_MOD CI/SC/SCR data=%012"PRIo64"\n",
+ cpu.ou.character_data);
+
+ cpu.cu.pot = 0;
+
+ if (Td == IT_SC)
+ {
+ // For each reference to the indirect word, the character
+ // counter, cf, is increased by 1 and the TALLY field is
+ // reduced by 1 after the computed address is formed.
+ // Character count arithmetic is modulo 6 for 6-bit
+ // characters and modulo 4 for 9-bit bytes. If the
+ // character count, cf, overflows to 6 for 6-bit characters
+ // or to 4 for 9-bit bytes, it is reset to 0 and ADDRESS is
+ // increased by 1. ADDRESS arithmetic is modulo 2^18. TALLY
+ // arithmetic is modulo 4096. If the TALLY field is reduced
+ // to 0, the tally runout indicator is set ON, otherwise it
+ // is set OFF.
+
+ os ++;
+
+ if (((sz == TB6) && (os > 5)) ||
+ ((sz == TB9) && (os > 3)))
+ {
+ os = 0;
+ Yi += 1;
+ Yi &= MASK18;
+ }
+ CPT (cpt2L, 6); // Update IT Tally; SC
+ tally --;
+ tally &= 07777; // keep to 12-bits
+ }
+
+ if (Td == IT_SC || Td == IT_SCR)
+ {
+ sim_debug (DBG_ADDRMOD, & cpu_dev,
+ "update IT tally now %o\n", tally);
+
+ //word36 new_indword = (word36) (((word36) Yi << 18) |
+ // (((word36) tally & 07777) << 6) |
+ // cpu.ou.characterOperandSize |
+ // cpu.ou.characterOperandOffset);
+ //Write (cpu.TPR.CA, new_indword, APU_DATA_STORE);
+#ifdef LOCKLESS
+ word36 indword_new;
+ core_read_lock(phys_address, &indword_new, __func__);
+ if (indword_new != indword)
+ sim_warn("indword changed from %llo to %llo\n", indword, indword_new);
+#endif
+ putbits36_18 (& indword, 0, Yi);
+ putbits36_12 (& indword, 18, tally);
+ putbits36_3 (& indword, 33, os);
+#ifdef LOCKLESS
+ core_write_unlock(phys_address, indword, __func__);
+#else
+ Write (indaddr, indword, APU_DATA_STORE);
+#endif
+
+ SC_I_TALLY (tally == 0);
+
+ sim_debug (DBG_ADDRMOD, & cpu_dev,
+ "update IT wrote tally word %012"PRIo64
+ " to %06o\n",
+ indword, cpu.TPR.CA);
+ }
+
+
+
+ // readOperand and writeOperand will not use cpu.TPR.CA; they
+ // will use the saved address, size, offset and data.
+ cpu.TPR.CA = cpu.ou.character_address;
+ return;
+ } // IT_CI, IT_SC, IT_SCR
+
+ case IT_I: // Indirect (Td = 11)
+ {
+ sim_debug (DBG_ADDRMOD, & cpu_dev,
+ "IT_MOD(IT_I): reading indirect word from %06o\n",
+ cpu.TPR.CA);
+
+ //Read2 (cpu.TPR.CA, cpu.itxPair, INDIRECT_WORD_FETCH);
+ ReadIndirect ();
+
+ sim_debug (DBG_ADDRMOD, & cpu_dev,
+ "IT_MOD(IT_I): indword=%012"PRIo64"\n",
+ cpu.itxPair[0]);
+
+ cpu.TPR.CA = GET_ADDR (cpu.itxPair[0]);
+ return;
+ } // IT_I
+
+ case IT_AD: ///< Add delta (Td = 13)
+ {
+ // The TAG field of the indirect word is interpreted as a
+ // 6-bit, unsigned, positive address increment value, delta.
+ // For each reference to the indirect word, the ADDRESS field
+ // is increased by delta and the TALLY field is reduced by 1
+ // after the computed address is formed. ADDRESS arithmetic is
+ // modulo 2^18. TALLY arithmetic is modulo 4096. If the TALLY
+ // field is reduced to 0, the tally runout indicator is set ON,
+ // otherwise it is set OFF. The computed address is the value
+ // of the unmodified ADDRESS field of the indirect word.
+
+ sim_debug (DBG_ADDRMOD, & cpu_dev,
+ "IT_MOD(IT_AD): reading indirect word from %06o\n",
+ cpu.TPR.CA);
+
+#ifdef TEST_OLIN
+ cmpxchg ();
+#endif
+#ifdef TEST_FENCE
+ fence ();
+#endif
+#ifdef THREADZ
+ lock_rmw ();
+#endif
+
+ word18 saveCA = cpu.TPR.CA;
+ word36 indword;
+ Read (cpu.TPR.CA, & indword, APU_DATA_RMW);
+
+ cpu.AM_tally = GET_TALLY (indword); // 12-bits
+ delta = GET_DELTA (indword); // 6-bits
+ Yi = GETHI (indword); // from where data live
+
+ sim_debug (DBG_ADDRMOD, & cpu_dev,
+ "IT_MOD(IT_AD): indword=%012"PRIo64"\n",
+ indword);
+ sim_debug (DBG_ADDRMOD, & cpu_dev,
+ "IT_MOD(IT_AD): address:%06o tally:%04o "
+ "delta:%03o\n",
+ Yi, cpu.AM_tally, delta);
+
+ cpu.TPR.CA = Yi;
+ word18 computedAddress = cpu.TPR.CA;
+
+ Yi += delta;
+ Yi &= MASK18;
+
+ cpu.AM_tally -= 1;
+ cpu.AM_tally &= 07777; // keep to 12-bits
+ // breaks emacs
+ //if (cpu.AM_tally == 0)
+ //SET_I_TALLY;
+ SC_I_TALLY (cpu.AM_tally == 0);
+ indword = (word36) (((word36) Yi << 18) |
+ (((word36) cpu.AM_tally & 07777) << 6) |
+ delta);
+#ifdef LOCKLESS
+ core_write_unlock(cpu.iefpFinalAddress, indword, __func__);
+#else
+ Write (saveCA, indword, APU_DATA_STORE);
+#endif
+
+#ifdef TEST_FENCE
+ fence ();
+#endif
+#ifdef THREADZ
+ unlock_rmw ();
+#endif
+
+ sim_debug (DBG_ADDRMOD, & cpu_dev,
+ "IT_MOD(IT_AD): wrote tally word %012"PRIo64
+ " to %06o\n",
+ indword, saveCA);
+
+ cpu.TPR.CA = computedAddress;
+ return;
+ } // IT_AD
+
+ case IT_SD: ///< Subtract delta (Td = 4)
+ {
+ // The TAG field of the indirect word is interpreted as a
+ // 6-bit, unsigned, positive address increment value, delta.
+ // For each reference to the indirect word, the ADDRESS field
+ // is reduced by delta and the TALLY field is increased by 1
+ // before the computed address is formed. ADDRESS arithmetic is
+ // modulo 2^18. TALLY arithmetic is modulo 4096. If the TALLY
+ // field overflows to 0, the tally runout indicator is set ON,
+ // otherwise it is set OFF. The computed address is the value
+ // of the decremented ADDRESS field of the indirect word.
+
+#ifdef TEST_OLIN
+ cmpxchg ();
+#endif
+#ifdef TEST_FENCE
+ fence ();
+#endif
+#ifdef THREADZ
+ lock_rmw ();
+#endif
+
+ word18 saveCA = cpu.TPR.CA;
+ word36 indword;
+ Read (cpu.TPR.CA, & indword, APU_DATA_RMW);
+
+ sim_debug (DBG_ADDRMOD, & cpu_dev,
+ "IT_MOD(IT_SD): reading indirect word from %06o\n",
+ cpu.TPR.CA);
+ cpu.AM_tally = GET_TALLY (indword); // 12-bits
+ delta = GET_DELTA (indword); // 6-bits
+ Yi = GETHI (indword); // from where data live
+
+ sim_debug (DBG_ADDRMOD, & cpu_dev,
+ "IT_MOD(IT_SD): indword=%012"PRIo64"\n",
+ indword);
+ sim_debug (DBG_ADDRMOD, & cpu_dev,
+ "IT_MOD(IT_SD): address:%06o tally:%04o "
+ "delta:%03o\n",
+ Yi, cpu.AM_tally, delta);
+
+ Yi -= delta;
+ Yi &= MASK18;
+ cpu.TPR.CA = Yi;
+
+ cpu.AM_tally += 1;
+ cpu.AM_tally &= 07777; // keep to 12-bits
+ if (cpu.AM_tally == 0)
+ SET_I_TALLY;
+
+ // write back out indword
+ indword = (word36) (((word36) Yi << 18) |
+ (((word36) cpu.AM_tally & 07777) << 6) |
+ delta);
+#ifdef LOCKLESS
+ core_write_unlock(cpu.iefpFinalAddress, indword, __func__);
+#else
+ Write (saveCA, indword, APU_DATA_STORE);
+#endif
+
+#ifdef TEST_FENCE
+ fence ();
+#endif
+#ifdef THREADZ
+ unlock_rmw ();
+#endif
+
+ sim_debug (DBG_ADDRMOD, & cpu_dev,
+ "IT_MOD(IT_SD): wrote tally word %012"PRIo64
+ " to %06o\n",
+ indword, saveCA);
+
+
+ cpu.TPR.CA = Yi;
+ return;
+ } // IT_SD
+
+ case IT_DI: ///< Decrement address, increment tally (Td = 14)
+ {
+ // For each reference to the indirect word, the ADDRESS field
+ // is reduced by 1 and the TALLY field is increased by 1 before
+ // the computed address is formed. ADDRESS arithmetic is modulo
+ // 2^18. TALLY arithmetic is modulo 4096. If the TALLY field
+ // overflows to 0, the tally runout indicator is set ON,
+ // otherwise it is set OFF. The TAG field of the indirect word
+ // is ignored. The computed address is the value of the
+ // decremented ADDRESS field.
+
+ sim_debug (DBG_ADDRMOD, & cpu_dev,
+ "IT_MOD(IT_DI): reading indirect word from %06o\n",
+ cpu.TPR.CA);
+
+#ifdef TEST_OLIN
+ cmpxchg ();
+#endif
+#ifdef TEST_FENCE
+ fence ();
+#endif
+#ifdef THREADZ
+ lock_rmw ();
+#endif
+
+ word18 saveCA = cpu.TPR.CA;
+ word36 indword;
+ Read (cpu.TPR.CA, & indword, APU_DATA_RMW);
+
+ Yi = GETHI (indword);
+ cpu.AM_tally = GET_TALLY (indword); // 12-bits
+ word6 junk = GET_TAG (indword); // get tag field, but ignore it
+
+ sim_debug (DBG_ADDRMOD, & cpu_dev,
+ "IT_MOD(IT_DI): indword=%012"PRIo64"\n",
+ indword);
+ sim_debug (DBG_ADDRMOD, & cpu_dev,
+ "IT_MOD(IT_DI): address:%06o tally:%04o\n",
+ Yi, cpu.AM_tally);
+
+ Yi -= 1;
+ Yi &= MASK18;
+ cpu.TPR.CA = Yi;
+
+ cpu.AM_tally += 1;
+ cpu.AM_tally &= 07777; // keep to 12-bits
+ SC_I_TALLY (cpu.AM_tally == 0);
+
+ // write back out indword
+
+ indword = (word36) (((word36) cpu.TPR.CA << 18) |
+ ((word36) cpu.AM_tally << 6) |
+ junk);
+
+ sim_debug (DBG_ADDRMOD, & cpu_dev,
+ "IT_MOD(IT_DI): writing indword=%012"PRIo64" to "
+ "addr %06o\n",
+ indword, saveCA);
+
+#ifdef LOCKLESS
+ core_write_unlock(cpu.iefpFinalAddress, indword, __func__);
+#else
+ Write (saveCA, indword, APU_DATA_STORE);
+#endif
+
+#ifdef TEST_FENCE
+ fence ();
+#endif
+#ifdef THREADZ
+ unlock_rmw ();
+#endif
+ cpu.TPR.CA = Yi;
+ return;
+ } // IT_DI
+
+ case IT_ID: ///< Increment address, decrement tally (Td = 16)
+ {
+ // For each reference to the indirect word, the ADDRESS field
+ // is increased by 1 and the TALLY field is reduced by 1 after
+ // the computed address is formed. ADDRESS arithmetic is modulo
+ // 2^18. TALLY arithmetic is modulo 4096. If the TALLY field is
+ // reduced to 0, the tally runout indicator is set ON,
+ // otherwise it is set OFF. The TAG field of the indirect word
+ // is ignored. The computed address is the value of the
+ // unmodified ADDRESS field.
+
+ word18 saveCA = cpu.TPR.CA;
+
+ sim_debug (DBG_ADDRMOD, & cpu_dev,
+ "IT_MOD(IT_ID): fetching indirect word from %06o\n",
+ cpu.TPR.CA);
+
+#ifdef TEST_OLIN
+ cmpxchg ();
+#endif
+#ifdef TEST_FENCE
+ fence ();
+#endif
+#ifdef THREADZ
+ lock_rmw ();
+#endif
+
+ word36 indword;
+ Read (cpu.TPR.CA, & indword, APU_DATA_RMW);
+
+ Yi = GETHI (indword);
+ cpu.AM_tally = GET_TALLY (indword); // 12-bits
+ // get tag field, but ignore it
+ word6 junk = GET_TAG (indword);
+ sim_debug (DBG_ADDRMOD, & cpu_dev,
+ "IT_MOD(IT_ID): indword=%012"PRIo64
+ " Yi=%06o tally=%04o\n",
+ indword, Yi, cpu.AM_tally);
+
+ cpu.TPR.CA = Yi;
+ word18 computedAddress = cpu.TPR.CA;
+
+ Yi += 1;
+ Yi &= MASK18;
+
+ cpu.AM_tally -= 1;
+ cpu.AM_tally &= 07777; // keep to 12-bits
+
+ // XXX Breaks boot?
+ //if (cpu.AM_tally == 0)
+ //SET_I_TALLY;
+ SC_I_TALLY (cpu.AM_tally == 0);
+
+ // write back out indword
+ indword = (word36) (((word36) Yi << 18) |
+ ((word36) cpu.AM_tally << 6) |
+ junk);
+
+ sim_debug (DBG_ADDRMOD, & cpu_dev,
+ "IT_MOD(IT_ID): writing indword=%012"PRIo64" to "
+ "addr %06o\n",
+ indword, saveCA);
+
+#ifdef LOCKLESS
+ core_write_unlock(cpu.iefpFinalAddress, indword, __func__);
+#else
+ Write (saveCA, indword, APU_DATA_STORE);
+#endif
+
+#ifdef TEST_FENCE
+ fence ();
+#endif
+#ifdef THREADZ
+ unlock_rmw ();
+#endif
+
+ cpu.TPR.CA = computedAddress;
+ return;
+ } // IT_ID
+
+ // Decrement address, increment tally, and continue (Td = 15)
+ case IT_DIC:
+ {
+ // The action for this variation is identical to that for the
+ // decrement address, increment tally variation except that the
+ // TAG field of the indirect word is interpreted and
+ // continuation of the indirect chain is possible. If the TAG
+ // of the indirect word invokes a register, that is, specifies
+ // r, ri, or ir modification, the effective Td value for the
+ // register is forced to "null" before the next computed
+ // address is formed.
+
+ // a:RJ78/idc1
+ // The address and tally fields are used as described under the
+ // ID variation. The tag field uses the set of variations for
+ // instruction address modification under the following
+ // restrictions: no variation is permitted that requires an
+ // indexing modification in the DIC cycle since the indexing
+ // adder is being used by the tally phase of the operation.
+ // Thus, permissible variations are any allowable form of IT or
+ // IR, but if RI or R is used, R must equal N (RI and R forced
+ // to N).
+
+ sim_debug (DBG_ADDRMOD, & cpu_dev,
+ "IT_MOD(IT_DIC): fetching indirect word from %06o\n",
+ cpu.TPR.CA);
+
+#ifdef TEST_OLIN
+ cmpxchg ();
+#endif
+#ifdef TEST_FENCE
+ fence ();
+#endif
+#ifdef THREADZ
+ lock_rmw ();
+#endif
+
+ word18 saveCA = cpu.TPR.CA;
+ word36 indword;
+ Read (cpu.TPR.CA, & indword, APU_DATA_RMW);
+
+ cpu.cu.pot = 0;
+
+ Yi = GETHI (indword);
+ cpu.AM_tally = GET_TALLY (indword); // 12-bits
+ idwtag = GET_TAG (indword);
+
+ sim_debug (DBG_ADDRMOD, & cpu_dev,
+ "IT_MOD(IT_DIC): indword=%012"PRIo64" Yi=%06o "
+ "tally=%04o idwtag=%02o\n",
+ indword, Yi, cpu.AM_tally, idwtag);
+
+#ifdef ISOLTS
+ word24 YiSafe2 = Yi; // save indirect address for later use
+#endif
+
+ Yi -= 1;
+ Yi &= MASK18;
+
+ cpu.AM_tally += 1;
+ cpu.AM_tally &= 07777; // keep to 12-bits
+// Set the tally after the indirect word is processed; if it faults, the IR
+// should be unchanged. ISOLTS ps791 test-02g
+ //SC_I_TALLY (cpu.AM_tally == 0);
+ //if (cpu.AM_tally == 0)
+ //SET_I_TALLY;
+
+ // write back out indword
+ indword = (word36) (((word36) Yi << 18) |
+ ((word36) cpu.AM_tally << 6) | idwtag);
+
+ sim_debug (DBG_ADDRMOD, & cpu_dev,
+ "IT_MOD(IT_DIC): writing indword=%012"PRIo64" to "
+ "addr %06o\n", indword, saveCA);
+
+#ifdef LOCKLESS
+ core_write_unlock(cpu.iefpFinalAddress, indword, __func__);
+#else
+ Write (saveCA, indword, APU_DATA_STORE);
+#endif
+
+#ifdef TEST_FENCE
+ fence ();
+#endif
+#ifdef THREADZ
+ unlock_rmw ();
+#endif
+ // If the TAG of the indirect word invokes a register, that is,
+ // specifies r, ri, or ir modification, the effective Td value
+ // for the register is forced to "null" before the next
+ // computed address is formed.
+
+ // Thus, permissible variations are any allowable form of IT or
+ // IR, but if RI or R is used, R must equal N (RI and R forced
+ // to N).
+ cpu.TPR.CA = Yi;
+
+ sim_debug (DBG_ADDRMOD, & cpu_dev,
+ "IT_MOD(IT_DIC): new CT_HOLD %02o new TAG %02o\n",
+ cpu.rTAG, idwtag);
+ cpu.cu.CT_HOLD = cpu.rTAG;
+ cpu.rTAG = idwtag;
+
+ Tm = GET_TM (cpu.rTAG);
+ if (Tm == TM_RI || Tm == TM_R)
+ {
+ if (GET_TD (cpu.rTAG) != 0)
+ {
+ doFault (FAULT_IPR, fst_ill_mod,
+ "DIC Incorrect address modifier");
+ }
+ }
+
+// Set the tally after the indirect word is processed; if it faults, the IR
+// should be unchanged. ISOLTS ps791 test-02g
+ SC_I_TALLY (cpu.AM_tally == 0);
+#ifdef ISOLTS
+ updateIWB (YiSafe2, cpu.rTAG);
+#else
+ updateIWB (cpu.TPR.CA, cpu.rTAG);
+#endif
+ goto startCA;
+ } // IT_DIC
+
+ // Increment address, decrement tally, and continue (Td = 17)
+ case IT_IDC:
+ {
+ // The action for this variation is identical to that for the
+ // increment address, decrement tally variation except that the
+ // TAG field of the indirect word is interpreted and
+ // continuation of the indirect chain is possible. If the TAG
+ // of the indirect word invokes a register, that is, specifies
+ // r, ri, or ir modification, the effective Td value for the
+ // register is forced to "null" before the next computed
+ // address is formed.
+
+ // a:RJ78/idc1
+ // The address and tally fields are used as described under the
+ // ID variation. The tag field uses the set of variations for
+ // instruction address modification under the following
+ // restrictions: no variation is permitted that requires an
+ // indexing modification in the IDC cycle since the indexing
+ // adder is being used by the tally phase of the operation.
+ // Thus, permissible variations are any allowable form of IT or
+ // IR, but if RI or R is used, R must equal N (RI and R forced
+ // to N).
+
+ sim_debug (DBG_ADDRMOD, & cpu_dev,
+ "IT_MOD(IT_IDC): fetching indirect word from %06o\n",
+ cpu.TPR.CA);
+
+#ifdef TEST_OLIN
+ cmpxchg ();
+#endif
+#ifdef TEST_FENCE
+ fence ();
+#endif
+#ifdef THREADZ
+ lock_rmw ();
+#endif
+
+ word18 saveCA = cpu.TPR.CA;
+ word36 indword;
+ Read (cpu.TPR.CA, & indword, APU_DATA_RMW);
+
+ cpu.cu.pot = 0;
+
+ Yi = GETHI (indword);
+ cpu.AM_tally = GET_TALLY (indword); // 12-bits
+ idwtag = GET_TAG (indword);
+
+ sim_debug (DBG_ADDRMOD, & cpu_dev,
+ "IT_MOD(IT_IDC): indword=%012"PRIo64" Yi=%06o "
+ "tally=%04o idwtag=%02o\n",
+ indword, Yi, cpu.AM_tally, idwtag);
+
+ word24 YiSafe = Yi; // save indirect address for later use
+
+ Yi += 1;
+ Yi &= MASK18;
+
+ cpu.AM_tally -= 1;
+ cpu.AM_tally &= 07777; // keep to 12-bits
+// Set the tally after the indirect word is processed; if it faults, the IR
+// should be unchanged. ISOLTS ps791 test-02f
+ //SC_I_TALLY (cpu.AM_tally == 0);
+
+ // write back out indword
+ indword = (word36) (((word36) Yi << 18) |
+ ((word36) cpu.AM_tally << 6) |
+ idwtag);
+
+ sim_debug (DBG_ADDRMOD, & cpu_dev,
+ "IT_MOD(IT_IDC): writing indword=%012"PRIo64""
+ " to addr %06o\n",
+ indword, saveCA);
+
+#ifdef LOCKLESS
+ core_write_unlock(cpu.iefpFinalAddress, indword, __func__);
+#else
+ Write (saveCA, indword, APU_DATA_STORE);
+#endif
+
+#ifdef TEST_FENCE
+ fence ();
+#endif
+#ifdef THREADZ
+ unlock_rmw ();
+#endif
+
+ // If the TAG of the indirect word invokes a register, that is,
+ // specifies r, ri, or ir modification, the effective Td value
+ // for the register is forced to "null" before the next
+ // computed address is formed.
+ // But for the dps88 you can use everything but ir/ri.
+
+ // Thus, permissible variations are any allowable form of IT or
+ // IR, but if RI or R is used, R must equal N (RI and R forced
+ // to N).
+ cpu.TPR.CA = YiSafe;
+
+ sim_debug (DBG_ADDRMOD, & cpu_dev,
+ "IT_MOD(IT_IDC): new CT_HOLD %02o new TAG %02o\n",
+ cpu.rTAG, idwtag);
+ cpu.cu.CT_HOLD = cpu.rTAG;
+ cpu.rTAG = idwtag;
+
+ Tm = GET_TM (cpu.rTAG);
+ if (Tm == TM_RI || Tm == TM_R)
+ {
+ if (GET_TD (cpu.rTAG) != 0)
+ {
+ doFault (FAULT_IPR, fst_ill_mod,
+ "IDC Incorrect address modifier");
+ }
+ }
+
+// Set the tally after the indirect word is processed; if it faults, the IR
+// should be unchanged. ISOLTS ps791 test-02f
+ SC_I_TALLY (cpu.AM_tally == 0);
+ updateIWB (cpu.TPR.CA, cpu.rTAG);
+
+ goto startCA;
+ } // IT_IDC
+ } // Td
+ sim_printf ("IT_MOD/Td how did we get here?\n");
+ return;
+ } // IT_MOD
+ } // do_caf
+
+
--- /dev/null
+/*
+ Copyright 2014-2016 by Charles Anthony
+
+ All rights reserved.
+
+ This software is made available under the terms of the
+ ICU License -- ICU 1.8.1 and later.
+ See the LICENSE file at the top-level directory of this distribution and
+ at https://sourceforge.net/p/dps8m/code/ci/master/tree/LICENSE
+ */
+
+void do_caf (void);
+void updateIWB (word18 addr, word6 tag);
--- /dev/null
+/*
+ Copyright 2012-2016 by Harry Reed
+ Copyright 2013-2017 by Charles Anthony
+ Copyright 2017 by Michal Tomek
+
+ All rights reserved.
+
+ This software is made available under the terms of the
+ ICU License -- ICU 1.8.1 and later.
+ See the LICENSE file at the top-level directory of this distribution and
+ at https://sourceforge.net/p/dps8m/code/ci/master/tree/LICENSE
+ */
+
+/**
+ * \file dps8_append.c
+ * \project dps8
+ * \date 10/28/12
+ * \copyright Copyright (c) 2012 Harry Reed. All rights reserved.
+*/
+
+#include <stdio.h>
+#include "dps8.h"
+#include "dps8_sys.h"
+#include "dps8_faults.h"
+//#include "dps8_scu.h"
+//#include "dps8_iom.h"
+//#include "dps8_cable.h"
+#include "dps8_cpu.h"
+#include "dps8_append.h"
+#include "dps8_addrmods.h"
+#include "dps8_utils.h"
+#if defined(THREADZ) || defined(LOCKLESS)
+//#include "threadz.h"
+#endif
+
+#define DBG_CTR cpu.cycleCnt
+
+/**
+ * The appending unit ...
+ */
+
+#ifdef TESTING
+#define DBG_CTR cpu.cycleCnt
+#define DBGAPP(...) sim_debug (DBG_APPENDING, & cpu_dev, __VA_ARGS__)
+#else
+#define DBGAPP(...)
+#endif
+
+#if 0
+void set_apu_status (apuStatusBits status)
+ {
+#if 1
+ word12 FCT = cpu.cu.APUCycleBits & MASK3;
+ cpu.cu.APUCycleBits = (status & 07770) | FCT;
+#else
+ cpu.cu.PI_AP = 0;
+ cpu.cu.DSPTW = 0;
+ cpu.cu.SDWNP = 0;
+ cpu.cu.SDWP = 0;
+ cpu.cu.PTW = 0;
+ cpu.cu.PTW2 = 0;
+ cpu.cu.FAP = 0;
+ cpu.cu.FANP = 0;
+ cpu.cu.FABS = 0;
+ switch (status)
+ {
+ case apuStatus_PI_AP:
+ cpu.cu.PI_AP = 1;
+ break;
+ case apuStatus_DSPTW:
+ case apuStatus_MDSPTW: // XXX this doesn't seem like the right solution.
+ // XXX there is a MDSPTW bit in the APU history
+ // register, but not in the CU.
+ cpu.cu.DSPTW = 1;
+ break;
+ case apuStatus_SDWNP:
+ cpu.cu.SDWNP = 1;
+ break;
+ case apuStatus_SDWP:
+ cpu.cu.SDWP = 1;
+ break;
+ case apuStatus_PTW:
+ case apuStatus_MPTW: // XXX this doesn't seem like the right solution.
+ // XXX there is a MPTW bit in the APU history
+ // XXX register, but not in the CU.
+ cpu.cu.PTW = 1;
+ break;
+ case apuStatus_PTW2:
+ cpu.cu.PTW2 = 1;
+ break;
+ case apuStatus_FAP:
+ cpu.cu.FAP = 1;
+ break;
+ case apuStatus_FANP:
+ cpu.cu.FANP = 1;
+ break;
+ case apuStatus_FABS:
+ cpu.cu.FABS = 1;
+ break;
+ }
+#endif
+ }
+#endif
+
+#ifdef WAM
+static char *str_sdw (char * buf, sdw_s *SDW);
+#endif
+
+//
+//
+// The Use of Bit 29 in the Instruction Word
+// The reader is reminded that there is a preliminary step of loading TPR.CA
+// with the ADDRESS field of the instruction word during instruction decode.
+// If bit 29 of the instruction word is set to 1, modification by pointer
+// register is invoked and the preliminary step is executed as follows:
+// 1. The ADDRESS field of the instruction word is interpreted as shown in
+// Figure 6-7 below.
+// 2. C(PRn.SNR) -> C(TPR.TSR)
+// 3. maximum of ( C(PRn.RNR), C(TPR.TRR), C(PPR.PRR) ) -> C(TPR.TRR)
+// 4. C(PRn.WORDNO) + OFFSET -> C(TPR.CA) (NOTE: OFFSET is a signed binary
+// number.)
+// 5. C(PRn.BITNO) -> TPR.BITNO
+//
+
+
+// Define this to do error detection on the PTWAM table.
+// Useful if PTWAM reports an error message, but it slows the emulator
+// down 50%
+
+#ifdef do_selftestPTWAM
+static void selftest_ptwaw (void)
+ {
+ int usages[N_WAM_ENTRIES];
+ for (int i = 0; i < N_WAM_ENTRIES; i ++)
+ usages[i] = -1;
+
+ for (int i = 0; i < N_WAM_ENTRIES; i ++)
+ {
+ ptw_s * p = cpu.PTWAM + i;
+ if (p->USE > N_WAM_ENTRIES - 1)
+ sim_printf ("PTWAM[%d].USE is %d; > %d!\n",
+ i, p->USE, N_WAM_ENTRIES - 1);
+ if (usages[p->USE] != -1)
+ sim_printf ("PTWAM[%d].USE is equal to PTWAM[%d].USE; %d\n",
+ i, usages[p->USE], p->USE);
+ usages[p->USE] = i;
+ }
+ for (int i = 0; i < N_WAM_ENTRIES; i ++)
+ {
+ if (usages[i] == -1)
+ sim_printf ("No PTWAM had a USE of %d\n", i);
+ }
+ }
+#endif
+
+/**
+ * implement ldbr instruction
+ */
+
+void do_ldbr (word36 * Ypair)
+ {
+ CPTUR (cptUseDSBR);
+#ifdef WAM
+ if (! cpu.switches.disable_wam)
+ {
+ if (cpu.cu.SD_ON)
+ {
+ // If SDWAM is enabled, then
+ // 0 -> C(SDWAM(i).FULL) for i = 0, 1, ..., 15
+ // i -> C(SDWAM(i).USE) for i = 0, 1, ..., 15
+ for (uint i = 0; i < N_WAM_ENTRIES; i ++)
+ {
+ cpu.SDWAM[i].FE = 0;
+#ifdef L68
+ cpu.SDWAM[i].USE = (word4) i;
+#endif
+#ifdef DPS8M
+ cpu.SDWAM[i].USE = 0;
+#endif
+ }
+ }
+
+ if (cpu.cu.PT_ON)
+ {
+ // If PTWAM is enabled, then
+ // 0 -> C(PTWAM(i).FULL) for i = 0, 1, ..., 15
+ // i -> C(PTWAM(i).USE) for i = 0, 1, ..., 15
+ for (uint i = 0; i < N_WAM_ENTRIES; i ++)
+ {
+ cpu.PTWAM[i].FE = 0;
+#ifdef L68
+ cpu.PTWAM[i].USE = (word4) i;
+#endif
+#ifdef DPS8M
+ cpu.PTWAM[i].USE = 0;
+#endif
+ }
+#ifdef do_selftestPTWAM
+ selftest_ptwaw ();
+#endif
+ }
+ }
+#else
+ cpu.SDW0.FE = 0;
+ cpu.SDW0.USE = 0;
+ cpu.PTW0.FE = 0;
+ cpu.PTW0.USE = 0;
+#endif // WAM
+
+ // If cache is enabled, reset all cache column and level full flags
+ // XXX no cache
+
+ // C(Y-pair) 0,23 -> C(DSBR.ADDR)
+ cpu.DSBR.ADDR = (Ypair[0] >> (35 - 23)) & PAMASK;
+
+ // C(Y-pair) 37,50 -> C(DSBR.BOUND)
+ cpu.DSBR.BND = (Ypair[1] >> (71 - 50)) & 037777;
+
+ // C(Y-pair) 55 -> C(DSBR.U)
+ cpu.DSBR.U = (Ypair[1] >> (71 - 55)) & 01;
+
+ // C(Y-pair) 60,71 -> C(DSBR.STACK)
+ cpu.DSBR.STACK = (Ypair[1] >> (71 - 71)) & 07777;
+ DBGAPP ("ldbr 0 -> SDWAM/PTWAM[*].F, i -> SDWAM/PTWAM[i].USE, "
+ "DSBR.ADDR 0%o, DSBR.BND 0%o, DSBR.U 0%o, DSBR.STACK 0%o\n",
+ cpu.DSBR.ADDR, cpu.DSBR.BND, cpu.DSBR.U, cpu.DSBR.STACK);
+ }
+
+
+
+/**
+ * fetch descriptor segment PTW ...
+ */
+
+// CANFAULT
+
+static void fetch_dsptw (word15 segno)
+ {
+ DBGAPP ("%s segno 0%o\n", __func__, segno);
+ PNL (L68_ (cpu.apu.state |= apu_FDPT;))
+
+ if (2 * segno >= 16 * (cpu.DSBR.BND + 1))
+ {
+ DBGAPP ("%s ACV15\n", __func__);
+ // generate access violation, out of segment bounds fault
+ PNL (cpu.acvFaults |= ACV15;)
+ PNL (L68_ (cpu.apu.state |= apu_FLT;))
+ doFault (FAULT_ACV, fst_acv15,
+ "acvFault: fetch_dsptw out of segment bounds fault");
+ }
+ set_apu_status (apuStatus_DSPTW);
+
+#ifndef SPEED
+ word24 y1 = (2u * segno) % 1024u;
+#endif
+ word24 x1 = (2u * segno) / 1024u; // floor
+
+ PNL (cpu.lastPTWOffset = segno;)
+ PNL (cpu.lastPTWIsDS = true;)
+
+ word36 PTWx1;
+ core_read ((cpu.DSBR.ADDR + x1) & PAMASK, & PTWx1, __func__);
+
+ cpu.PTW0.ADDR = GETHI (PTWx1);
+ cpu.PTW0.U = TSTBIT (PTWx1, 9);
+ cpu.PTW0.M = TSTBIT (PTWx1, 6);
+ cpu.PTW0.DF = TSTBIT (PTWx1, 2);
+ cpu.PTW0.FC = PTWx1 & 3;
+
+#ifdef L68
+ if (cpu.MR_cache.emr && cpu.MR_cache.ihr)
+ add_APU_history (APUH_FDSPTW);
+#endif
+
+ DBGAPP ("%s x1 0%o y1 0%o DSBR.ADDR 0%o PTWx1 0%012"PRIo64" "
+ "PTW0: ADDR 0%o U %o M %o F %o FC %o\n",
+ __func__, x1, y1, cpu.DSBR.ADDR, PTWx1, cpu.PTW0.ADDR, cpu.PTW0.U,
+ cpu.PTW0.M, cpu.PTW0.DF, cpu.PTW0.FC);
+ }
+
+
+/**
+ * modify descriptor segment PTW (Set U=1) ...
+ */
+
+// CANFAULT
+
+static void modify_dsptw (word15 segno)
+ {
+
+ PNL (L68_ (cpu.apu.state |= apu_MDPT;))
+
+ set_apu_status (apuStatus_MDSPTW);
+
+ word24 x1 = (2u * segno) / 1024u; // floor
+
+#ifdef TEST_OLIN
+ cmpxchg ();
+#endif
+#ifdef TEST_FENCE
+ fence ();
+#endif
+#ifdef THREADZ
+ bool lck = get_rmw_lock ();
+ if (! lck)
+ lock_rmw ();
+#endif
+
+ word36 PTWx1;
+#ifdef LOCKLESS
+ core_read_lock ((cpu.DSBR.ADDR + x1) & PAMASK, & PTWx1, __func__);
+ PTWx1 = SETBIT (PTWx1, 9);
+ core_write_unlock ((cpu.DSBR.ADDR + x1) & PAMASK, PTWx1, __func__);
+#else
+ core_read ((cpu.DSBR.ADDR + x1) & PAMASK, & PTWx1, __func__);
+ PTWx1 = SETBIT (PTWx1, 9);
+ core_write ((cpu.DSBR.ADDR + x1) & PAMASK, PTWx1, __func__);
+#endif
+
+#ifdef TEST_FENCE
+ fence ();
+#endif
+#ifdef THREADZ
+ if (! lck)
+ unlock_rmw ();
+#endif
+
+ cpu.PTW0.U = 1;
+#ifdef L68
+ if (cpu.MR_cache.emr && cpu.MR_cache.ihr)
+ add_APU_history (APUH_MDSPTW);
+#endif
+ }
+
+
+#ifdef WAM
+#ifdef DPS8M
+static word6 calc_hit_am (word6 LRU, uint hit_level)
+ {
+ switch (hit_level)
+ {
+ case 0: // hit level A
+ return (LRU | 070);
+ case 1: // hit level B
+ return ((LRU & 037) | 06);
+ case 2: // hit level C
+ return ((LRU & 053) | 01);
+ case 3: // hit level D
+ return (LRU & 064);
+ default:
+ DBGAPP ("%s: invalid AM level\n", __func__);
+ return 0;
+ }
+ }
+#endif
+
+static sdw_s * fetch_sdw_from_sdwam (word15 segno)
+ {
+ DBGAPP ("%s(0):segno=%05o\n", __func__, segno);
+
+ if (cpu.switches.disable_wam || ! cpu.cu.SD_ON)
+ {
+ DBGAPP ("%s(0): SDWAM disabled\n", __func__);
+ return NULL;
+ }
+
+#ifdef L68
+ int nwam = N_WAM_ENTRIES;
+ for (int _n = 0; _n < nwam; _n++)
+ {
+ // make certain we initialize SDWAM prior to use!!!
+ if (cpu.SDWAM[_n].FE && segno == cpu.SDWAM[_n].POINTER)
+ {
+ DBGAPP ("%s(1):found match for segno %05o "
+ "at _n=%d\n",
+ __func__, segno, _n);
+
+ cpu.cu.SDWAMM = 1;
+ cpu.SDWAMR = (word4) _n;
+ cpu.SDW = & cpu.SDWAM[_n];
+
+ // If the SDWAM match logic circuitry indicates a hit, all usage
+ // counts (SDWAM.USE) greater than the usage count of the register
+ // hit are decremented by one, the usage count of the register hit
+ // is set to 15, and the contents of the register hit are read out
+ // into the address preparation circuitry.
+
+ for (int _h = 0; _h < nwam; _h++)
+ {
+ if (cpu.SDWAM[_h].USE > cpu.SDW->USE)
+ cpu.SDWAM[_h].USE -= 1;
+ }
+ cpu.SDW->USE = N_WAM_ENTRIES - 1;
+
+ char buf[256];
+ DBGAPP ("%s(2):SDWAM[%d]=%s\n",
+ __func__, _n, str_sdw (buf, cpu.SDW));
+
+ return cpu.SDW;
+ }
+ }
+#endif
+#ifdef DPS8M
+ uint setno = segno & 017;
+ uint toffset;
+ sdw_s *p;
+ for (toffset = 0; toffset < 64; toffset += 16)
+ {
+ p = & cpu.SDWAM[toffset + setno];
+ if (p->FE && segno == p->POINTER)
+ {
+ DBGAPP ("%s(1):found match for segno %05o "
+ "at _n=%d\n",
+ __func__, segno, toffset + setno);
+
+ cpu.cu.SDWAMM = 1;
+ cpu.SDWAMR = (word6) (toffset + setno);
+ cpu.SDW = p; // export pointer for appending
+
+ word6 u = calc_hit_am (p->USE, toffset >> 4);
+ for (toffset = 0; toffset < 64; toffset += 16) // update LRU
+ {
+ p = & cpu.SDWAM[toffset + setno];
+ if (p->FE)
+ p->USE = u;
+ }
+
+ char buf[256];
+ DBGAPP ("%s(2):SDWAM[%d]=%s\n",
+ __func__, toffset + setno, str_sdw (buf, cpu.SDW));
+ return cpu.SDW;
+ }
+ }
+#endif
+ DBGAPP ("%s(3):SDW for segment %05o not found in SDWAM\n",
+ __func__, segno);
+ cpu.cu.SDWAMM = 0;
+ return NULL; // segment not referenced in SDWAM
+ }
+#endif // WAM
+
+/**
+ * Fetches an SDW from a paged descriptor segment.
+ */
+// CANFAULT
+
+static void fetch_psdw (word15 segno)
+ {
+ DBGAPP ("%s(0):segno=%05o\n",
+ __func__, segno);
+
+ PNL (L68_ (cpu.apu.state |= apu_FSDP;))
+
+ set_apu_status (apuStatus_SDWP);
+ word24 y1 = (2 * segno) % 1024;
+
+ word36 SDWeven, SDWodd;
+
+ core_read2 (((((word24) cpu.PTW0.ADDR & 0777760) << 6) + y1) & PAMASK,
+ & SDWeven, & SDWodd, __func__);
+
+ // even word
+ cpu.SDW0.ADDR = (SDWeven >> 12) & 077777777;
+ cpu.SDW0.R1 = (SDWeven >> 9) & 7;
+ cpu.SDW0.R2 = (SDWeven >> 6) & 7;
+ cpu.SDW0.R3 = (SDWeven >> 3) & 7;
+ cpu.SDW0.DF = TSTBIT (SDWeven, 2);
+ cpu.SDW0.FC = SDWeven & 3;
+
+ // odd word
+ cpu.SDW0.BOUND = (SDWodd >> 21) & 037777;
+ cpu.SDW0.R = TSTBIT (SDWodd, 20);
+ cpu.SDW0.E = TSTBIT (SDWodd, 19);
+ cpu.SDW0.W = TSTBIT (SDWodd, 18);
+ cpu.SDW0.P = TSTBIT (SDWodd, 17);
+ cpu.SDW0.U = TSTBIT (SDWodd, 16);
+ cpu.SDW0.G = TSTBIT (SDWodd, 15);
+ cpu.SDW0.C = TSTBIT (SDWodd, 14);
+ cpu.SDW0.EB = SDWodd & 037777;
+
+#ifdef L68
+ if (cpu.MR_cache.emr && cpu.MR_cache.ihr)
+ add_APU_history (APUH_FSDWP);
+#endif
+ DBGAPP ("%s y1 0%o p->ADDR 0%o SDW 0%012"PRIo64" 0%012"PRIo64" "
+ "ADDR %o R %o%o%o BOUND 0%o REWPUGC %o%o%o%o%o%o%o "
+ "F %o FC %o FE %o USE %o\n",
+ __func__, y1, cpu.PTW0.ADDR, SDWeven, SDWodd, cpu.SDW0.ADDR,
+ cpu.SDW0.R1, cpu.SDW0.R2, cpu.SDW0.R3, cpu.SDW0.BOUND,
+ cpu.SDW0.R, cpu.SDW0.E, cpu.SDW0.W, cpu.SDW0.P, cpu.SDW0.U,
+ cpu.SDW0.G, cpu.SDW0.C, cpu.SDW0.DF, cpu.SDW0.FC, cpu.SDW0.FE,
+ cpu.SDW0.USE);
+ }
+
+// Nonpaged SDW Fetch
+// Fetches an SDW from an unpaged descriptor segment.
+// CANFAULT
+
+static void fetch_nsdw (word15 segno)
+ {
+ DBGAPP ("%s (0):segno=%05o\n", __func__, segno);
+
+ PNL (L68_ (cpu.apu.state |= apu_FSDN;))
+
+ set_apu_status (apuStatus_SDWNP);
+
+ if (2 * segno >= 16 * (cpu.DSBR.BND + 1))
+ {
+ DBGAPP ("%s (1):Access Violation, out of segment bounds for "
+ "segno=%05o DSBR.BND=%d\n",
+ __func__, segno, cpu.DSBR.BND);
+ // generate access violation, out of segment bounds fault
+ PNL (cpu.acvFaults |= ACV15;)
+ PNL (L68_ (cpu.apu.state |= apu_FLT;))
+ doFault (FAULT_ACV, fst_acv15,
+ "acvFault fetch_dsptw: out of segment bounds fault");
+ }
+ DBGAPP ("%s (2):fetching SDW from %05o\n",
+ __func__, cpu.DSBR.ADDR + 2u * segno);
+
+ word36 SDWeven, SDWodd;
+ core_read2 ((cpu.DSBR.ADDR + 2u * segno) & PAMASK,
+ & SDWeven, & SDWodd, __func__);
+
+ // even word
+ cpu.SDW0.ADDR = (SDWeven >> 12) & 077777777;
+ cpu.SDW0.R1 = (SDWeven >> 9) & 7;
+ cpu.SDW0.R2 = (SDWeven >> 6) & 7;
+ cpu.SDW0.R3 = (SDWeven >> 3) & 7;
+ cpu.SDW0.DF = TSTBIT (SDWeven, 2);
+ cpu.SDW0.FC = SDWeven & 3;
+
+ // odd word
+ cpu.SDW0.BOUND = (SDWodd >> 21) & 037777;
+ cpu.SDW0.R = TSTBIT (SDWodd, 20);
+ cpu.SDW0.E = TSTBIT (SDWodd, 19);
+ cpu.SDW0.W = TSTBIT (SDWodd, 18);
+ cpu.SDW0.P = TSTBIT (SDWodd, 17);
+ cpu.SDW0.U = TSTBIT (SDWodd, 16);
+ cpu.SDW0.G = TSTBIT (SDWodd, 15);
+ cpu.SDW0.C = TSTBIT (SDWodd, 14);
+ cpu.SDW0.EB = SDWodd & 037777;
+
+#ifdef L68
+ if (cpu.MR_cache.emr && cpu.MR_cache.ihr)
+ add_APU_history (0 /* No fetch no paged bit */);
+#endif
+#ifndef SPEED
+ char buf[256];
+ DBGAPP ("%s (2):SDW0=%s\n", __func__, str_SDW0 (buf, & cpu.SDW0));
+#endif
+ }
+
+#ifdef WAM
+static char *str_sdw (char * buf, sdw_s *SDW)
+ {
+ if (! SDW->FE)
+ sprintf (buf, "*** SDW Uninitialized ***");
+ else
+ sprintf (buf,
+ "ADDR:%06o R1:%o R2:%o R3:%o BOUND:%o R:%o E:%o W:%o P:%o "
+ "U:%o G:%o C:%o CL:%o DF:%o FC:%o POINTER=%o USE=%d",
+ SDW->ADDR,
+ SDW->R1,
+ SDW->R2,
+ SDW->R3,
+ SDW->BOUND,
+ SDW->R,
+ SDW->E,
+ SDW->W,
+ SDW->P,
+ SDW->U,
+ SDW->G,
+ SDW->C,
+ SDW->EB,
+ SDW->DF,
+ SDW->FC,
+ SDW->POINTER,
+ SDW->USE);
+ return buf;
+ }
+#endif
+
+#ifdef WAM
+#ifdef L68
+
+/**
+ * dump SDWAM...
+ */
+
+t_stat dump_sdwam (void)
+ {
+ char buf[256];
+ for (int _n = 0; _n < N_WAM_ENTRIES; _n++)
+ {
+ sdw_s *p = & cpu.SDWAM[_n];
+
+ if (p->FE)
+ sim_printf ("SDWAM n:%d %s\n", _n, str_sdw (buf, p));
+ }
+ return SCPE_OK;
+ }
+#endif
+
+#ifdef DPS8M
+static uint to_be_discarded_am (word6 LRU)
+ {
+#if 0
+ uint cA=0,cB=0,cC=0,cD=0;
+ if (LRU & 040) cB++; else cA++;
+ if (LRU & 020) cC++; else cA++;
+ if (LRU & 010) cD++; else cA++;
+ if (cA==3) return 0;
+ if (LRU & 04) cC++; else cB++;
+ if (LRU & 02) cD++; else cB++;
+ if (cB==3) return 1;
+ if (LRU & 01) return 3; else return 2;
+#endif
+
+ if ((LRU & 070) == 070) return 0;
+ if ((LRU & 046) == 006) return 1;
+ if ((LRU & 025) == 001) return 2;
+ return 3;
+ }
+#endif
+#endif
+
+/**
+ * load the current in-core SDW0 into the SDWAM ...
+ */
+
+static void load_sdwam (word15 segno, UNUSED bool nomatch)
+ {
+#ifndef WAM
+ cpu.SDW0.POINTER = segno;
+ cpu.SDW0.USE = 0;
+
+ cpu.SDW0.FE = true; // in use by SDWAM
+
+ cpu.SDW = & cpu.SDW0;
+
+#else
+ if (nomatch || cpu.switches.disable_wam || ! cpu.cu.SD_ON)
+ {
+ DBGAPP ("%s: SDWAM disabled\n", __func__);
+ sdw_s * p = & cpu.SDW0;
+ p->POINTER = segno;
+ p->USE = 0;
+ p->FE = true; // in use by SDWAM
+ cpu.SDW = p;
+ return;
+ }
+
+#ifdef L68
+ // If the SDWAM match logic does not indicate a hit, the SDW is fetched
+ // from the descriptor segment in main memory and loaded into the SDWAM
+ // register with usage count 0 (the oldest), all usage counts are
+ // decremented by one with the newly loaded register rolling over from 0 to
+ // 15, and the newly loaded register is read out into the address
+ // preparation circuitry.
+
+ for (int _n = 0; _n < N_WAM_ENTRIES; _n++)
+ {
+ sdw_s * p = & cpu.SDWAM[_n];
+ if (! p->FE || p->USE == 0)
+ {
+ DBGAPP ("%s(1):SDWAM[%d] FE=0 || USE=0\n", __func__, _n);
+
+ * p = cpu.SDW0;
+ p->POINTER = segno;
+ p->USE = 0;
+ p->FE = true; // in use by SDWAM
+
+ for (int _h = 0; _h < N_WAM_ENTRIES; _h++)
+ {
+ sdw_s * q = & cpu.SDWAM[_h];
+ q->USE -= 1;
+ q->USE &= N_WAM_MASK;
+ }
+
+ cpu.SDW = p;
+
+ char buf[256];
+ DBGAPP ("%s(2):SDWAM[%d]=%s\n",
+ __func__, _n, str_sdw (buf, p));
+
+ return;
+ }
+ }
+ // if we reach this, USE is scrambled
+ DBGAPP ("%s(3) no USE=0 found for segment=%d\n", __func__, segno);
+
+ sim_printf ("%s(%05o): no USE=0 found!\n", __func__, segno);
+ dump_sdwam ();
+#endif
+
+#ifdef DPS8M
+ uint setno = segno & 017;
+ uint toffset;
+ sdw_s *p;
+ for (toffset = 0; toffset < 64; toffset += 16)
+ {
+ p = & cpu.SDWAM[toffset + setno];
+ if (!p->FE)
+ break;
+ }
+ if (toffset == 64) // all FE==1
+ {
+ toffset = to_be_discarded_am (p->USE) << 4;
+ p = & cpu.SDWAM[toffset + setno];
+ }
+ DBGAPP ("%s(1):SDWAM[%d] FE=0 || LRU\n",
+ __func__, toffset + setno);
+
+ word6 u = calc_hit_am (p->USE, toffset >> 4); // before loading the SDWAM!
+ * p = cpu.SDW0; // load the SDW
+ p->POINTER = segno;
+ p->FE = true; // in use
+ cpu.SDW = p; // export pointer for appending
+
+ for (uint toffset1 = 0; toffset1 < 64; toffset1 += 16) // update LRU
+ {
+ p = & cpu.SDWAM[toffset1 + setno];
+ if (p->FE)
+ p->USE = u;
+ }
+
+ char buf[256];
+ DBGAPP ("%s(2):SDWAM[%d]=%s\n",
+ __func__, toffset + setno, str_sdw (buf, cpu.SDW));
+#endif
+#endif // WAM
+ }
+
+#ifdef WAM
+static ptw_s * fetch_ptw_from_ptwam (word15 segno, word18 CA)
+ {
+ if (cpu.switches.disable_wam || ! cpu.cu.PT_ON)
+ {
+ DBGAPP ("%s: PTWAM disabled\n", __func__);
+ return NULL;
+ }
+
+#ifdef L68
+ int nwam = N_WAM_ENTRIES;
+ for (int _n = 0; _n < nwam; _n++)
+ {
+ if (cpu.PTWAM[_n].FE && ((CA >> 6) & 07760) == cpu.PTWAM[_n].PAGENO &&
+ cpu.PTWAM[_n].POINTER == segno) //_initialized)
+ {
+ DBGAPP ("%s: found match for segno=%o pageno=%o "
+ "at _n=%d\n",
+ __func__, segno, cpu.PTWAM[_n].PAGENO, _n);
+ cpu.cu.PTWAMM = 1;
+ cpu.PTWAMR = (word4) _n;
+ cpu.PTW = & cpu.PTWAM[_n];
+
+ // If the PTWAM match logic circuitry indicates a hit, all usage
+ // counts (PTWAM.USE) greater than the usage count of the register
+ // hit are decremented by one, the usage count of the register hit
+ // is set to 15, and the contents of the register hit are read out
+ // into the address preparation circuitry.
+
+ for (int _h = 0; _h < nwam; _h++)
+ {
+ if (cpu.PTWAM[_h].USE > cpu.PTW->USE)
+ cpu.PTWAM[_h].USE -= 1; //PTW->USE -= 1;
+ }
+ cpu.PTW->USE = N_WAM_ENTRIES - 1;
+#ifdef do_selftestPTWAM
+ selftest_ptwaw ();
+#endif
+ DBGAPP ("%s: ADDR 0%o U %o M %o F %o FC %o\n",
+ __func__, cpu.PTW->ADDR, cpu.PTW->U, cpu.PTW->M,
+ cpu.PTW->DF, cpu.PTW->FC);
+ return cpu.PTW;
+ }
+ }
+#endif
+
+#ifdef DPS8M
+ uint setno = (CA >> 10) & 017;
+ uint toffset;
+ ptw_s *p;
+ for (toffset = 0; toffset < 64; toffset += 16)
+ {
+ p = & cpu.PTWAM[toffset + setno];
+
+ if (p->FE && ((CA >> 6) & 07760) == p->PAGENO && p->POINTER == segno)
+ {
+ DBGAPP ("%s: found match for segno=%o pageno=%o "
+ "at _n=%d\n",
+ __func__, segno, p->PAGENO, toffset + setno);
+ cpu.cu.PTWAMM = 1;
+ cpu.PTWAMR = (word6) (toffset + setno);
+ cpu.PTW = p; // export pointer for appending
+
+ word6 u = calc_hit_am (p->USE, toffset >> 4);
+ for (toffset = 0; toffset < 64; toffset += 16) // update LRU
+ {
+ p = & cpu.PTWAM[toffset + setno];
+ if (p->FE)
+ p->USE = u;
+ }
+
+ DBGAPP ("%s: ADDR 0%o U %o M %o F %o FC %o\n",
+ __func__, cpu.PTW->ADDR, cpu.PTW->U, cpu.PTW->M,
+ cpu.PTW->DF, cpu.PTW->FC);
+ return cpu.PTW;
+ }
+ }
+#endif
+ cpu.cu.PTWAMM = 0;
+ return NULL; // page not referenced in PTWAM
+ }
+#endif // WAM
+
+static void fetch_ptw (sdw_s *sdw, word18 offset)
+ {
+ // AL39 p.5-7
+ // Fetches a PTW from a page table other than a descriptor segment page
+ // table and sets the page accessed bit (PTW.U)
+ PNL (L68_ (cpu.apu.state |= apu_FPTW;))
+ set_apu_status (apuStatus_PTW);
+
+#ifndef SPEED
+ word24 y2 = offset % 1024;
+#endif
+ word24 x2 = (offset) / 1024; // floor
+
+ word36 PTWx2;
+
+ DBGAPP ("%s address %08o\n", __func__, sdw->ADDR + x2);
+
+ PNL (cpu.lastPTWOffset = offset;)
+ PNL (cpu.lastPTWIsDS = false;)
+
+#ifdef TEST_OLIN
+ cmpxchg ();
+#endif
+#ifdef TEST_FENCE
+ fence ();
+#endif
+#ifdef THREADZ
+ bool lck = get_rmw_lock ();
+ if (! lck)
+ lock_rmw ();
+#endif
+#ifdef LOCKLESS
+ core_read_lock ((sdw->ADDR + x2) & PAMASK, & PTWx2, __func__);
+#else
+ core_read ((sdw->ADDR + x2) & PAMASK, & PTWx2, __func__);
+#endif
+
+ cpu.PTW0.ADDR = GETHI (PTWx2);
+ cpu.PTW0.U = TSTBIT (PTWx2, 9);
+ cpu.PTW0.M = TSTBIT (PTWx2, 6);
+ cpu.PTW0.DF = TSTBIT (PTWx2, 2);
+ cpu.PTW0.FC = PTWx2 & 3;
+
+ // ISOLTS-861 02
+#ifndef LOCKLESS
+ if (! cpu.PTW0.U)
+#endif
+ {
+ PTWx2 = SETBIT (PTWx2, 9);
+#ifdef LOCKLESS
+ core_write_unlock ((sdw->ADDR + x2) & PAMASK, PTWx2, __func__);
+#else
+ core_write ((sdw->ADDR + x2) & PAMASK, PTWx2, __func__);
+#endif
+ cpu.PTW0.U = 1;
+ }
+
+#ifdef TEST_FENCE
+ fence ();
+#endif
+#ifdef THREADZ
+ if (! lck)
+ unlock_rmw ();
+#endif
+
+#ifdef L68
+ if (cpu.MR_cache.emr && cpu.MR_cache.ihr)
+ add_APU_history (APUH_FPTW);
+#endif
+
+ DBGAPP ("%s x2 0%o y2 0%o sdw->ADDR 0%o PTWx2 0%012"PRIo64" "
+ "PTW0: ADDR 0%o U %o M %o F %o FC %o\n",
+ __func__, x2, y2, sdw->ADDR, PTWx2, cpu.PTW0.ADDR, cpu.PTW0.U,
+ cpu.PTW0.M, cpu.PTW0.DF, cpu.PTW0.FC);
+ }
+
+static void loadPTWAM (word15 segno, word18 offset, UNUSED bool nomatch)
+ {
+#ifndef WAM
+ cpu.PTW0.PAGENO = (offset >> 6) & 07760;
+ cpu.PTW0.POINTER = segno;
+ cpu.PTW0.USE = 0;
+ cpu.PTW0.FE = true;
+
+ cpu.PTW = & cpu.PTW0;
+#else
+ if (nomatch || cpu.switches.disable_wam || ! cpu.cu.PT_ON)
+ {
+ DBGAPP ("loadPTWAM: PTWAM disabled\n");
+ ptw_s * p = & cpu.PTW0;
+ p->PAGENO = (offset >> 6) & 07760; // ISOLTS-861 02, AL39 p.3-22
+ p->POINTER = segno;
+ p->USE = 0;
+ p->FE = true;
+
+ cpu.PTW = p;
+ return;
+ }
+
+#ifdef L68
+ // If the PTWAM match logic does not indicate a hit, the PTW is fetched
+ // from main memory and loaded into the PTWAM register with usage count 0
+ // (the oldest), all usage counts are decremented by one with the newly
+ // loaded register rolling over from 0 to 15, and the newly loaded register
+ // is read out into the address preparation circuitry.
+
+ for (int _n = 0; _n < N_WAM_ENTRIES; _n++)
+ {
+ ptw_s * p = & cpu.PTWAM[_n];
+ if (! p->FE || p->USE == 0)
+ {
+ DBGAPP ("loadPTWAM(1):PTWAM[%d] FE=0 || USE=0\n", _n);
+ *p = cpu.PTW0;
+ p->PAGENO = (offset >> 6) & 07760;
+ p->POINTER = segno;
+ p->USE = 0;
+ p->FE = true;
+
+ for (int _h = 0; _h < N_WAM_ENTRIES; _h++)
+ {
+ ptw_s * q = & cpu.PTWAM[_h];
+ q->USE -= 1;
+ q->USE &= N_WAM_MASK;
+ }
+
+ cpu.PTW = p;
+ DBGAPP ("loadPTWAM(2): ADDR 0%o U %o M %o F %o FC %o "
+ "POINTER=%o PAGENO=%o USE=%d\n",
+ cpu.PTW->ADDR, cpu.PTW->U, cpu.PTW->M, cpu.PTW->DF,
+ cpu.PTW->FC, cpu.PTW->POINTER, cpu.PTW->PAGENO,
+ cpu.PTW->USE);
+#ifdef do_selftestPTWAM
+ selftest_ptwaw ();
+#endif
+ return;
+ }
+ }
+ // if we reach this, USE is scrambled
+ sim_printf ("loadPTWAM(segno=%05o, offset=%012o): no USE=0 found!\n",
+ segno, offset);
+#endif
+
+#ifdef DPS8M
+ uint setno = (offset >> 10) & 017;
+ uint toffset;
+ ptw_s *p;
+ for (toffset = 0; toffset < 64; toffset += 16)
+ {
+ p = & cpu.PTWAM[toffset + setno];
+ if (! p->FE)
+ break;
+ }
+ if (toffset == 64) // all FE==1
+ {
+ toffset = to_be_discarded_am (p->USE) << 4;
+ p = & cpu.PTWAM[toffset + setno];
+ }
+
+ DBGAPP ("loadPTWAM(1):PTWAM[%d] FE=0 || LRU\n",
+ toffset + setno);
+
+ word6 u = calc_hit_am (p->USE, toffset >> 4); // before loading the PTWAM
+ * p = cpu.PTW0; // load the PTW
+ p->PAGENO = (offset >> 6) & 07760;
+ p->POINTER = segno;
+ p->FE = true; // in use
+ cpu.PTW = p; // export pointer for appending
+
+ for (uint toffset1 = 0; toffset1 < 64; toffset1 += 16) // update LRU
+ {
+ p = & cpu.PTWAM[toffset1 + setno];
+ if (p->FE)
+ p->USE = u;
+ }
+
+ DBGAPP ("loadPTWAM(2): ADDR 0%o U %o M %o F %o FC %o POINTER=%o "
+ "PAGENO=%o USE=%d\n",
+ cpu.PTW->ADDR, cpu.PTW->U, cpu.PTW->M, cpu.PTW->DF,
+ cpu.PTW->FC, cpu.PTW->POINTER, cpu.PTW->PAGENO, cpu.PTW->USE);
+#endif
+#endif // WAM
+ }
+
+/**
+ * modify target segment PTW (Set M=1) ...
+ */
+
+static void modify_ptw (sdw_s *sdw, word18 offset)
+ {
+ PNL (L68_ (cpu.apu.state |= apu_MPTW;))
+ //word24 y2 = offset % 1024;
+ word24 x2 = offset / 1024; // floor
+
+ word36 PTWx2;
+
+ set_apu_status (apuStatus_MPTW);
+
+#ifdef TEST_OLIN
+ cmpxchg ();
+#endif
+#ifdef TEST_FENCE
+ fence ();
+#endif
+#ifdef THREADZ
+ bool lck = get_rmw_lock ();
+ if (! lck)
+ lock_rmw ();
+#endif
+#ifdef LOCKLESS
+ core_read_lock ((sdw->ADDR + x2) & PAMASK, & PTWx2, __func__);
+ PTWx2 = SETBIT (PTWx2, 6);
+ core_write_unlock ((sdw->ADDR + x2) & PAMASK, PTWx2, __func__);
+#else
+ core_read ((sdw->ADDR + x2) & PAMASK, & PTWx2, __func__);
+ PTWx2 = SETBIT (PTWx2, 6);
+ core_write ((sdw->ADDR + x2) & PAMASK, PTWx2, __func__);
+#endif
+#ifdef TEST_FENCE
+ fence ();
+#endif
+#ifdef THREADZ
+ if (! lck)
+ unlock_rmw ();
+#endif
+ cpu.PTW->M = 1;
+#ifdef L68
+ if (cpu.MR_cache.emr && cpu.MR_cache.ihr)
+ add_APU_history (APUH_MPTW);
+#endif
+ }
+
+static void do_ptw2 (sdw_s *sdw, word18 offset)
+ {
+ PNL (L68_ (cpu.apu.state |= apu_FPTW2;))
+ set_apu_status (apuStatus_PTW2);
+
+#ifndef SPEED
+ word24 y2 = offset % 1024;
+#endif
+ word24 x2 = (offset) / 1024; // floor
+
+ word36 PTWx2n;
+
+ DBGAPP ("%s address %08o\n", __func__, sdw->ADDR + x2 + 1);
+
+ core_read ((sdw->ADDR + x2 + 1) & PAMASK, & PTWx2n, __func__);
+
+ ptw_s PTW2;
+ PTW2.ADDR = GETHI (PTWx2n);
+ PTW2.U = TSTBIT (PTWx2n, 9);
+ PTW2.M = TSTBIT (PTWx2n, 6);
+ PTW2.DF = TSTBIT (PTWx2n, 2);
+ PTW2.FC = PTWx2n & 3;
+
+#ifdef L68
+ if (cpu.MR_cache.emr && cpu.MR_cache.ihr)
+ add_APU_history (APUH_FPTW2);
+#endif
+
+ DBGAPP ("%s x2 0%o y2 0%o sdw->ADDR 0%o PTW2 0%012"PRIo64" "
+ "PTW2: ADDR 0%o U %o M %o F %o FC %o\n",
+ __func__, x2, y2, sdw->ADDR, PTWx2n, PTW2.ADDR, PTW2.U, PTW2.M,
+ PTW2.DF, PTW2.FC);
+
+ // check that PTW2 is the next page of the same segment
+ // ISOLTS 875 02a
+ if ((PTW2.ADDR & 0777760) == (cpu.PTW->ADDR & 0777760) + 16)
+ //Is PTW2.F set ON?
+ if (! PTW2.DF)
+ // initiate a directed fault
+ doFault (FAULT_DF0 + PTW2.FC, fst_zero, "PTW2.F == 0");
+
+ }
+
+
+/**
+ * Is the instruction a SToRage OPeration ?
+ */
+
+#ifndef QUIET_UNUSED
+static char *str_access_type (MemoryAccessType accessType)
+ {
+ switch (accessType)
+ {
+ case UnknownMAT: return "Unknown";
+ case OperandRead: return "OperandRead";
+ case OperandWrite: return "OperandWrite";
+ default: return "???";
+ }
+ }
+#endif
+
+#ifndef QUIET_UNUSED
+static char *str_acv (_fault_subtype acv)
+ {
+ switch (acv)
+ {
+ case ACV0: return "Illegal ring order (ACV0=IRO)";
+ case ACV1: return "Not in execute bracket (ACV1=OEB)";
+ case ACV2: return "No execute permission (ACV2=E-OFF)";
+ case ACV3: return "Not in read bracket (ACV3=ORB)";
+ case ACV4: return "No read permission (ACV4=R-OFF)";
+ case ACV5: return "Not in write bracket (ACV5=OWB)";
+ case ACV6: return "No write permission (ACV6=W-OFF)";
+ case ACV7: return "Call limiter fault (ACV7=NO GA)";
+ case ACV8: return "Out of call brackets (ACV8=OCB)";
+ case ACV9: return "Outward call (ACV9=OCALL)";
+ case ACV10: return "Bad outward call (ACV10=BOC)";
+ case ACV11: return "Inward return (ACV11=INRET) XXX ??";
+ case ACV12: return "Invalid ring crossing (ACV12=CRT)";
+ case ACV13: return "Ring alarm (ACV13=RALR)";
+ case ACV14: return "Associative memory error XXX ??";
+ case ACV15: return "Out of segment bounds (ACV15=OOSB)";
+ //case ACDF0: return "Directed fault 0";
+ //case ACDF1: return "Directed fault 1";
+ //case ACDF2: return "Directed fault 2";
+ //case ACDF3: return "Directed fault 3";
+ default:
+ break;
+ }
+ return "unhandled acv in str_acv";
+ }
+#endif
+
+static char *str_pct (processor_cycle_type t)
+ {
+ switch (t)
+ {
+ case UNKNOWN_CYCLE: return "UNKNOWN_CYCLE";
+ case OPERAND_STORE : return "OPERAND_STORE";
+ case OPERAND_READ : return "OPERAND_READ";
+ case INDIRECT_WORD_FETCH: return "INDIRECT_WORD_FETCH";
+ case RTCD_OPERAND_FETCH: return "RTCD_OPERAND_FETCH";
+ case INSTRUCTION_FETCH: return "INSTRUCTION_FETCH";
+ case APU_DATA_READ: return "APU_DATA_READ";
+ case APU_DATA_STORE: return "APU_DATA_STORE";
+ case ABSA_CYCLE : return "ABSA_CYCLE";
+#ifdef LOCKLESS
+ case OPERAND_RMW : return "OPERAND_RMW";
+ case APU_DATA_RMW : return "APU_DATA_RMW";
+#endif
+
+ default:
+ return "Unhandled processor_cycle_type";
+ }
+
+ }
+
+
+/*
+ * recoding APU functions to more closely match Fig 5,6 & 8 ...
+ * Returns final address suitable for core_read/write
+ */
+
+//
+// Phase 1:
+//
+// A: Get the SDW
+//
+// B: Check the ring
+//
+// Phase 2:
+//
+// B1: If CALL6 operand
+// goto E
+// If instruction fetch or transfer instruction operand
+// goto F
+// If write
+// check write permission
+// else
+// check read permission
+// goto G
+//
+// E: -- CALL6 operand handling
+// Check execute and gate bits
+// Get the ring
+// goto G
+//
+// F: -- instruction fetch or transfer instruction operand
+// Check execute bit and ring
+// goto D
+//
+// D: Check RALR
+// goto G
+//
+// Phase 3
+//
+// G: Check BOUND
+// If not paged
+// goto H
+// Fetch PTW
+// Fetch prepage PTW
+// Goto I
+//
+// H: Compute final address
+// Goto HI
+//
+// I: If write
+// set PTW.M
+// Compute final address
+// Goto HI
+//
+// Phase 4
+//
+// HI: --
+// If indirect word fetch
+// goto J
+// if rtcd operand fetch
+// goto K
+// if CALL6
+// goto N
+// If instruction fetch or transfer instruction operand
+// goto L
+// APU data movement?
+// load/store APU data
+//
+// K: Set PPR.P
+// Goto J
+//
+// J: return
+//
+
+// CANFAULT
+
+word24 do_append_cycle (processor_cycle_type thisCycle, word36 * data,
+ uint nWords)
+ {
+ DCDstruct * i = & cpu.currentInstruction;
+ DBGAPP ("do_append_cycle(Entry) thisCycle=%s\n",
+ str_pct (thisCycle));
+ DBGAPP ("do_append_cycle(Entry) lastCycle=%s\n",
+ str_pct (cpu.apu.lastCycle));
+ DBGAPP ("do_append_cycle(Entry) CA %06o\n",
+ cpu.TPR.CA);
+ DBGAPP ("do_append_cycle(Entry) n=%2u\n",
+ nWords);
+ DBGAPP ("do_append_cycle(Entry) PPR.PRR=%o PPR.PSR=%05o\n",
+ cpu.PPR.PRR, cpu.PPR.PSR);
+ DBGAPP ("do_append_cycle(Entry) TPR.TRR=%o TPR.TSR=%05o\n",
+ cpu.TPR.TRR, cpu.TPR.TSR);
+
+ if (i->b29)
+ {
+ DBGAPP ("do_append_cycle(Entry) isb29 PRNO %o\n",
+ GET_PRN (IWB_IRODD));
+ }
+
+ bool StrOp = (thisCycle == OPERAND_STORE ||
+ thisCycle == APU_DATA_STORE);
+
+#ifdef WAM
+ // AL39: The associative memory is ignored (forced to "no match") during
+ // address preparation.
+ // lptp,lptr,lsdp,lsdr,sptp,sptr,ssdp,ssdr
+ // Unfortunately, ISOLTS doesn't try to execute any of these in append mode.
+ // XXX should this be only for OPERAND_READ and OPERAND_STORE?
+ bool nomatch = ((i->opcode == 0232 || i->opcode == 0254 ||
+ i->opcode == 0154 || i->opcode == 0173) &&
+ i->opcodeX ) ||
+ ((i->opcode == 0557 || i->opcode == 0257) &&
+ ! i->opcodeX);
+#else
+ const bool nomatch = true;
+#endif
+
+ processor_cycle_type lastCycle = cpu.apu.lastCycle;
+ cpu.apu.lastCycle = thisCycle;
+
+ DBGAPP ("do_append_cycle(Entry) XSF %o\n", cpu.cu.XSF);
+
+ PNL (L68_ (cpu.apu.state = 0;))
+
+ cpu.RSDWH_R1 = 0;
+
+ cpu.acvFaults = 0;
+
+//#define FMSG(x) x
+#define FMSG(x)
+ FMSG (char * acvFaultsMsg = "<unknown>";)
+
+ word24 finalAddress = (word24) -1; // not everything requires a final
+ // address
+
+////////////////////////////////////////
+//
+// Sheet 1: "START APPEND"
+//
+////////////////////////////////////////
+
+// START APPEND
+ word3 n = 0; // PRn to be saved to TSN_PRNO
+
+ // If the rtcd instruction is executed with the processor in absolute
+ // mode with bit 29 of the instruction word set OFF and without
+ // indirection through an ITP or ITS pair, then:
+ //
+ // appending mode is entered for address preparation for the
+ // rtcd operand and is retained if the instruction executes
+ // successfully, and the effective segment number generated for
+ // the SDW fetch and subsequent loading into C(TPR.TSR) is equal
+ // to C(PPR.PSR) and may be undefined in absolute mode, and the
+ // effective ring number loaded into C(TPR.TRR) prior to the SDW
+ // fetch is equal to C(PPR.PRR) (which is 0 in absolute mode)
+ // implying that control is always transferred into ring 0.
+ //
+ if (thisCycle == RTCD_OPERAND_FETCH &&
+ get_addr_mode() == ABSOLUTE_mode &&
+ ! (cpu.cu.XSF || cpu.currentInstruction.b29) /*get_went_appending()*/)
+ {
+ cpu.TPR.TSR = 0;
+ DBGAPP ("RTCD_OPERAND_FETCH ABSOLUTE mode set TSR %05o TRR %o\n", cpu.TPR.TSR, cpu.TPR.TRR);
+ }
+
+ goto A;
+
+////////////////////////////////////////
+//
+// Sheet 2: "A"
+//
+////////////////////////////////////////
+
+//
+// A:
+// Get SDW
+
+A:;
+
+ //PNL (cpu.APUMemAddr = address;)
+ PNL (cpu.APUMemAddr = cpu.TPR.CA;)
+
+ DBGAPP ("do_append_cycle(A)\n");
+
+#ifdef WAM
+ // is SDW for C(TPR.TSR) in SDWAM?
+ if (nomatch || ! fetch_sdw_from_sdwam (cpu.TPR.TSR))
+#endif
+ {
+ // No
+ DBGAPP ("do_append_cycle(A):SDW for segment %05o not in SDWAM\n",
+ cpu.TPR.TSR);
+
+ DBGAPP ("do_append_cycle(A):DSBR.U=%o\n",
+ cpu.DSBR.U);
+
+ if (cpu.DSBR.U == 0)
+ {
+ fetch_dsptw (cpu.TPR.TSR);
+
+ if (! cpu.PTW0.DF)
+ doFault (FAULT_DF0 + cpu.PTW0.FC, fst_zero,
+ "do_append_cycle(A): PTW0.F == 0");
+
+ if (! cpu.PTW0.U)
+ modify_dsptw (cpu.TPR.TSR);
+
+ fetch_psdw (cpu.TPR.TSR);
+ }
+ else
+ fetch_nsdw (cpu.TPR.TSR); // load SDW0 from descriptor segment table.
+
+ if (cpu.SDW0.DF == 0)
+ {
+ if (thisCycle != ABSA_CYCLE)
+ {
+ DBGAPP ("do_append_cycle(A): SDW0.F == 0! "
+ "Initiating directed fault\n");
+ // initiate a directed fault ...
+ doFault (FAULT_DF0 + cpu.SDW0.FC, fst_zero, "SDW0.F == 0");
+ }
+ }
+ // load SDWAM .....
+ load_sdwam (cpu.TPR.TSR, nomatch);
+ }
+ DBGAPP ("do_append_cycle(A) R1 %o R2 %o R3 %o E %o\n",
+ cpu.SDW->R1, cpu.SDW->R2, cpu.SDW->R3, cpu.SDW->E);
+
+ // Yes...
+ cpu.RSDWH_R1 = cpu.SDW->R1;
+
+////////////////////////////////////////
+//
+// Sheet 3: "B"
+//
+////////////////////////////////////////
+
+//
+// B: Check the ring
+//
+
+ DBGAPP ("do_append_cycle(B)\n");
+
+ // check ring bracket consistency
+
+ //C(SDW.R1) <= C(SDW.R2) <= C(SDW .R3)?
+ if (! (cpu.SDW->R1 <= cpu.SDW->R2 && cpu.SDW->R2 <= cpu.SDW->R3))
+ {
+ // Set fault ACV0 = IRO
+ cpu.acvFaults |= ACV0;
+ PNL (L68_ (cpu.apu.state |= apu_FLT;))
+ FMSG (acvFaultsMsg = "acvFaults(B) C(SDW.R1) <= C(SDW.R2) <= "
+ "C(SDW .R3)";)
+ }
+
+ // lastCycle == RTCD_OPERAND_FETCH
+ // if a fault happens between the RTCD_OPERAND_FETCH and the INSTRUCTION_FETCH
+ // of the next instruction - this happens about 35 time for just booting and
+ // shutting down multics -- a stored lastCycle is useless.
+ // the opcode is preserved accross faults and only replaced as the
+ // INSTRUCTION_FETCH succeeds.
+ if (thisCycle == INSTRUCTION_FETCH &&
+ i->opcode == 0610 && ! i->opcodeX)
+ goto C;
+ else if (lastCycle == RTCD_OPERAND_FETCH)
+ sim_warn ("%s: lastCycle == RTCD_OPERAND_FETCH opcode %0#o\n", __func__, i->opcode);
+
+ //
+ // B1: The operand is one of: an instruction, data to be read or data to be
+ // written
+ //
+
+ // Is OPCODE call6?
+ if (thisCycle == OPERAND_READ && (i->info->flags & CALL6_INS))
+ goto E;
+
+#if 0
+ // If the instruction is a transfer operand or we are doing an instruction
+ // fetch, the operand is destined to be executed. Verify that the operand
+ // is executable
+
+ // The flowchart trips up on the TSP PRn|foo,* for the INDIRECT_WORD_FETCH.
+ // Also, it transfers to F on RTCD PRn,n and E-OFFs; the operand is not in
+ // an executable segment, and should be treated as READ_OPERAND here.
+
+ bool boolA = (thisCycle == INSTRUCTION_FETCH ||
+ ((i->info->flags & TRANSFER_INS) &&
+ thisCycle != INDIRECT_WORD_FETCH &&
+ thisCycle != RTCD_OPERAND_FETCH));
+ bool boolB = (thisCycle == INSTRUCTION_FETCH ||
+ ((i->info->flags & TRANSFER_INS) &&
+ thisCycle == OPERAND_READ));
+ if (boolA != boolB)
+ sim_warn ("do_append_cycle(B) boolA %d != boolB %d cycle %s insflag %d\n",
+ boolA, boolB, str_pct (thisCycle), i->info->flags & TRANSFER_INS);
+#endif
+
+ // Transfer or instruction fetch?
+ if (thisCycle == INSTRUCTION_FETCH ||
+ (thisCycle == OPERAND_READ && (i->info->flags & TRANSFER_INS)))
+ goto F;
+
+ //
+ // check read bracket for read access
+ //
+
+#ifdef LOCKLESS
+ if (!StrOp || thisCycle == OPERAND_RMW || thisCycle == APU_DATA_RMW)
+#else
+ if (!StrOp)
+#endif
+ {
+ DBGAPP ("do_append_cycle(B):!STR-OP\n");
+
+ // No
+ // C(TPR.TRR) > C(SDW .R2)?
+ if (cpu.TPR.TRR > cpu.SDW->R2)
+ {
+ DBGAPP ("ACV3\n");
+ DBGAPP ("do_append_cycle(B) ACV3\n");
+ //Set fault ACV3 = ORB
+ cpu.acvFaults |= ACV3;
+ PNL (L68_ (cpu.apu.state |= apu_FLT;))
+ FMSG (acvFaultsMsg = "acvFaults(B) C(TPR.TRR) > C(SDW .R2)";)
+ }
+
+ if (cpu.SDW->R == 0)
+ {
+ // isolts 870
+ cpu.TPR.TRR = cpu.PPR.PRR;
+
+ //C(PPR.PSR) = C(TPR.TSR)?
+ if (cpu.PPR.PSR != cpu.TPR.TSR)
+ {
+ DBGAPP ("ACV4\n");
+ DBGAPP ("do_append_cycle(B) ACV4\n");
+ //Set fault ACV4 = R-OFF
+ cpu.acvFaults |= ACV4;
+ PNL (L68_ (cpu.apu.state |= apu_FLT;))
+ FMSG (acvFaultsMsg = "acvFaults(B) C(PPR.PSR) = C(TPR.TSR)";)
+ }
+ else
+ {
+ // sim_warn ("do_append_cycle(B) SDW->R == 0 && cpu.PPR.PSR == cpu.TPR.TSR: %0#o\n", cpu.PPR.PSR);
+ }
+ }
+ }
+
+ //
+ // check write bracket for write access
+ //
+#ifdef LOCKLESS
+ if (StrOp || thisCycle == OPERAND_RMW || thisCycle == APU_DATA_RMW)
+#else
+ if (StrOp)
+#endif
+ {
+ DBGAPP ("do_append_cycle(B):STR-OP\n");
+
+ // isolts 870
+ if (cpu.TPR.TSR == cpu.PPR.PSR)
+ cpu.TPR.TRR = cpu.PPR.PRR;
+
+ // C(TPR.TRR) > C(SDW .R1)? Note typo in AL39, R2 should be R1
+ if (cpu.TPR.TRR > cpu.SDW->R1)
+ {
+ DBGAPP ("ACV5 TRR %o R1 %o\n",
+ cpu.TPR.TRR, cpu.SDW->R1);
+ //Set fault ACV5 = OWB
+ cpu.acvFaults |= ACV5;
+ PNL (L68_ (cpu.apu.state |= apu_FLT;))
+ FMSG (acvFaultsMsg = "acvFaults(B) C(TPR.TRR) > C(SDW .R1)";)
+ }
+
+ if (! cpu.SDW->W)
+ {
+ // isolts 870
+ cpu.TPR.TRR = cpu.PPR.PRR;
+
+ DBGAPP ("ACV6\n");
+ // Set fault ACV6 = W-OFF
+ cpu.acvFaults |= ACV6;
+ PNL (L68_ (cpu.apu.state |= apu_FLT;))
+ FMSG (acvFaultsMsg = "acvFaults(B) ACV6 = W-OFF";)
+ }
+
+ }
+#ifdef LOCKLESS
+ if (StrOp || thisCycle == OPERAND_RMW || thisCycle == APU_DATA_RMW)
+#else
+ if (StrOp)
+#endif
+ {
+ DBGAPP ("do_append_cycle(B):STR-OP\n");
+
+ // C(TPR.TRR) > C(SDW .R1)? Note typo in AL39, R2 should be R1
+ if (cpu.TPR.TRR > cpu.SDW->R1)
+ {
+ DBGAPP ("ACV5 TRR %o R1 %o\n",
+ cpu.TPR.TRR, cpu.SDW->R1);
+ //Set fault ACV5 = OWB
+ cpu.acvFaults |= ACV5;
+ PNL (L68_ (cpu.apu.state |= apu_FLT;))
+ FMSG (acvFaultsMsg = "acvFaults(B) C(TPR.TRR) > C(SDW .R1)";)
+ }
+
+ if (! cpu.SDW->W)
+ {
+ DBGAPP ("ACV6\n");
+ // Set fault ACV6 = W-OFF
+ cpu.acvFaults |= ACV6;
+ PNL (L68_ (cpu.apu.state |= apu_FLT;))
+ FMSG (acvFaultsMsg = "acvFaults(B) ACV6 = W-OFF";)
+ }
+
+ }
+ goto G;
+
+////////////////////////////////////////
+//
+// Sheet 4: "C" "D"
+//
+////////////////////////////////////////
+
+C:;
+ DBGAPP ("do_append_cycle(C)\n");
+
+ //
+ // check ring bracket for instruction fetch after rtcd instruction
+ //
+ // allow outbound transfers (cpu.TPR.TRR >= cpu.PPR.PRR)
+ //
+
+ // C(TPR.TRR) < C(SDW.R1)?
+ // C(TPR.TRR) > C(SDW.R2)?
+ if (cpu.TPR.TRR < cpu.SDW->R1 ||
+ cpu.TPR.TRR > cpu.SDW->R2)
+ {
+ DBGAPP ("ACV1 c\n");
+ DBGAPP ("acvFaults(C) ACV1 ! ( C(SDW .R1) %o <= C(TPR.TRR) %o <= C(SDW .R2) %o )\n",
+ cpu.SDW->R1, cpu.TPR.TRR, cpu.SDW->R2);
+ //Set fault ACV1 = OEB
+ cpu.acvFaults |= ACV1;
+ PNL (L68_ (cpu.apu.state |= apu_FLT;))
+ FMSG (acvFaultsMsg = "acvFaults(C) C(SDW.R1 > C(TPR.TRR) > C(SDW.R2)";)
+ }
+ // SDW.E set ON?
+ if (! cpu.SDW->E)
+ {
+ DBGAPP ("ACV2 a\n");
+ DBGAPP ("do_append_cycle(C) ACV2\n");
+ //Set fault ACV2 = E-OFF
+ cpu.acvFaults |= ACV2;
+ PNL (L68_ (cpu.apu.state |= apu_FLT;))
+ FMSG (acvFaultsMsg = "acvFaults(C) SDW.E";)
+ }
+ if (cpu.TPR.TRR > cpu.PPR.PRR)
+ sim_warn ("rtcd: outbound call cpu.TPR.TRR %d cpu.PPR.PRR %d\n",
+ cpu.TPR.TRR, cpu.PPR.PRR);
+ // C(TPR.TRR) >= C(PPR.PRR)
+ if (cpu.TPR.TRR < cpu.PPR.PRR)
+ {
+ DBGAPP ("ACV11\n");
+ DBGAPP ("do_append_cycle(C) ACV11\n");
+ //Set fault ACV11 = INRET
+ cpu.acvFaults |= ACV11;
+ PNL (L68_ (cpu.apu.state |= apu_FLT;))
+ FMSG (acvFaultsMsg = "acvFaults(C) TRR>=PRR";)
+ }
+
+D:;
+ DBGAPP ("do_append_cycle(D)\n");
+
+ // transfer or instruction fetch
+
+ // check ring alarm to catch outbound transfers
+
+ if (cpu.rRALR == 0)
+ goto G;
+
+ // C(PPR.PRR) < RALR?
+ if (! (cpu.PPR.PRR < cpu.rRALR))
+ {
+ DBGAPP ("ACV13\n");
+ DBGAPP ("acvFaults(D) C(PPR.PRR) %o < RALR %o\n",
+ cpu.PPR.PRR, cpu.rRALR);
+ cpu.acvFaults |= ACV13;
+ PNL (L68_ (cpu.apu.state |= apu_FLT;))
+ FMSG (acvFaultsMsg = "acvFaults(D) C(PPR.PRR) < RALR";)
+ }
+
+ goto G;
+
+////////////////////////////////////////
+//
+// Sheet 5: "E"
+//
+////////////////////////////////////////
+
+E:;
+
+ //
+ // check ring bracket for instruction fetch after call6 instruction
+ // (this is the call6 read operand)
+ //
+
+ DBGAPP ("do_append_cycle(E): CALL6\n");
+ DBGAPP ("do_append_cycle(E): E %o G %o PSR %05o TSR %05o CA %06o "
+ "EB %06o R %o%o%o TRR %o PRR %o\n",
+ cpu.SDW->E, cpu.SDW->G, cpu.PPR.PSR, cpu.TPR.TSR, cpu.TPR.CA,
+ cpu.SDW->EB, cpu.SDW->R1, cpu.SDW->R2, cpu.SDW->R3,
+ cpu.TPR.TRR, cpu.PPR.PRR);
+
+ //SDW.E set ON?
+ if (! cpu.SDW->E)
+ {
+ DBGAPP ("ACV2 b\n");
+ DBGAPP ("do_append_cycle(E) ACV2\n");
+ // Set fault ACV2 = E-OFF
+ cpu.acvFaults |= ACV2;
+ PNL (L68_ (cpu.apu.state |= apu_FLT;))
+ FMSG (acvFaultsMsg = "acvFaults(E) SDW .E set OFF";)
+ }
+
+ //SDW .G set ON?
+ if (cpu.SDW->G)
+ goto E1;
+
+ // C(PPR.PSR) = C(TPR.TSR)?
+ if (cpu.PPR.PSR == cpu.TPR.TSR && ! TST_I_ABS)
+ goto E1;
+
+ // XXX This doesn't seem right
+ // EB is word 15; masking address makes no sense; rather 0-extend EB
+ // Fixes ISOLTS 880-01
+ if (cpu.TPR.CA >= (word18) cpu.SDW->EB)
+ {
+ DBGAPP ("ACV7\n");
+ DBGAPP ("do_append_cycle(E) ACV7\n");
+ // Set fault ACV7 = NO GA
+ cpu.acvFaults |= ACV7;
+ PNL (L68_ (cpu.apu.state |= apu_FLT;))
+ FMSG (acvFaultsMsg = "acvFaults(E) TPR.CA4-17 >= SDW.CL";)
+ }
+
+E1:
+ DBGAPP ("do_append_cycle(E1): CALL6 (cont'd)\n");
+
+ // C(TPR.TRR) > SDW.R3?
+ if (cpu.TPR.TRR > cpu.SDW->R3)
+ {
+ DBGAPP ("ACV8\n");
+ DBGAPP ("do_append_cycle(E) ACV8\n");
+ //Set fault ACV8 = OCB
+ cpu.acvFaults |= ACV8;
+ PNL (L68_ (cpu.apu.state |= apu_FLT;))
+ FMSG (acvFaultsMsg = "acvFaults(E1) C(TPR.TRR) > SDW.R3";)
+ }
+
+ // C(TPR.TRR) < SDW.R1?
+ if (cpu.TPR.TRR < cpu.SDW->R1)
+ {
+ DBGAPP ("ACV9\n");
+ DBGAPP ("do_append_cycle(E) ACV9\n");
+ // Set fault ACV9 = OCALL
+ cpu.acvFaults |= ACV9;
+ PNL (L68_ (cpu.apu.state |= apu_FLT;))
+ FMSG (acvFaultsMsg = "acvFaults(E1) C(TPR.TRR) < SDW.R1";)
+ }
+
+
+ // C(TPR.TRR) > C(PPR.PRR)?
+ if (cpu.TPR.TRR > cpu.PPR.PRR)
+ {
+ // C(PPR.PRR) < SDW.R2?
+ if (cpu.PPR.PRR < cpu.SDW->R2)
+ {
+ DBGAPP ("ACV10\n");
+ DBGAPP ("do_append_cycle(E) ACV10\n");
+ // Set fault ACV10 = BOC
+ cpu.acvFaults |= ACV10;
+ PNL (L68_ (cpu.apu.state |= apu_FLT;))
+ FMSG (acvFaultsMsg = "acvFaults(E1) C(TPR.TRR) > C(PPR.PRR) && "
+ "C(PPR.PRR) < SDW.R2";)
+ }
+ }
+
+
+ DBGAPP ("do_append_cycle(E1): CALL6 TPR.TRR %o SDW->R2 %o\n",
+ cpu.TPR.TRR, cpu.SDW->R2);
+
+ // C(TPR.TRR) > SDW.R2?
+ if (cpu.TPR.TRR > cpu.SDW->R2)
+ {
+ // SDW.R2 -> C(TPR.TRR)
+ cpu.TPR.TRR = cpu.SDW->R2;
+ }
+
+ DBGAPP ("do_append_cycle(E1): CALL6 TPR.TRR %o\n", cpu.TPR.TRR);
+
+ goto G;
+
+////////////////////////////////////////
+//
+// Sheet 6: "F"
+//
+////////////////////////////////////////
+
+F:;
+ PNL (L68_ (cpu.apu.state |= apu_PIAU;))
+ DBGAPP ("do_append_cycle(F): transfer or instruction fetch\n");
+
+ //
+ // check ring bracket for instruction fetch
+ //
+
+ // C(TPR.TRR) < C(SDW .R1)?
+ // C(TPR.TRR) > C(SDW .R2)?
+ if (cpu.TPR.TRR < cpu.SDW->R1 ||
+ cpu.TPR.TRR > cpu.SDW->R2)
+ {
+ DBGAPP ("ACV1 a/b\n");
+ DBGAPP ("acvFaults(F) ACV1 !( C(SDW .R1) %o <= C(TPR.TRR) %o <= C(SDW .R2) %o )\n",
+ cpu.SDW->R1, cpu.TPR.TRR, cpu.SDW->R2);
+ cpu.acvFaults |= ACV1;
+ PNL (L68_ (cpu.apu.state |= apu_FLT;))
+ FMSG (acvFaultsMsg = "acvFaults(F) C(TPR.TRR) < C(SDW .R1)";)
+ }
+ // SDW .E set ON?
+ if (! cpu.SDW->E)
+ {
+ DBGAPP ("ACV2 c \n");
+ DBGAPP ("do_append_cycle(F) ACV2\n");
+ cpu.acvFaults |= ACV2;
+ PNL (L68_ (cpu.apu.state |= apu_FLT;))
+ FMSG (acvFaultsMsg = "acvFaults(F) SDW .E set OFF";)
+ }
+
+ // C(PPR.PRR) = C(TPR.TRR)?
+ if (cpu.PPR.PRR != cpu.TPR.TRR)
+ {
+ DBGAPP ("ACV12\n");
+ DBGAPP ("do_append_cycle(F) ACV12\n");
+ //Set fault ACV12 = CRT
+ cpu.acvFaults |= ACV12;
+ PNL (L68_ (cpu.apu.state |= apu_FLT;))
+ FMSG (acvFaultsMsg = "acvFaults(F) C(PPR.PRR) != C(TPR.TRR)";)
+ }
+
+ goto D;
+
+////////////////////////////////////////
+//
+// Sheet 7: "G"
+//
+////////////////////////////////////////
+
+G:;
+
+ DBGAPP ("do_append_cycle(G)\n");
+
+ //C(TPR.CA)0,13 > SDW.BOUND?
+ if (((cpu.TPR.CA >> 4) & 037777) > cpu.SDW->BOUND)
+ {
+ DBGAPP ("ACV15\n");
+ DBGAPP ("do_append_cycle(G) ACV15\n");
+ cpu.acvFaults |= ACV15;
+ PNL (L68_ (cpu.apu.state |= apu_FLT;))
+ FMSG (acvFaultsMsg = "acvFaults(G) C(TPR.CA)0,13 > SDW.BOUND";)
+ DBGAPP ("acvFaults(G) C(TPR.CA)0,13 > SDW.BOUND\n"
+ " CA %06o CA>>4 & 037777 %06o SDW->BOUND %06o",
+ cpu.TPR.CA, ((cpu.TPR.CA >> 4) & 037777), cpu.SDW->BOUND);
+ }
+
+ if (cpu.acvFaults)
+ {
+ DBGAPP ("do_append_cycle(G) acvFaults\n");
+ PNL (L68_ (cpu.apu.state |= apu_FLT;))
+ // Initiate an access violation fault
+ doFault (FAULT_ACV, (_fault_subtype) {.fault_acv_subtype=cpu.acvFaults},
+ "ACV fault");
+ }
+
+ // is segment C(TPR.TSR) paged?
+ if (cpu.SDW->U)
+ goto H; // Not paged
+
+ // Yes. segment is paged ...
+ // is PTW for C(TPR.CA) in PTWAM?
+
+ DBGAPP ("do_append_cycle(G) CA %06o\n", cpu.TPR.CA);
+#ifdef WAM
+ if (nomatch ||
+ ! fetch_ptw_from_ptwam (cpu.SDW->POINTER, cpu.TPR.CA)) //TPR.CA))
+#endif
+ {
+ fetch_ptw (cpu.SDW, cpu.TPR.CA);
+ if (! cpu.PTW0.DF)
+ {
+ if (thisCycle != ABSA_CYCLE)
+ {
+ // initiate a directed fault
+ doFault (FAULT_DF0 + cpu.PTW0.FC, (_fault_subtype) {.bits=0},
+ "PTW0.F == 0");
+ }
+ }
+ loadPTWAM (cpu.SDW->POINTER, cpu.TPR.CA, nomatch); // load PTW0 to PTWAM
+ }
+
+ // Prepage mode?
+ // check for "uninterruptible" EIS instruction
+ // ISOLTS-878 02: mvn,cmpn,mvne,ad3d; obviously also
+ // ad2/3d,sb2/3d,mp2/3d,dv2/3d
+ // DH03 p.8-13: probably also mve,btd,dtb
+ if (i->opcodeX && ((i->opcode & 0770)== 0200|| (i->opcode & 0770) == 0220
+ || (i->opcode & 0770)== 020|| (i->opcode & 0770) == 0300))
+ {
+ do_ptw2 (cpu.SDW, cpu.TPR.CA);
+ }
+ goto I;
+
+////////////////////////////////////////
+//
+// Sheet 8: "H", "I"
+//
+////////////////////////////////////////
+
+H:;
+ DBGAPP ("do_append_cycle(H): FANP\n");
+
+ PNL (L68_ (cpu.apu.state |= apu_FANP;))
+#if 0
+ // ISOLTS pa865 test-01a 101232
+ if (get_bar_mode ())
+ {
+ set_apu_status (apuStatus_FABS);
+ }
+ else
+ ....
+#endif
+ set_apu_status (apuStatus_FANP);
+
+ DBGAPP ("do_append_cycle(H): SDW->ADDR=%08o CA=%06o \n",
+ cpu.SDW->ADDR, cpu.TPR.CA);
+
+ if (thisCycle == RTCD_OPERAND_FETCH &&
+ get_addr_mode () == ABSOLUTE_mode &&
+ ! (cpu.cu.XSF || cpu.currentInstruction.b29) /*get_went_appending ()*/)
+ {
+ finalAddress = cpu.TPR.CA;
+ }
+ else
+ {
+ finalAddress = (cpu.SDW->ADDR & 077777760) + cpu.TPR.CA;
+ finalAddress &= 0xffffff;
+ }
+ PNL (cpu.APUMemAddr = finalAddress;)
+
+ DBGAPP ("do_append_cycle(H:FANP): (%05o:%06o) finalAddress=%08o\n",
+ cpu.TPR.TSR, cpu.TPR.CA, finalAddress);
+
+ //if (thisCycle == ABSA_CYCLE)
+ // goto J;
+ goto HI;
+
+I:;
+
+// Set PTW.M
+
+ DBGAPP ("do_append_cycle(I): FAP\n");
+#ifdef LOCKLESS
+ if ((StrOp ||
+ thisCycle == OPERAND_RMW ||
+ thisCycle == APU_DATA_RMW) && cpu.PTW->M == 0) // is this the right way to do this?
+#else
+ if (StrOp && cpu.PTW->M == 0) // is this the right way to do this?
+#endif
+ {
+ modify_ptw (cpu.SDW, cpu.TPR.CA);
+ }
+
+ // final address paged
+ set_apu_status (apuStatus_FAP);
+ PNL (L68_ (cpu.apu.state |= apu_FAP;))
+
+ word24 y2 = cpu.TPR.CA % 1024;
+
+ // AL39: The hardware ignores low order bits of the main memory page
+ // address according to page size
+ finalAddress = (((word24)cpu.PTW->ADDR & 0777760) << 6) + y2;
+ finalAddress &= 0xffffff;
+ PNL (cpu.APUMemAddr = finalAddress;)
+
+#ifdef L68
+ if (cpu.MR_cache.emr && cpu.MR_cache.ihr)
+ add_APU_history (APUH_FAP);
+#endif
+ DBGAPP ("do_append_cycle(H:FAP): (%05o:%06o) finalAddress=%08o\n",
+ cpu.TPR.TSR, cpu.TPR.CA, finalAddress);
+
+ //if (thisCycle == ABSA_CYCLE)
+ // goto J;
+ goto HI;
+
+HI:
+ DBGAPP ("do_append_cycle(HI)\n");
+
+ // isolts 870
+ if (thisCycle != ABSA_CYCLE)
+ {
+ cpu.cu.XSF = 1;
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "loading of cpu.TPR.TSR sets XSF to 1\n");
+ }
+
+ if (thisCycle == OPERAND_STORE && cpu.useZone)
+ {
+ core_write_zone (finalAddress, * data, str_pct (thisCycle));
+ }
+ else if (StrOp)
+ {
+ core_writeN (finalAddress, data, nWords, str_pct (thisCycle));
+ }
+ else
+ {
+#ifdef LOCKLESS
+ if ((thisCycle == OPERAND_RMW || thisCycle == APU_DATA_RMW) && nWords == 1)
+ {
+ core_read_lock (finalAddress, data, str_pct (thisCycle));
+ }
+ else
+ {
+ if (thisCycle == OPERAND_RMW || thisCycle == APU_DATA_RMW)
+ sim_warn("do_append_cycle: RMW nWords %d !=1\n", nWords);
+ core_readN (finalAddress, data, nWords, str_pct (thisCycle));
+ }
+#else
+ core_readN (finalAddress, data, nWords, str_pct (thisCycle));
+#endif
+ }
+
+ // Was this an indirect word fetch?
+ if (thisCycle == INDIRECT_WORD_FETCH)
+ goto J;
+
+ // Was this an rtcd operand fetch?
+ if (thisCycle == RTCD_OPERAND_FETCH)
+ goto K;
+
+ // is OPCODE call6?
+ if (thisCycle == OPERAND_READ && (i->info->flags & CALL6_INS))
+ goto N;
+
+ // Transfer or instruction fetch?
+ if (thisCycle == INSTRUCTION_FETCH ||
+ (thisCycle == OPERAND_READ && (i->info->flags & TRANSFER_INS)))
+ goto L;
+
+ // APU data movement?
+ // handled above
+ goto Exit;
+
+
+////////////////////////////////////////
+//
+// Sheet 9: "J"
+//
+////////////////////////////////////////
+
+// Indirect operand fetch
+
+J:;
+ DBGAPP ("do_append_cycle(J)\n");
+
+ // ri or ir & TPC.CA even?
+ word6 tag = GET_TAG (IWB_IRODD);
+ if ((GET_TM (tag) == TM_IR || GET_TM (tag) == TM_RI) &&
+ (cpu.TPR.CA & 1) == 0)
+ {
+ if (ISITS (* data))
+ goto O;
+ if (ISITP (* data))
+ goto P;
+ }
+
+ // C(Y) tag == other indirect?
+ // TM_R never indirects
+ // TM_RI always indirects
+ // TM_IR always indirects
+ // TM_IT always indirects
+#if 0
+ // IT_CI, IT_SC, IT_SCR -- address is used for tally word
+ // IT_I indirects
+ // IT_AD -- address is used for tally word
+ // IT_SD -- address is used for tally word
+ // IT_DI -- address is used for tally word
+ // IT_ID -- address is used for tally word
+ // IT_DIC -- address is used for tally word
+ // IT_IDC -- address is used for tally word
+ static const bool isInd[64] =
+ {
+ // 00-15 R_MOD
+ false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false,
+ // 16-31 RI_MOD
+ true, true, true, true, true, true, true, true,
+ true, true, true, true, true, true, true, true,
+ // 32-47 IT_MOD
+ // f1 und und und sd scr f2 f3
+ false, false, false, false, true, true, false, false,
+ // ci i sc ad di dic id idc
+ true, true, true, true, true, true, true, true,
+ // 48-63 IR_MOD
+ true, true, true, true, true, true, true, true,
+ true, true, true, true, true, true, true, true
+ };
+ if (isInd[(* data) & MASK6])
+#else
+ if ((* data) & 060)
+#endif
+ {
+ // C(Y)0,17 -> C(IWB)0,17
+ // C(Y)30,35 -> C(IWB)30,35
+ // 0 -> C(IWB)29
+ updateIWB (GET_ADDR (* data), (* data) & MASK6);
+ //cpu.cu.TSN_PRNO[0] = n;
+ //cpu.cu.TSN_VALID[0] = 1;
+
+ }
+
+ goto Exit;
+
+////////////////////////////////////////
+//
+// Sheet 10: "K", "L", "M", "N"
+//
+////////////////////////////////////////
+
+K:; // RTCD operand fetch
+ DBGAPP ("do_append_cycle(K)\n");
+
+ word3 y = GET_ITS_RN (data);
+
+ // C(Y-pair)3,17 -> C(PPR.PSR)
+ // We set TSR here; TSR will be copied to PSR at KL
+ cpu.TPR.TSR = GET_ITS_SEGNO (data);
+
+ // Maximum of
+ // C(Y-pair)18,20; C(TPR.TRR); C(SDW.R1) -> C(PPR.PRR)
+ // We set TRR here as well
+ cpu.PPR.PRR = cpu.TPR.TRR = max3 (y, cpu.TPR.TRR, cpu.RSDWH_R1);
+
+ // C(Y-pair)36,53 -> C(PPR.IC)
+ // We set CA here; copied to IC at KL
+ cpu.TPR.CA = GET_ITS_WORDNO (data);
+
+ // If C(PPR.PRR) = 0 then C(SDW.P) -> C(PPR.P);
+ // otherwise 0 -> C(PPR.P)
+ // Done at M
+
+ goto KL;
+
+L:; // Transfer or instruction fetch
+
+ DBGAPP ("do_append_cycle(L)\n");
+
+ // Is OPCODE tspn?
+ if (thisCycle == OPERAND_READ && (i->info->flags & TSPN_INS))
+ {
+ //word3 n;
+ if (i->opcode <= 0273)
+ n = (i->opcode & 3);
+ else
+ n = (i->opcode & 3) + 4;
+
+ // C(PPR.PRR) -> C(PRn .RNR)
+ // C(PPR.PSR) -> C(PRn .SNR)
+ // C(PPR.IC) -> C(PRn .WORDNO)
+ // 000000 -> C(PRn .BITNO)
+ cpu.PR[n].RNR = cpu.PPR.PRR;
+// According the AL39, the PSR is 'undefined' in absolute mode.
+// ISOLTS thinks means don't change the operand
+ if (get_addr_mode () == APPEND_mode)
+ cpu.PR[n].SNR = cpu.PPR.PSR;
+ cpu.PR[n].WORDNO = (cpu.PPR.IC + 1) & MASK18;
+ SET_PR_BITNO (n, 0);
+ HDBGRegPR (n);
+ }
+
+ // lastCycle == RTCD_OPERAND_FETCH
+
+ if (thisCycle == INSTRUCTION_FETCH &&
+ i->opcode == 0610 && ! i->opcodeX)
+ {
+ // C(PPR.PRR) -> C(PRn.RNR) for n = (0, 1, ..., 7)
+ // Use TRR here; PRR not set until KL
+ CPTUR (cptUsePRn + 0);
+ CPTUR (cptUsePRn + 1);
+ CPTUR (cptUsePRn + 2);
+ CPTUR (cptUsePRn + 3);
+ CPTUR (cptUsePRn + 4);
+ CPTUR (cptUsePRn + 5);
+ CPTUR (cptUsePRn + 6);
+ CPTUR (cptUsePRn + 7);
+ cpu.PR[0].RNR =
+ cpu.PR[1].RNR =
+ cpu.PR[2].RNR =
+ cpu.PR[3].RNR =
+ cpu.PR[4].RNR =
+ cpu.PR[5].RNR =
+ cpu.PR[6].RNR =
+ cpu.PR[7].RNR = cpu.TPR.TRR;
+ HDBGRegPR (0);
+ HDBGRegPR (1);
+ HDBGRegPR (2);
+ HDBGRegPR (3);
+ HDBGRegPR (4);
+ HDBGRegPR (5);
+ HDBGRegPR (6);
+ HDBGRegPR (7);
+ }
+ goto KL;
+
+KL:
+ DBGAPP ("do_append_cycle(KL)\n");
+
+ // C(TPR.TSR) -> C(PPR.PSR)
+ cpu.PPR.PSR = cpu.TPR.TSR;
+ // C(TPR.CA) -> C(PPR.IC)
+ cpu.PPR.IC = cpu.TPR.CA;
+
+ goto M;
+
+M: // Set P
+ DBGAPP ("do_append_cycle(M)\n");
+
+ // C(TPR.TRR) = 0?
+ if (cpu.TPR.TRR == 0)
+ {
+ // C(SDW.P) -> C(PPR.P)
+ cpu.PPR.P = cpu.SDW->P;
+ }
+ else
+ {
+ // 0 C(PPR.P)
+ cpu.PPR.P = 0;
+ }
+
+ goto Exit;
+
+N: // CALL6
+ DBGAPP ("do_append_cycle(N)\n");
+
+ // C(TPR.TRR) = C(PPR.PRR)?
+ if (cpu.TPR.TRR == cpu.PPR.PRR)
+ {
+ // C(PR6.SNR) -> C(PR7.SNR)
+ cpu.PR[7].SNR = cpu.PR[6].SNR;
+ DBGAPP ("do_append_cycle(N) PR7.SNR = PR6.SNR %05o\n", cpu.PR[7].SNR);
+ }
+ else
+ {
+ // C(DSBR.STACK) || C(TPR.TRR) -> C(PR7.SNR)
+ cpu.PR[7].SNR = ((word15) (cpu.DSBR.STACK << 3)) | cpu.TPR.TRR;
+ DBGAPP ("do_append_cycle(N) STACK %05o TRR %o\n",
+ cpu.DSBR.STACK, cpu.TPR.TRR);
+ DBGAPP ("do_append_cycle(N) PR7.SNR = STACK||TRR %05o\n", cpu.PR[7].SNR);
+ }
+
+ // C(TPR.TRR) -> C(PR7.RNR)
+ cpu.PR[7].RNR = cpu.TPR.TRR;
+ // 00...0 -> C(PR7.WORDNO)
+ cpu.PR[7].WORDNO = 0;
+ // 000000 -> C(PR7.BITNO)
+ SET_PR_BITNO (7, 0);
+ HDBGRegPR (7);
+ // C(TPR.TRR) -> C(PPR.PRR)
+ cpu.PPR.PRR = cpu.TPR.TRR;
+ // C(TPR.TSR) -> C(PPR.PSR)
+ cpu.PPR.PSR = cpu.TPR.TSR;
+ // C(TPR.CA) -> C(PPR.IC)
+ cpu.PPR.IC = cpu.TPR.CA;
+
+ goto M;
+
+////////////////////////////////////////
+//
+// Sheet 11: "O", "P"
+//
+////////////////////////////////////////
+
+O:; // ITS, RTCD
+ DBGAPP ("do_append_cycle(O)\n");
+ word3 its_RNR = GET_ITS_RN (data);
+ DBGAPP ("do_append_cycle(O) TRR %o RSDWH.R1 %o ITS.RNR %o\n",
+ cpu.TPR.TRR, cpu.RSDWH_R1, its_RNR);
+
+ // Maximum of
+ // C(Y)18,20; C(TPR.TRR); C(SDW.R1) -> C(TPR.TRR)
+ cpu.TPR.TRR = max3 (its_RNR, cpu.TPR.TRR, cpu.RSDWH_R1);
+ DBGAPP ("do_append_cycle(O) Set TRR to %o\n", cpu.TPR.TRR);
+
+ goto Exit;
+
+P:; // ITP
+
+ DBGAPP ("do_append_cycle(P)\n");
+
+ n = GET_ITP_PRNUM (data);
+ DBGAPP ("do_append_cycle(P) TRR %o RSDWH.R1 %o PR[n].RNR %o\n",
+ cpu.TPR.TRR, cpu.RSDWH_R1, cpu.PR[n].RNR);
+
+ // Maximum of
+ // cpu.PR[n].RNR; C(TPR.TRR); C(SDW.R1) -> C(TPR.TRR)
+ cpu.TPR.TRR = max3 (cpu.PR[n].RNR, cpu.TPR.TRR, cpu.RSDWH_R1);
+ DBGAPP ("do_append_cycle(P) Set TRR to %o\n", cpu.TPR.TRR);
+
+ goto Exit;
+
+
+Exit:;
+
+ PNL (cpu.APUDataBusOffset = cpu.TPR.CA;)
+ PNL (cpu.APUDataBusAddr = finalAddress;)
+
+ PNL (L68_ (cpu.apu.state |= apu_FA;))
+
+ DBGAPP ("do_append_cycle (Exit) PRR %o PSR %05o P %o IC %06o\n",
+ cpu.PPR.PRR, cpu.PPR.PSR, cpu.PPR.P, cpu.PPR.IC);
+ DBGAPP ("do_append_cycle (Exit) TRR %o TSR %05o TBR %02o CA %06o\n",
+ cpu.TPR.TRR, cpu.TPR.TSR, cpu.TPR.TBR, cpu.TPR.CA);
+
+ return finalAddress; // or 0 or -1???
+ }
+
+// Translate a segno:offset to a absolute address.
+// Return 0 if successful.
+
+int dbgLookupAddress (word18 segno, word18 offset, word24 * finalAddress,
+ char * * msg)
+ {
+ // Local copies so we don't disturb machine state
+
+ ptw_s PTW1;
+ sdw_s SDW1;
+
+ if (2u * segno >= 16u * (cpu.DSBR.BND + 1u))
+ {
+ if (msg)
+ * msg = "DSBR boundary violation.";
+ return 1;
+ }
+
+ if (cpu.DSBR.U == 0)
+ {
+ // fetch_dsptw
+
+ word24 y1 = (2 * segno) % 1024;
+ word24 x1 = (2 * segno) / 1024; // floor
+
+ word36 PTWx1;
+ core_read ((cpu.DSBR.ADDR + x1) & PAMASK, & PTWx1, __func__);
+
+ PTW1.ADDR = GETHI (PTWx1);
+ PTW1.U = TSTBIT (PTWx1, 9);
+ PTW1.M = TSTBIT (PTWx1, 6);
+ PTW1.DF = TSTBIT (PTWx1, 2);
+ PTW1.FC = PTWx1 & 3;
+
+ if (! PTW1.DF)
+ {
+ if (msg)
+ * msg = "!PTW0.F";
+ return 2;
+ }
+
+ // fetch_psdw
+
+ y1 = (2 * segno) % 1024;
+
+ word36 SDWeven, SDWodd;
+
+ core_read2 (((((word24)PTW1. ADDR & 0777760) << 6) + y1) & PAMASK,
+ & SDWeven, & SDWodd, __func__);
+
+ // even word
+ SDW1.ADDR = (SDWeven >> 12) & 077777777;
+ SDW1.R1 = (SDWeven >> 9) & 7;
+ SDW1.R2 = (SDWeven >> 6) & 7;
+ SDW1.R3 = (SDWeven >> 3) & 7;
+ SDW1.DF = TSTBIT (SDWeven, 2);
+ SDW1.FC = SDWeven & 3;
+
+ // odd word
+ SDW1.BOUND = (SDWodd >> 21) & 037777;
+ SDW1.R = TSTBIT (SDWodd, 20);
+ SDW1.E = TSTBIT (SDWodd, 19);
+ SDW1.W = TSTBIT (SDWodd, 18);
+ SDW1.P = TSTBIT (SDWodd, 17);
+ SDW1.U = TSTBIT (SDWodd, 16);
+ SDW1.G = TSTBIT (SDWodd, 15);
+ SDW1.C = TSTBIT (SDWodd, 14);
+ SDW1.EB = SDWodd & 037777;
+ }
+ else // ! DSBR.U
+ {
+ // fetch_nsdw
+
+ word36 SDWeven, SDWodd;
+
+ core_read2 ((cpu.DSBR.ADDR + 2 * segno) & PAMASK,
+ & SDWeven, & SDWodd, __func__);
+
+ // even word
+ SDW1.ADDR = (SDWeven >> 12) & 077777777;
+ SDW1.R1 = (SDWeven >> 9) & 7;
+ SDW1.R2 = (SDWeven >> 6) & 7;
+ SDW1.R3 = (SDWeven >> 3) & 7;
+ SDW1.DF = TSTBIT (SDWeven, 2);
+ SDW1.FC = SDWeven & 3;
+
+ // odd word
+ SDW1.BOUND = (SDWodd >> 21) & 037777;
+ SDW1.R = TSTBIT (SDWodd, 20);
+ SDW1.E = TSTBIT (SDWodd, 19);
+ SDW1.W = TSTBIT (SDWodd, 18);
+ SDW1.P = TSTBIT (SDWodd, 17);
+ SDW1.U = TSTBIT (SDWodd, 16);
+ SDW1.G = TSTBIT (SDWodd, 15);
+ SDW1.C = TSTBIT (SDWodd, 14);
+ SDW1.EB = SDWodd & 037777;
+
+ }
+
+ if (SDW1.DF == 0)
+ {
+ if (msg)
+ * msg = "!SDW0.F != 0";
+ return 3;
+ }
+
+ if (((offset >> 4) & 037777) > SDW1.BOUND)
+ {
+ if (msg)
+ * msg = "C(TPR.CA)0,13 > SDW.BOUND";
+ return 4;
+ }
+
+ // is segment C(TPR.TSR) paged?
+ if (SDW1.U)
+ {
+ * finalAddress = (SDW1.ADDR + offset) & PAMASK;
+ }
+ else
+ {
+ // fetch_ptw
+ word24 y2 = offset % 1024;
+ word24 x2 = (offset) / 1024; // floor
+
+ word36 PTWx2;
+
+ core_read ((SDW1.ADDR + x2) & PAMASK, & PTWx2, __func__);
+
+ PTW1.ADDR = GETHI (PTWx2);
+ PTW1.U = TSTBIT (PTWx2, 9);
+ PTW1.M = TSTBIT (PTWx2, 6);
+ PTW1.DF = TSTBIT (PTWx2, 2);
+ PTW1.FC = PTWx2 & 3;
+
+ if (! PTW1.DF)
+ {
+ if (msg)
+ * msg = "!PTW0.F";
+ return 5;
+ }
+
+ y2 = offset % 1024;
+
+ * finalAddress = ((((word24)PTW1.ADDR & 0777760) << 6) + y2) & PAMASK;
+ }
+ if (msg)
+ * msg = "";
+ return 0;
+ }
+
+
+
--- /dev/null
+/*
+ Copyright 2013-2017 by Charles Anthony
+
+ All rights reserved.
+
+ This software is made available under the terms of the
+ ICU License -- ICU 1.8.1 and later.
+ See the LICENSE file at the top-level directory of this distribution and
+ at https://sourceforge.net/p/dps8m/code/ci/master/tree/LICENSE
+ */
+
+// Appending unit stuff .......
+
+// Once segno and offset are formed in TPR.SNR and TPR.CA, respectively, the
+// process of generating the 24-bit absolute main memory address can involve a
+// number of different and distinct appending unit cycles.
+//
+// The operation of the appending unit is shown in the flowchart in Figure 5-4.
+// This flowchart assumes that directed faults, store faults, and parity faults
+// do not occur.
+//
+// A segment boundary check is made in every cycle except PSDW. If a boundary
+// violation is detected, an access violation, out of segment bounds, fault is
+// generated and the execution of the instruction interrupted. The occurrence
+// of any fault interrupts the sequence at the point of occurrence. The
+// operating system software should store the control unit data for possible
+// later continuation and attempt to resolve the fault condition.
+//
+// The value of the associative memories may be seen in the flowchart by
+// observing the number of appending unit cycles bypassed if an SDW or PTW is
+// found in the associative memories.
+//
+// There are nine different appending unit cycles that involve accesses to main
+// memory. Two of these (FANP, FAP) generate the 24-bit absolute main memory
+// address and initiate a main memory access for the operand, indirect word, or
+// instruction pair; five (NSDW, PSDW, PTW, PTW2, and DSPTW) generate a main
+// memory access to fetch an SDW or PTW; and two (MDSPTW and MPTW) generate a
+// main memory access to update page status bits (PTW.U and PTW.M) in a PTW.
+// The cycles are defined in Table 5-1.
+
+// enum _appendingUnit_cycle_type {
+// apuCycle_APPUNKNOWN = 0, // unknown
+//
+// apuCycle_FIAP, // Fetch instruction
+//
+// apuCycle_FANP, // Final address nonpaged.
+// // Generates the 24-bit absolute main memory address and
+// // initiates a main memory access to an unpaged segment for
+// // operands, indirect words, or instructions.
+//
+// apuCycle_FAP, // Final address paged
+// // Generates the 24-bit absolute main memory address and
+// // initiates a main memory access to a paged segment for
+// // operands, indirect words, or instructions.
+//
+// apuCycle_NSDW, // Nonpaged SDW Fetch
+// // Fetches an SDW from an unpaged descriptor segment.
+//
+// apuCycle_PSDW, // Paged SDW Fetch
+// // Fetches an SDW from a paged descriptor segment.
+//
+// apuCycle_PTWfetch, // PTW fetch
+// // Fetches a PTW from a page table other than a descriptor
+// // segment page table and sets the page accessed bit (PTW.U).
+//
+// apuCycle_PTW2, // Prepage PTW fetch
+// // Fetches the next PTW from a page table other than a
+// // descriptor segment page table during hardware prepaging for
+// // certain uninterruptible EIS instructions. This cycle does
+// // not load the next PTW into the appending unit. It merely
+// // assures that the PTW is not faulted (PTW.F = 1) and that
+// // the target page will be in main memory when and if needed
+// // by the instruction.
+//
+// apuCycle_DSPTW, // Descriptor segment PTW fetch
+// // Fetches a PTW from a descriptor segment page table.
+//
+// apuCycle_MDSPTW, // Modify DSPTW
+// // Sets the page accessed bit (PTW.U) in the PTW for a page
+// // in a descriptor segment page table. This cycle always
+// // immediately follows a DSPTW cycle.
+//
+// apuCycle_MPTW // Modify PTW
+// // Sets the page modified bit (PTW.M) in the PTW for a page
+// // in other than a descriptor segment page table.
+// };
+
+// These bits are aligned to match the CU word 0 APU status bit positions.
+// This produces some oddness in the scu save/restore code.
+
+typedef enum apuStatusBits
+ {
+ apuStatus_PI_AP = 1u << (35 - 24), // -AP Instruction fetch append cycle
+ apuStatus_DSPTW = 1u << (35 - 25), // Fetch descriptor segment PTW
+ apuStatus_SDWNP = 1u << (35 - 26), // Fetch SDW non paged
+ apuStatus_SDWP = 1u << (35 - 27), // Fetch SDW paged
+ apuStatus_PTW = 1u << (35 - 28), // Fetch PTW
+ apuStatus_PTW2 = 1u << (35 - 29), // Fetch prepage PTW
+ apuStatus_FAP = 1u << (35 - 30), // Fetch final address - paged
+ apuStatus_FANP = 1u << (35 - 31), // Fetch final address - nonpaged
+ apuStatus_FABS = 1u << (35 - 32), // Fetch final address - absolute
+
+ // XXX these don't seem like the right solution.
+ // XXX there are MDSPTW and MPTW bits in the APU history
+ // register, but not in the CU.
+
+ apuStatus_MDSPTW = 1u << (35 - 25), // Fetch descriptor segment PTW
+ apuStatus_MPTW = 1u << (35 - 28) // Fetch PTW
+ } apuStatusBits;
+
+static inline void set_apu_status (apuStatusBits status)
+ {
+ word12 FCT = cpu.cu.APUCycleBits & MASK3;
+ cpu.cu.APUCycleBits = (status & 07770) | FCT;
+ }
+
+t_stat dump_sdwam (void);
+word24 do_append_cycle (processor_cycle_type thisCycle,
+ word36 * data, uint nWords);
+void do_ldbr (word36 * Ypair);
+void do_sdbr (word36 * Ypair);
+void do_camp (word36 Y);
+void do_cams (word36 Y);
+int dbgLookupAddress (word18 segno, word18 offset, word24 * finalAddress,
+ char * * msg);
+sdw0_s * getSDW (word15 segno);
+
+static inline void fauxDoAppendCycle (processor_cycle_type thisCycle)
+ {
+ cpu.apu.lastCycle = thisCycle;
+ }
+
--- /dev/null
+/*
+ Copyright (c) 2007-2013 Michael Mondy
+ Copyright 2012-2016 by Harry Reed
+ Copyright 2013-2018 by Charles Anthony
+ Copyright 2017 by Michal Tomek
+
+ All rights reserved.
+
+ This software is made available under the terms of the
+ ICU License -- ICU 1.8.1 and later.
+ See the LICENSE file at the top-level directory of this distribution and
+ at https://sourceforge.net/p/dps8m/code/ci/master/tree/LICENSE
+ */
+
+ /** * \file dps8_cpu.c
+ * \project dps8
+ * \date 9/17/12
+ * \copyright Copyright (c) 2012 Harry Reed. All rights reserved.
+*/
+
+#include <stdio.h>
+#include <unistd.h>
+
+#include "dps8.h"
+#include "dps8_addrmods.h"
+#include "dps8_sys.h"
+#include "dps8_faults.h"
+//#include "dps8_scu.h"
+//#include "dps8_iom.h"
+//#include "dps8_cable.h"
+#include "dps8_cpu.h"
+#include "dps8_append.h"
+#include "dps8_ins.h"
+//#include "dps8_state.h"
+//#include "dps8_loader.h"
+#include "dps8_math.h"
+#include "dps8_iefp.h"
+//#include "dps8_console.h"
+//#include "dps8_fnp2.h"
+//#include "dps8_socket_dev.h"
+//#include "dps8_crdrdr.h"
+//#include "dps8_absi.h"
+#include "dps8_utils.h"
+#ifdef M_SHARED
+//#include "shm.h"
+#endif
+#include "dps8_opcodetable.h"
+#include "sim_defs.h"
+#if defined(THREADZ) || defined(LOCKLESS)
+//#include "threadz.h"
+
+__thread uint current_running_cpu_idx;
+#endif
+
+#define DBG_CTR cpu.cycleCnt
+
+// XXX Use this when we assume there is only a single cpu unit
+#define ASSUME0 0
+
+// CPU data structures
+
+static UNIT cpu_unit [N_CPU_UNITS_MAX] =
+ {
+ [0 ... N_CPU_UNITS_MAX-1] =
+ {
+ UDATA (NULL, UNIT_FIX|UNIT_BINK, MEMSIZE),
+ 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL
+ }
+ };
+
+#define UNIT_IDX(uptr) ((uptr) - cpu_unit)
+
+// Assume CPU clock ~ 1MIPS. lockup time is 32 ms
+#define LOCKUP_KIPS 1000
+static uint kips = LOCKUP_KIPS;
+static uint64 luf_limits[] =
+ {
+ 2000*LOCKUP_KIPS/1000,
+ 4000*LOCKUP_KIPS/1000,
+ 8000*LOCKUP_KIPS/1000,
+ 16000*LOCKUP_KIPS/1000,
+ 32000*LOCKUP_KIPS/1000
+ };
+
+struct stall_point_s stall_points [N_STALL_POINTS];
+bool stall_point_active = false;
+
+#ifdef PANEL
+static void panel_process_event (void);
+#endif
+
+static t_stat cpu_show_config (UNUSED FILE * st, UNIT * uptr,
+ UNUSED int val, UNUSED const void * desc)
+ {
+ long cpu_unit_idx = UNIT_IDX (uptr);
+ if (cpu_unit_idx < 0 || cpu_unit_idx >= N_CPU_UNITS_MAX)
+ {
+ sim_warn ("error: invalid unit number %ld\n", cpu_unit_idx);
+ return SCPE_ARG;
+ }
+
+ sim_msg ("CPU unit number %ld\n", cpu_unit_idx);
+
+ sim_msg ("Fault base: %03o(8)\n",
+ cpus[cpu_unit_idx].switches.FLT_BASE);
+ sim_msg ("CPU number: %01o(8)\n",
+ cpus[cpu_unit_idx].switches.cpu_num);
+ sim_msg ("Data switches: %012"PRIo64"(8)\n",
+ cpus[cpu_unit_idx].switches.data_switches);
+ sim_msg ("Address switches: %06o(8)\n",
+ cpus[cpu_unit_idx].switches.addr_switches);
+ for (int i = 0; i < N_CPU_PORTS; i ++)
+ {
+ sim_msg ("Port%c enable: %01o(8)\n",
+ 'A' + i, cpus[cpu_unit_idx].switches.enable [i]);
+ sim_msg ("Port%c init enable: %01o(8)\n",
+ 'A' + i, cpus[cpu_unit_idx].switches.init_enable [i]);
+ sim_msg ("Port%c assignment: %01o(8)\n",
+ 'A' + i, cpus[cpu_unit_idx].switches.assignment [i]);
+ sim_msg ("Port%c interlace: %01o(8)\n",
+ 'A' + i, cpus[cpu_unit_idx].switches.assignment [i]);
+ sim_msg ("Port%c store size: %01o(8)\n",
+ 'A' + i, cpus[cpu_unit_idx].switches.store_size [i]);
+ }
+ sim_msg ("Processor mode: %s [%o]\n",
+ cpus[cpu_unit_idx].switches.proc_mode ? "Multics" : "GCOS",
+ cpus[cpu_unit_idx].switches.proc_mode);
+ sim_msg ("Processor speed: %02o(8)\n",
+ cpus[cpu_unit_idx].switches.proc_speed);
+ sim_msg ("DIS enable: %01o(8)\n",
+ cpus[cpu_unit_idx].switches.dis_enable);
+// sim_msg ("Steady clock: %01o(8)\n",
+// scu [0].steady_clock);
+ sim_msg ("Halt on unimplemented: %01o(8)\n",
+ cpus[cpu_unit_idx].switches.halt_on_unimp);
+ sim_msg ("Disable SDWAM/PTWAM: %01o(8)\n",
+ cpus[cpu_unit_idx].switches.disable_wam);
+ sim_msg ("Report faults: %01o(8)\n",
+ cpus[cpu_unit_idx].switches.report_faults);
+ sim_msg ("TRO faults enabled: %01o(8)\n",
+ cpus[cpu_unit_idx].switches.tro_enable);
+// sim_msg ("Y2K enabled: %01o(8)\n",
+// scu [0].y2k);
+ sim_msg ("drl fatal enabled: %01o(8)\n",
+ cpus[cpu_unit_idx].switches.drl_fatal);
+ sim_msg ("useMap: %d\n",
+ cpus[cpu_unit_idx].switches.useMap);
+ sim_msg ("Disable cache: %01o(8)\n",
+ cpus[cpu_unit_idx].switches.disable_cache);
+
+#ifdef AFFINITY
+ if (cpus[cpu_unit_idx].set_affinity)
+ sim_msg ("CPU affinity %d\n", cpus[cpu_unit_idx].affinity);
+ else
+ sim_msg ("CPU affinity not set\n");
+#endif
+
+ return SCPE_OK;
+ }
+
+//
+// set cpu0 config=<blah> [;<blah>]
+//
+// blah =
+// faultbase = n
+// num = n
+// data = n
+// portenable = n
+// portconfig = n
+// portinterlace = n
+// mode = n
+// speed = n
+// Hacks:
+// dis_enable = n
+// steadyclock = on|off
+// halt_on_unimplmented = n
+// disable_wam = n
+// report_faults = n
+// n = 0 don't
+// n = 1 report
+// n = 2 report overflow
+// tro_enable = n
+// y2k
+// drl_fatal
+
+static config_value_list_t cfg_multics_fault_base [] =
+ {
+ { "multics", 2 },
+ { NULL, 0 }
+ };
+
+static config_value_list_t cfg_on_off [] =
+ {
+ { "off", 0 },
+ { "on", 1 },
+ { "disable", 0 },
+ { "enable", 1 },
+ { NULL, 0 }
+ };
+
+static config_value_list_t cfg_cpu_mode [] =
+ {
+ { "gcos", 0 },
+ { "multics", 1 },
+ { NULL, 0 }
+ };
+
+static config_value_list_t cfg_port_letter [] =
+ {
+ { "a", 0 },
+ { "b", 1 },
+ { "c", 2 },
+ { "d", 3 },
+#ifdef L68
+ { "e", 4 },
+ { "f", 5 },
+ { "g", 6 },
+ { "h", 7 },
+#endif
+ { NULL, 0 }
+ };
+
+static config_value_list_t cfg_interlace [] =
+ {
+ { "off", 0 },
+ { "2", 2 },
+ { "4", 4 },
+ { NULL, 0 }
+ };
+
+#ifdef AFFINITY
+static config_value_list_t cfg_affinity [] =
+ {
+ { "off", -1 },
+ { NULL, 0 }
+ };
+#endif
+
+static config_value_list_t cfg_size_list [] =
+ {
+#ifdef L68
+// rsw.incl.pl1
+//
+// /* DPS and L68 memory sizes */
+// dcl dps_mem_size_table (0:7) fixed bin (24) static options (constant) init
+// (32768, 65536, 4194304, 131072, 524288, 1048576, 2097152, 262144);
+//
+// Note that the third array element above, is changed incompatibly in MR10.0.
+// In previous releases, this array element was used to decode a port size of
+// 98304 (96K). With MR10.0 it is now possible to address 4MW per CPU port, by
+// installing FCO # PHAF183 and using a group 10 patch plug, on L68 and DPS
+// CPUs.
+
+ { "32", 0 }, // 32768
+ { "64", 1 }, // 65536
+ { "4096", 2 }, // 4194304
+ { "128", 3 }, // 131072
+ { "512", 4 }, // 524288
+ { "1024", 5 }, // 1048576
+ { "2048", 6 }, // 2097152
+ { "256", 7 }, // 262144
+
+ { "32K", 0 },
+ { "64K", 1 },
+ { "4096K", 2 },
+ { "128K", 3 },
+ { "512K", 4 },
+ { "1024K", 5 },
+ { "2048K", 6 },
+ { "256K", 7 },
+
+ { "1M", 5 },
+ { "2M", 6 },
+ { "4M", 2 },
+#endif // L68
+
+#ifdef DPS8M
+// These values are taken from the dps8_mem_size_table loaded by the boot tape.
+
+ { "32", 0 },
+ { "64", 1 },
+ { "128", 2 },
+ { "256", 3 },
+ { "512", 4 },
+ { "1024", 5 },
+ { "2048", 6 },
+ { "4096", 7 },
+
+ { "32K", 0 },
+ { "64K", 1 },
+ { "128K", 2 },
+ { "256K", 3 },
+ { "512K", 4 },
+ { "1024K", 5 },
+ { "2048K", 6 },
+ { "4096K", 7 },
+
+ { "1M", 5 },
+ { "2M", 6 },
+ { "4M", 7 },
+#endif // DPS8M
+ { NULL, 0 }
+
+ };
+
+static config_list_t cpu_config_list [] =
+ {
+ { "faultbase", 0, 0177, cfg_multics_fault_base },
+ { "num", 0, 07, NULL },
+ { "data", 0, 0777777777777, NULL },
+ { "stopnum", 0, 999999, NULL },
+ { "mode", 0, 01, cfg_cpu_mode },
+ { "speed", 0, 017, NULL }, // XXX use keywords
+ { "port", 0, N_CPU_PORTS - 1, cfg_port_letter },
+ { "assignment", 0, 7, NULL },
+ { "interlace", 0, 1, cfg_interlace },
+ { "enable", 0, 1, cfg_on_off },
+ { "init_enable", 0, 1, cfg_on_off },
+ { "store_size", 0, 7, cfg_size_list },
+
+ // Hacks
+
+ { "dis_enable", 0, 1, cfg_on_off },
+ // steady_clock was moved to SCU; keep here for script compatibility
+ { "steady_clock", 0, 1, cfg_on_off },
+ { "halt_on_unimplemented", 0, 1, cfg_on_off },
+ { "disable_wam", 0, 1, cfg_on_off },
+ { "report_faults", 0, 2, NULL },
+ { "tro_enable", 0, 1, cfg_on_off },
+ // y2k was moved to SCU; keep here for script compatibility
+ { "y2k", 0, 1, cfg_on_off },
+ { "drl_fatal", 0, 1, cfg_on_off },
+ { "useMap", 0, 1, cfg_on_off },
+ { "address", 0, 0777777, NULL },
+ { "disable_cache", 0, 1, cfg_on_off },
+
+ // Tuning
+
+#ifdef AFFINITY
+ { "affinity", -1, 32767, cfg_affinity },
+#endif
+
+ { NULL, 0, 0, NULL }
+ };
+
+static t_stat cpu_set_config (UNIT * uptr, UNUSED int32 value,
+ const char * cptr, UNUSED void * desc)
+ {
+ long cpu_unit_idx = UNIT_IDX (uptr);
+ if (cpu_unit_idx < 0 || cpu_unit_idx >= N_CPU_UNITS_MAX)
+ {
+ sim_warn ("error: cpu_set_config: invalid unit number %ld\n",
+ cpu_unit_idx);
+ return SCPE_ARG;
+ }
+
+ static int port_num = 0;
+
+ config_state_t cfg_state = { NULL, NULL };
+
+ for (;;)
+ {
+ int64_t v;
+ int rc = cfg_parse (__func__, cptr, cpu_config_list,
+ & cfg_state, & v);
+ if (rc == -1) // done
+ {
+ break;
+ }
+ if (rc == -2) // error
+ {
+ cfg_parse_done (& cfg_state);
+ return SCPE_ARG;
+ }
+
+ const char * p = cpu_config_list [rc] . name;
+ if (strcmp (p, "faultbase") == 0)
+ cpus[cpu_unit_idx].switches.FLT_BASE = (uint) v;
+ else if (strcmp (p, "num") == 0)
+ cpus[cpu_unit_idx].switches.cpu_num = (uint) v;
+ else if (strcmp (p, "data") == 0)
+ cpus[cpu_unit_idx].switches.data_switches = (word36) v;
+ else if (strcmp (p, "stopnum") == 0)
+ {
+ // set up for check stop
+ // convert stopnum to bcd
+ int64_t d1 = (v / 1000) % 10;
+ int64_t d2 = (v / 100) % 10;
+ int64_t d3 = (v / 10) % 10;
+ int64_t d4 = (v / 1) % 10;
+ word36 d = 0123000000000;
+ putbits36_6 (& d, 9, (word4) d1);
+ putbits36_6 (& d, 15, (word4) d2);
+ putbits36_6 (& d, 21, (word4) d3);
+ putbits36_6 (& d, 27, (word4) d4);
+ cpus[cpu_unit_idx].switches.data_switches = d;
+ }
+ else if (strcmp (p, "address") == 0)
+ cpus[cpu_unit_idx].switches.addr_switches = (word18) v;
+ else if (strcmp (p, "mode") == 0)
+ cpus[cpu_unit_idx].switches.proc_mode = (uint) v;
+ else if (strcmp (p, "speed") == 0)
+ cpus[cpu_unit_idx].switches.proc_speed = (uint) v;
+ else if (strcmp (p, "port") == 0)
+ port_num = (int) v;
+ else if (strcmp (p, "assignment") == 0)
+ cpus[cpu_unit_idx].switches.assignment [port_num] = (uint) v;
+ else if (strcmp (p, "interlace") == 0)
+ cpus[cpu_unit_idx].switches.interlace [port_num] = (uint) v;
+ else if (strcmp (p, "enable") == 0)
+ cpus[cpu_unit_idx].switches.enable [port_num] = (uint) v;
+ else if (strcmp (p, "init_enable") == 0)
+ cpus[cpu_unit_idx].switches.init_enable [port_num] = (uint) v;
+ else if (strcmp (p, "store_size") == 0)
+ cpus[cpu_unit_idx].switches.store_size [port_num] = (uint) v;
+ else if (strcmp (p, "dis_enable") == 0)
+ cpus[cpu_unit_idx].switches.dis_enable = (uint) v;
+// else if (strcmp (p, "steady_clock") == 0)
+// scu [0].steady_clock = (uint) v;
+ else if (strcmp (p, "halt_on_unimplemented") == 0)
+ cpus[cpu_unit_idx].switches.halt_on_unimp = (uint) v;
+ else if (strcmp (p, "disable_wam") == 0)
+ cpus[cpu_unit_idx].switches.disable_wam = (uint) v;
+ else if (strcmp (p, "report_faults") == 0)
+ cpus[cpu_unit_idx].switches.report_faults = (uint) v;
+ else if (strcmp (p, "tro_enable") == 0)
+ cpus[cpu_unit_idx].switches.tro_enable = (uint) v;
+// else if (strcmp (p, "y2k") == 0)
+// scu [0].y2k = (uint) v;
+ else if (strcmp (p, "drl_fatal") == 0)
+ cpus[cpu_unit_idx].switches.drl_fatal = (uint) v;
+ else if (strcmp (p, "useMap") == 0)
+ cpus[cpu_unit_idx].switches.useMap = v;
+ else if (strcmp (p, "disable_cache") == 0)
+ cpus[cpu_unit_idx].switches.disable_cache = v;
+#ifdef AFFINITY
+ else if (strcmp (p, "affinity") == 0)
+ if (v < 0)
+ {
+ cpus[cpu_unit_idx].set_affinity = false;
+ }
+ else
+ {
+ cpus[cpu_unit_idx].set_affinity = true;
+ cpus[cpu_unit_idx].affinity = (uint) v;
+ }
+#endif
+ else
+ {
+ sim_warn ("error: cpu_set_config: invalid cfg_parse rc <%d>\n",
+ rc);
+ cfg_parse_done (& cfg_state);
+ return SCPE_ARG;
+ }
+ } // process statements
+ cfg_parse_done (& cfg_state);
+
+ return SCPE_OK;
+ }
+
+static t_stat cpu_show_nunits (UNUSED FILE * st, UNUSED UNIT * uptr,
+ UNUSED int val, UNUSED const void * desc)
+ {
+ sim_msg ("Number of CPUs in system is %d\n", cpu_dev.numunits);
+ return SCPE_OK;
+ }
+
+static t_stat cpu_set_nunits (UNUSED UNIT * uptr, UNUSED int32 value,
+ const char * cptr, UNUSED void * desc)
+ {
+ if (! cptr)
+ return SCPE_ARG;
+ int n = atoi (cptr);
+ if (n < 1 || n > N_CPU_UNITS_MAX)
+ return SCPE_ARG;
+ cpu_dev.numunits = (uint32) n;
+ return SCPE_OK;
+ }
+
+static t_stat cpu_show_kips (UNUSED FILE * st, UNUSED UNIT * uptr,
+ UNUSED int val, UNUSED const void * desc)
+ {
+ sim_msg ("CPU KIPS %u\n", kips);
+ return SCPE_OK;
+ }
+
+static t_stat cpu_set_kips (UNUSED UNIT * uptr, UNUSED int32 value,
+ const char * cptr, UNUSED void * desc)
+ {
+ if (! cptr)
+ return SCPE_ARG;
+ int n = atoi (cptr);
+ if (n < 1 || n > 1000000)
+ return SCPE_ARG;
+ kips = (uint) n;
+ luf_limits[0] = 2000*kips/1000;
+ luf_limits[1] = 4000*kips/1000;
+ luf_limits[2] = 8000*kips/1000;
+ luf_limits[3] = 16000*kips/1000;
+ luf_limits[4] = 32000*kips/1000;
+ return SCPE_OK;
+ }
+
+static t_stat cpu_show_stall (UNUSED FILE * st, UNUSED UNIT * uptr,
+ UNUSED int val, UNUSED const void * desc)
+ {
+ if (! stall_point_active)
+ {
+ sim_printf ("No stall points\n");
+ return SCPE_OK;
+ }
+
+ sim_printf ("Stall points\n");
+ for (int i = 0; i < N_STALL_POINTS; i ++)
+ if (stall_points[i].segno || stall_points[i].offset)
+ {
+ sim_printf ("%2d %05o:%06o %6u\n", i, stall_points[i].segno, stall_points[i].offset, stall_points[i].time);
+ }
+ return SCPE_OK;
+ }
+
+// set cpu stall=n=s:o=t
+// n stall point number
+// s segment number (octal)
+// o offset (octal)
+// t time in microseconds (decimal)
+
+static t_stat cpu_set_stall (UNUSED UNIT * uptr, UNUSED int32 value,
+ const char * cptr, UNUSED void * desc)
+ {
+ if (! cptr)
+ return SCPE_ARG;
+
+ long n, s, o, t;
+
+ char * end;
+ n = strtol (cptr, & end, 0);
+ if (* end != '=')
+ return SCPE_ARG;
+ if (n < 0 || n >= N_STALL_POINTS)
+ return SCPE_ARG;
+
+ s = strtol (end + 1, & end, 8);
+ if (* end != ':')
+ return SCPE_ARG;
+ if (s < 0 || s > MASK15)
+ return SCPE_ARG;
+
+ o = strtol (end + 1, & end, 8);
+ if (* end != '=')
+ return SCPE_ARG;
+ if (o < 0 || o > MASK18)
+ return SCPE_ARG;
+
+ t = strtol (end + 1, & end, 0);
+ if (* end != 0)
+ return SCPE_ARG;
+ if (t < 0 || t >= 1000000)
+ return SCPE_ARG;
+
+ stall_points[n].segno = (word15) s;
+ stall_points[n].offset = (word18) o;
+ stall_points[n].time = (unsigned int) t;
+ stall_point_active = false;
+
+ for (int i = 0; i < N_STALL_POINTS; i ++)
+ if (stall_points[n].segno && stall_points[n].offset)
+ stall_point_active = true;
+
+ return SCPE_OK;
+ }
+
+static char * cycle_str (cycles_e cycle)
+ {
+ switch (cycle)
+ {
+ //case ABORT_cycle:
+ //return "ABORT_cycle";
+ case FAULT_cycle:
+ return "FAULT_cycle";
+ case EXEC_cycle:
+ return "EXEC_cycle";
+ case FAULT_EXEC_cycle:
+ return "FAULT_EXEC_cycle";
+ case INTERRUPT_cycle:
+ return "INTERRUPT_cycle";
+ case INTERRUPT_EXEC_cycle:
+ return "INTERRUPT_EXEC_cycle";
+ case FETCH_cycle:
+ return "FETCH_cycle";
+ case PSEUDO_FETCH_cycle:
+ return "PSEUDO_FETCH_cycle";
+ case SYNC_FAULT_RTN_cycle:
+ return "SYNC_FAULT_RTN_cycle";
+ default:
+ return "unknown cycle";
+ }
+ }
+
+static void set_cpu_cycle (cycles_e cycle)
+ {
+ sim_debug (DBG_CYCLE, & cpu_dev, "Setting cycle to %s\n",
+ cycle_str (cycle));
+ cpu.cycle = cycle;
+ }
+
+// DPS8M Memory of 36 bit words is implemented as an array of 64 bit words.
+// Put state information into the unused high order bits.
+#define MEM_UNINITIALIZED (1LLU<<62)
+
+uint set_cpu_idx (UNUSED uint cpu_idx)
+ {
+ uint prev = current_running_cpu_idx;
+#if defined(THREADZ) || defined(LOCKLESS)
+ current_running_cpu_idx = cpu_idx;
+#endif
+#ifdef ROUND_ROBIN
+ current_running_cpu_idx = cpu_idx;
+#endif
+ cpup = & cpus [current_running_cpu_idx];
+ return prev;
+ }
+
+void cpu_reset_unit_idx (UNUSED uint cpun, bool clear_mem)
+ {
+ uint save = set_cpu_idx (cpun);
+ if (clear_mem)
+ {
+#ifdef SCUMEM
+ for (int cpu_port_num = 0; cpu_port_num < N_CPU_PORTS; cpu_port_num ++)
+ {
+ if (get_scu_in_use (current_running_cpu_idx, cpu_port_num))
+ {
+ uint sci_unit_idx = get_scu_idx (current_running_cpu_idx, cpu_port_num);
+ // Clear lock bits
+ for (uint i = 0; i < SCU_MEM_SIZE; i ++)
+ {
+ //scu [sci_unit_idx].M[i] = MEM_UNINITIALIZED;
+ scu [sci_unit_idx].M[i] &= (MASK36 | MEM_UNINITIALIZED);
+ }
+ }
+ }
+#else
+ for (uint i = 0; i < MEMSIZE; i ++)
+ {
+ //M [i] = MEM_UNINITIALIZED;
+ M[i] &= (MASK36 | MEM_UNINITIALIZED);
+ }
+#endif
+ }
+ cpu.rA = 0;
+ cpu.rQ = 0;
+
+ cpu.PPR.IC = 0;
+ cpu.PPR.PRR = 0;
+ cpu.PPR.PSR = 0;
+ cpu.PPR.P = 1;
+ cpu.RSDWH_R1 = 0;
+ cpu.rTR = MASK27;
+//#if defined(THREADZ) || defined(LOCKLESS)
+// clock_gettime (CLOCK_BOOTTIME, & cpu.rTRTime);
+//#endif
+#if ISOLTS
+ cpu.shadowTR = 0;
+ cpu.rTRlsb = 0;
+#endif
+ cpu.rTR = MASK27;
+ cpu.rTRticks = 0;
+
+ set_addr_mode (ABSOLUTE_mode);
+ SET_I_NBAR;
+
+ cpu.CMR.luf = 3; // default of 16 mS
+ cpu.cu.SD_ON = cpu.switches.disable_wam ? 0 : 1;
+ cpu.cu.PT_ON = cpu.switches.disable_wam ? 0 : 1;
+
+ set_cpu_cycle (FETCH_cycle);
+
+ cpu.wasXfer = false;
+ cpu.wasInhibited = false;
+
+ cpu.interrupt_flag = false;
+ cpu.g7_flag = false;
+
+ cpu.faultRegister [0] = 0;
+ cpu.faultRegister [1] = 0;
+
+#ifdef RAPRx
+ cpu.apu.lastCycle = UNKNOWN_CYCLE;
+#endif
+
+ memset (& cpu.PPR, 0, sizeof (struct ppr_s));
+
+// setup_scbank_map ();
+
+ tidy_cu ();
+ set_cpu_idx (save);
+#ifdef TEST_OLIN
+ cmpxchg ();
+#endif
+#ifdef TEST_FENCE
+ fence ();
+#endif
+#ifdef THREADZ
+ fence ();
+#endif
+ }
+
+static t_stat simh_cpu_reset_and_clear_unit (UNUSED UNIT * uptr,
+ UNUSED int32 value,
+ UNUSED const char * cptr,
+ UNUSED void * desc)
+ {
+ long cpu_unit_idx = UNIT_IDX (uptr);
+#ifdef ISOLTS
+ cpu_state_t * cpun = cpus + cpu_unit_idx;
+ if (cpun->switches.useMap)
+ {
+ for (uint pgnum = 0; pgnum < N_SCBANKS; pgnum ++)
+ {
+ int os = cpun->scbank_pg_os [pgnum];
+ if (os < 0)
+ continue;
+ for (uint addr = 0; addr < SCBANK; addr ++)
+ M [addr + (uint) os] = MEM_UNINITIALIZED;
+ }
+ }
+#else
+ // Crashes console?
+ cpu_reset_unit_idx ((uint) cpu_unit_idx, true);
+#endif
+ return SCPE_OK;
+ }
+
+static t_stat simh_cpu_reset_unit (UNIT * uptr,
+ UNUSED int32 value,
+ UNUSED const char * cptr,
+ UNUSED void * desc)
+ {
+ long cpu_unit_idx = UNIT_IDX (uptr);
+ cpu_reset_unit_idx ((uint) cpu_unit_idx, false); // no clear memory
+ return SCPE_OK;
+ }
+
+#ifndef NO_EV_POLL
+static uv_loop_t * ev_poll_loop;
+static uv_timer_t ev_poll_handle;
+#endif
+
+static MTAB cpu_mod[] =
+ {
+ {
+ MTAB_unit_value, // mask
+ 0, // match
+ "CONFIG", // print string
+ "CONFIG", // match string
+ cpu_set_config, // validation routine
+ cpu_show_config, // display routine
+ NULL, // value descriptor
+ NULL // help
+ },
+
+
+// RESET -- reset CPU
+// INITIALIZE -- reset CPU
+
+ {
+ MTAB_unit_value, // mask
+ 0, // match
+ "RESET", // print string
+ "RESET", // match string
+ simh_cpu_reset_unit, // validation routine
+ NULL, // display routine
+ NULL, // value descriptor
+ NULL // help
+ },
+
+ {
+ MTAB_unit_value, // mask
+ 0, // match
+ "INITIALIZE", // print string
+ "INITIALIZE", // match string
+ simh_cpu_reset_unit, // validation routine
+ NULL, // display routine
+ NULL, // value descriptor
+ NULL // help
+ },
+
+// INITAILIZEANDCLEAR -- reset CPU, clear Memory
+// IAC -- reset CPU, clear Memory
+
+ {
+ MTAB_unit_value, // mask
+ 0, // match
+ "INITIALIZEANDCLEAR", // print string
+ "INITIALIZEANDCLEAR", // match string
+ simh_cpu_reset_and_clear_unit, // validation routine
+ NULL, // display routine
+ NULL, // value descriptor
+ NULL // help
+ },
+
+ {
+ MTAB_unit_value, // mask
+ 0, // match
+ "IAC", // print string
+ "IAC", // match string
+ simh_cpu_reset_and_clear_unit, // validation routine
+ NULL, // display routine
+ NULL, // value descriptor
+ NULL // help
+ },
+
+ {
+ MTAB_dev_value, // mask
+ 0, // match
+ "NUNITS", // print string
+ "NUNITS", // match string
+ cpu_set_nunits, // validation routine
+ cpu_show_nunits, // display routine
+ NULL, // value descriptor
+ NULL // help
+ },
+
+
+ {
+ MTAB_dev_value, // mask
+ 0, // match
+ "KIPS", // print string
+ "KIPS", // match string
+ cpu_set_kips, // validation routine
+ cpu_show_kips, // display routine
+ NULL, // value descriptor
+ NULL // help
+ },
+
+ {
+ MTAB_dev_value, // mask
+ 0, // match
+ "STALL", // print string
+ "STALL", // match string
+ cpu_set_stall, // validation routine
+ cpu_show_stall, // display routine
+ NULL, // value descriptor
+ NULL // help
+ },
+
+ { 0, 0, NULL, NULL, NULL, NULL, NULL, NULL }
+ };
+
+static DEBTAB cpu_dt[] =
+ {
+ { "TRACE", DBG_TRACE, NULL },
+ { "TRACEEXT", DBG_TRACEEXT, NULL },
+ { "MESSAGES", DBG_MSG, NULL },
+
+ { "REGDUMPAQI", DBG_REGDUMPAQI, NULL },
+ { "REGDUMPIDX", DBG_REGDUMPIDX, NULL },
+ { "REGDUMPPR", DBG_REGDUMPPR, NULL },
+ { "REGDUMPPPR", DBG_REGDUMPPPR, NULL },
+ { "REGDUMPDSBR",DBG_REGDUMPDSBR, NULL },
+ { "REGDUMPFLT", DBG_REGDUMPFLT, NULL },
+ // don't move as it messes up DBG message
+ { "REGDUMP", DBG_REGDUMP, NULL },
+
+ { "ADDRMOD", DBG_ADDRMOD, NULL },
+ { "APPENDING", DBG_APPENDING, NULL },
+
+ { "NOTIFY", DBG_NOTIFY, NULL },
+ { "INFO", DBG_INFO, NULL },
+ { "ERR", DBG_ERR, NULL },
+ { "WARN", DBG_WARN, NULL },
+ { "DEBUG", DBG_DEBUG, NULL },
+ // don't move as it messes up DBG message
+ { "ALL", DBG_ALL, NULL },
+
+ { "FAULT", DBG_FAULT, NULL },
+ { "INTR", DBG_INTR, NULL },
+ { "CORE", DBG_CORE, NULL },
+ { "CYCLE", DBG_CYCLE, NULL },
+ { "CAC", DBG_CAC, NULL },
+ { "FINAL", DBG_FINAL, NULL },
+ { "AVC", DBG_AVC, NULL },
+ { NULL, 0, NULL }
+ };
+
+// This is part of the simh interface
+const char *sim_stop_messages[] =
+ {
+ "Unknown error", // SCPE_OK
+ "Simulation stop", // STOP_STOP
+ "Breakpoint", // STOP_BKPT
+ };
+
+/* End of simh interface */
+
+/* Processor configuration switches
+ *
+ * From AM81-04 Multics System Maintainance Procedures
+ *
+ * "A level 68 IOM system may contain a maximum of 7 CPUs, 4 IOMs, 8 SCUs and
+ * 16MW of memory
+ * [CAC]: but AN87 says multics only supports two IOMs
+ *
+ * ASSIGNMENT: 3 toggle switches determine the base address of the SCU
+ * connected to the port. The base address (in KW) is the product of this
+ * number and the value defined by the STORE SIZE patch plug for the port.
+ *
+ * ADDRESS RANGE: toggle FULL/HALF. Determines the size of the SCU as full or
+ * half of the STORE SIZE patch.
+ *
+ * PORT ENABLE: (4? toggles)
+ *
+ * INITIALIZE ENABLE: (4? toggles) These switches enable the receipt of an
+ * initialize signal from the SCU connected to the ports. This signal is used
+ * during the first part of bootload to set all CPUs to a known (idle) state.
+ * The switch for each port connected to an SCU should be ON, otherwise off.
+ *
+ * INTERLACE: ... All INTERLACE switches should be OFF for Multics operation.
+ *
+ */
+
+#ifndef SPEED
+static bool watch_bits [MEMSIZE];
+#endif
+
+// XXX PPR.IC oddly incremented. ticket #6
+
+
+char * str_SDW0 (char * buf, sdw0_s * SDW)
+ {
+ sprintf (buf, "ADDR=%06o R1=%o R2=%o R3=%o F=%o FC=%o BOUND=%o R=%o "
+ "E=%o W=%o P=%o U=%o G=%o C=%o EB=%o",
+ SDW->ADDR, SDW->R1, SDW->R2, SDW->R3, SDW->DF,
+ SDW->FC, SDW->BOUND, SDW->R, SDW->E, SDW->W,
+ SDW->P, SDW->U, SDW->G, SDW->C, SDW->EB);
+ return buf;
+ }
+
+static t_stat cpu_boot (UNUSED int32 cpu_unit_idx, UNUSED DEVICE * dptr)
+ {
+ sim_warn ("Try 'BOOT IOMn'\n");
+ return SCPE_ARG;
+ }
+
+//void setup_scbank_map (void)
+// {
+// sim_debug (DBG_DEBUG, & cpu_dev,
+// "setup_scbank_map: SCBANK %d N_SCBANKS %d MEM_SIZE_MAX %d\n",
+// SCBANK, N_SCBANKS, MEM_SIZE_MAX);
+//
+// // Initalize to unmapped
+// for (uint pg = 0; pg < N_SCBANKS; pg ++)
+// {
+// // The port number that the page of memory can be accessed through
+// cpu.scbank_map [pg] = -1;
+// // The offset in M of the page of memory on the other side of the
+// // port
+// cpu.scbank_pg_os [pg] = -1;
+// }
+//
+// // For each port (which is connected to a SCU
+// for (int port_num = 0; port_num < N_CPU_PORTS; port_num ++)
+// {
+// if (! cpu.switches.enable [port_num])
+// continue;
+// // This will happen during SIMH early initialization before
+// // the cables are run.
+// if (! cables->cpu_to_scu[current_running_cpu_idx][port_num].in_use)
+// {
+// //sim_warn ("%s SCU not cabled\n", __func__);
+// continue;
+// }
+// uint scu_unit_idx = cables->cpu_to_scu[current_running_cpu_idx][port_num].scu_unit_idx;
+//
+// // Calculate the amount of memory in the SCU in words
+// uint store_size = cpu.switches.store_size [port_num];
+// // Map store size configuration switch (0-8) to memory size.
+//#ifdef DPS8M
+// uint store_table [8] =
+// { 32768, 65536, 131072, 262144, 524288, 1048576, 2097152, 4194304 };
+//#endif
+//#ifdef L68
+//#ifdef ISOLTS
+//// ISOLTS sez:
+//// for DPS88:
+//// 3. set store size switches to 2222.
+//// for L68:
+//// 3. remove the right free-edge connector on the 645pq wwb at slot ab28.
+////
+//// During ISOLTS initialization, it requires that the memory switch be set to
+//// '3' for all eight ports; this corresponds to '2' for the DPS8M (131072)
+//// Then:
+//// isolts: a "lda 65536" (64k) failed to produce a store fault
+////
+//// So it seems that the memory size is expected to be 64K, not 128K as per
+//// the swithes; presumably step 3 causes this. Fake it by tweaking store table:
+////
+// uint store_table [8] =
+// { 32768, 65536, 4194304, 65536, 524288, 1048576, 2097152, 262144 };
+//#else
+// uint store_table [8] =
+// { 32768, 65536, 4194304, 131072, 524288, 1048576, 2097152, 262144 };
+//#endif
+//#endif
+// uint sz = store_table [store_size];
+// // Calculate the base address of the memory in words
+// uint assignment = cpu.switches.assignment [port_num];
+// uint base = assignment * sz;
+//
+// // Now convert to SCBANK (number of pages, page number)
+// uint sz_pages = sz / SCBANK;
+// uint scbase = base / SCBANK;
+//
+// sim_debug (DBG_DEBUG, & cpu_dev,
+// "setup_scbank_map: port:%d ss:%u as:%u sz_pages:%u ba:%u\n",
+// port_num, store_size, assignment, sz_pages, scbase);
+//
+// for (uint pg = 0; pg < sz_pages; pg ++)
+// {
+// uint scpg = scbase + pg;
+// if (scpg < N_SCBANKS)
+// {
+// if (cpu.scbank_map [scpg] != -1)
+// {
+// sim_warn ("scbank overlap scpg %d (%o) old port %d "
+// "newport %d\n",
+// scpg, scpg, cpu.scbank_map [scpg], port_num);
+// }
+// else
+// {
+// cpu.scbank_map [scpg] = port_num;
+// cpu.scbank_base [scpg] = base;
+// cpu.scbank_pg_os [scpg] =
+// (int) ((uint) scu_unit_idx * 4u * 1024u * 1024u +
+// scpg * SCBANK);
+// }
+// }
+// else
+// {
+// sim_warn ("scpg too big port %d scpg %d (%o), "
+// "limit %d (%o)\n",
+// port_num, scpg, scpg, N_SCBANKS, N_SCBANKS);
+// }
+// }
+// }
+// for (uint pg = 0; pg < N_SCBANKS; pg ++)
+// sim_debug (DBG_DEBUG, & cpu_dev, "setup_scbank_map: %d:%d\n",
+// pg, cpu.scbank_map [pg]);
+// }
+
+#ifdef SCUMEM
+int lookup_cpu_mem_map (word24 addr, word24 * offset)
+ {
+ uint scpg = addr / SCBANK;
+ if (scpg < N_SCBANKS)
+ {
+ * offset = addr - cpu.scbank_base[scpg];
+ return cpu.scbank_map[scpg];
+ }
+ return -1;
+ }
+#else
+int lookup_cpu_mem_map (word24 addr)
+ {
+ uint scpg = addr / SCBANK;
+ if (scpg < N_SCBANKS)
+ {
+ return cpu.scbank_map[scpg];
+ }
+ return -1;
+ }
+#endif
+//
+// serial.txt format
+//
+// sn: number[,number]
+//
+// Additional numbers will be for multi-cpu systems.
+// Other fields to be added.
+
+static void get_serial_number (void)
+ {
+ bool havesn = false;
+ FILE * fp = fopen ("./serial.txt", "r");
+ while (fp && ! feof (fp))
+ {
+ char buffer [81] = "";
+ fgets (buffer, sizeof (buffer), fp);
+ uint cpun, sn;
+ if (sscanf (buffer, "sn: %u", & cpu.switches.serno) == 1)
+ {
+ sim_msg ("Serial number is %u\n", cpu.switches.serno);
+ havesn = true;
+ }
+ else if (sscanf (buffer, "sn%u: %u", & cpun, & sn) == 2)
+ {
+ if (cpun < N_CPU_UNITS_MAX)
+ {
+ cpus[cpun].switches.serno = sn;
+ sim_msg ("Serial number of CPU %u is %u\n",
+ cpun, cpus[cpun].switches.serno);
+ havesn = true;
+ }
+ }
+ }
+ if (! havesn)
+ {
+ sim_msg ("Please register your system at "
+ "https://ringzero.wikidot.com/wiki:register\n");
+ sim_msg ("or create the file 'serial.txt' containing the line "
+ "'sn: 0'.\n");
+ }
+ if (fp)
+ fclose (fp);
+ }
+
+
+#ifdef STATS
+static void do_stats (void)
+ {
+ static struct timespec stats_time;
+ static bool first = true;
+ if (first)
+ {
+ first = false;
+ clock_gettime (CLOCK_BOOTTIME, & stats_time);
+ sim_msg ("stats started\r\n");
+ }
+ else
+ {
+ struct timespec now, delta;
+ clock_gettime (CLOCK_BOOTTIME, & now);
+ timespec_diff (& stats_time, & now, & delta);
+ stats_time = now;
+ sim_msg ("stats %6ld.%02ld\r\n", delta.tv_sec,
+ delta.tv_nsec / 10000000);
+
+ sim_msg ("Instruction counts\r\n");
+ for (uint i = 0; i < 8; i ++)
+ {
+ sim_msg (" %9lld", cpus[i].instrCnt);
+ cpus[i].instrCnt = 0;
+ }
+ sim_msg ("\r\n");
+
+ sim_msg ("\r\n");
+ }
+ }
+#endif
+
+#ifndef NO_EV_POLL
+// The 100Hz timer as expired; poll I/O
+
+static void ev_poll_cb (uv_timer_t * UNUSED handle)
+ {
+ // Call the one hertz stuff every 100 loops
+ static uint oneHz = 0;
+ if (oneHz ++ >= sys_opts.sys_slow_poll_interval) // ~ 1Hz
+ {
+ oneHz = 0;
+ rdrProcessEvent ();
+#ifdef STATS
+ do_stats ();
+#endif
+ cpu.instrCntT0 = cpu.instrCntT1;
+ cpu.instrCntT1 = cpu.instrCnt;
+
+ }
+// fnpProcessEvent ();
+//#ifndef __MINGW64__
+// sk_process_event ();
+//#endif
+// consoleProcess ();
+// machine_room_process ();
+//#ifdef IO_ASYNC_PAYLOAD_CHAN
+// iomProcess ();
+//#endif
+//#ifndef __MINGW64__
+// absi_process_event ();
+//#endif
+// PNL (panel_process_event ());
+ }
+#endif
+
+
+// called once initialization
+
+void cpu_init (void)
+ {
+
+// !!!! Do not use 'cpu' in this routine; usage of 'cpus' violates 'restrict'
+// !!!! attribute
+
+#if 0
+#ifndef SCUMEM
+#ifdef M_SHARED
+ if (! M)
+ {
+ M = (word36 *) create_shm ("M", MEMSIZE * sizeof (word36));
+ }
+#else
+ if (! M)
+ {
+ M = (word36 *) calloc (MEMSIZE, sizeof (word36));
+ }
+#endif
+ if (! M)
+ {
+ sim_fatal ("create M failed\n");
+ }
+#endif
+
+#ifdef M_SHARED
+ if (! cpus)
+ {
+ cpus = (cpu_state_t *) create_shm ("cpus",
+ N_CPU_UNITS_MAX *
+ sizeof (cpu_state_t));
+ }
+ if (! cpus)
+ {
+ sim_fatal ("create cpus failed\n");
+ }
+#endif
+#endif
+
+ abort(); //M = system_state->M;
+#ifdef M_SHARED
+ cpus = system_state->cpus;
+#endif
+
+#ifndef SPEED
+ memset (& watch_bits, 0, sizeof (watch_bits));
+#endif
+
+ set_cpu_idx (0);
+
+ memset (cpus, 0, sizeof (cpu_state_t) * N_CPU_UNITS_MAX);
+ cpus [0].switches.FLT_BASE = 2; // Some of the UnitTests assume this
+
+ get_serial_number ();
+
+#ifndef NO_EV_POLL
+ ev_poll_loop = uv_default_loop ();
+ uv_timer_init (ev_poll_loop, & ev_poll_handle);
+ // 10 ms == 100Hz
+ uv_timer_start (& ev_poll_handle, ev_poll_cb, sys_opts.sys_poll_interval, sys_opts.sys_poll_interval);
+#endif
+
+ // TODO: reset *all* other structures to zero
+
+ cpu.instrCnt = 0;
+ cpu.cycleCnt = 0;
+ for (int i = 0; i < N_FAULTS; i ++)
+ cpu.faultCnt [i] = 0;
+
+
+#ifdef MATRIX
+ initializeTheMatrix ();
+#endif
+ }
+
+static void cpu_reset (void)
+ {
+ for (uint i = 0; i < N_CPU_UNITS_MAX; i ++)
+ {
+ cpu_reset_unit_idx (i, true);
+ }
+
+ set_cpu_idx (0);
+
+ sim_debug (DBG_INFO, & cpu_dev, "CPU reset: Running\n");
+
+ }
+
+static t_stat sim_cpu_reset (UNUSED DEVICE *dptr)
+ {
+ //memset (M, -1, MEMSIZE * sizeof (word36));
+
+ // Fill DPS8M memory with zeros, plus a flag only visible to the emulator
+ // marking the memory as uninitialized.
+
+
+ cpu_reset ();
+ return SCPE_OK;
+ }
+
+/* Memory examine */
+// t_stat examine_routine (t_val *eval_array, t_addr addr, UNIT *uptr, int32
+// switches)
+// Copy sim_emax consecutive addresses for unit uptr, starting
+// at addr, into eval_array. The switch variable has bit<n> set if the n'th
+// letter was specified as a switch to the examine command.
+// Not true...
+
+static t_stat cpu_ex (t_value *vptr, t_addr addr, UNUSED UNIT * uptr,
+ UNUSED int32 sw)
+ {
+ if (addr>= MEMSIZE)
+ return SCPE_NXM;
+ if (vptr != NULL)
+ {
+#ifdef SCUMEM
+ word36 w;
+ core_read (addr, & w, __func__);
+ *vptr = w;
+#else
+ *vptr = M[addr] & DMASK;
+#endif
+ }
+ return SCPE_OK;
+ }
+
+/* Memory deposit */
+
+static t_stat cpu_dep (t_value val, t_addr addr, UNUSED UNIT * uptr,
+ UNUSED int32 sw)
+ {
+ if (addr >= MEMSIZE) return SCPE_NXM;
+#ifdef SCUMEM
+ word36 w = val & DMASK;
+ core_write (addr, w, __func__);
+#else
+ M[addr] = val & DMASK;
+#endif
+ return SCPE_OK;
+ }
+
+
+
+/*
+ * register stuff ...
+ */
+
+#ifdef M_SHARED
+// simh has to have a statically allocated IC to refer to.
+static word18 dummy_IC;
+#endif
+
+static REG cpu_reg[] =
+ {
+ // IC must be the first; see sim_PC.
+#ifdef M_SHARED
+ { ORDATA (IC, dummy_IC, VASIZE), 0, 0, 0 },
+#else
+ { ORDATA (IC, cpus[0].PPR.IC, VASIZE), 0, 0, 0 },
+#endif
+ { NULL, NULL, 0, 0, 0, 0, NULL, NULL, 0, 0, 0 }
+ };
+
+/*
+ * simh interface
+ */
+
+REG *sim_PC = & cpu_reg[0];
+
+/* CPU device descriptor */
+
+DEVICE cpu_dev =
+ {
+ "CPU", // name
+ cpu_unit, // units
+ cpu_reg, // registers
+ cpu_mod, // modifiers
+ N_CPU_UNITS, // #units
+ 8, // address radix
+ PASIZE, // address width
+ 1, // addr increment
+ 8, // data radix
+ 36, // data width
+ & cpu_ex, // examine routine
+ & cpu_dep, // deposit routine
+ & sim_cpu_reset,// reset routine
+ & cpu_boot, // boot routine
+ NULL, // attach routine
+ NULL, // detach routine
+ NULL, // context
+ DEV_DEBUG, // device flags
+ 0, // debug control flags
+ cpu_dt, // debug flag names
+ NULL, // memory size change
+ NULL, // logical name
+ NULL, // help
+ NULL, // attach help
+ NULL, // help context
+ NULL, // description
+ NULL
+ };
+
+#ifdef M_SHARED
+cpu_state_t * cpus = NULL;
+#else
+cpu_state_t cpus [N_CPU_UNITS_MAX];
+#endif
+#if defined(THREADZ) || defined(LOCKLESS)
+__thread cpu_state_t * restrict cpup;
+#else
+cpu_state_t * restrict cpup;
+#endif
+#ifdef ROUND_ROBIN
+uint current_running_cpu_idx;
+#endif
+
+//// Scan the SCUs; it one has an interrupt present, return the fault pair
+//// address for the highest numbered interrupt on that SCU. If no interrupts
+//// are found, return 1.
+//
+//// Called with SCU lock set
+//
+//static uint get_highest_intr (void)
+// {
+// uint fp = 1;
+// for (uint scu_unit_idx = 0; scu_unit_idx < N_SCU_UNITS_MAX; scu_unit_idx ++)
+// {
+// if (cpu.events.XIP [scu_unit_idx])
+// {
+// fp = scu_get_highest_intr (scu_unit_idx); // CALLED WITH SCU LOCK
+// if (fp != 1)
+// break;
+// }
+// }
+// return fp;
+// }
+//
+//bool sample_interrupts (void)
+// {
+// cpu.lufCounter = 0;
+// for (uint scu_unit_idx = 0; scu_unit_idx < N_SCU_UNITS_MAX; scu_unit_idx ++)
+// {
+// if (cpu.events.XIP [scu_unit_idx])
+// {
+// return true;
+// }
+// }
+// return false;
+// }
+//
+//t_stat simh_hooks (void)
+// {
+// int reason = 0;
+//
+// if (breakEnable && stop_cpu)
+// return STOP_STOP;
+//
+//#ifdef ISOLTS
+// if (current_running_cpu_idx == 0)
+//#endif
+// // check clock queue
+// if (sim_interval <= 0)
+// {
+// reason = sim_process_event ();
+// if ((! breakEnable) && reason == SCPE_STOP)
+// reason = SCPE_OK;
+// if (reason)
+// return reason;
+// }
+//
+// sim_interval --;
+//
+//#if !defined(THREADZ) && !defined(LOCKLESS)
+//// This is needed for BCE_TRAP in install scripts
+// // sim_brk_test expects a 32 bit address; PPR.IC into the low 18, and
+// // PPR.PSR into the high 12
+// if (sim_brk_summ &&
+// sim_brk_test ((cpu.PPR.IC & 0777777) |
+// ((((t_addr) cpu.PPR.PSR) & 037777) << 18),
+// SWMASK ('E'))) /* breakpoint? */
+// return STOP_BKPT; /* stop simulation */
+//#ifndef SPEED
+// if (sim_deb_break && cpu.cycleCnt >= sim_deb_break)
+// return STOP_BKPT; /* stop simulation */
+//#endif
+//#endif
+//
+// return reason;
+// }
+
+
+#ifdef PANEL
+static void panel_process_event (void)
+ {
+ // INITIALIZE pressed; treat at as a BOOT.
+ if (cpu.panelInitialize && cpu.DATA_panel_s_trig_sw == 0)
+ {
+ // Wait for release
+ while (cpu.panelInitialize)
+ ;
+ if (cpu.DATA_panel_init_sw)
+ cpu_reset_unit_idx (ASSUME0, true); // INITIALIZE & CLEAR
+ else
+ cpu_reset_unit_idx (ASSUME0, false); // INITIALIZE
+ // XXX Until a boot switch is wired up
+ do_boot ();
+ }
+ // EXECUTE pressed; EXECUTE PB set, EXECUTE FAULT set
+ if (cpu.DATA_panel_s_trig_sw == 0 &&
+ cpu.DATA_panel_execute_sw && // EXECUTE buttton
+ cpu.DATA_panel_scope_sw && // 'EXECUTE PB/SCOPE REPEAT' set to PB
+ cpu.DATA_panel_exec_sw == 0) // 'EXECUTE SWITCH/EXECUTE FAULT'
+ // set to FAULT
+ {
+ // Wait for release
+ while (cpu.DATA_panel_execute_sw)
+ ;
+
+ if (cpu.DATA_panel_exec_sw) // EXECUTE SWITCH
+ {
+ cpu_reset_unit_idx (ASSUME0, false);
+ cpu.cu.IWB = cpu.switches.data_switches;
+ set_cpu_cycle (EXEC_cycle);
+ }
+ else // EXECUTE FAULT
+ {
+ setG7fault (current_running_cpu_idx, FAULT_EXF, fst_zero);
+ }
+ }
+ }
+#endif
+
+bool bce_dis_called = false;
+
+#if defined(THREADZ) || defined(LOCKLESS)
+// The hypervisor CPU for the threadz model
+t_stat sim_instr (void)
+ {
+ t_stat reason = 0;
+
+#if 0
+ static bool inited = false;
+ if (! inited)
+ {
+ inited = true;
+
+#ifdef IO_THREADZ
+// Create channel threads
+
+ for (uint iom_unit_idx = 0; iom_unit_idx < N_IOM_UNITS_MAX; iom_unit_idx ++)
+ {
+ for (uint chan_num = 0; chan_num < MAX_CHANNELS; chan_num ++)
+ {
+ if (get_ctlr_in_use (iom_unit_idx, chan_num))
+ {
+ enum ctlr_type_e ctlr_type =
+ cables->iom_to_ctlr[iom_unit_idx][chan_num].ctlr_type;
+ createChnThread (iom_unit_idx, chan_num,
+ ctlr_type_strs [ctlr_type]);
+ chnRdyWait (iom_unit_idx, chan_num);
+ }
+ }
+ }
+
+// Create IOM threads
+
+ for (uint iom_unit_idx = 0;
+ iom_unit_idx < N_IOM_UNITS_MAX;
+ iom_unit_idx ++)
+ {
+ createIOMThread (iom_unit_idx);
+ iomRdyWait (iom_unit_idx);
+ }
+#endif
+
+// Create CPU threads
+
+ //for (uint cpu_idx = 0; cpu_idx < N_CPU_UNITS_MAX; cpu_idx ++)
+ for (uint cpu_idx = 0; cpu_idx < cpu_dev.numunits; cpu_idx ++)
+ {
+ createCPUThread (cpu_idx);
+ }
+ }
+#endif
+ if (cpuThreadz[0].run == false)
+ createCPUThread (0);
+ do
+ {
+ reason = 0;
+ // Process deferred events and breakpoints
+ reason = simh_hooks ();
+ if (reason)
+ {
+ break;
+ }
+
+#if 0
+// Check for CPU 0 stopped
+ if (! cpuThreadz[0].run)
+ {
+ sim_msg ("CPU 0 stopped\n");
+ return STOP_STOP;
+ }
+#endif
+#if 0
+
+// Check for all CPUs stopped
+
+// This doesn't work for multiple CPU Multics; only one processor does the
+// BCE dis; the other processors are doing the pxss 'dis 0777' dance;
+
+ uint n_running = 0;
+ for (uint i = 0; i < cpu_dev.numunits; i ++)
+ {
+ struct cpuThreadz_t * p = & cpuThreadz[i];
+ if (p->run)
+ n_running ++;
+ }
+ if (! n_running)
+ return STOP_STOP;
+#endif
+
+ if (bce_dis_called)
+ return STOP_STOP;
+
+// Loop runs at 1000 Hz
+
+#ifdef LOCKLESS
+ lock_iom();
+#endif
+ lock_libuv ();
+ uv_run (ev_poll_loop, UV_RUN_NOWAIT);
+ unlock_libuv ();
+#ifdef LOCKLESS
+ unlock_iom();
+#endif
+ PNL (panel_process_event ());
+
+ int con_unit_idx = check_attn_key ();
+ if (con_unit_idx != -1)
+ console_attn_idx (con_unit_idx);
+
+#ifdef IO_ASYNC_PAYLOAD_CHAN_THREAD
+ struct timespec next_time;
+ clock_gettime (CLOCK_REALTIME, & next_time);
+ next_time.tv_nsec += 1000l * 1000l;
+ if (next_time.tv_nsec >= 1000l * 1000l *1000l)
+ {
+ next_time.tv_nsec -= 1000l * 1000l *1000l;
+ next_time.tv_sec += (time_t) 1;
+ }
+ struct timespec new_time;
+ do
+ {
+ pthread_mutex_lock (& iom_start_lock);
+ pthread_cond_timedwait (& iomCond,
+ & iom_start_lock,
+ & next_time);
+ pthread_mutex_unlock (& iom_start_lock);
+ lock_iom();
+ lock_libuv ();
+
+ iomProcess ();
+
+ unlock_libuv ();
+ unlock_iom ();
+
+ clock_gettime (CLOCK_REALTIME, & new_time);
+ }
+ while ((next_time.tv_sec == new_time.tv_sec) ? (next_time.tv_nsec > new_time.tv_nsec) : (next_time.tv_sec > new_time.tv_sec));
+#else
+ usleep (1000); // 1000 us == 1 ms == 1/1000 sec.
+#endif
+ }
+ while (reason == 0);
+#ifdef HDBG
+ hdbgPrint ();
+#endif
+ return reason;
+ }
+#endif
+
+#if !defined(THREADZ) && !defined(LOCKLESS)
+#ifndef NO_EV_POLL
+static uint fast_queue_subsample = 0;
+#endif
+#endif
+
+//
+// Okay, lets treat this as a state machine
+//
+// INTERRUPT_cycle
+// clear interrupt, load interrupt pair into instruction buffer
+// set INTERRUPT_EXEC_cycle
+// INTERRUPT_EXEC_cycle
+// execute instruction in instruction buffer
+// if (! transfer) set INTERUPT_EXEC2_cycle
+// else set FETCH_cycle
+// INTERRUPT_EXEC2_cycle
+// execute odd instruction in instruction buffer
+// set INTERUPT_EXEC2_cycle
+//
+// FAULT_cycle
+// fetch fault pair into instruction buffer
+// set FAULT_EXEC_cycle
+// FAULT_EXEC_cycle
+// execute instructions in instruction buffer
+// if (! transfer) set FAULT_EXE2_cycle
+// else set FETCH_cycle
+// FAULT_EXEC2_cycle
+// execute odd instruction in instruction buffer
+// set FETCH_cycle
+//
+// FETCH_cycle
+// fetch instruction into instruction buffer
+// set EXEC_cycle
+//
+// EXEC_cycle
+// execute instruction in instruction buffer
+// if (repeat conditions) keep cycling
+// if (pair) set EXEC2_cycle
+// else set FETCH_cycle
+// EXEC2_cycle
+// execute odd instruction in instruction buffer
+//
+// XEC_cycle
+// load instruction into instruction buffer
+// set EXEC_cyvle
+//
+// XED_cycle
+// load instruction pair into instruction buffer
+// set EXEC_cyvle
+//
+// other extant cycles:
+// ABORT_cycle
+
+#if defined(THREADZ) || defined(LOCKLESS)
+void * cpu_thread_main (void * arg)
+ {
+ int myid = * (int *) arg;
+ set_cpu_idx ((uint) myid);
+
+ sim_msg ("CPU %c thread created\n", 'a' + myid);
+
+ setSignals ();
+ threadz_sim_instr ();
+ return NULL;
+ }
+#endif // THREADZ
+
+static void do_LUF_fault (void)
+ {
+ CPT (cpt1U, 16); // LUF
+ cpu.lufCounter = 0;
+ cpu.lufOccurred = false;
+#ifdef ISOLTS
+// This is a hack to fix ISOLTS 776. ISOLTS checks that the TR has
+// decremented by the LUF timeout value. To implement this, we set
+// the TR to the expected value.
+
+// LUF time
+// 0 2ms
+// 1 4ms
+// 2 8ms
+// 3 16ms
+// units
+// you have: 2ms
+// units
+// You have: 512000Hz
+// You want: 1/2ms
+// * 1024
+// / 0.0009765625
+//
+// TR = 1024 << LUF
+ cpu.shadowTR = (word27) cpu.TR0 - (1024u << (is_priv_mode () ? 4 : cpu.CMR.luf));
+
+
+// That logic fails for test 785.
+//
+// set slave mode, LUF time 16ms.
+// loop for 15.9 ms.
+// set master mode.
+// loop for 15.9 ms. The LUF should be noticed, and lufOccurred set.
+// return to slave mode. The LUF should fire, with the timer register
+// being set for 31.1 ms.
+// With out accurate cycle timing or simply fudging the results, I don't
+// see how to fix this one.
+
+#endif
+ doFault (FAULT_LUF, fst_zero, "instruction cycle lockup");
+ }
+
+#if !defined(THREADZ) && !defined(LOCKLESS)
+#define threadz_sim_instr sim_instr
+#endif
+
+/*
+ * addr_modes_e get_addr_mode()
+ *
+ * Report what mode the CPU is in.
+ * This is determined by examining a couple of IR flags.
+ *
+ * TODO: get_addr_mode() probably belongs in the CPU source file.
+ *
+ */
+
+static void set_temporary_absolute_mode (void)
+ {
+ CPT (cpt1L, 20); // set temp. abs. mode
+ cpu.secret_addressing_mode = true;
+ cpu.cu.XSF = false;
+sim_debug (DBG_TRACEEXT, & cpu_dev, "set_temporary_absolute_mode bit 29 sets XSF to 0\n");
+ //cpu.went_appending = false;
+ }
+
+static bool clear_temporary_absolute_mode (void)
+ {
+ CPT (cpt1L, 21); // clear temp. abs. mode
+ cpu.secret_addressing_mode = false;
+ return cpu.cu.XSF;
+ //return cpu.went_appending;
+ }
+
+t_stat threadz_sim_instr (void)
+ {
+//cpu.have_tst_lock = false;
+
+ t_stat reason = 0;
+
+#if !defined(THREADZ) && !defined(LOCKLESS)
+ set_cpu_idx (0);
+#ifdef M_SHARED
+// simh needs to have the IC statically allocated, so a placeholder was
+// created. Copy the placeholder in so the IC can be set by simh.
+
+ cpus [0].PPR.IC = dummy_IC;
+#endif
+
+#ifdef ROUND_ROBIN
+ cpu.isRunning = true;
+ set_cpu_idx (cpu_dev.numunits - 1);
+
+setCPU:;
+ uint current = current_running_cpu_idx;
+ uint c;
+ for (c = 0; c < cpu_dev.numunits; c ++)
+ {
+ set_cpu_idx (c);
+ if (cpu.isRunning)
+ break;
+ }
+ if (c == cpu_dev.numunits)
+ {
+ sim_msg ("All CPUs stopped\n");
+ goto leave;
+ }
+ set_cpu_idx ((current + 1) % cpu_dev.numunits);
+ if (! cpu . isRunning)
+ goto setCPU;
+#endif
+#endif
+
+ // This allows long jumping to the top of the state machine
+ int val = setjmp (cpu.jmpMain);
+
+ switch (val)
+ {
+ case JMP_ENTRY:
+ case JMP_REENTRY:
+ reason = 0;
+ break;
+ case JMP_SYNC_FAULT_RETURN:
+ set_cpu_cycle (SYNC_FAULT_RTN_cycle);
+ break;
+ case JMP_STOP:
+ reason = STOP_STOP;
+ goto leave;
+ case JMP_REFETCH:
+
+ // Not necessarily so, but the only times
+ // this path is taken is after an RCU returning
+ // from an interrupt, which could only happen if
+ // was xfer was false; or in a DIS cycle, in
+ // which case we want it false so interrupts
+ // can happen.
+ cpu.wasXfer = false;
+
+ set_cpu_cycle (FETCH_cycle);
+ break;
+ case JMP_RESTART:
+ set_cpu_cycle (EXEC_cycle);
+ break;
+ default:
+ sim_warn ("longjmp value of %d unhandled\n", val);
+ goto leave;
+ }
+
+ // Main instruction fetch/decode loop
+
+ DCDstruct * ci = & cpu.currentInstruction;
+
+ if (cpu.restart)
+ {
+ set_cpu_cycle (FAULT_cycle);
+ }
+
+ do
+ {
+ reason = 0;
+
+//#if !defined(THREADZ) && !defined(LOCKLESS)
+// // Process deferred events and breakpoints
+// reason = simh_hooks ();
+// if (reason)
+// {
+// break;
+// }
+//
+//#ifndef NO_EV_POLL
+//// The event poll is consuming 40% of the CPU according to pprof.
+//// We only want to process at 100Hz; yet we are testing at ~1MHz.
+//// If we only test every 1000 cycles, we shouldn't miss by more then
+//// 10%...
+//
+// //if ((! cpu.wasInhibited) && fast_queue_subsample ++ > 1024) // ~ 1KHz
+// //static uint fastQueueSubsample = 0;
+// if (fast_queue_subsample ++ > sys_opts.sys_poll_check_rate) // ~ 1KHz
+// {
+// fast_queue_subsample = 0;
+//#ifdef CONSOLE_FIX
+//#if defined(THREADZ) || defined(LOCKLESS)
+// lock_libuv ();
+//#endif
+//#endif
+// uv_run (ev_poll_loop, UV_RUN_NOWAIT);
+//#ifdef CONSOLE_FIX
+//#if defined(THREADZ) || defined(LOCKLESS)
+// unlock_libuv ();
+//#endif
+//#endif
+// PNL (panel_process_event ());
+// }
+//#else
+// static uint slowQueueSubsample = 0;
+// if (slowQueueSubsample ++ > 1024000) // ~ 1Hz
+// {
+// slowQueueSubsample = 0;
+// rdrProcessEvent ();
+// cpu.instrCntT0 = cpu.instrCntT1;
+// cpu.instrCntT1 = cpu.instrCnt;
+// }
+// static uint queueSubsample = 0;
+// if (queueSubsample ++ > 10240) // ~ 100Hz
+// {
+// queueSubsample = 0;
+// fnpProcessEvent ();
+// consoleProcess ();
+// machine_room_process ();
+// absi_process_event ();
+// PNL (panel_process_event ());
+// }
+//#endif
+//#endif // ! THREADZ
+//
+// cpu.cycleCnt ++;
+//
+//#ifdef THREADZ
+// // If we faulted somewhere with the memory lock set, clear it.
+// unlock_mem_force ();
+//
+// // wait on run/switch
+// cpuRunningWait ();
+//#endif // THREADZ
+//#ifdef LOCKLESS
+// core_unlock_all ();
+// // wait on run/switch
+// // cpuRunningWait ();
+//#endif
+//
+//#ifdef LOCKLESS
+// core_unlock_all();
+//#endif // LOCKLESS
+//
+// int con_unit_idx = check_attn_key ();
+// if (con_unit_idx != -1)
+// console_attn_idx (con_unit_idx);
+//
+//#ifndef NO_EV_POLL
+//#if !defined(THREADZ) && !defined(LOCKLESS)
+//#ifdef ISOLTS
+// if (cpu.cycle != FETCH_cycle)
+// {
+// // Sync. the TR with the emulator clock.
+// cpu.rTRlsb ++;
+// if (cpu.rTRlsb >= 4)
+// {
+// cpu.rTRlsb = 0;
+// cpu.shadowTR = (cpu.shadowTR - 1) & MASK27;
+// if (cpu.shadowTR == 0) // passing thorugh 0...
+// {
+// if (cpu.switches.tro_enable)
+// setG7fault (current_running_cpu_idx, FAULT_TRO, fst_zero);
+// }
+// }
+// }
+//#endif
+//#endif
+//#endif
+
+// Check for TR underflow. The TR is stored in a uint32_t, but is 27 bits wide.
+// The TR update code decrements the TR; if it passes through 0, the high bits
+// will be set.
+
+// If we assume a 1 MIPS reference platform, the TR would be decremented every
+// two instructions (1/2 MHz)
+
+//#if 0
+// cpu.rTR -= cpu.rTRticks * 100;
+// //cpu.rTR -= cpu.rTRticks * 50;
+// cpu.rTRticks = 0;
+//#else
+//#define TR_RATE 2
+//
+// cpu.rTR -= cpu.rTRticks / TR_RATE;
+// cpu.rTRticks %= TR_RATE;
+//
+//#endif
+//
+//
+// if (cpu.rTR & ~MASK27)
+// {
+// cpu.rTR &= MASK27;
+// if (cpu.switches.tro_enable)
+// setG7fault (current_running_cpu_idx, FAULT_TRO, fst_zero);
+// }
+
+ sim_debug (DBG_CYCLE, & cpu_dev, "Cycle is %s\n",
+ cycle_str (cpu.cycle));
+
+ switch (cpu.cycle)
+ {
+ case INTERRUPT_cycle:
+ abort();
+// {
+// CPT (cpt1U, 0); // Interupt cycle
+// // In the INTERRUPT CYCLE, the processor safe-stores
+// // the Control Unit Data (see Section 3) into
+// // program-invisible holding registers in preparation
+// // for a Store Control Unit (scu) instruction, enters
+// // temporary absolute mode, and forces the current
+// // ring of execution C(PPR.PRR) to
+// // 0. It then issues an XEC system controller command
+// // to the system controller on the highest priority
+// // port for which there is a bit set in the interrupt
+// // present register.
+//
+// uint intr_pair_addr = get_highest_intr ();
+//#ifdef HDBG
+// hdbgIntr (intr_pair_addr);
+//#endif
+// cpu.cu.FI_ADDR = (word5) (intr_pair_addr / 2);
+// cu_safe_store ();
+// // XXX the whole interrupt cycle should be rewritten as an xed
+// // instruction pushed to IWB and executed
+//
+// CPT (cpt1U, 1); // safe store complete
+// // Temporary absolute mode
+// set_temporary_absolute_mode ();
+//
+// // Set to ring 0
+// cpu.PPR.PRR = 0;
+// cpu.TPR.TRR = 0;
+//
+// sim_debug (DBG_INTR, & cpu_dev, "intr_pair_addr %u flag %d\n",
+// intr_pair_addr, cpu.interrupt_flag);
+//#ifndef SPEED
+// if_sim_debug (DBG_INTR, & cpu_dev)
+// traceInstruction (DBG_INTR);
+//#endif
+// // Check that an interrupt is actually pending
+// if (cpu.interrupt_flag)
+// {
+// CPT (cpt1U, 2); // interrupt pending
+// // clear interrupt, load interrupt pair into instruction
+// // buffer; set INTERRUPT_EXEC_cycle.
+//
+// // In the h/w this is done later, but doing it now allows
+// // us to avoid clean up for no interrupt pending.
+//
+// if (intr_pair_addr != 1) // no interrupts
+// {
+//
+// CPT (cpt1U, 3); // interrupt identified
+//
+// // get interrupt pair
+// core_read2 (intr_pair_addr,
+// & cpu.cu.IWB, & cpu.cu.IRODD, __func__);
+// cpu.cu.xde = 1;
+// cpu.cu.xdo = 1;
+// cpu.isExec = true;
+// cpu.isXED = true;
+//
+// CPT (cpt1U, 4); // interrupt pair fetched
+// cpu.interrupt_flag = false;
+// set_cpu_cycle (INTERRUPT_EXEC_cycle);
+// break;
+// } // int_pair != 1
+// } // interrupt_flag
+//
+// // If we get here, there was no interrupt
+//
+// CPT (cpt1U, 5); // interrupt pair spurious
+// cpu.interrupt_flag = false;
+// clear_temporary_absolute_mode ();
+// // Restores addressing mode
+// cu_safe_restore ();
+// // We can only get here if wasXfer was
+// // false, so we can assume it still is.
+// cpu.wasXfer = false;
+//// The only place cycle is set to INTERRUPT_cycle in FETCH_cycle; therefore
+//// we can safely assume that is the state that should be restored.
+// set_cpu_cycle (FETCH_cycle);
+// }
+// break;
+
+ case FETCH_cycle:
+#ifdef PANEL
+ memset (cpu.cpt, 0, sizeof (cpu.cpt));
+#endif
+ CPT (cpt1U, 13); // fetch cycle
+
+ PNL (L68_ (cpu.INS_FETCH = false;))
+
+// "If the interrupt inhibit bit is not set in the currect instruction
+// word at the point of the next sequential instruction pair virtual
+// address formation, the processor samples the [group 7 and interrupts]."
+
+// Since XEx/RPx may overwrite IWB, we must remember
+// the inhibit bits (cpu.wasInhibited).
+
+
+// If the instruction pair virtual address being formed is the result of a
+// transfer of control condition or if the current instruction is
+// Execute (xec), Execute Double (xed), Repeat (rpt), Repeat Double (rpd),
+// or Repeat Link (rpl), the group 7 faults and interrupt present lines are
+// not sampled.
+
+// Group 7 Faults
+//
+// Shutdown
+//
+// An external power shutdown condition has been detected. DC POWER shutdown
+// will occur in approximately one millisecond.
+//
+// Timer Runout
+//
+// The timer register has decremented to or through the value zero. If the
+// processor is in privileged mode or absolute mode, recognition of this fault
+// is delayed until a return to normal mode or BAR mode. Counting in the timer
+// register continues.
+//
+// Connect
+//
+// A connect signal ($CON strobe) has been received from a system controller.
+// This event is to be distinguished from a Connect Input/Output Channel (cioc)
+// instruction encountered in the program sequence.
+
+
+ // check BAR bound and raise store fault if above
+ // pft 04d 10070, ISOLTS-776 06ad
+ if (get_bar_mode ())
+ get_BAR_address (cpu.PPR.IC);
+
+ // Don't check timer runout if privileged
+ // ISOLTS-776 04bcf, 785 02c
+ // (but do if in a DIS instruction with bit28 clear)
+ bool tmp_priv_mode = is_priv_mode ();
+ bool is_dis = cpu.currentInstruction.opcode == 0616 &&
+ cpu.currentInstruction.opcodeX == 0;
+ bool noCheckTR = tmp_priv_mode &&
+ ! (is_dis && GET_I (cpu.cu.IWB) == 0);
+
+// if (is_dis)
+// {
+// // take interrupts and g7 faults as long as
+// // last instruction is DIS (??)
+// cpu.interrupt_flag = sample_interrupts ();
+// cpu.g7_flag =
+// noCheckTR ? bG7PendingNoTRO () : bG7Pending ();
+// }
+// else if (! (cpu.cu.xde | cpu.cu.xdo |
+// cpu.cu.rpt | cpu.cu.rd | cpu.cu.rl))
+// {
+// if ((!cpu.wasInhibited) &&
+// (cpu.PPR.IC & 1) == 0 &&
+// (! cpu.wasXfer))
+// {
+// CPT (cpt1U, 14); // sampling interrupts
+// cpu.interrupt_flag = sample_interrupts ();
+// cpu.g7_flag =
+// noCheckTR ? bG7PendingNoTRO () : bG7Pending ();
+// }
+// cpu.wasInhibited = false;
+// }
+// else
+// {
+// // XEx at an odd location disables interrupt sampling
+// // also for the next instruction pair. ISOLTS-785 02g,
+// // 776 04g
+// // Set the inhibit flag
+// // (I assume RPx behaves in the same way)
+// if ((cpu.PPR.IC & 1) == 1)
+// {
+// cpu.wasInhibited = true;
+// }
+// }
+
+
+// Multics executes a CPU connect instruction (which should eventually cause a
+// connect fault) while interrupts are inhibited and an IOM interrupt is
+// pending. Multics then executes a DIS instruction (Delay Until Interrupt
+// Set). This should cause the processor to "sleep" until an interrupt is
+// signaled. The DIS instruction sees that an interrupt is pending, sets
+// cpu.interrupt_flag to signal that the CPU to service the interrupt and
+// resumes the CPU.
+//
+// The CPU state machine sets up to fetch the next instruction. If checks to
+// see if this instruction should check for interrupts or faults according to
+// the complex rules (interrupts inhibited, even address, not RPT or XEC,
+// etc.); it this case, the test fails as the next instruction is at an odd
+// address. If the test had passed, the cpu.interrupt_flag would be set or
+// cleared depending on the pending interrupt state data, AND the cpu.g7_flag
+// would be set or cleared depending on the faults pending data (in this case,
+// the connect fault).
+//
+// Because the flags were not updated, after the test, cpu.interrupt_flag is
+// set (since the DIS instruction set it) and cpu.g7_flag is not set.
+//
+// Next, the CPU sees the that cpu.interrupt flag is set, and starts the
+// interrupt cycle despite the fact that a higher priority g7 fault is pending.
+
+
+// To fix this, check (or recheck) g7 if an interrupt is going to be faulted.
+// Either DIS set interrupt_flag and FETCH_cycle didn't so g7 needs to be
+// checked, or FETCH_cycle did check it when it set interrupt_flag in which
+// case it is being rechecked here. It is [locally] idempotent and light
+// weight, so this should be okay.
+
+// not necessary any more because of is_dis logic
+#if 0
+ if (cpu.interrupt_flag)
+ cpu.g7_flag = noCheckTR ? bG7PendingNoTRO () : bG7Pending ();
+#endif
+ if (cpu.g7_flag)
+ {
+ cpu.g7_flag = false;
+ cpu.interrupt_flag = false;
+ sim_debug (DBG_CYCLE, & cpu_dev,
+ "call doG7Fault (%d)\n", !noCheckTR);
+ doG7Fault (!noCheckTR);
+ }
+ if (cpu.interrupt_flag)
+ {
+// This is the only place cycle is set to INTERRUPT_cycle; therefore
+// return from interrupt can safely assume the it should set the cycle
+// to FETCH_cycle.
+ CPT (cpt1U, 15); // interrupt
+ set_cpu_cycle (INTERRUPT_cycle);
+ break;
+ }
+
+// "While in absolute mode or privileged mode the lockup fault is signalled at
+// the end of the time limit set in the lockup timer but is not recognized
+// until the 32 millisecond limit. If the processor returns to normal mode or
+// BAR mode after the fault has been signalled but before the 32 millisecond
+// limit, the fault is recognized before any instruction in the new mode is
+// executed."
+
+ // fall through
+ case PSEUDO_FETCH_cycle:
+
+// tmp_priv_mode = is_priv_mode ();
+// if (! (luf_flag && tmp_priv_mode))
+// cpu.lufCounter ++;
+//#if 1
+// if (cpu.lufCounter > luf_limits[cpu.CMR.luf])
+// {
+// if (tmp_priv_mode)
+// {
+// // In priv. mode the LUF is noted but not executed
+// cpu.lufOccurred = true;
+// }
+// else
+// {
+// do_LUF_fault ();
+// }
+// } // lufCounter > luf_limit
+//
+//
+// // After 32ms, the LUF fires regardless of priv.
+// if (cpu.lufCounter > luf_limits[4])
+// {
+// do_LUF_fault ();
+// }
+//
+// // If the LUF occured in priv. mode and we left priv. mode,
+// // fault.
+// if (! tmp_priv_mode && cpu.lufOccurred)
+// {
+// do_LUF_fault ();
+// }
+//#else
+// if ((tmp_priv_mode && cpu.lufCounter > luf_limits[4]) ||
+// (! tmp_priv_mode &&
+// cpu.lufCounter > luf_limits[cpu.CMR.luf]))
+// {
+// CPT (cpt1U, 16); // LUF
+// cpu.lufCounter = 0;
+//#ifdef ISOLTS
+//// This is a hack to fix ISOLTS 776. ISOLTS checks that the TR has
+//// decremented by the LUF timeout value. To implement this, we set
+//// the TR to the expected value.
+//
+//// LUF time
+//// 0 2ms
+//// 1 4ms
+//// 2 8ms
+//// 3 16ms
+//// units
+//// you have: 2ms
+//// units
+//// You have: 512000Hz
+//// You want: 1/2ms
+//// * 1024
+//// / 0.0009765625
+////
+//// TR = 1024 << LUF
+// cpu.shadowTR = (word27) cpu.TR0 - (1024u << (is_priv_mode () ? 4 : cpu.CMR.luf));
+//#endif
+// doFault (FAULT_LUF, fst_zero, "instruction cycle lockup");
+// }
+//#endif
+ if (cpu.cycle == PSEUDO_FETCH_cycle)
+ {
+ cpu.apu.lastCycle = INSTRUCTION_FETCH;
+ cpu.cu.XSF = 0;
+ cpu.cu.TSN_VALID [0] = 0;
+ cpu.TPR.TSR = cpu.PPR.PSR;
+ cpu.TPR.TRR = cpu.PPR.PRR;
+ cpu.wasInhibited = false;
+ }
+ else
+ {
+ CPT (cpt1U, 20); // not XEC or RPx
+ cpu.isExec = false;
+ cpu.isXED = false;
+ // fetch next instruction into current instruction struct
+ //clr_went_appending (); // XXX not sure this is the right
+ // place
+ cpu.cu.XSF = 0;
+sim_debug (DBG_TRACEEXT, & cpu_dev, "fetchCycle bit 29 sets XSF to 0\n");
+ cpu.cu.TSN_VALID [0] = 0;
+ cpu.TPR.TSR = cpu.PPR.PSR;
+ cpu.TPR.TRR = cpu.PPR.PRR;
+ PNL (cpu.prepare_state = ps_PIA);
+ PNL (L68_ (cpu.INS_FETCH = true;))
+ fetchInstruction (cpu.PPR.IC);
+ }
+
+ CPT (cpt1U, 21); // go to exec cycle
+ advanceG7Faults ();
+ set_cpu_cycle (EXEC_cycle);
+ break;
+
+ case EXEC_cycle:
+ case FAULT_EXEC_cycle:
+ case INTERRUPT_EXEC_cycle:
+ {
+ CPT (cpt1U, 22); // exec cycle
+
+#ifdef LOCKLESS
+ if (stall_point_active)
+ {
+ for (int i = 0; i < N_STALL_POINTS; i ++)
+ if (stall_points[i].segno && stall_points[i].segno == cpu.PPR.PSR &&
+ stall_points[i].offset && stall_points[i].offset == cpu.PPR.IC)
+ {
+#ifdef CTRACE
+ fprintf (stderr, "%10lu %s stall %d\n", seqno (), cpunstr[current_running_cpu_idx], i);
+#endif
+ //sim_printf ("stall %2d %05o:%06o\n", i, stall_points[i].segno, stall_points[i].offset);
+ //pthread_yield ();
+ usleep(stall_points[i].time);
+ break;
+ }
+ }
+#endif
+
+ // The only time we are going to execute out of IRODD is
+ // during RPD, at which time interrupts are automatically
+ // inhibited; so the following can igore RPD harmelessly
+ if (GET_I (cpu.cu.IWB))
+ cpu.wasInhibited = true;
+
+ t_stat ret = executeInstruction ();
+#ifdef TR_WORK_EXEC
+ cpu.rTRticks ++;
+#endif
+ CPT (cpt1U, 23); // execution complete
+
+ add_CU_history ();
+
+ if (ret > 0)
+ {
+ reason = ret;
+ break;
+ }
+
+ if (ret == CONT_XEC)
+ {
+ CPT (cpt1U, 27); // XEx instruction
+ cpu.wasXfer = false;
+ cpu.isExec = true;
+ if (cpu.cu.xdo)
+ cpu.isXED = true;
+
+ cpu.cu.XSF = 0;
+ cpu.cu.TSN_VALID [0] = 0;
+ cpu.TPR.TSR = cpu.PPR.PSR;
+ cpu.TPR.TRR = cpu.PPR.PRR;
+ break;
+ }
+
+ if (ret == CONT_TRA || ret == CONT_RET)
+ {
+ CPT (cpt1U, 24); // transfer instruction
+ cpu.cu.xde = cpu.cu.xdo = 0;
+ cpu.isExec = false;
+ cpu.isXED = false;
+ // even for CONT_RET else isolts 886 fails
+ cpu.wasXfer = true;
+
+ if (cpu.cycle != EXEC_cycle) // fault or interrupt
+ {
+
+ clearFaultCycle ();
+
+// BAR mode: [NBAR] is set ON (taking the processor
+// out of BAR mode) by the execution of any transfer instruction
+// other than tss during a fault or interrupt trap.
+
+ if (! (cpu.currentInstruction.opcode == 0715 &&
+ cpu.currentInstruction.opcodeX == 0))
+ {
+ CPT (cpt1U, 9); // nbar set
+ SET_I_NBAR;
+ }
+
+ if (!clear_temporary_absolute_mode ())
+ {
+ // didn't go appending
+ sim_debug (DBG_TRACEEXT, & cpu_dev,
+ "setting ABS mode\n");
+ CPT (cpt1U, 10); // temporary absolute mode
+ set_addr_mode (ABSOLUTE_mode);
+ }
+ else
+ {
+ // went appending
+ sim_debug (DBG_TRACEEXT, & cpu_dev,
+ "not setting ABS mode\n");
+ }
+
+ } // fault or interrupt
+
+
+ //if (TST_I_ABS && get_went_appending ())
+ if (TST_I_ABS && cpu.cu.XSF)
+ {
+ set_addr_mode (APPEND_mode);
+ }
+
+ if (ret == CONT_TRA)
+ {
+ // PSEUDO_FETCH_cycle does not check interrupts/g7faults
+ cpu.wasXfer = false;
+ set_cpu_cycle (PSEUDO_FETCH_cycle);
+ }
+ else
+ set_cpu_cycle (FETCH_cycle);
+ break; // don't bump PPR.IC, instruction already did it
+ }
+
+ if (ret == CONT_DIS)
+ {
+ CPT (cpt1U, 25); // DIS instruction
+
+
+// If we get here, we have encountered a DIS instruction in EXEC_cycle.
+//
+// We need to idle the CPU until one of the following conditions:
+//
+// An external interrupt occurs.
+// The Timer Register underflows.
+// The emulator polled devices need polling.
+//
+// The external interrupt will only be posted to the CPU engine if the
+// device poll posts an interrupt. This means that we do not need to
+// detect the interrupts here; if we wake up and poll the devices, the
+// interrupt will be detected by the DIS instruction when it is re-executed.
+//
+// The Timer Register is a fast, high-precision timer but Multics uses it
+// in only two ways: detecting I/O lockup during early boot, and process
+// quantum scheduling (1/4 second for Multics).
+//
+// Neither of these require high resolution or high accuracy.
+//
+// The goal of the polling code is sample at about 100Hz; updating the timer
+// register at that rate should suffice.
+//
+// sleep for 1/100 of a second
+// update the polling state to trigger a poll
+// update the timer register by 1/100 of a second
+// force the simh queues to process
+// continue processing
+//
+
+
+//// The usleep logic is not smart enough w.r.t. ROUND_ROBIN/ISOLTS.
+//// The sleep should only happen if all running processors are in
+//// DIS mode.
+//#ifndef ROUND_ROBIN
+// // 1/100 is .01 secs.
+// // *1000 is 10 milliseconds
+// // *1000 is 10000 microseconds
+// // in uSec;
+//#if defined(THREADZ) || defined(LOCKLESS)
+//
+//// XXX If interupt inhibit set, then sleep forever instead of TRO
+// // rTR is 512KHz; sleepCPU is in 1Mhz
+// // rTR * 1,000,000 / 512,000
+// // rTR * 1000 / 512
+// // rTR * 500 / 256
+// // rTR * 250 / 128
+// // rTR * 125 / 64
+//
+//#ifdef NO_TIMEWAIT
+// //usleep (sys_opts.sys_poll_interval * 1000/*10000*/);
+// struct timespec req, rem;
+// uint ms = sys_opts.sys_poll_interval;
+// long int nsec = (long int) ms * 1000 * 1000;
+// req.tv_nsec = nsec;
+// req.tv_sec += req.tv_nsec / 1000000000;
+// req.tv_nsec %= 1000000000;
+// int rc = nanosleep (& req, & rem);
+// // Awakened early?
+// if (rc == -1)
+// {
+// ms = (uint) (rem.tv_nsec / 1000 + req.tv_sec * 1000);
+// }
+// word27 ticks = ms * 512;
+// if (cpu.rTR <= ticks)
+// {
+// if (cpu.switches.tro_enable)
+// setG7fault (current_running_cpu_idx, FAULT_TRO,
+// fst_zero);
+// }
+// cpu.rTR = (cpu.rTR - ticks) & MASK27;
+//#else // !NO_TIMEWAIT
+// unsigned long left = cpu.rTR * 125u / 64u;
+// lock_scu ();
+// if (!sample_interrupts ())
+// {
+// left = sleepCPU (left);
+// }
+// unlock_scu ();
+// if (left)
+// {
+// cpu.rTR = (word27) (left * 64 / 125);
+// }
+// else
+// {
+// if (cpu.switches.tro_enable)
+// {
+// lock_scu ();
+// setG7fault (current_running_cpu_idx, FAULT_TRO,
+// fst_zero);
+// unlock_scu ();
+// }
+// cpu.rTR = 0;
+// }
+//#endif // !NO_TIMEWAIT
+// cpu.rTRticks = 0;
+// break;
+//#else // !THREADZ
+// //usleep (10000);
+// usleep (sys_opts.sys_poll_interval * 1000/*10000*/);
+//#ifndef NO_EV_POLL
+// // Trigger I/O polling
+//#ifdef CONSOLE_FIX
+//#if defined(THREADZ) || defined(LOCKLESS)
+// lock_libuv ();
+//#endif
+//#endif
+// uv_run (ev_poll_loop, UV_RUN_NOWAIT);
+//#ifdef CONSOLE_FIX
+//#if defined(THREADZ) || defined(LOCKLESS)
+// unlock_libuv ();
+//#endif
+//#endif
+// fast_queue_subsample = 0;
+//#else // NO_EV_POLL
+// // this ignores the amount of time since the last poll;
+// // worst case is the poll delay of 1/50th of a second.
+// slowQueueSubsample += 10240; // ~ 1Hz
+// queueSubsample += 10240; // ~100Hz
+//#endif // NO_EV_POLL
+//
+// sim_interval = 0;
+//#endif // !THREADZ
+// // Timer register runs at 512 KHz
+// // 512000 is 1 second
+// // 512000/100 -> 5120 is .01 second
+//
+// cpu.rTRticks = 0;
+// // Would we have underflowed while sleeping?
+// //if ((cpu.rTR & ~ MASK27) || cpu.rTR <= 5120)
+// //if (cpu.rTR <= 5120)
+//
+// // Timer register runs at 512 KHz
+// // 512Khz / 512 is millisecods
+// if (cpu.rTR <= sys_opts.sys_poll_interval * 512)
+// {
+// if (cpu.switches.tro_enable)
+// setG7fault (current_running_cpu_idx, FAULT_TRO,
+// fst_zero);
+// }
+// cpu.rTR = (cpu.rTR - sys_opts.sys_poll_interval * 512) & MASK27;
+//#endif // ! ROUND_ROBIN
+ break;
+ }
+
+ cpu.wasXfer = false;
+
+ if (ret < 0)
+ {
+ sim_warn ("executeInstruction returned %d?\n", ret);
+ break;
+ }
+
+ if ((! cpu.cu.repeat_first) &&
+ (cpu.cu.rpt ||
+ (cpu.cu.rd && (cpu.PPR.IC & 1)) ||
+ cpu.cu.rl))
+ {
+ CPT (cpt1U, 26); // RPx instruction
+ if (cpu.cu.rd)
+ -- cpu.PPR.IC;
+ cpu.wasXfer = false;
+ set_cpu_cycle (FETCH_cycle);
+ break;
+ }
+
+ // If we just did the odd word of a fault pair
+ if (cpu.cycle == FAULT_EXEC_cycle &&
+ !cpu.cu.xde && cpu.cu.xdo)
+ {
+ clear_temporary_absolute_mode ();
+ cu_safe_restore ();
+ CPT (cpt1U, 12); // cu restored
+ clearFaultCycle ();
+ // cu_safe_restore calls decode_instruction ()
+ // we can determine the instruction length.
+ // decode_instruction() restores ci->info->ndes
+ cpu.wasXfer = false;
+ cpu.isExec = false;
+ cpu.isXED = false;
+
+ cpu.PPR.IC += ci->info->ndes;
+ cpu.PPR.IC ++;
+
+ set_cpu_cycle (FETCH_cycle);
+ break;
+ }
+
+ // If we just did the odd word of a interrupt pair
+ if (cpu.cycle == INTERRUPT_EXEC_cycle &&
+ !cpu.cu.xde && cpu.cu.xdo)
+ {
+ clear_temporary_absolute_mode ();
+ cu_safe_restore ();
+ // cpu.cu.xdo = 0;
+// The only place cycle is set to INTERRUPT_cycle in FETCH_cycle; therefore
+// we can safely assume that is the state that should be restored.
+ CPT (cpt1U, 12); // cu restored
+ cpu.wasXfer = false;
+ cpu.isExec = false;
+ cpu.isXED = false;
+
+ set_cpu_cycle (FETCH_cycle);
+ break;
+ }
+
+ // Even word of fault or interrupt pair or xed
+ if (cpu.cu.xde && cpu.cu.xdo)
+ {
+ // Get the odd
+ cpu.cu.IWB = cpu.cu.IRODD;
+ cpu.cu.xde = 0;
+ cpu.isExec = true;
+ cpu.isXED = true;
+ cpu.cu.XSF = 0;
+ cpu.cu.TSN_VALID [0] = 0;
+ cpu.TPR.TSR = cpu.PPR.PSR;
+ cpu.TPR.TRR = cpu.PPR.PRR;
+ break; // go do the odd word
+ }
+
+ if (cpu.cu.xde || cpu.cu.xdo) // we are in an XEC/XED
+ {
+ cpu.cu.xde = cpu.cu.xdo = 0;
+ cpu.isExec = false;
+ cpu.isXED = false;
+ CPT (cpt1U, 27); // XEx instruction
+ cpu.wasXfer = false;
+ cpu.PPR.IC ++;
+ if (ci->info->ndes > 0)
+ cpu.PPR.IC += ci->info->ndes;
+ cpu.wasInhibited = true;
+ set_cpu_cycle (FETCH_cycle);
+ break;
+ }
+
+ //ASSURE (cpu.cycle == EXEC_cycle);
+ if (cpu.cycle != EXEC_cycle)
+ sim_warn ("expected EXEC_cycle (%d)\n", cpu.cycle);
+
+ cpu.cu.xde = cpu.cu.xdo = 0;
+ cpu.isExec = false;
+ cpu.isXED = false;
+
+ // use prefetched instruction from cpu.cu.IRODD
+ // we must have finished an instruction at an even location
+ // skip multiword EIS instructions
+ // skip repeat instructions for now
+ // skip dis - we may need to take interrupts/g7faults
+ // skip if (last instruction) wrote to current instruction range
+ // the hardware really does this and isolts tests it
+ // multics differences manual DPS8 70/M
+ // should take segment number into account?
+ if ((cpu.PPR.IC & 1) == 0 &&
+ ci->info->ndes == 0 &&
+ !cpu.cu.repeat_first && !cpu.cu.rpt && !cpu.cu.rd && !cpu.cu.rl &&
+ !(cpu.currentInstruction.opcode == 0616 && cpu.currentInstruction.opcodeX == 0) &&
+ (cpu.PPR.IC & ~3u) != (cpu.last_write & ~3u))
+ {
+ cpu.PPR.IC ++;
+ cpu.wasXfer = false;
+ cpu.cu.IWB = cpu.cu.IRODD;
+ set_cpu_cycle (PSEUDO_FETCH_cycle);
+ break;
+ }
+
+ cpu.PPR.IC ++;
+ if (ci->info->ndes > 0)
+ cpu.PPR.IC += ci->info->ndes;
+
+ CPT (cpt1U, 28); // enter fetch cycle
+ cpu.wasXfer = false;
+ set_cpu_cycle (FETCH_cycle);
+ }
+ break;
+
+ case SYNC_FAULT_RTN_cycle:
+ {
+ CPT (cpt1U, 29); // sync. fault return
+ cpu.wasXfer = false;
+ // cu_safe_restore should have restored CU.IWB, so
+ // we can determine the instruction length.
+ // decode_instruction() restores ci->info->ndes
+
+ cpu.PPR.IC += ci->info->ndes;
+ cpu.PPR.IC ++;
+ cpu.wasXfer = false;
+ set_cpu_cycle (FETCH_cycle);
+ }
+ break;
+
+ case FAULT_cycle:
+ {
+ CPT (cpt1U, 30); // fault cycle
+ // In the FAULT CYCLE, the processor safe-stores the Control
+ // Unit Data (see Section 3) into program-invisible holding
+ // registers in preparation for a Store Control Unit ( scu)
+ // instruction, then enters temporary absolute mode, forces the
+ // current ring of execution C(PPR.PRR) to 0, and generates a
+ // computed address for the fault trap pair by concatenating
+ // the setting of the FAULT BASE switches on the processor
+ // configuration panel with twice the fault number (see Table
+ // 7-1). This computed address and the operation code for the
+ // Execute Double (xed) instruction are forced into the
+ // instruction register and executed as an instruction. Note
+ // that the execution of the instruction is not done in a
+ // normal EXECUTE CYCLE but in the FAULT CYCLE with the
+ // processor in temporary absolute mode.
+
+ // F(A)NP should never be stored when faulting.
+ // ISOLTS-865 01a,870 02d
+ // Unconditional reset of APU status to FABS breaks boot.
+ // Checking for F(A)NP here is equivalent to checking that the
+ // last append cycle has made it as far as H/I without a fault.
+ // Also reset it on TRB fault. ISOLTS-870 05a
+ if (cpu.cu.APUCycleBits & 060 || cpu.secret_addressing_mode)
+ set_apu_status (apuStatus_FABS);
+
+ // XXX the whole fault cycle should be rewritten as an xed
+ // instruction pushed to IWB and executed
+
+ // AL39: TRB fault doesn't safestore CUD - the original fault
+ // CUD should be stored
+
+ // ISOLTS-870 05a: CUD[5] and IWB are safe stored, possibly
+ // due to CU overlap
+
+ // keep IRODD untouched if TRB occurred in an even location
+ if (cpu.faultNumber != FAULT_TRB || cpu.cu.xde == 0)
+ {
+ cu_safe_store ();
+ }
+ else
+ {
+ word36 tmpIRODD = cpu.scu_data[7];
+ cu_safe_store ();
+ cpu.scu_data[7] = tmpIRODD;
+ }
+ CPT (cpt1U, 31); // safe store complete
+
+ // Temporary absolute mode
+ set_temporary_absolute_mode ();
+
+ // Set to ring 0
+ cpu.PPR.PRR = 0;
+ cpu.TPR.TRR = 0;
+
+ // (12-bits of which the top-most 7-bits are used)
+ uint fltAddress = (cpu.switches.FLT_BASE << 5) & 07740;
+#ifdef L68
+ if (cpu.is_FFV)
+ {
+ cpu.is_FFV = false;
+ CPTUR (cptUseMR);
+ // The high 15 bits
+ fltAddress = (cpu.MR.FFV & MASK15) << 3;
+ }
+#endif
+
+ // absolute address of fault YPair
+ word24 addr = fltAddress + 2 * cpu.faultNumber;
+
+ if (cpu.restart)
+ {
+ cpu.restart = false;
+ addr = cpu.restart_address;
+ }
+
+ core_read2 (addr, & cpu.cu.IWB, & cpu.cu.IRODD, __func__);
+ cpu.cu.xde = 1;
+ cpu.cu.xdo = 1;
+ cpu.isExec = true;
+ cpu.isXED = true;
+
+ CPT (cpt1U, 33); // set fault exec cycle
+ set_cpu_cycle (FAULT_EXEC_cycle);
+
+ break;
+ }
+
+
+ } // switch (cpu.cycle)
+ }
+#ifdef ROUND_ROBIN
+ while (0);
+ if (reason == 0)
+ goto setCPU;
+#else
+ while (reason == 0);
+#endif
+
+leave:
+
+#if defined(THREADZ) || defined(LOCKLESS)
+ // setCPURun (current_running_cpu_idx, false);
+#endif
+
+#ifdef HDBG
+ hdbgPrint ();
+#endif
+ sim_msg ("\ncycles = %"PRIu64"\n", cpu.cycleCnt);
+ sim_msg ("instructions %15"PRIu64"\n", cpu.instrCnt);
+ sim_msg ("lockCnt %15"PRIu64"\n", cpu.lockCnt);
+ sim_msg ("lockImmediate %15"PRIu64"\n", cpu.lockImmediate);
+ sim_msg ("lockWait %15"PRIu64"\n", cpu.lockWait);
+ sim_msg ("lockWaitMax %15"PRIu64"\n", cpu.lockWaitMax);
+ sim_msg ("lockYield %15"PRIu64"\n", cpu.lockYield);
+#if 0
+ for (int i = 0; i < N_FAULTS; i ++)
+ {
+ if (cpu.faultCnt [i])
+ sim_msg ("%s faults = %ld\n",
+ faultNames [i], cpu.faultCnt [i]);
+ }
+#endif
+
+#if defined(THREADZ) || defined(LOCKLESS)
+ stopCPUThread();
+#endif
+
+#ifdef M_SHARED
+// simh needs to have the IC statically allocated, so a placeholder was
+// created. Update the placeholder in so the IC can be seen by simh, and
+// restarting sim_instr doesn't lose the place.
+
+ set_cpu_idx (0);
+ dummy_IC = cpu.PPR.IC;
+#endif
+
+ return reason;
+ }
+
+
+/*!
+ cd@libertyhaven.com - sez ....
+ If the instruction addresses a block of four words, the target of the
+instruction is supposed to be an address that is aligned on a four-word
+boundary (0 mod 4). If not, the processor will grab the four-word block
+containing that address that begins on a four-word boundary, even if it has to
+go back 1 to 3 words. Analogous explanation for 8, 16, and 32 cases.
+
+ olin@olinsibert.com - sez ...
+ It means that the appropriate low bits of the address are forced to zero. So
+it's the previous words, not the succeeding words, that are used to satisfy the
+request.
+
+ -- Olin
+
+ */
+
+int operand_size (void)
+ {
+ DCDstruct * i = & cpu.currentInstruction;
+ if (i->info->flags & (READ_OPERAND | STORE_OPERAND))
+ return 1;
+ else if (i->info->flags & (READ_YPAIR | STORE_YPAIR))
+ return 2;
+ else if (i->info->flags & (READ_YBLOCK8 | STORE_YBLOCK8))
+ return 8;
+ else if (i->info->flags & (READ_YBLOCK16 | STORE_YBLOCK16))
+ return 16;
+ else if (i->info->flags & (READ_YBLOCK32 | STORE_YBLOCK32))
+ return 32;
+ return 0;
+ }
+
+// read instruction operands
+
+t_stat read_operand (word18 addr, processor_cycle_type cyctyp)
+ {
+ CPT (cpt1L, 6); // read_operand
+
+#ifdef THREADZ
+ if (cyctyp == OPERAND_READ)
+ {
+ DCDstruct * i = & cpu.currentInstruction;
+#if 1
+ if (RMWOP (i))
+#else
+ if ((i -> opcode == 0034 && ! i -> opcodeX) || // ldac
+ (i -> opcode == 0032 && ! i -> opcodeX) || // ldqc
+ (i -> opcode == 0354 && ! i -> opcodeX) || // stac
+ (i -> opcode == 0654 && ! i -> opcodeX) || // stacq
+ (i -> opcode == 0214 && ! i -> opcodeX)) // sznc
+#endif
+ {
+ lock_rmw ();
+ }
+ }
+#endif
+
+ switch (operand_size ())
+ {
+ case 1:
+ CPT (cpt1L, 7); // word
+ Read (addr, & cpu.CY, cyctyp);
+ return SCPE_OK;
+ case 2:
+ CPT (cpt1L, 8); // double word
+ addr &= 0777776; // make even
+ Read2 (addr, cpu.Ypair, cyctyp);
+ break;
+ case 8:
+ CPT (cpt1L, 9); // oct word
+ addr &= 0777770; // make on 8-word boundary
+ Read8 (addr, cpu.Yblock8, cpu.currentInstruction.b29);
+ break;
+ case 16:
+ CPT (cpt1L, 10); // 16 words
+ addr &= 0777770; // make on 8-word boundary
+ Read16 (addr, cpu.Yblock16);
+ break;
+ case 32:
+ CPT (cpt1L, 11); // 32 words
+ addr &= 0777740; // make on 32-word boundary
+ for (uint j = 0 ; j < 32 ; j += 1)
+ Read (addr + j, cpu.Yblock32 + j, cyctyp);
+
+ break;
+ }
+ //cpu.TPR.CA = addr; // restore address
+
+ return SCPE_OK;
+
+ }
+
+// write instruction operands
+
+t_stat write_operand (word18 addr, UNUSED processor_cycle_type cyctyp)
+ {
+ switch (operand_size ())
+ {
+ case 1:
+ CPT (cpt1L, 12); // word
+ Write (addr, cpu.CY, OPERAND_STORE);
+ break;
+ case 2:
+ CPT (cpt1L, 13); // double word
+ addr &= 0777776; // make even
+ Write2 (addr + 0, cpu.Ypair, OPERAND_STORE);
+ break;
+ case 8:
+ CPT (cpt1L, 14); // 8 words
+ addr &= 0777770; // make on 8-word boundary
+ Write8 (addr, cpu.Yblock8, cpu.currentInstruction.b29);
+ break;
+ case 16:
+ CPT (cpt1L, 15); // 16 words
+ addr &= 0777770; // make on 8-word boundary
+ Write16 (addr, cpu.Yblock16);
+ break;
+ case 32:
+ CPT (cpt1L, 16); // 32 words
+ addr &= 0777740; // make on 32-word boundary
+ //for (uint j = 0 ; j < 32 ; j += 1)
+ //Write (addr + j, cpu.Yblock32[j], OPERAND_STORE);
+ Write32 (addr, cpu.Yblock32);
+ break;
+ }
+
+#ifdef THREADZ
+ if (cyctyp == OPERAND_STORE)
+ {
+ DCDstruct * i = & cpu.currentInstruction;
+ if (RMWOP (i))
+ unlock_mem ();
+ }
+#endif
+ return SCPE_OK;
+
+ }
+
+#ifndef SPEED
+t_stat set_mem_watch (int32 arg, const char * buf)
+ {
+ if (strlen (buf) == 0)
+ {
+ if (arg)
+ {
+ sim_warn ("no argument to watch?\n");
+ return SCPE_ARG;
+ }
+ sim_msg ("Clearing all watch points\n");
+ memset (& watch_bits, 0, sizeof (watch_bits));
+ return SCPE_OK;
+ }
+ char * end;
+ long int n = strtol (buf, & end, 0);
+ if (* end || n < 0 || n >= MEMSIZE)
+ {
+ sim_warn ("invalid argument to watch?\n");
+ return SCPE_ARG;
+ }
+ watch_bits [n] = arg != 0;
+ return SCPE_OK;
+ }
+#endif
+
+/*!
+ * "Raw" core interface ....
+ */
+
+#ifndef SPEED
+static void nem_check (word24 addr, char * context)
+ {
+#ifdef SCUMEM
+ word24 offset;
+ if (lookup_cpu_mem_map (addr, & offset) < 0)
+ {
+ doFault (FAULT_STR, fst_str_nea, context);
+ }
+#else
+ if (lookup_cpu_mem_map (addr) < 0)
+ {
+ doFault (FAULT_STR, fst_str_nea, context);
+ }
+#endif
+ }
+#endif
+
+#ifdef SCUMEM
+#ifndef SPEED
+static uint get_scu_unit_idx (word24 addr, word24 * offset)
+ {
+ int cpu_port_num = lookup_cpu_mem_map (addr, offset);
+ if (cpu_port_num < 0) // Can't happen, we passed nem_check above
+ {
+ sim_warn ("cpu_port_num < 0");
+ doFault (FAULT_STR, fst_str_nea, __func__);
+ }
+ return cables->cpu_to_scu [current_running_cpu_idx][cpu_port_num].scu_unit_idx;
+ }
+#endif
+#endif
+
+#if !defined(SPEED) || !defined(INLINE_CORE)
+int32 core_read (word24 addr, word36 *data, const char * ctx)
+ {
+ PNL (cpu.portBusy = true;)
+#ifdef ISOLTS
+ if (cpu.switches.useMap)
+ {
+ uint pgnum = addr / SCBANK;
+ int os = cpu.scbank_pg_os [pgnum];
+ if (os < 0)
+ {
+ doFault (FAULT_STR, fst_str_nea, __func__);
+ }
+ addr = (uint) os + addr % SCBANK;
+ }
+ else
+#endif
+#ifndef SPEED
+ nem_check (addr, "core_read nem");
+#endif
+
+#if 0 // XXX Controlled by TEST/NORMAL switch
+#ifdef ISOLTS
+ if (cpu.MR.sdpap)
+ {
+ sim_warn ("failing to implement sdpap\n");
+ cpu.MR.sdpap = 0;
+ }
+ if (cpu.MR.separ)
+ {
+ sim_warn ("failing to implement separ\n");
+ cpu.MR.separ = 0;
+ }
+#endif
+#endif
+#ifdef SCUMEM
+ word24 offset;
+ uint scu_unit_idx = get_scu_unit_idx (addr, & offset);
+ LOCK_MEM_RD;
+ *data = scu [scu_unit_idx].M[offset] & DMASK;
+ UNLOCK_MEM;
+ if (watch_bits [addr])
+ {
+ sim_msg ("WATCH [%"PRId64"] %05o:%06o read %08o %012"PRIo64" "
+ "(%s)\n", cpu.cycleCnt, cpu.PPR.PSR, cpu.PPR.IC, addr,
+ * data, ctx);
+ }
+#else
+#ifndef LOCKLESS
+ if (M[addr] & MEM_UNINITIALIZED)
+ {
+ sim_debug (DBG_WARN, & cpu_dev,
+ "Unitialized memory accessed at address %08o; "
+ "IC is 0%06o:0%06o (%s(\n",
+ addr, cpu.PPR.PSR, cpu.PPR.IC, ctx);
+ }
+#endif
+#ifndef SPEED
+ if (watch_bits [addr])
+ {
+ sim_msg ("WATCH [%"PRId64"] %05o:%06o read %08o %012"PRIo64" "
+ "(%s)\n",
+ cpu.cycleCnt, cpu.PPR.PSR, cpu.PPR.IC, addr, M [addr],
+ ctx);
+ traceInstruction (0);
+ }
+#endif
+#ifdef LOCKLESS
+ word36 v;
+ LOAD_ACQ_CORE_WORD(v, addr);
+ *data = v & DMASK;
+#else
+ LOCK_MEM_RD;
+ *data = M[addr] & DMASK;
+ UNLOCK_MEM;
+#endif
+
+#endif
+#ifdef TR_WORK_MEM
+ cpu.rTRticks ++;
+#endif
+ sim_debug (DBG_CORE, & cpu_dev,
+ "core_read %08o %012"PRIo64" (%s)\n",
+ addr, * data, ctx);
+ PNL (trackport (addr, * data));
+ return 0;
+ }
+#endif
+
+#ifdef LOCKLESS
+int32 core_read_lock (word24 addr, word36 *data, const char * ctx)
+{
+#ifdef ISOLTS
+ if (cpu.switches.useMap)
+ {
+ uint pgnum = addr / SCBANK;
+ int os = cpu.scbank_pg_os [pgnum];
+ if (os < 0)
+ {
+ doFault (FAULT_STR, fst_str_nea, __func__);
+ }
+ addr = (uint) os + addr % SCBANK;
+ }
+ else
+#endif
+#ifndef SPEED
+ nem_check (addr, "core_read nem");
+#endif
+ LOCK_CORE_WORD(addr);
+ if (cpu.locked_addr != 0) {
+ sim_warn ("core_read_lock: locked %08o locked_addr %08o %c %05o:%06o\n",
+ addr, cpu.locked_addr, current_running_cpu_idx + 'A',
+ cpu.PPR.PSR, cpu.PPR.IC);
+ core_unlock_all ();
+ }
+ cpu.locked_addr = addr;
+ word36 v;
+ LOAD_ACQ_CORE_WORD(v, addr);
+ * data = v & DMASK;
+ return 0;
+}
+#endif
+
+#if !defined(SPEED) || !defined(INLINE_CORE)
+int core_write (word24 addr, word36 data, const char * ctx)
+ {
+ PNL (cpu.portBusy = true;)
+#ifdef ISOLTS
+ if (cpu.switches.useMap)
+ {
+ uint pgnum = addr / SCBANK;
+ int os = cpu.scbank_pg_os [pgnum];
+ if (os < 0)
+ {
+ doFault (FAULT_STR, fst_str_nea, __func__);
+ }
+ addr = (uint) os + addr % SCBANK;
+ }
+ else
+#endif
+#ifndef SPEED
+ nem_check (addr, "core_write nem");
+#endif
+#ifdef ISOLTS
+ if (cpu.MR.sdpap)
+ {
+ sim_warn ("failing to implement sdpap\n");
+ cpu.MR.sdpap = 0;
+ }
+ if (cpu.MR.separ)
+ {
+ sim_warn ("failing to implement separ\n");
+ cpu.MR.separ = 0;
+ }
+#endif
+#ifdef SCUMEM
+ word24 offset;
+ uint sci_unit_idx = get_scu_unit_idx (addr, & offset);
+ LOCK_MEM_WR;
+ scu[sci_unit_idx].M[offset] = data & DMASK;
+ UNLOCK_MEM;
+ if (watch_bits [addr])
+ {
+ sim_msg ("WATCH [%"PRId64"] %05o:%06o write %08o %012"PRIo64" "
+ "(%s)\n", cpu.cycleCnt, cpu.PPR.PSR, cpu.PPR.IC, addr,
+ scu[sci_unit_idx].M[offset], ctx);
+ }
+#else
+#ifdef LOCKLESS
+ LOCK_CORE_WORD(addr);
+ STORE_REL_CORE_WORD(addr, data);
+#else
+ LOCK_MEM_WR;
+ M[addr] = data & DMASK;
+ UNLOCK_MEM;
+#endif
+#ifndef SPEED
+ if (watch_bits [addr])
+ {
+ sim_msg ("WATCH [%"PRId64"] %05o:%06o write %08o %012"PRIo64" "
+ "(%s)\n", cpu.cycleCnt, cpu.PPR.PSR, cpu.PPR.IC, addr,
+ M [addr], ctx);
+ traceInstruction (0);
+ }
+#endif
+#endif
+#ifdef TR_WORK_MEM
+ cpu.rTRticks ++;
+#endif
+ sim_debug (DBG_CORE, & cpu_dev,
+ "core_write %08o %012"PRIo64" (%s)\n",
+ addr, data, ctx);
+ PNL (trackport (addr, data));
+ return 0;
+ }
+#endif
+
+#ifdef LOCKLESS
+int core_write_unlock (word24 addr, word36 data, const char * ctx)
+{
+#ifdef ISOLTS
+ if (cpu.switches.useMap)
+ {
+ uint pgnum = addr / SCBANK;
+ int os = cpu.scbank_pg_os [pgnum];
+ if (os < 0)
+ {
+ doFault (FAULT_STR, fst_str_nea, __func__);
+ }
+ addr = (uint) os + addr % SCBANK;
+ }
+ else
+#endif
+#ifndef SPEED
+ nem_check (addr, "core_read nem");
+#endif
+ if (cpu.locked_addr != addr)
+ {
+ sim_warn ("core_write_unlock: locked %08o locked_addr %08o %c %05o:%06o\n",
+ addr, cpu.locked_addr, current_running_cpu_idx + 'A',
+ cpu.PPR.PSR, cpu.PPR.IC);
+ core_unlock_all ();
+ }
+
+ STORE_REL_CORE_WORD(addr, data);
+ cpu.locked_addr = 0;
+ return 0;
+}
+
+int core_unlock_all ()
+{
+ if (cpu.locked_addr != 0) {
+ sim_warn ("core_unlock_all: locked %08o %c %05o:%06o\n",
+ cpu.locked_addr, current_running_cpu_idx + 'A',
+ cpu.PPR.PSR, cpu.PPR.IC);
+ STORE_REL_CORE_WORD(cpu.locked_addr, M[cpu.locked_addr]);
+ cpu.locked_addr = 0;
+ }
+ return 0;
+}
+#endif
+
+#if !defined(SPEED) || !defined(INLINE_CORE)
+int core_write_zone (word24 addr, word36 data, const char * ctx)
+ {
+ PNL (cpu.portBusy = true;)
+#ifdef ISOLTS
+ if (cpu.MR.sdpap)
+ {
+ sim_warn ("failing to implement sdpap\n");
+ cpu.MR.sdpap = 0;
+ }
+ if (cpu.MR.separ)
+ {
+ sim_warn ("failing to implement separ\n");
+ cpu.MR.separ = 0;
+ }
+#endif
+#ifdef SCUMEM
+ word24 offset;
+ uint sci_unit_idx = get_scu_unit_idx (addr, & offset);
+ LOCK_MEM_WR;
+ scu[sci_unit_idx].M[offset] = (scu[sci_unit_idx].M[offset] & ~cpu.zone) |
+ (data & cpu.zone);
+ UNLOCK_MEM;
+ cpu.useZone = false; // Safety
+ if (watch_bits [addr])
+ {
+ sim_msg ("WATCH [%"PRId64"] %05o:%06o writez %08o %012"PRIo64" "
+ "(%s)\n", cpu.cycleCnt, cpu.PPR.PSR, cpu.PPR.IC, addr,
+ scu[sci_unit_idx].M[offset], ctx);
+ }
+#else
+#ifdef LOCKLESS
+ word36 v;
+ core_read_lock(addr, &v, ctx);
+ v = (v & ~cpu.zone) | (data & cpu.zone);
+ core_write_unlock(addr, v, ctx);
+#else
+#ifdef ISOLTS
+ if (cpu.switches.useMap)
+ {
+ uint pgnum = addr / SCBANK;
+ int os = cpu.scbank_pg_os [pgnum];
+ if (os < 0)
+ {
+ doFault (FAULT_STR, fst_str_nea, __func__);
+ }
+ addr = (uint) os + addr % SCBANK;
+ }
+ else
+#endif
+#ifndef SPEED
+ nem_check (addr, "core_read nem");
+#endif
+ LOCK_MEM_WR;
+ M[addr] = (M[addr] & ~cpu.zone) | (data & cpu.zone);
+ UNLOCK_MEM;
+#endif
+ cpu.useZone = false; // Safety
+#ifndef SPEED
+ if (watch_bits [addr])
+ {
+ sim_msg ("WATCH [%"PRId64"] %05o:%06o writez %08o %012"PRIo64" "
+ "(%s)\n", cpu.cycleCnt, cpu.PPR.PSR, cpu.PPR.IC, addr,
+ M [addr], ctx);
+ traceInstruction (0);
+ }
+#endif
+#endif
+#ifdef TR_WORK_MEM
+ cpu.rTRticks ++;
+#endif
+ sim_debug (DBG_CORE, & cpu_dev,
+ "core_write_zone %08o %012"PRIo64" (%s)\n",
+ addr, data, ctx);
+ PNL (trackport (addr, data));
+ return 0;
+ }
+#endif
+
+#if !defined(SPEED) || !defined(INLINE_CORE)
+int core_read2 (word24 addr, word36 *even, word36 *odd, const char * ctx)
+ {
+ PNL (cpu.portBusy = true;)
+ if (addr & 1)
+ {
+ sim_debug (DBG_MSG, & cpu_dev,
+ "warning: subtracting 1 from pair at %o in "
+ "core_read2 (%s)\n", addr, ctx);
+ addr &= (word24)~1; /* make it an even address */
+ }
+#ifdef ISOLTS
+ if (cpu.switches.useMap)
+ {
+ uint pgnum = addr / SCBANK;
+ int os = cpu.scbank_pg_os [pgnum];
+ if (os < 0)
+ {
+ doFault (FAULT_STR, fst_str_nea, __func__);
+ }
+ addr = (uint) os + addr % SCBANK;
+ }
+ else
+#endif
+#ifndef SPEED
+ nem_check (addr, "core_read2 nem");
+#endif
+
+#if 0 // XXX Controlled by TEST/NORMAL switch
+#ifdef ISOLTS
+ if (cpu.MR.sdpap)
+ {
+ sim_warn ("failing to implement sdpap\n");
+ cpu.MR.sdpap = 0;
+ }
+ if (cpu.MR.separ)
+ {
+ sim_warn ("failing to implement separ\n");
+ cpu.MR.separ = 0;
+ }
+#endif
+#endif
+#ifdef SCUMEM
+ word24 offset;
+ uint sci_unit_idx = get_scu_unit_idx (addr, & offset);
+ LOCK_MEM_RD;
+ *even = scu [sci_unit_idx].M[offset++] & DMASK;
+ UNLOCK_MEM;
+#ifndef SPEED
+ if (watch_bits [addr])
+ {
+ sim_msg ("WATCH [%"PRId64"] %05o:%06o read2 %08o %012"PRIo64" "
+ "(%s)\n", cpu.cycleCnt, cpu.PPR.PSR, cpu.PPR.IC, addr,
+ * even, ctx);
+ }
+#endif
+
+ sim_debug (DBG_CORE, & cpu_dev,
+ "core_read2 %08o %012"PRIo64" (%s)\n",
+ addr, * even, ctx);
+ LOCK_MEM_RD;
+ *odd = scu [sci_unit_idx].M[offset] & DMASK;
+ UNLOCK_MEM;
+#ifndef SPEED
+ if (watch_bits [addr+1])
+ {
+ sim_msg ("WATCH [%"PRId64"] %05o:%06o read2 %08o %012"PRIo64" "
+ "(%s)\n", cpu.cycleCnt, cpu.PPR.PSR, cpu.PPR.IC, addr+1,
+ * odd, ctx);
+ }
+#endif
+
+ sim_debug (DBG_CORE, & cpu_dev,
+ "core_read2 %08o %012"PRIo64" (%s)\n",
+ addr+1, * odd, ctx);
+#else
+#ifndef LOCKLESS
+ if (M[addr] & MEM_UNINITIALIZED)
+ {
+ sim_debug (DBG_WARN, & cpu_dev,
+ "Unitialized memory accessed at address %08o; "
+ "IC is 0%06o:0%06o (%s)\n",
+ addr, cpu.PPR.PSR, cpu.PPR.IC, ctx);
+ }
+#endif
+#ifndef SPEED
+ if (watch_bits [addr])
+ {
+ sim_msg ("WATCH [%"PRId64"] %05o:%06o read2 %08o %012"PRIo64" "
+ "(%s)\n", cpu.cycleCnt, cpu.PPR.PSR, cpu.PPR.IC, addr,
+ M [addr], ctx);
+ traceInstruction (0);
+ }
+#endif
+#ifdef LOCKLESS
+ word36 v;
+ LOAD_ACQ_CORE_WORD(v, addr);
+ if (v & MEM_LOCKED)
+ sim_warn ("core_read2: even locked %08o locked_addr %08o %c %05o:%06o\n",
+ addr, cpu.locked_addr, current_running_cpu_idx + 'A',
+ cpu.PPR.PSR, cpu.PPR.IC);
+ *even = v & DMASK;
+ addr++;
+#else
+ LOCK_MEM_RD;
+ *even = M[addr++] & DMASK;
+ UNLOCK_MEM;
+#endif
+ sim_debug (DBG_CORE, & cpu_dev,
+ "core_read2 %08o %012"PRIo64" (%s)\n",
+ addr - 1, * even, ctx);
+
+ // if the even address is OK, the odd will be
+ //nem_check (addr, "core_read2 nem");
+#ifndef LOCKLESS
+ if (M[addr] & MEM_UNINITIALIZED)
+ {
+ sim_debug (DBG_WARN, & cpu_dev,
+ "Unitialized memory accessed at address %08o; "
+ "IC is 0%06o:0%06o (%s)\n",
+ addr, cpu.PPR.PSR, cpu.PPR.IC, ctx);
+ }
+#endif
+#ifndef SPEED
+ if (watch_bits [addr])
+ {
+ sim_msg ("WATCH [%"PRId64"] %05o:%06o read2 %08o %012"PRIo64" "
+ "(%s)\n", cpu.cycleCnt, cpu.PPR.PSR, cpu.PPR.IC, addr,
+ M [addr], ctx);
+ traceInstruction (0);
+ }
+#endif
+#ifdef LOCKLESS
+ LOAD_ACQ_CORE_WORD(v, addr);
+ if (v & MEM_LOCKED)
+ sim_warn ("core_read2: odd locked %08o locked_addr %08o %c %05o:%06o\n",
+ addr, cpu.locked_addr, current_running_cpu_idx + 'A',
+ cpu.PPR.PSR, cpu.PPR.IC);
+ *odd = v & DMASK;
+#else
+ LOCK_MEM_RD;
+ *odd = M[addr] & DMASK;
+ UNLOCK_MEM;
+#endif
+ sim_debug (DBG_CORE, & cpu_dev,
+ "core_read2 %08o %012"PRIo64" (%s)\n",
+ addr, * odd, ctx);
+#endif
+#ifdef TR_WORK_MEM
+ cpu.rTRticks ++;
+#endif
+ PNL (trackport (addr - 1, * even));
+ return 0;
+ }
+#endif
+
+#if !defined(SPEED) || !defined(INLINE_CORE)
+int core_write2 (word24 addr, word36 even, word36 odd, const char * ctx)
+ {
+ PNL (cpu.portBusy = true;)
+ if (addr & 1)
+ {
+ sim_debug (DBG_MSG, & cpu_dev,
+ "warning: subtracting 1 from pair at %o in core_write2 "
+ "(%s)\n", addr, ctx);
+ addr &= (word24)~1; /* make it even a dress, or iron a skirt ;) */
+ }
+#ifdef ISOLTS
+ if (cpu.switches.useMap)
+ {
+ uint pgnum = addr / SCBANK;
+ int os = cpu.scbank_pg_os [pgnum];
+ if (os < 0)
+ {
+ doFault (FAULT_STR, fst_str_nea, __func__);
+ }
+ addr = (word24)os + addr % SCBANK;
+ }
+ else
+#endif
+#ifndef SPEED
+ nem_check (addr, "core_write2 nem");
+#endif
+#ifdef ISOLTS
+ if (cpu.MR.sdpap)
+ {
+ sim_warn ("failing to implement sdpap\n");
+ cpu.MR.sdpap = 0;
+ }
+ if (cpu.MR.separ)
+ {
+ sim_warn ("failing to implement separ\n");
+ cpu.MR.separ = 0;
+ }
+#endif
+
+#ifdef SCUMEM
+ word24 offset;
+ uint sci_unit_idx = get_scu_unit_idx (addr, & offset);
+ scu [sci_unit_idx].M[offset++] = even & DMASK;
+ if (watch_bits [addr])
+ {
+ sim_msg ("WATCH [%"PRId64"] %05o:%06o write2 %08o %012"PRIo64" "
+ "(%s)\n", cpu.cycleCnt, cpu.PPR.PSR, cpu.PPR.IC, addr,
+ even, ctx);
+ }
+ LOCK_MEM_WR;
+ scu [sci_unit_idx].M[offset] = odd & DMASK;
+ UNLOCK_MEM;
+ if (watch_bits [addr+1])
+ {
+ sim_msg ("WATCH [%"PRId64"] %05o:%06o write2 %08o %012"PRIo64" "
+ "(%s)\n", cpu.cycleCnt, cpu.PPR.PSR, cpu.PPR.IC, addr+1,
+ odd, ctx);
+ }
+#else
+#ifndef SPEED
+ if (watch_bits [addr])
+ {
+ sim_msg ("WATCH [%"PRId64"] %05o:%06o write2 %08o %012"PRIo64" "
+ "(%s)\n", cpu.cycleCnt, cpu.PPR.PSR, cpu.PPR.IC, addr,
+ even, ctx);
+ traceInstruction (0);
+ }
+#endif
+#ifdef LOCKLESS
+ LOCK_CORE_WORD(addr);
+ STORE_REL_CORE_WORD(addr, even);
+ addr++;
+#else
+ LOCK_MEM_WR;
+ M[addr++] = even & DMASK;
+ UNLOCK_MEM;
+#endif
+ sim_debug (DBG_CORE, & cpu_dev,
+ "core_write2 %08o %012"PRIo64" (%s)\n",
+ addr - 1, even, ctx);
+
+ // If the even address is OK, the odd will be
+ //nem_check (addr, "core_write2 nem");
+
+#ifndef SPEED
+ if (watch_bits [addr])
+ {
+ sim_msg ("WATCH [%"PRId64"] %05o:%06o write2 %08o %012"PRIo64" "
+ "(%s)\n", cpu.cycleCnt, cpu.PPR.PSR, cpu.PPR.IC, addr,
+ odd, ctx);
+ traceInstruction (0);
+ }
+#endif
+#ifdef LOCKLESS
+ LOCK_CORE_WORD(addr);
+ STORE_REL_CORE_WORD(addr, odd);
+#else
+ LOCK_MEM_WR;
+ M[addr] = odd & DMASK;
+ UNLOCK_MEM;
+#endif
+#endif
+#ifdef TR_WORK_MEM
+ cpu.rTRticks ++;
+#endif
+ PNL (trackport (addr - 1, even));
+ sim_debug (DBG_CORE, & cpu_dev,
+ "core_write2 %08o %012"PRIo64" (%s)\n",
+ addr, odd, ctx);
+ return 0;
+ }
+#endif
+
+
+
+/*
+ * instruction fetcher ...
+ * fetch + decode instruction at 18-bit address 'addr'
+ */
+
+/*
+ * instruction decoder .....
+ *
+ */
+
+void decode_instruction (word36 inst, DCDstruct * p)
+ {
+ CPT (cpt1L, 17); // instruction decoder
+ memset (p, 0, sizeof (DCDstruct));
+
+ p->opcode = GET_OP (inst); // get opcode
+ p->opcodeX = GET_OPX(inst); // opcode extension
+ p->opcode10 = p->opcode | (p->opcodeX ? 01000 : 0);
+ p->address = GET_ADDR (inst); // address field from instruction
+ p->b29 = GET_A (inst); // "A" the indirect via pointer register flag
+ p->i = GET_I (inst); // "I" inhibit interrupt flag
+ p->tag = GET_TAG (inst); // instruction tag
+
+ p->info = get_iwb_info (p); // get info for IWB instruction
+
+ if (p->info->flags & IGN_B29)
+ p->b29 = 0; // make certain 'a' bit is valid always
+
+ if (p->info->ndes > 0)
+ {
+ p->b29 = 0;
+ p->tag = 0;
+ if (p->info->ndes > 1)
+ {
+ memset (& cpu.currentEISinstruction, 0,
+ sizeof (cpu.currentEISinstruction));
+ }
+ }
+ }
+
+// MM stuff ...
+
+//
+// is_priv_mode()
+//
+// Report whether or or not the CPU is in privileged mode.
+// True if in absolute mode or if priv bit is on in segment TPR.TSR
+// The processor executes instructions in privileged mode when forming
+// addresses in absolute mode or when forming addresses in append mode and the
+// segment descriptor word (SDW) for the segment in execution specifies a
+// privileged procedure and the execution ring is equal to zero.
+//
+// PPR.P A flag controlling execution of privileged instructions.
+//
+// Its value is 1 (permitting execution of privileged instructions) if PPR.PRR
+// is 0 and the privileged bit in the segment descriptor word (SDW.P) for the
+// procedure is 1; otherwise, its value is 0.
+//
+
+int is_priv_mode (void)
+ {
+
+// Back when it was ABS/APP/BAR, this test was right; now that
+// it is ABS/APP,BAR/NBAR, check bar mode.
+// Fixes ISOLTS 890 05a.
+ if (get_bar_mode ())
+ return 0;
+
+// PPR.P is only relevant if we're in APPEND mode. ABSOLUTE mode ignores it.
+ if (get_addr_mode () == ABSOLUTE_mode)
+ return 1;
+ else if (cpu.PPR.P)
+ return 1;
+
+ return 0;
+ }
+
+
+/*
+ * get_bar_mode: During fault processing, we do not want to fetch and execute
+ * the fault vector instructions in BAR mode. We leverage the
+ * secret_addressing_mode flag that is set in set_TEMPORARY_ABSOLUTE_MODE to
+ * direct us to ignore the I_NBAR indicator register.
+ */
+
+bool get_bar_mode (void)
+ {
+ return ! (cpu.secret_addressing_mode || TST_I_NBAR);
+ }
+
+addr_modes_e get_addr_mode (void)
+ {
+ if (cpu.secret_addressing_mode)
+ return ABSOLUTE_mode; // This is not the mode you are looking for
+
+ // went_appending does not alter privileged state (only enables appending)
+ // the went_appending check is only required by ABSA, AFAICT
+ // pft 02b 013255, ISOLTS-860
+ //if (cpu.went_appending)
+ // return APPEND_mode;
+
+ if (TST_I_ABS)
+ {
+ return ABSOLUTE_mode;
+ }
+ else
+ {
+ return APPEND_mode;
+ }
+ }
+
+
+/*
+ * set_addr_mode()
+ *
+ * Put the CPU into the specified addressing mode. This involves
+ * setting a couple of IR flags and the PPR priv flag.
+ *
+ */
+
+void set_addr_mode (addr_modes_e mode)
+ {
+// cpu.cu.XSF = false;
+//sim_debug (DBG_TRACEEXT, & cpu_dev, "set_addr_mode bit 29 sets XSF to 0\n");
+ //cpu.went_appending = false;
+// Temporary hack to fix fault/intr pair address mode state tracking
+// 1. secret_addressing_mode is only set in fault/intr pair processing.
+// 2. Assume that the only set_addr_mode that will occur is the b29 special
+// case or ITx.
+ //if (secret_addressing_mode && mode == APPEND_mode)
+ //set_went_appending ();
+
+ cpu.secret_addressing_mode = false;
+ if (mode == ABSOLUTE_mode)
+ {
+ CPT (cpt1L, 22); // set abs mode
+ sim_debug (DBG_DEBUG, & cpu_dev, "APU: Setting absolute mode.\n");
+
+ SET_I_ABS;
+ cpu.PPR.P = 1;
+
+ }
+ else if (mode == APPEND_mode)
+ {
+ CPT (cpt1L, 23); // set append mode
+ if (! TST_I_ABS && TST_I_NBAR)
+ sim_debug (DBG_DEBUG, & cpu_dev, "APU: Keeping append mode.\n");
+ else
+ sim_debug (DBG_DEBUG, & cpu_dev, "APU: Setting append mode.\n");
+
+ CLR_I_ABS;
+ }
+ else
+ {
+ sim_debug (DBG_ERR, & cpu_dev,
+ "APU: Unable to determine address mode.\n");
+ sim_warn ("APU: Unable to determine address mode. Can't happen!\n");
+ }
+ }
+
+/*
+ * stuff to handle BAR mode ...
+ */
+
+
+/*
+ * The Base Address Register provides automatic hardware Address relocation and
+ * Address range limitation when the processor is in BAR mode.
+ *
+ * BAR.BASE: Contains the 9 high-order bits of an 18-bit address relocation
+ * constant. The low-order bits are generated as zeros.
+ *
+ * BAR.BOUND: Contains the 9 high-order bits of the unrelocated address limit.
+ * The low- order bits are generated as zeros. An attempt to access main memory
+ * beyond this limit causes a store fault, out of bounds. A value of 0 is truly
+ * 0, indicating a null memory range.
+ *
+ * In BAR mode, the base address register (BAR) is used. The BAR contains an
+ * address bound and a base address. All computed addresses are relocated by
+ * adding the base address. The relocated address is combined with the
+ * procedure pointer register to form the virtual memory address. A program is
+ * kept within certain limits by subtracting the unrelocated computed address
+ * from the address bound. If the result is zero or negative, the relocated
+ * address is out of range, and a store fault occurs.
+ */
+
+// CANFAULT
+word18 get_BAR_address (word18 addr)
+ {
+ if (cpu . BAR.BOUND == 0)
+ // store fault, out of bounds.
+ doFault (FAULT_STR, fst_str_oob, "BAR store fault; out of bounds");
+
+ // A program is kept within certain limits by subtracting the
+ // unrelocated computed address from the address bound. If the result
+ // is zero or negative, the relocated address is out of range, and a
+ // store fault occurs.
+ //
+ // BAR.BOUND - CA <= 0
+ // BAR.BOUND <= CA
+ // CA >= BAR.BOUND
+ //
+ if (addr >= (((word18) cpu . BAR.BOUND) << 9))
+ // store fault, out of bounds.
+ doFault (FAULT_STR, fst_str_oob, "BAR store fault; out of bounds");
+
+ word18 barAddr = (addr + (((word18) cpu . BAR.BASE) << 9)) & 0777777;
+ return barAddr;
+ }
+
+//=============================================================================
+
+static void add_history (uint hset, word36 w0, word36 w1)
+ {
+ //if (cpu.MR.emr)
+ {
+ cpu.history [hset] [cpu.history_cyclic[hset]] [0] = w0;
+ cpu.history [hset] [cpu.history_cyclic[hset]] [1] = w1;
+ cpu.history_cyclic[hset] = (cpu.history_cyclic[hset] + 1) % N_HIST_SIZE;
+ }
+ }
+
+void add_history_force (uint hset, word36 w0, word36 w1)
+ {
+ cpu.history [hset] [cpu.history_cyclic[hset]] [0] = w0;
+ cpu.history [hset] [cpu.history_cyclic[hset]] [1] = w1;
+ cpu.history_cyclic[hset] = (cpu.history_cyclic[hset] + 1) % N_HIST_SIZE;
+ }
+
+#ifdef DPS8M
+void add_CU_history (void)
+ {
+ if (cpu.skip_cu_hist)
+ return;
+ if (! cpu.MR_cache.emr)
+ return;
+ if (! cpu.MR_cache.ihr)
+ return;
+ if (cpu.MR_cache.hrxfr && ! cpu.wasXfer)
+ return;
+
+ word36 flags = 0; // XXX fill out
+ word5 proccmd = 0; // XXX fill out
+ word7 flags2 = 0; // XXX fill out
+ word36 w0 = 0, w1 = 0;
+ w0 |= flags & 0777777000000;
+ w0 |= IWB_IRODD & MASK18;
+ w1 |= (cpu.iefpFinalAddress & MASK24) << 12;
+ w1 |= (proccmd & MASK5) << 7;
+ w1 |= flags2 & 0176;
+ add_history (CU_HIST_REG, w0, w1);
+ }
+
+void add_DUOU_history (word36 flags, word18 ICT, word9 RS_REG, word9 flags2)
+ {
+ word36 w0 = flags, w1 = 0;
+ w1 |= (ICT & MASK18) << 18;
+ w1 |= (RS_REG & MASK9) << 9;
+ w1 |= flags2 & MASK9;
+ add_history (DU_OU_HIST_REG, w0, w1);
+ }
+
+void add_APU_history (word15 ESN, word21 flags, word24 RMA, word3 RTRR, word9 flags2)
+ {
+ word36 w0 = 0, w1 = 0;
+ w0 |= (ESN & MASK15) << 21;
+ w0 |= flags & MASK21;
+ w1 |= (RMA & MASK24) << 12;
+ w1 |= (RTRR & MASK3) << 9;
+ w1 |= flags2 & MASK9;
+ add_history (APU_HIST_REG, w0, w1);
+ }
+
+void add_EAPU_history (word18 ZCA, word18 opcode)
+ {
+ word36 w0 = 0;
+ w0 |= (ZCA & MASK18) << 18;
+ w0 |= opcode & MASK18;
+ add_history (EAPU_HIST_REG, w0, 0);
+ //cpu.eapu_hist[cpu.eapu_cyclic].ZCA = ZCA;
+ //cpu.eapu_hist[cpu.eapu_cyclic].opcode = opcode;
+ //cpu.history_cyclic[EAPU_HIST_REG] =
+ //(cpu.history_cyclic[EAPU_HIST_REG] + 1) % N_HIST_SIZE;
+ }
+#endif // DPS8M
+
+#ifdef L68
+
+// According to ISOLTS
+//
+// 0 PIA
+// 1 POA
+// 2 RIW
+// 3 SIW
+// 4 POT
+// 5 PON
+// 6 RAW
+// 7 SAW
+// 8 TRGO
+// 9 XDE
+// 10 XDO
+// 11 IC
+// 12 RPTS
+// 13 WI
+// 14 AR F/E
+// 15 XIP
+// 16 FLT
+// 17 COMPL. ADD BASE
+// 18:23 OPCODE/TAG
+// 24:29 ADDREG
+// 30:34 COMMAND A/B/C/D/E
+// 35:38 PORT A/B/C/D
+// 39 FB XEC
+// 40 INS FETCH
+// 41 CU STORE
+// 42 OU STORE
+// 43 CU LOAD
+// 44 OU LOAD
+// 45 RB DIRECT
+// 46 -PC BUSY
+// 47 PORT BUSY
+
+void add_CU_history (void)
+ {
+ CPT (cpt1L, 24); // add cu hist
+// XXX strobe on opcode match
+ if (cpu.skip_cu_hist)
+ return;
+ if (! cpu.MR_cache.emr)
+ return;
+ if (! cpu.MR_cache.ihr)
+ return;
+
+ word36 w0 = 0, w1 = 0;
+
+ // 0 PIA
+ // 1 POA
+ // 2 RIW
+ // 3 SIW
+ // 4 POT
+ // 5 PON
+ // 6 RAW
+ // 7 SAW
+ PNL (putbits36_8 (& w0, 0, cpu.prepare_state);)
+ // 8 TRG
+ putbits36_1 (& w0, 8, cpu.wasXfer);
+ // 9 XDE
+ putbits36_1 (& w0, 9, cpu.cu.xde);
+ // 10 XDO
+ putbits36_1 (& w0, 10, cpu.cu.xdo);
+ // 11 IC
+ putbits36_1 (& w0, 11, USE_IRODD?1:0);
+ // 12 RPT
+ putbits36_1 (& w0, 12, cpu.cu.rpt);
+ // 13 WI Wait for instruction fetch XXX Not tracked
+ // 14 ARF "AR F/E" Address register Full/Empty Address has valid data
+ PNL (putbits36_1 (& w0, 14, cpu.AR_F_E);)
+ // 15 !XA/Z "-XIP NOT prepare interrupt address"
+ putbits36_1 (& w0, 15, cpu.cycle != INTERRUPT_cycle?1:0);
+ // 16 !FA/Z Not tracked. (cu.-FL?)
+ putbits36_1 (& w0, 16, cpu.cycle != FAULT_cycle?1:0);
+ // 17 M/S (master/slave, cu.-BASE?, NOT BAR MODE)
+ putbits36_1 (& w0, 17, TSTF (cpu.cu.IR, I_NBAR)?1:0);
+ // 18:35 IWR (lower half of IWB)
+ putbits36_18 (& w0, 18, (word18) (IWB_IRODD & MASK18));
+
+ // 36:53 CA
+ putbits36_18 (& w1, 0, cpu.TPR.CA);
+ // 54:58 CMD system controller command XXX
+ // 59:62 SEL port select (XXX ignoring "only valid if port A-D is selected")
+ PNL (putbits36_1 (& w1, 59-36, (cpu.portSelect == 0)?1:0);)
+ PNL (putbits36_1 (& w1, 60-36, (cpu.portSelect == 1)?1:0);)
+ PNL (putbits36_1 (& w1, 61-36, (cpu.portSelect == 2)?1:0);)
+ PNL (putbits36_1 (& w1, 62-36, (cpu.portSelect == 3)?1:0);)
+ // 63 XEC-INT An interrupt is present
+ putbits36_1 (& w1, 63-36, cpu.interrupt_flag?1:0);
+ // 64 INS-FETCH Perform an instruction fetch
+ PNL (putbits36_1 (& w1, 64-36, cpu.INS_FETCH?1:0);)
+ // 65 CU-STORE Control unit store cycle XXX
+ // 66 OU-STORE Operations unit store cycle XXX
+ // 67 CU-LOAD Control unit load cycle XXX
+ // 68 OU-LOAD Operations unit load cycle XXX
+ // 69 DIRECT Direct cycle XXX
+ // 70 -PC-BUSY Port control logic not busy XXX
+ // 71 BUSY Port interface busy XXX
+
+ add_history (CU_HIST_REG, w0, w1);
+
+ // Check for overflow
+ CPTUR (cptUseMR);
+ if (cpu.MR.hrhlt && cpu.history_cyclic[CU_HIST_REG] == 0)
+ {
+ //cpu.history_cyclic[CU_HIST_REG] = 15;
+ if (cpu.MR.ihrrs)
+ {
+ cpu.MR.ihr = 0;
+ }
+ set_FFV_fault (4);
+ return;
+ }
+ }
+
+// du history register inputs(actual names)
+// bit 00= fpol-cx;010 bit 36= fdud-dg;112
+// bit 01= fpop-cx;010 bit 37= fgdlda-dc;010
+// bit 02= need-desc-bd;000 bit 38= fgdldb-dc;010
+// bit 03= sel-adr-bd;000 bit 39= fgdldc-dc;010
+// bit 04= dlen=direct-bd;000bit 40= fnld1-dp;110
+// bit 05= dfrst-bd;021 bit 41= fgldp1-dc;110
+// bit 06= fexr-bd;010 bit 42= fnld2-dp;110
+// bit 07= dlast-frst-bd;010 bit 43= fgldp2-dc;110
+// bit 08= ddu-ldea-bd;000 bit 44= fanld1-dp;110
+// bit 09= ddu-stea-bd;000 bit 45= fanld2-dp;110
+// bit 10= dredo-bd;030 bit 46= fldwrt1-dp;110
+// bit 11= dlvl<wd-sz-bg;000 bit 47= fldwrt2-dp;110
+// bit 12= exh-bg;000 bit 48= data-avldu-cm;000
+// bit 13= dend-seg-bd;111 bit 49= fwrt1-dp;110
+// bit 14= dend-bd;000 bit 50= fgstr-dc;110
+// bit 15= du=rd+wrt-bd;010 bit 51= fanstr-dp;110
+// bit 16= ptra00-bd;000 bit 52= fstr-op-av-dg;010
+// bit 17= ptra01-bd;000 bit 53= fend-seg-dg;010
+// bit 18= fa/i1-bd;110 bit 54= flen<128-dg;010
+// bit 19= fa/i2-bd;110 bit 55= fgch-dp;110
+// bit 20= fa/i3-bd;110 bit 56= fanpk-dp;110
+// bit 21= wrd-bd;000 bit 57= fexmop-dl;110
+// bit 22= nine-bd;000 bit 58= fblnk-dp;100
+// bit 23= six-bd;000 bit 59= unused
+// bit 24= four-bd;000 bit 60= dgbd-dc;100
+// bit 25= bit-bd;000 bit 61= dgdb-dc;100
+// bit 26= unused bit 62= dgsp-dc;100
+// bit 27= unused bit 63= ffltg-dc;110
+// bit 28= unused bit 64= frnd-dg;120
+// bit 29= unused bit 65= dadd-gate-dc;100
+// bit 30= fsampl-bd;111 bit 66= dmp+dv-gate-db;100
+// bit 31= dfrst-ct-bd;010 bit 67= dxpn-gate-dg;100
+// bit 32= adj-lenint-cx;000 bit 68= unused
+// bit 33= fintrptd-cx;010 bit 69= unused
+// bit 34= finhib-stc1-cx;010bit 70= unused
+// bit 35= unused bit 71= unused
+
+void add_DU_history (void)
+ {
+ CPT (cpt1L, 25); // add du hist
+ PNL (add_history (DU_HIST_REG, cpu.du.cycle1, cpu.du.cycle2);)
+ }
+
+
+void add_OU_history (void)
+ {
+ CPT (cpt1L, 26); // add ou hist
+ word36 w0 = 0, w1 = 0;
+
+ // 0-16 RP
+ // 0-8 OP CODE
+ PNL (putbits36_9 (& w0, 0, cpu.ou.RS);)
+
+ // 9 CHAR
+ putbits36_1 (& w0, 9, cpu.ou.characterOperandSize ? 1 : 0);
+
+ // 10-12 TAG 1/2/3
+ putbits36_3 (& w0, 10, cpu.ou.characterOperandOffset);
+
+ // 13 CRFLAG
+ putbits36_1 (& w0, 13, cpu.ou.crflag);
+
+ // 14 DRFLAG
+ putbits36_1 (& w0, 14, cpu.ou.directOperandFlag ? 1 : 0);
+
+ // 15-16 EAC
+ putbits36_2 (& w0, 15, cpu.ou.eac);
+
+ // 17 0
+ // 18-26 RS REG
+ PNL (putbits36_9 (& w0, 18, cpu.ou.RS);)
+
+ // 27 RB1 FULL
+ putbits36_1 (& w0, 27, cpu.ou.RB1_FULL);
+
+ // 28 RP FULL
+ putbits36_1 (& w0, 28, cpu.ou.RP_FULL);
+
+ // 29 RS FULL
+ putbits36_1 (& w0, 29, cpu.ou.RS_FULL);
+
+ // 30-35 GIN/GOS/GD1/GD2/GOE/GOA
+ putbits36_6 (& w0, 30, (word6) (cpu.ou.cycle >> 3));
+
+ // 36-38 GOM/GON/GOF
+ putbits36_3 (& w1, 36-36, (word3) cpu.ou.cycle);
+
+ // 39 STR OP
+ putbits36_1 (& w1, 39-36, cpu.ou.STR_OP);
+
+ // 40 -DA-AV XXX
+
+ // 41-50 stuvwyyzAB -A-REG -Q-REG -X0-REG .. -X7-REG
+ PNL (putbits36_10 (& w1, 41-36,
+ (word10) ~opcodes10 [cpu.ou.RS].reg_use);)
+
+ // 51-53 0
+
+ // 54-71 ICT TRACKER
+ putbits36_18 (& w1, 54 - 36, cpu.PPR.IC);
+
+ add_history (OU_HIST_REG, w0, w1);
+ }
+
+// According to ISOLTS
+// 0:2 OPCODE RP
+// 3 9 BIT CHAR
+// 4:6 TAG 3/4/5
+// 7 CR FLAG
+// 8 DIR FLAG
+// 9 RP15
+// 10 RP16
+// 11 SPARE
+// 12:14 OPCODE RS
+// 15 RB1 FULL
+// 16 RP FULL
+// 17 RS FULL
+// 18 GIN
+// 19 GOS
+// 20 GD1
+// 21 GD2
+// 22 GOE
+// 23 GOA
+// 24 GOM
+// 25 GON
+// 26 GOF
+// 27 STORE OP
+// 28 DA NOT
+// 29:38 COMPLEMENTED REGISTER IN USE FLAG A/Q/0/1/2/3/4/5/6/7
+// 39 ?
+// 40 ?
+// 41 ?
+// 42:47 ICT TRACT
+
+// XXX add_APU_history
+
+// 0:5 SEGMENT NUMBER
+// 6 SNR/ESN
+// 7 TSR/ESN
+// 8 FSDPTW
+// 9 FPTW2
+// 10 MPTW
+// 11 FANP
+// 12 FAP
+// 13 AMSDW
+// 14:15 AMSDW #
+// 16 AMPTW
+// 17:18 AMPW #
+// 19 ACV/DF
+// 20:27 ABSOLUTE MEMORY ADDRESS
+// 28 TRR #
+// 29 FLT HLD
+
+void add_APU_history (enum APUH_e op)
+ {
+ CPT (cpt1L, 28); // add apu hist
+ word36 w0 = 0, w1 = 0;
+
+ w0 = op; // set 17-24 FDSPTW/.../FAP bits
+
+ // 0-14 ESN
+ putbits36_15 (& w0, 0, cpu.TPR.TSR);
+ // 15-16 BSY
+ PNL (putbits36_1 (& w0, 15, (cpu.apu.state & apu_ESN_SNR) ? 1 : 0);)
+ PNL (putbits36_1 (& w0, 16, (cpu.apu.state & apu_ESN_TSR) ? 1 : 0);)
+ // 25 SDWAMM
+ putbits36_1 (& w0, 25, cpu.cu.SDWAMM);
+ // 26-29 SDWAMR
+#ifdef WAM
+ putbits36_4 (& w0, 26, cpu.SDWAMR);
+#endif
+ // 30 PTWAMM
+ putbits36_1 (& w0, 30, cpu.cu.PTWAMM);
+ // 31-34 PTWAMR
+#ifdef WAM
+ putbits36_4 (& w0, 31, cpu.PTWAMR);
+#endif
+ // 35 FLT
+ PNL (putbits36_1 (& w0, 35, (cpu.apu.state & apu_FLT) ? 1 : 0);)
+
+ // 36-59 ADD
+ PNL (putbits36_24 (& w1, 0, cpu.APUMemAddr);)
+ // 60-62 TRR
+ putbits36_3 (& w1, 24, cpu.TPR.TRR);
+ // 66 XXX Multiple match error in SDWAM
+ // 70 Segment is encachable
+ putbits36_1 (& w1, 34, cpu.SDW0.C);
+ // 71 XXX Multiple match error in PTWAM
+
+ add_history (APU_HIST_REG, w0, w1);
+ }
+
+#endif
+
+#if defined(THREADZ) || defined(LOCKLESS)
+//static pthread_mutex_t debug_lock = PTHREAD_MUTEX_INITIALIZER;
+
+static const char * get_dbg_verb (uint32 dbits, DEVICE * dptr)
+ {
+ static const char * debtab_none = "DEBTAB_ISNULL";
+ static const char * debtab_nomatch = "DEBTAB_NOMATCH";
+ const char * some_match = NULL;
+ int32 offset = 0;
+
+ if (dptr->debflags == 0)
+ return debtab_none;
+
+ dbits &= dptr->dctrl; /* Look for just the bits tha matched */
+
+ /* Find matching words for bitmask */
+
+ while (dptr->debflags[offset].name && (offset < 32))
+ {
+ if (dptr->debflags[offset].mask == dbits) /* All Bits Match */
+ return dptr->debflags[offset].name;
+ if (dptr->debflags[offset].mask & dbits)
+ some_match = dptr->debflags[offset].name;
+ offset ++;
+ }
+ return some_match ? some_match : debtab_nomatch;
+ }
+
+void dps8_sim_debug (uint32 dbits, DEVICE * dptr, unsigned long long cnt, const char* fmt, ...)
+ {
+ //pthread_mutex_lock (& debug_lock);
+ if (sim_deb && dptr && (dptr->dctrl & dbits))
+ {
+ const char * debug_type = get_dbg_verb (dbits, dptr);
+ char stackbuf[STACKBUFSIZE];
+ int32 bufsize = sizeof (stackbuf);
+ char * buf = stackbuf;
+ va_list arglist;
+ int32 i, j, len;
+ struct timespec t;
+ clock_gettime(CLOCK_REALTIME, &t);
+
+ buf [bufsize-1] = '\0';
+
+ while (1)
+ { /* format passed string, args */
+ va_start (arglist, fmt);
+#if defined(NO_vsnprintf)
+ len = vsprintf (buf, fmt, arglist);
+#else /* !defined(NO_vsnprintf) */
+ len = vsnprintf (buf, (unsigned long) bufsize-1, fmt, arglist);
+#endif /* NO_vsnprintf */
+ va_end (arglist);
+
+/* If the formatted result didn't fit into the buffer, then grow the buffer and try again */
+
+ if ((len < 0) || (len >= bufsize-1))
+ {
+ if (buf != stackbuf)
+ free (buf);
+ bufsize = bufsize * 2;
+ if (bufsize < len + 2)
+ bufsize = len + 2;
+ buf = (char *) malloc ((unsigned long) bufsize);
+ if (buf == NULL) /* out of memory */
+ return;
+ buf[bufsize-1] = '\0';
+ continue;
+ }
+ break;
+ }
+
+/* Output the formatted data expanding newlines where they exist */
+
+ for (i = j = 0; i < len; ++i)
+ {
+ if ('\n' == buf[i])
+ {
+ if (i >= j)
+ {
+ if ((i != j) || (i == 0))
+ {
+ fprintf (sim_deb, "%ld.%06ld: DBG(%lld) %o: %s %s %.*s\r\n", t.tv_sec, t.tv_nsec/1000, cnt, current_running_cpu_idx, dptr->name, debug_type, i-j, &buf[j]);
+ }
+ }
+ j = i + 1;
+ }
+ }
+
+/* Set unterminated flag for next time */
+
+ if (buf != stackbuf)
+ free (buf);
+ }
+ //pthread_mutex_unlock (& debug_lock);
+ }
+#endif
--- /dev/null
+/*
+ Copyright (c) 2007-2013 Michael Mondy
+ Copyright 2012-2016 by Harry Reed
+ Copyright 2013-2018 by Charles Anthony
+ Copyright 2015 by Eric Swenson
+
+ All rights reserved.
+
+ This software is made available under the terms of the
+ ICU License -- ICU 1.8.1 and later.
+ See the LICENSE file at the top-level directory of this distribution and
+ at https://sourceforge.net/p/dps8m/code/ci/master/tree/LICENSE
+ */
+
+#include "hdbg.h"
+
+// simh only explicitly supports a single cpu
+
+#define N_CPU_UNITS 1 // Default
+
+// JMP_ENTRY must be 0, which is the return value of the setjmp initial
+// entry
+#define JMP_ENTRY 0
+#define JMP_REENTRY 1
+#define JMP_STOP 2
+#define JMP_SYNC_FAULT_RETURN 3
+#define JMP_REFETCH 4
+#define JMP_RESTART 5
+
+
+// The CPU supports 3 addressing modes
+// [CAC] I tell a lie: 4 modes...
+// [CAC] I tell another lie: 5 modes...
+
+typedef enum
+ {
+ ABSOLUTE_mode,
+ APPEND_mode,
+ } addr_modes_e;
+
+
+// The control unit of the CPU is always in one of several states. We
+// don't currently use all of the states used in the physical CPU.
+// The FAULT_EXEC cycle did not exist in the physical hardware.
+
+typedef enum
+ {
+ FAULT_cycle,
+ EXEC_cycle,
+ FAULT_EXEC_cycle,
+ INTERRUPT_cycle,
+ INTERRUPT_EXEC_cycle,
+ FETCH_cycle,
+ PSEUDO_FETCH_cycle,
+ SYNC_FAULT_RTN_cycle,
+ } cycles_e;
+
+struct tpr_s
+ {
+ word3 TRR; // The current effective ring number
+ word15 TSR; // The current effective segment number
+ word6 TBR; // The current bit offset as calculated from ITS and ITP
+ // pointer pairs.
+ word18 CA; // The current computed address relative to the origin of the
+ // segment whose segment number is in TPR.TSR
+ };
+
+struct ppr_s
+ {
+ word3 PRR; // The number of the ring in which the process is executing.
+ // It is set to the effective ring number of the procedure
+ // segment when control is transferred to the procedure.
+ word15 PSR; // The segment number of the procedure being executed.
+ word1 P; // A flag controlling execution of privileged instructions.
+ // Its value is 1 (permitting execution of privileged
+ // instructions) if PPR.PRR is 0 and the privileged bit in
+ // the segment descriptor word (SDW.P) for the procedure is
+ // 1; otherwise, its value is 0.
+ word18 IC; // The word offset from the origin of the procedure segment
+ // to the current instruction. (same as PPR.IC)
+ };
+
+/////
+
+// The terms "pointer register" and "address register" both apply to the same
+// physical hardware. The distinction arises from the manner in which the
+// register is used and in the interpretation of the register contents.
+// "Pointer register" refers to the register as used by the appending unit and
+// "address register" refers to the register as used by the decimal unit.
+//
+// The three forms are compatible and may be freely intermixed. For example,
+// PRn may be loaded in pointer register form with the Effective Pointer to
+// Pointer Register n (eppn) instruction, then modified in pointer register
+// form with the Effective Address to Word/Bit Number of Pointer Register n
+// (eawpn) instruction, then further modified in address register form
+// (assuming character size k) with the Add k-Bit Displacement to Address
+// Register (akbd) instruction, and finally invoked in operand descriptor form
+// by the use of MF.AR in an EIS multiword instruction .
+//
+// The reader's attention is directed to the presence of two bit number
+// registers, PRn.BITNO and ARn.BITNO. Because the Multics processor was
+// implemented as an enhancement to an existing design, certain apparent
+// anomalies appear. One of these is the difference in the handling of
+// unaligned data items by the appending unit and decimal unit. The decimal
+// unit handles all unaligned data items with a 9-bit byte number and bit
+// offset within the byte. Conversion from the description given in the EIS
+// operand descriptor is done automatically by the hardware. The appending unit
+// maintains compatibility with the earlier generation Multics processor by
+// handling all unaligned data items with a bit offset from the prior word
+// boundary; again with any necessary conversion done automatically by the
+// hardware. Thus, a pointer register, PRn, may be loaded from an ITS pointer
+// pair having a pure bit offset and modified by one of the EIS address
+// register instructions (a4bd, s9bd, etc.) using character displacement
+// counts. The automatic conversion performed ensures that the pointer
+// register, PRi, and its matching address register, ARi, both describe the
+// same physical bit in main memory.
+//
+// N.B. Subtle differences between the interpretation of PR/AR. Need to take
+// this into account.
+//
+// * For Pointer Registers:
+// - PRn.WORDNO The offset in words from the base or origin of the
+// segment to the data item.
+// - PRn.BITNO The number of the bit within PRn.WORDNO that is the
+// first bit of the data item. Data items aligned on word
+// boundaries always have the value 0. Unaligned data items
+// may have any value in the range [1,35].
+//
+// * For Address Registers:
+// - ARn.WORDNO The offset in words relative to the current addressing
+// base referent (segment origin, BAR.BASE, or absolute 0
+// depending on addressing mode) to the word containing the
+// next data item element.
+// - ARn.CHAR The number of the 9-bit byte within ARn.WORDNO
+// containing the first bit of the next data item element.
+// - ARn.BITNO The number of the bit within ARn.CHAR that is the
+// first bit of the next data item element.
+//
+
+struct par_s
+ {
+ word15 SNR; // The segment number of the segment containing the data
+ // item described by the pointer register.
+ word3 RNR; // The final effective ring number value calculated during
+ // execution of the instruction that last loaded the PR.
+
+ word6 PR_BITNO; // The number of the bit within PRn.WORDNO that is the
+ // first bit of the data item. Data items aligned on word
+ // boundaries always have the value 0. Unaligned data
+ // items may have any value in the range [1,35].
+ word2 AR_CHAR;
+ word4 AR_BITNO;
+
+ word18 WORDNO; // The offset in words from the base or origin of the
+ // segment to the data item.
+ };
+
+// N.B. remember there are subtle differences between AR/PR.BITNO
+
+#define AR PAR
+#define PR PAR
+
+struct bar_s
+ {
+ word9 BASE; // Contains the 9 high-order bits of an 18-bit address
+ // relocation constant. The low-order bits are generated
+ // as zeros.
+ word9 BOUND; // Contains the 9 high-order bits of the unrelocated
+ // address limit. The low- order bits are generated as
+ // zeros. An attempt to access main memory beyond this
+ // limit causes a store fault, out of bounds. A value of
+ // 0 is truly 0, indicating a null memory range.
+ };
+
+struct dsbr_s
+ {
+ word24 ADDR; // If DSBR.U = 1, the 24-bit absolute main memory address
+ // of the origin of the current descriptor segment;
+ // otherwise, the 24-bit absolute main memory address of
+ // the page table for the current descriptor segment.
+ word14 BND; // The 14 most significant bits of the highest Y-block16
+ // address of the descriptor segment that can be
+ // addressed without causing an access violation, out of
+ // segment bounds, fault.
+ word1 U; // A flag specifying whether the descriptor segment is
+ // unpaged (U = 1) or paged (U = 0).
+ word12 STACK; // The upper 12 bits of the 15-bit stack base segment
+ // number. It is used only during the execution of the
+ // call6 instruction. (See Section 8 for a discussion
+ // of generation of the stack segment number.)
+ };
+
+// The segment descriptor word (SDW) pair contains information that controls
+// the access to a segment. The SDW for segment n is located at offset 2n in
+// the descriptor segment whose description is currently loaded into the
+// descriptor segment base register (DSBR).
+
+struct sdw_s
+ {
+ word24 ADDR; // The 24-bit absolute main memory address of the page
+ // table for the target segment if SDWAM.U = 0;
+ // otherwise, the 24-bit absolute main memory address
+ // of the origin of the target segment.
+ word3 R1; // Upper limit of read/write ring bracket
+ word3 R2; // Upper limit of read/execute ring bracket
+ word3 R3; // Upper limit of call ring bracket
+ word14 BOUND; // The 14 high-order bits of the last Y-block16 address
+ // within the segment that can be referenced without an
+ // access violation, out of segment bound, fault.
+ word1 R; // Read permission bit. If this bit is set ON, read
+ // access requests are allowed.
+ word1 E; // Execute permission bit. If this bit is set ON, the SDW
+ // may be loaded into the procedure pointer register
+ // (PPR) and instructions fetched from the segment for
+ // execution.
+ word1 W; // Write permission bit. If this bit is set ON, write
+ // access requests are allowed.
+ word1 P; // Privileged flag bit. If this bit is set ON, privileged
+ // instructions from the segment may be executed if
+ // PPR.PRR is 0.
+ word1 U; // Unpaged flag bit. If this bit is set ON, the segment
+ // is unpaged and SDWAM.ADDR is the 24-bit absolute
+ // main memory address of the origin of the segment. If
+ // this bit is set OFF, the segment is paged andis
+ // SDWAM.ADDR the 24-bit absolute main memory address of
+ // the page table for the segment.
+ word1 G; // Gate control bit. If this bit is set OFF, calls and
+ // transfers into the segment must be to an offset no
+ // greater than the value of SDWAM.CL as described
+ // below.
+ word1 C; // Cache control bit. If this bit is set ON, data and/or
+ // instructions from the segment may be placed in the
+ // cache memory.
+ word14 EB; // Call limiter (entry bound) value. If SDWAM.G is set
+ // OFF, transfers of control into the segment must be to
+ // segment addresses no greater than this value.
+ word15 POINTER; // The effective segment number used to fetch this SDW
+ // from main memory.
+ word1 DF; // Directed fault flag (called F in AL39).
+ // * 0 = page not in main memory; execute directed fault
+ // FC
+ // * 1 = page is in main memory
+ word2 FC; // Directed fault number for page fault.
+ word1 FE; // Full/empty bit. If this bit is set ON, the SDW in the
+ // register is valid. If this bit is set OFF, a hit is
+ // not possible. All SDWAM.F bits are set OFF by the
+ // instructions that clear the SDWAM.
+#ifdef DPS8M
+ word6 USE;
+#endif
+#ifdef L68
+ word4 USE;
+#endif
+ // Usage count for the register. The SDWAM.USE field is
+ // used to maintain a strict FIFO queue order among the
+ // SDWs. When an SDW is matched, its USE value is set to
+ // 15 (newest) on the DPS/L68 and to 63 on the DPS 8M,
+ // and the queue is reordered. SDWs newly fetched from
+ // main memory replace the SDW with USE value 0 (oldest)
+ // and the queue is reordered.
+ };
+
+typedef struct sdw_s sdw_s;
+typedef struct sdw_s sdw0_s;
+
+#if 0
+// in-core SDW (i.e. not cached, or in SDWAM)
+
+struct sdw0_s
+ {
+ // even word
+ word24 ADDR; // The 24-bit absolute main memory address of the page
+ // table for the target segment if SDWAM.U = 0;
+ // otherwise, the 24-bit absolute main memory address of
+ // the origin of the target segment.
+ word3 R1; // Upper limit of read/write ring bracket
+ word3 R2; // Upper limit of read/execute ring bracket
+ word3 R3; // Upper limit of call ring bracket
+ word1 DF; // Directed fault flag (called F in AL39).
+ // * 0 = page not in main memory; execute directed fault
+ // FC
+ // * 1 = page is in main memory
+ word2 FC; // Directed fault number for page fault.
+
+ // odd word
+ word14 BOUND; // The 14 high-order bits of the last Y-block16 address
+ // within the segment that can be referenced without an
+ // access violation, out of segment bound, fault.
+ word1 R; // Read permission bit. If this bit is set ON, read
+ // access requests are allowed.
+ word1 E; // Execute permission bit. If this bit is set ON, the SDW
+ // may be loaded into the procedure pointer register
+ // (PPR) and instructions fetched from the segment for
+ // execution.
+ word1 W; // Write permission bit. If this bit is set ON, write
+ // access requests are allowed.
+ word1 P; // Privileged flag bit. If this bit is set ON,
+ // privileged instructions from the segment may be
+ // executed if PPR.PRR is 0.
+ word1 U; // Unpaged flag bit. If this bit is set ON, the segment
+ // is unpaged and SDWAM.ADDR is the 24-bit absolute
+ // main memory address of the origin of the segment.
+ // If this bit is set OFF, the segment is paged and
+ // SDWAM.ADDR is the 24-bit absolute main memory
+ // address of the page table for the segment.
+ word1 G; // Gate control bit. If this bit is set OFF, calls and
+ // transfers into the segment must be to an offset no
+ // greater than the value of SDWAM.CL as described
+ // below.
+ word1 C; // Cache control bit. If this bit is set ON, data and/or
+ // instructions from the segment may be placed in the
+ // cache memory.
+ word14 EB; // Entry bound. Any call into this segment must be to
+ // an offset less than EB if G=0
+};
+
+typedef struct sdw0_s sdw0_s;
+#endif
+
+
+// PTW as used by APU
+
+struct ptw_s
+ {
+ word18 ADDR; // The 18 high-order bits of the 24-bit absolute
+ // main memory address of the page.
+ word1 U; // * 1 = page has been used (referenced)
+ word1 M; // Page modified flag bit. This bit is set ON whenever
+ // the PTW is used for a store type instruction. When
+ // the bit changes value from 0 to 1, a special
+ // extra cycle is generated to write it back into the
+ // PTW in the page table in main memory.
+ word1 DF; // Directed fault flag
+ // * 0 = page not in main memory; execute directed fault FC
+ // * 1 = page is in main memory
+ word2 FC; // Directed fault number for page fault.
+ word15 POINTER; // The effective segment number used to fetch this PTW
+ // from main memory.
+ word12 PAGENO; // The 12 high-order bits of the 18-bit computed
+ // address (TPR.CA) used to fetch this PTW from main
+ // memory.
+ word1 FE; // Full/empty bit. If this bit is set ON, the PTW in
+ // the register is valid. If this bit is set OFF, a
+ // hit is not possible. All PTWAM.F bits are set OFF
+ // by the instructions that clear the PTWAM.
+#ifdef DPS8M
+ word6 USE;
+#endif
+
+#ifdef L68
+ word4 USE;
+#endif
+ // Usage count for the register. The PTWAM.USE field
+ // is used to maintain a strict FIFO queue order
+ // among the PTWs. When an PTW is matched its USE
+ // value is set to 15 (newest) on the DPS/L68 and to
+ // 63 on the DPS 8M, and the queue is reordered.
+ // PTWs newly fetched from main memory replace the
+ // PTW with USE value 0 (oldest) and the queue is
+ // reordered.
+
+ };
+
+typedef struct ptw_s ptw_s;
+typedef struct ptw_s ptw0_s;
+
+#if 0
+// in-core PTW
+
+struct ptw0_s
+ {
+ word18 ADDR; // The 18 high-order bits of the 24-bit absolute main
+ // memory address of the page.
+ word1 U; // * 1 = page has been used (referenced)
+ word1 M; // * 1 = page has been modified
+ word1 DF; // Directed fault flag
+ // * 0 = page not in main memory; execute directed fault FC
+ // * 1 = page is in main memory
+ word2 FC; // Directed fault number for page fault.
+
+ };
+
+typedef struct ptw0_s ptw0_s;
+#endif
+
+//
+// Cache Mode Regsiter
+//
+
+struct cache_mode_register_s
+ {
+ word15 cache_dir_address;
+ word1 par_bit;
+ word1 lev_ful;
+ word1 csh1_on; // 1: The lower half of the cache memory is active and
+ // enabled as per the state of inst_on
+ word1 csh2_on; // 1: The upper half of the cache memory is active and
+ // enabled as per the state of inst_on
+#ifdef L68
+ word1 opnd_on; // 1: The cache memory (if active) is used for
+ // operands.
+#endif
+ word1 inst_on; // 1: The cache memory (if active) is used for
+ // instructions.
+ // When the cache-to-register mode flag (bit 59 of the cache mode register)
+ // is set ON, the processor is forced to fetch the operands of all
+ // double-precision operations unit load operations from the cache memory.
+ // Y0,12 are ignored, Y15,21 select a column, and Y13,14 select a level.
+ // All other operations (e.g., instruction fetches, single-precision
+ // operands, etc.) are treated normally.
+ word1 csh_reg;
+ word1 str_asd;
+ word1 col_ful;
+ word2 rro_AB;
+#ifdef DPS8M
+ word1 bypass_cache;
+#endif
+ word2 luf; // LUF value
+ // 0 1 2 3
+ // Lockup time
+ // 2ms 4ms 8ms 16ms
+ // The lockup timer is set to 16ms when the
+ // processor is initialized.
+ };
+
+typedef struct cache_mode_register_s cache_mode_register_s;
+
+typedef struct mode_register_s
+ {
+ word36 r;
+#ifdef L68
+ // 8M L68
+ word15 FFV; // 0 FFV 0 - 14
+ word1 OC_TRAP; // 0 a 16
+ word1 ADR_TRAP; // 0 b 17
+ word9 OPCODE; // 0 OPCODE 18 - 26
+ word1 OPCODEX; // 0 OPCODE 27
+#endif
+ // word1 cuolin; // a c 18 control unit overlap inhibit
+ // word1 solin; // b d 19 store overlap inhibit
+ word1 sdpap; // c e 20 store incorrect data parity
+ word1 separ; // d f 21 store incorrect ZAC
+ // word2 tm; // e g 22 - 23 timing margins
+ // word2 vm; // f h 24 - 25 voltage margins
+ // 0 0 26 history register overflow trap
+ // 0 0 27 strobe HR on opcode match
+ word1 hrhlt; // g i 28 history register overflow trap
+
+#ifdef DPS8M
+ word1 hrxfr; // h j 29 strobe HR on transfer made
+#endif
+#ifdef L68
+ // h j 29 strobe HR on opcode match
+#endif
+ word1 ihr; // i k 30 Enable HR
+ word1 ihrrs; // j 31 HR reset options
+ // l 31 HR lock control
+ // k 32 margin control
+ // m 32 test mode indicator
+#ifdef DPS8M
+ word1 hexfp; // l 0 33 hex mode
+#endif
+ // 0 0 34
+ word1 emr; // m n 35 enable MR
+ } mode_register_s;
+
+extern DEVICE cpu_dev;
+
+typedef struct MOP_struct_s
+ {
+ char * mopName; // name of microoperation
+ int (* f) (void); // pointer to mop() [returns character to be stored]
+ } MOP_struct;
+
+// address of an EIS operand
+typedef struct EISaddr_s
+ {
+#ifndef EIS_PTR
+ word18 address; // 18-bit virtual address
+#endif
+
+ word36 data;
+ word1 bit;
+ eRW mode;
+
+ int last_bit_posn; // track for caching tests
+
+ // for type of data being address by this object
+
+ // eisDataType _type; // type of data - alphunumeric/numeric
+
+#ifndef EIS_PTR3
+ int TA; // type of Alphanumeric chars in src
+#endif
+ int TN; // type of Numeric chars in src
+ int cPos;
+ int bPos;
+
+#ifndef EIS_PTR4
+ // for when using AR/PR register addressing
+ word15 SNR; // The segment number of the segment containing the
+ // data item described by the pointer register.
+ word3 RNR; // The effective ring number value calculated during
+ // execution of the instruction that last loaded
+ MemoryAccessType mat; // memory access type for operation
+#endif
+
+ // Cache
+
+ // There is a cache for each operand, but they do not cross check;
+ // this means that if one of them has a cached dirty word, the
+ // others will not check for a hit, and will use the old value.
+ // AL39 warns that overlapping operands can cause unexpected behavior
+ // due to caching issues, so the this behavior is closer to the actual
+ // h/w then to the theoretical need for cache consistancy.
+
+ // We don't need to cache mat or TPR because they will be constant
+ // across an instruction.
+
+ bool cacheValid;
+ bool cacheDirty;
+#define paragraphSz 8
+#define paragraphMask 077777770
+#define paragraphOffsetMask 07
+ word36 cachedParagraph [paragraphSz];
+#ifdef CWO
+ bool wordDirty [paragraphSz];
+#endif
+ word18 cachedAddr;
+
+ } EISaddr;
+
+typedef struct EISstruct_s
+ {
+ word36 op [3]; // raw operand descriptors
+#define OP1 op [0] // 1st descriptor (2nd ins word)
+#define OP2 op [1] // 2nd descriptor (3rd ins word)
+#define OP3 op [2] // 3rd descriptor (4th ins word)
+
+ bool P; // 4-bit data sign character control
+
+ uint MF [3];
+#define MF1 MF [0] // Modification field for operand descriptor 1
+#define MF2 MF [1] // Modification field for operand descriptor 2
+#define MF3 MF [2] // Modification field for operand descriptor 3
+
+
+ uint CN [3];
+#define CN1 CN [0]
+#define CN2 CN [1]
+#define CN3 CN [2]
+
+ uint WN [3];
+#define WN1 WN [0]
+#define WN2 WN [1]
+#define WN3 CN [2]
+
+ uint C [3];
+#define C1 C [0]
+#define C2 C [1]
+#define C3 C [2]
+
+ uint B [3];
+#define B1 B [0]
+#define B2 B [1]
+#define B3 B [2]
+
+ uint N [3];
+#define N1 N [0]
+#define N2 N [1]
+#define N3 N [2]
+
+ uint TN [3]; // type numeric
+#define TN1 TN [0]
+#define TN2 TN [1]
+#define TN3 TN [2]
+
+#ifdef EIS_PTR3
+#define TA1 cpu.du.TAk[0]
+#define TA2 cpu.du.TAk[1]
+#define TA3 cpu.du.TAk[2]
+#else
+ uint TA [3]; // type alphanumeric
+#define TA1 TA [0]
+#define TA2 TA [1]
+#define TA3 TA [2]
+#endif
+
+ uint S [3]; // Sign and decimal type of number
+#define S1 S [0]
+#define S2 S [1]
+#define S3 S [2]
+
+ int SF [3]; // scale factor
+#define SF1 SF [0]
+#define SF2 SF [1]
+#define SF3 SF [2]
+
+ word18 _flags; // flags set during operation
+ word18 _faults; // faults generated by instruction
+
+ word72s x; // a signed, 128-bit integers for playing with ...
+
+ // Stuff for Micro-operations and Edit instructions...
+
+ word9 editInsertionTable [8]; // 8 9-bit chars
+
+ int mopIF; // current micro-operation IF field
+ MOP_struct *m; // pointer to current MOP struct
+
+ word9 inBuffer [64]; // decimal unit input buffer
+ word9 *in; // pointer to current read position in inBuffer
+ uint inBufferCnt; // number of characters in inBuffer
+ word9 outBuffer [64]; // output buffer
+ word9 *out; // pointer to current write position in outBuffer;
+
+ int exponent; // For decimal floating-point (evil)
+ int sign; // For signed decimal (1, -1)
+
+#ifdef EIS_PTR2
+#define KMOP 1
+#else
+ EISaddr *mopAddress; // mopAddress, pointer to addr [0], [1], or [2]
+#endif
+
+ int mopTally; // number of micro-ops
+ int mopPos; // current mop char posn
+
+ // Edit Flags
+ // The processor provides the following four edit flags for use by the
+ // micro operations.
+
+ bool mopES; // End Suppression flag; initially OFF, set ON by
+ // a micro operation when zero-suppression ends.
+ bool mopSN; // Sign flag; initially set OFF if the sending
+ // string has an alphanumeric descriptor or an
+ // unsigned numeric descriptor. If the sending
+ // string has a signed numeric descriptor, the
+ // sign is initially read from the sending string
+ // from the digit position defined by the sign
+ // and the decimal type field (S); SN is set
+ // OFF if positive, ON if negative. If all
+ // digits are zero, the data is assumed positive
+ // and the SN flag is set OFF, even when the
+ // sign is negative.
+ bool mopZ; // Zero flag; initially set ON. It is set OFF
+ // whenever a sending string character that is not
+ // decimal zero is moved into the receiving string.
+ bool mopBZ; // Blank-when-zero flag; initially set OFF and
+ // set ON by either the ENF or SES micro
+ // operation. If, at the completion of a move
+ // (L1 exhausted), both the Z and BZ flags are
+ // ON, the receiving string is filled with
+ // character 1 of the edit insertion table.
+
+ EISaddr addr [3];
+
+#define ADDR1 addr [0]
+ int srcTally; // number of chars in src (max 63)
+ int srcTA; // type of Alphanumeric chars in src
+ int srcSZ; // size of chars in src (4-, 6-, or 9-bits)
+
+#define ADDR2 addr [1]
+
+#define ADDR3 addr [2]
+ int dstTally; // number of chars in dst (max 63)
+ int dstSZ; // size of chars in dst (4-, 6-, or 9-bits)
+
+ bool mvne; // for MSES micro-op. True when mvne, false when mve
+ } EISstruct;
+
+// Instruction decode structure. Used to represent instrucion information
+
+typedef struct DCDstruct_s
+ {
+ struct opcode_s * info; // opcode_s *
+ uint32 opcode; // opcode
+ bool opcodeX; // opcode extension
+ uint32 opcode10; // opcode | (opcodeX ? 01000 : 0)
+ word18 address; // bits 0-17 of instruction
+ word1 b29; // bit-29 - address via pointer register. Usually.
+ bool i; // interrupt inhinit bit.
+ word6 tag; // instruction tag
+
+ bool stiTally; // for sti instruction
+ bool restart; // instruction is to be restarted
+ } DCDstruct;
+
+// Emulator-only interrupt and fault info
+
+typedef struct
+ {
+ vol int fault [N_FAULT_GROUPS];
+ // only one fault in groups 1..6 can be pending
+ vol bool XIP [N_SCU_UNITS_MAX];
+ } events_t;
+
+// Physical Switches
+
+typedef struct
+ {
+ // Switches on the Processor's maintenance and configuration panels
+ uint FLT_BASE; // normally 7 MSB of 12bit fault base addr
+ uint cpu_num; // zero for CPU 'A', one for 'B' etc.
+ word36 data_switches;
+ word18 addr_switches;
+ uint assignment [N_CPU_PORTS];
+ uint interlace [N_CPU_PORTS]; // 0/2/4
+ uint enable [N_CPU_PORTS];
+ uint init_enable [N_CPU_PORTS];
+ uint store_size [N_CPU_PORTS]; // 0-7 encoding 32K-4M
+ uint proc_mode; // 1 bit Read by rsw instruction; format unknown
+ uint proc_speed; // 4 bits Read by rsw instruction; format unknown
+
+ // Emulator run-time options (virtual switches)
+ uint dis_enable; // If non-zero, DIS works
+ uint halt_on_unimp; // If non-zero, halt CPU on unimplemented instruction
+ // instead of faulting
+ uint disable_wam; // If non-zero, disable PTWAM, STWAM
+ uint report_faults; // If set, faults are reported and ignored
+ uint tro_enable; // If set, Timer runout faults are generated.
+ uint drl_fatal;
+ uint serno;
+ bool useMap;
+ bool disable_cache;
+ } switches_t;
+
+#ifdef L68
+enum ou_cycle_e
+ {
+ ou_GIN = 0400,
+ ou_GOS = 0200,
+ ou_GD1 = 0100,
+ ou_GD2 = 0040,
+ ou_GOE = 0020,
+ ou_GOA = 0010,
+ ou_GOM = 0004,
+ ou_GON = 0002,
+ ou_GOF = 0001
+ };
+#endif
+
+typedef struct
+ {
+ // Operations Unit/Address Modification
+ bool directOperandFlag;
+ word36 directOperand;
+ word6 characterOperandSize; // just the left most bit
+ word3 characterOperandOffset;
+ word18 character_address;
+ word36 character_data;
+ bool crflag;
+#ifdef L68
+ word2 eac;
+ word1 RB1_FULL;
+ word1 RP_FULL;
+ word1 RS_FULL;
+ word9 cycle;
+ word1 STR_OP;
+#endif
+#ifdef PANEL
+ word9 RS;
+ word4 opsz;
+ word10 reguse;
+#endif
+ } ou_unit_data_t;
+
+// APU history operation parameter
+
+enum APUH_e
+ {
+ APUH_FDSPTW = 1llu << (35 - 17),
+ APUH_MDSPTW = 1llu << (35 - 18),
+ APUH_FSDWP = 1llu << (35 - 19),
+ APUH_FPTW = 1llu << (35 - 20),
+ APUH_FPTW2 = 1llu << (35 - 21),
+ APUH_MPTW = 1llu << (35 - 22),
+ APUH_FANP = 1llu << (35 - 23),
+ APUH_FAP = 1llu << (35 - 24)
+ };
+
+enum {
+// AL39 pg 64 APU hist.
+ apu_FLT = 1ll << (33 - 0), // 0 l FLT Access violation or directed
+ // fault on this cycle
+ // 1-2 a BSY Data source for ESN
+ apu_ESN_PSR = 0, // 00 PPR.PSR
+ apu_ESN_SNR = 1ll << (33- 1), // 01 PRn.SNR
+ apu_ESN_TSR = 1ll << (33- 2), // 10 TPR.TSR
+ // 11 not used
+ // 3 PRAP
+ apu_HOLD = 1ll << (33- 4), // 4 HOLD An access violation or
+ // directed fault is waiting
+ // 5 FRIW
+ // 6 XSF
+ // 7 STF
+ apu_TP_P = 1ll << (33- 8), // 8 TP P Guessing PPR.p set from
+ // SDW.P
+ apu_PP_P = 1ll << (33- 9), // 9 PP P PPR.P?
+ // 10 ?
+ // 11 S-ON Segment on?
+ // 12 ZMAS
+ // 13 SDMF Seg. Descr. Modify?
+ // 14 SFND
+ // 15 ?
+ // 16 P-ON Page on?
+ // 17 ZMAP
+ // 18 PTMF
+ // 19 PFND
+ apu_FDPT = 1ll << (33-20), // 20 b FDPT Fetch descriptor segment PTW
+ apu_MDPT = 1ll << (33-21), // 21 c MDPT Modify descriptor segment PTW
+ apu_FSDP = 1ll << (33-22), // 22 d FSDP Fetch SDW paged descr. seg.
+ apu_FSDN = 1ll << (33-23), // 23 FSDN Fetch SDW non-paged
+ apu_FPTW = 1ll << (33-24), // 24 e FPTW Fetch PTW
+ apu_MPTW = 1ll << (33-25), // 25 g MPTW Modify PTW
+ apu_FPTW2 = 1ll << (33-26), // 26 f FPT2 // Fetch prepage
+ apu_FAP = 1ll << (33-27), // 27 i FAP Final address fetch from
+ // paged seg.
+ apu_FANP = 1ll << (33-28), // 28 h FANP Final address fetch from
+ // non-paged segment
+ // 29 FAAB Final address absolute?
+ apu_FA = 1ll << (33-30), // 30 FA Final address?
+ // 31 EAAU
+ apu_PIAU = 1ll << (33-32) // 32 PIAU Instruction fetch?
+ // 33 TGAU
+ };
+
+typedef struct
+ {
+ processor_cycle_type lastCycle;
+#ifdef PANEL
+ word34 state;
+#endif
+ } apu_unit_data_t;
+
+typedef struct
+ {
+ // NB: Some of the data normally stored here is represented
+ // elsewhere -- e.g.,the PPR is a variable outside of this
+ // struct. Other data is live and only stored here.
+
+ // This is a collection of flags and registers from the
+ // appending unit and the control unit. The scu and rcu
+ // instructions store and load these values to an 8 word
+ // memory block.
+ //
+ // The CU data may only be valid for use with the scu and
+ // rcu instructions.
+ //
+ // Comments indicate format as stored in 8 words by the scu
+ // instruction.
+
+ // NOTE: PPR (procedure pointer register) is a combination of registers:
+ // From the Appending Unit
+ // PRR bits [0..2] of word 0
+ // PSR bits [3..17] of word 0
+ // P bit 18 of word 0
+ // From the Control Unit
+ // IC bits [0..17] of word 4
+
+ /* word 0 */
+ // 0-2 PRR is stored in PPR
+ // 3-17 PSR is stored in PPR
+ // 18 P is stored in PPR
+ word1 XSF; // 19 XSF External segment flag
+ word1 SDWAMM; // 20 SDWAMM Match on SDWAM
+ word1 SD_ON; // 21 SDWAM enabled
+ word1 PTWAMM; // 22 PTWAMM Match on PTWAM
+ word1 PT_ON; // 23 PTWAM enabled
+
+#if 0
+ word1 PI_AP; // 24 PI-AP Instruction fetch append cycle
+ word1 DSPTW; // 25 DSPTW Fetch descriptor segment PTW
+ word1 SDWNP; // 26 SDWNP Fetch SDW non paged
+ word1 SDWP; // 27 SDWP Fetch SDW paged
+ word1 PTW; // 28 PTW Fetch PTW
+ word1 PTW2; // 29 PTW2 Fetch prepage PTW
+ word1 FAP; // 30 FAP Fetch final address - paged
+ word1 FANP; // 31 FANP Fetch final address - nonpaged
+ word1 FABS; // 32 FABS Fetch final address - absolute
+ // 33-35 FCT Fault counter - counts retries
+#else
+ word12 APUCycleBits;
+#endif
+
+ /* word 1 */
+ // AVF Access Violation Fault
+ // SF Store Fault
+ // IPF Illegal Procedure Fault
+ //
+ word1 IRO_ISN; // 0 IRO AVF Illegal Ring Order
+ // ISN SF Illegal segment number
+ word1 OEB_IOC; // 1 ORB AVF Out of execute bracket [sic] should
+ // be OEB?
+ // IOC IPF Illegal op code
+ word1 EOFF_IAIM;
+ // 2 E-OFF AVF Execute bit is off
+ // IA+IM IPF Illegal address of modifier
+ word1 ORB_ISP; // 3 ORB AVF Out of read bracket
+ // ISP IPF Illegal slave procedure
+ word1 ROFF_IPR;// 4 R-OFF AVF Read bit is off
+ // IPR IPF Illegal EIS digit
+ word1 OWB_NEA; // 5 OWB AVF Out of write bracket
+ // NEA SF Nonexistant address
+ word1 WOFF_OOB;// 6 W-OFF AVF Write bit is off
+ // OOB SF Out of bounds (BAR mode)
+ word1 NO_GA; // 7 NO GA AVF Not a gate
+ word1 OCB; // 8 OCB AVF Out of call bracket
+ word1 OCALL; // 9 OCALL AVF Outward call
+ word1 BOC; // 10 BOC AVF Bad outward call
+// PTWAM error is DPS8M only
+ word1 PTWAM_ER;// 11 PTWAM_ER AVF PTWAM error // inward return
+ word1 CRT; // 12 CRT AVF Cross ring transfer
+ word1 RALR; // 13 RALR AVF Ring alarm
+// On DPS8M a SDWAM error, on DP8/L68 a WAM error
+ word1 SDWAM_ER;// 14 SWWAM_ER AVF SDWAM error
+ word1 OOSB; // 15 OOSB AVF Out of segment bounds
+ word1 PARU; // 16 PARU Parity fault - processor parity upper
+ word1 PARL; // 17 PARL Parity fault - processor parity lower
+ word1 ONC1; // 18 ONC1 Operation not complete fault error #1
+ word1 ONC2; // 19 ONC2 Operation not complete fault error #2
+ word4 IA; // 20-23 IA System control illegal action lines
+ word3 IACHN; // 24-26 IACHN Illegal action processor port
+ word3 CNCHN; // 27-29 CNCHN Connect fault - connect processor port
+ word5 FI_ADDR; // 30-34 F/I ADDR Modulo 2 fault/interrupt vector address
+ word1 FLT_INT; // 35 F/I 0 = interrupt; 1 = fault
+
+ /* word 2 */
+ // 0- 2 TRR
+ // 3-17 TSR
+ // 18-21 PTW
+ // 18 PTWAM levels A, B enabled
+ // 19 PTWAM levels C, D enabled
+ // 20 PTWAM levels A, B match
+ // 21 PTWAM levels C, D match
+ // 22-25 SDW
+ // 22 SDWAM levels A, B enabled
+ // 23 SDWAM levels C, D enabled
+ // 24 SDWAM levels A, B match
+ // 25 SDWAM levels C, D match
+ // 26 0
+ // 27-29 CPU CPU Number
+ word6 delta; // 30-35 DELTA addr increment for repeats
+
+ /* word 3 */
+ // 0-17 0
+ // 18-21 TSNA Pointer register number for non-EIS
+ // operands or EIS Operand #1
+ // 18-20 PRNO Pointer register number
+ // 21 PRNO is valid
+ // 22-25 TSNB Pointer register number for EIS operand #2
+ // 22-24 PRNO Pointer register number
+ // 25 PRNO is valid
+ // 26-29 TSNC Pointer register number for EIS operand #2
+ // 26-28 PRNO Pointer register number
+ // 29 PRNO is valid
+ word3 TSN_PRNO [3];
+ word1 TSN_VALID [3];
+
+ // 30-35 TEMP BIT Current bit offset (TPR.TBR)
+
+ /* word 4 */
+ // 0-17 PPR.IC
+ word18 IR; // 18-35 Indicator register
+ // 18 ZER0
+ // 19 NEG
+ // 20 CARY
+ // 21 OVFL
+ // 22 EOVF
+ // 23 EUFL
+ // 24 OFLM
+ // 25 TRO
+ // 26 PAR
+ // 27 PARM
+ // 28 -BM
+ // 29 TRU
+ // 30 MIF
+ // 31 ABS
+ // 32 HEX [sic] Figure 3-32 is wrong.
+ // 33-35 0
+
+ /* word 5 */
+
+ // 0-17 COMPUTED ADDRESS (TPR.CA)
+ word1 repeat_first;
+ // 18 RF First cycle of all repeat instructions
+ word1 rpt; // 19 RPT Execute an Repeat (rpt) instruction
+ word1 rd; // 20 RD Execute an Repeat Double (rpd) instruction
+ word1 rl; // 21 RL Execute a Repeat Link (rpl) instruction
+ word1 pot; // 22 POT Prepare operand tally
+ // 23 PON Prepare operand no tally
+ //xde xdo
+ // 0 0 no execute -> 0 0
+ // 1 0 execute XEC -> 0 0
+ // 1 1 execute even of XED -> 0 1
+ // 0 1 execute odd of XED -> 0 0
+ word1 xde; // 24 XDE Execute instruction from Execute Double even
+ // pair
+ word1 xdo; // 25 XDO Execute instruction from Execute Double odd pair
+ word1 itp; // 26 ITP Execute ITP indirect cycle
+ word1 rfi; // 27 RFI Restart this instruction
+ word1 its; // 28 ITS Execute ITS indirect cycle
+ word1 FIF; // 29 FIF Fault occured during instruction fetch
+ word6 CT_HOLD; // 30-35 CT HOLD contents of the "remember modifier" register
+
+
+
+ /* word 6 */
+ word36 IWB;
+
+ /* word 7 */
+ word36 IRODD; /* Instr holding register; odd word of last pair fetched */
+ } ctl_unit_data_t;
+
+#define USE_IRODD (cpu.cu.rd && ((cpu. PPR.IC & 1) != 0))
+#define IWB_IRODD (USE_IRODD ? cpu.cu.IRODD : cpu.cu.IWB)
+
+#ifdef L68
+enum du_cycle1_e
+ {
+ // 0 -FPOL Prepare operand length
+ du1_nFPOL = 0400000000000ll,
+ // 1 -FPOP Prepare operand pointer
+ du1_nFPOP = 0200000000000ll,
+ // 2 -NEED-DESC Need descriptor
+ du1_nNEED_DESC = 0100000000000ll,
+ // 3 -SEL-ADR Select address register
+ du1_nSEL_DIR = 0040000000000ll,
+ // 4 -DLEN=DIRECT Length equals direct
+ du1_nDLEN_DIRECT = 0020000000000ll,
+ // 5 -DFRST Descriptor processed for first time
+ du1_nDFRST = 0010000000000ll,
+ // 6 -FEXR Extended register modification
+ du1_nFEXR = 0004000000000ll,
+ // 7 -DLAST-FRST Last cycle of DFRST
+ du1_nLAST_DFRST = 0002000000000ll,
+ // 8 -DDU-LDEA Decimal unit load (lpl?)
+ du1_nDDU_LDEA = 0001000000000ll,
+ // 9 -DDU-STAE Decimal unit store (spl?)
+ du1_nDDU_STEA = 0000400000000ll,
+ // 10 -DREDO Redo operation without pointer and length update
+ du1_nDREDO = 0000200000000ll,
+ // 11 -DLVL<WD-SZ Load with count less than word size
+ du1_nDLVL_WD_SZ = 0000100000000ll,
+ // 12 -EXH Exhaust
+ du1_nEXH = 0000040000000ll,
+ // 13 DEND-SEQ End of sequence
+ du1_DEND_SEQ = 0000020000000ll,
+ // 14 -DEND End of instruction
+ du1_nEND = 0000010000000ll,
+ // 15 -DU=RD+WRT Decimal unit write-back
+ du1_nDU_RD_WRT = 0000004000000ll,
+ // 16 -PTRA00 PR address bit 0
+ du1_nPTRA00 = 0000002000000ll,
+ // 17 -PTRA01 PR address bit 1
+ du1_nPTRA01 = 0000001000000ll,
+ // 18 FA/Il Descriptor l active
+ du1_FA_I1 = 0000000400000ll,
+ // 19 FA/I2 Descriptor 2 active
+ du1_FA_I2 = 0000000200000ll,
+ // 20 FA/I3 Descriptor 3 active
+ du1_FA_I3 = 0000000100000ll,
+ // 21 -WRD Word operation
+ du1_nWRD = 0000000040000ll,
+ // 22 -NINE 9-bit character operation
+ du1_nNINE = 0000000020000ll,
+ // 23 -SIX 6-bit character operation
+ du1_nSIX = 0000000010000ll,
+ // 24 -FOUR 4-bit character operation
+ du1_nFOUR = 0000000004000ll,
+ // 25 -BIT Bit operation
+ du1_nBIT = 0000000002000ll,
+ // 26 Unused
+ // = 0000000001000ll,
+ // 27 Unused
+ // = 0000000000400ll,
+ // 28 Unused
+ // = 0000000000200ll,
+ // 29 Unused
+ // = 0000000000100ll,
+ // 30 FSAMPL Sample for mid-instruction interrupt
+ du1_FSAMPL = 0000000000040ll,
+ // 31 -DFRST-CT Specified first count of a sequence
+ du1_nDFRST_CT = 0000000000020ll,
+ // 32 -ADJ-LENGTH Adjust length
+ du1_nADJ_LENTGH = 0000000000010ll,
+ // 33 -INTRPTD Mid-instruction interrupt
+ du1_nINTRPTD = 0000000000004ll,
+ // 34 -INHIB Inhibit STC1 (force "STC0")
+ du1_nINHIB = 0000000000002ll,
+ // 35 Unused
+ // = 0000000000001ll,
+ };
+
+enum du_cycle2_e
+ {
+ // 36 DUD Decimal unit idle
+ du2_DUD = 0400000000000ll,
+ // 37 -GDLDA Descriptor load gate A
+ du2_nGDLDA = 0200000000000ll,
+ // 38 -GDLDB Descriptor load gate B
+ du2_nGDLDB = 0100000000000ll,
+ // 39 -GDLDC Descriptor load gate C
+ du2_nGDLDC = 0040000000000ll,
+ // 40 NLD1 Prepare alignment count for first numeric operand load
+ du2_NLD1 = 0020000000000ll,
+ // 41 GLDP1 Numeric operand one load gate
+ du2_GLDP1 = 0010000000000ll,
+ // 42 NLD2 Prepare alignment count for second numeric operand load
+ du2_NLD2 = 0004000000000ll,
+ // 43 GLDP2 Numeric operand two load gate
+ du2_GLDP2 = 0002000000000ll,
+ // 44 ANLD1 Alphanumeric operand one load gate
+ du2_ANLD1 = 0001000000000ll,
+ // 45 ANLD2 Alphanumeric operand two load gate
+ du2_ANLD2 = 0000400000000ll,
+ // 46 LDWRT1 Load rewrite register one gate (XXX Guess indirect desc. MFkID)
+ du2_LDWRT1 = 0000200000000ll,
+ // 47 LDWRT2 Load rewrite register two gate (XXX Guess indirect desc. MFkID)
+ du2_LDWRT2 = 0000100000000ll,
+ // 50 -DATA-AVLDU Decimal unit data available
+ du2_nDATA_AVLDU = 0000040000000ll,
+ // 49 WRT1 Rewrite register one loaded
+ du2_WRT1 = 0000020000000ll,
+ // 50 GSTR Numeric store gate
+ du2_GSTR = 0000010000000ll,
+ // 51 ANSTR Alphanumeric store gate
+ du2_ANSTR = 0000004000000ll,
+ // 52 FSTR-OP-AV Operand available to be stored
+ du2_FSTR_OP_AV = 0000002000000ll,
+ // 53 -FEND-SEQ End sequence flag
+ du2_nFEND_SEQ = 0000001000000ll,
+ // 54 -FLEN<128 Length less than 128
+ du2_nFLEN_128 = 0000000400000ll,
+ // 55 FGCH Character operation gate
+ du2_FGCH = 0000000200000ll,
+ // 56 FANPK Alphanumeric packing cycle gate
+ du2_FANPK = 0000000100000ll,
+ // 57 FEXMOP Execute MOP gate
+ du2_FEXOP = 0000000040000ll,
+ // 58 FBLNK Blanking gate
+ du2_FBLNK = 0000000020000ll,
+ // 59 Unused
+ // = 0000000010000ll,
+ // 60 DGBD Binary to decimal execution gate
+ du2_DGBD = 0000000004000ll,
+ // 61 DGDB Decimal to binary execution gate
+ du2_DGDB = 0000000002000ll,
+ // 62 DGSP Shift procedure gate
+ du2_DGSP = 0000000001000ll,
+ // 63 FFLTG Floating result flag
+ du2_FFLTG = 0000000000400ll,
+ // 64 FRND Rounding flag
+ du2_FRND = 0000000000200ll,
+ // 65 DADD-GATE Add/subtract execute gate
+ du2_DADD_GATE = 0000000000100ll,
+ // 66 DMP+DV-GATE Multiply/divide execution gate
+ du2_DMP_DV_GATE = 0000000000040ll,
+ // 67 DXPN-GATE Exponent network execution gate
+ du2_DXPN_GATE = 0000000000020ll,
+ // 68 Unused
+ // = 0000000000010ll,
+ // 69 Unused
+ // = 0000000000004ll,
+ // 70 Unused
+ // = 0000000000002ll,
+ // 71 Unused
+ // = 0000000000001ll,
+ };
+
+#define DU_CYCLE_GDLDA { clrmask (& cpu.du.cycle2, du2_nGDLDA); \
+ setmask (& cpu.du.cycle2, du2_nGDLDB | du2_nGDLDC); }
+#define DU_CYCLE_GDLDB { clrmask (& cpu.du.cycle2, du2_nGDLDB); \
+ setmask (& cpu.du.cycle2, du2_nGDLDA | du2_nGDLDC); }
+#define DU_CYCLE_GDLDC { clrmask (& cpu.du.cycle2, du2_nGDLDC); \
+ setmask (& cpu.du.cycle2, du2_nGDLDA | du2_nGDLDB); }
+#define DU_CYCLE_FA_I1 setmask (& cpu.du.cycle1, du1_FA_I1)
+#define DU_CYCLE_FA_I2 setmask (& cpu.du.cycle1, du1_FA_I2)
+#define DU_CYCLE_FA_I3 setmask (& cpu.du.cycle1, du1_FA_I3)
+#define DU_CYCLE_ANLD1 setmask (& cpu.du.cycle2, du2_ANLD1)
+#define DU_CYCLE_ANLD2 setmask (& cpu.du.cycle2, du2_ANLD2)
+#define DU_CYCLE_NLD1 setmask (& cpu.du.cycle2, du2_NLD1)
+#define DU_CYCLE_NLD2 setmask (& cpu.du.cycle2, du2_NLD2)
+#define DU_CYCLE_FRND setmask (& cpu.du.cycle2, du2_FRND)
+#define DU_CYCLE_DGBD setmask (& cpu.du.cycle2, du2_DGBD)
+#define DU_CYCLE_DGDB setmask (& cpu.du.cycle2, du2_DGDB)
+#define DU_CYCLE_DDU_LDEA clrmask (& cpu.du.cycle1, du1_nDDU_LDEA)
+#define DU_CYCLE_DDU_STEA clrmask (& cpu.du.cycle1, du1_nDDU_STEA)
+#define DU_CYCLE_END clrmask (& cpu.du.cycle1, du1_nEND)
+#define DU_CYCLE_LDWRT1 setmask (& cpu.du.cycle2, du2_LDWRT1)
+#define DU_CYCLE_LDWRT2 setmask (& cpu.du.cycle2, du2_LDWRT2)
+#define DU_CYCLE_FEXOP setmask (& cpu.du.cycle2, du2_FEXOP)
+#define DU_CYCLE_ANSTR setmask (& cpu.du.cycle2, du2_ANSTR)
+#define DU_CYCLE_GSTR setmask (& cpu.du.cycle2, du2_GSTR)
+#define DU_CYCLE_FLEN_128 clrmask (& cpu.du.cycle2, du2_nFLEN_128)
+#define DU_CYCLE_FDUD { cpu.du.cycle1 = \
+ du1_nFPOL | \
+ du1_nFPOP | \
+ du1_nNEED_DESC | \
+ du1_nSEL_DIR | \
+ du1_nDLEN_DIRECT | \
+ du1_nDFRST | \
+ du1_nFEXR | \
+ du1_nLAST_DFRST | \
+ du1_nDDU_LDEA | \
+ du1_nDDU_STEA | \
+ du1_nDREDO | \
+ du1_nDLVL_WD_SZ | \
+ du1_nEXH | \
+ du1_nEND | \
+ du1_nDU_RD_WRT | \
+ du1_nWRD | \
+ du1_nNINE | \
+ du1_nSIX | \
+ du1_nFOUR | \
+ du1_nBIT | \
+ du1_nINTRPTD | \
+ du1_nINHIB; \
+ cpu.du.cycle2 = \
+ du2_DUD | \
+ du2_nGDLDA | \
+ du2_nGDLDB | \
+ du2_nGDLDC | \
+ du2_nDATA_AVLDU | \
+ du2_nFEND_SEQ | \
+ du2_nFLEN_128; \
+ }
+#define DU_CYCLE_nDUD clrmask (& cpu.du.cycle2, du2_DUD)
+#endif
+
+#ifdef PANEL
+// Control points
+
+#define CPT(R,C) cpu.cpt[R][C]=1
+#define CPTUR(C) cpu.cpt[cpt5L][C]=1
+#else
+#define CPT(R,C)
+#define CPTUR(C)
+#endif
+
+#if 0
+#ifdef PANEL
+// 6180 panel DU control flags with guessed meanings based on DU history
+// register bits.
+//
+enum du_cycle1_e
+ {
+ du1_FDUD = 01000000000000ll, // Decimal Unit Idle
+ du1_GDLD = 00400000000000ll, // Decimal Unit Load
+ du1_GLP1 = 00200000000000ll, // PR address bit 0
+ du1_GLP2 = 00100000000000ll, // PR address bit 1
+ du1_GEA1 = 00040000000000ll, // Descriptor 1 active
+ du1_GEM1 = 00020000000000ll, //
+ du1_GED1 = 00010000000000ll, // Prepare alignment count for first numeric
+ // operand load
+ du1_GDB = 00004000000000ll, // Decimal to binary gate
+ du1_GBD = 00002000000000ll, // Binary to decimal gate
+ du1_GSP = 00001000000000ll, // Shift procedure gate
+ du1_GED2 = 00000400000000ll, // Prepare alignment count for second numeric
+ // operand load
+ du1_GEA2 = 00000200000000ll, // Descriptor 2 active
+ du1_GADD = 00000100000000ll, // Add subtract execute gate
+ du1_GCMP = 00000040000000ll, //
+ du1_GMSY = 00000020000000ll, //
+ du1_GMA = 00000010000000ll, //
+ du1_GMS = 00000004000000ll, //
+ du1_GQDF = 00000002000000ll, //
+ du1_GQPA = 00000001000000ll, //
+ du1_GQR1 = 00000000400000ll, // Load rewrite register one gate
+ du1_GQR2 = 00000000200000ll, // Load rewrite register two gate
+ du1_GRC = 00000000100000ll, //
+ du1_GRND = 00000000040000ll, //
+ du1_GCLZ = 00000000020000ll, // Load with count less than word size
+ du1_GEDJ = 00000000010000ll, // ? is the GED3?
+ du1_GEA3 = 00000000004000ll, // Descriptor 3 active
+ du1_GEAM = 00000000002000ll, //
+ du1_GEDC = 00000000001000ll, //
+ du1_GSTR = 00000000000400ll, // Decimal unit store
+ du1_GSDR = 00000000000200ll, //
+ du1_NSTR = 00000000000100ll, // Numeric store gate
+ du1_SDUD = 00000000000040ll, //
+ du1_U32 = 00000000000020ll, // ?
+ du1_U33 = 00000000000010ll, // ?
+ du1_U34 = 00000000000004ll, // ?
+ du1_FLTG = 00000000000002ll, // Floating result flag
+ du1_FRND = 00000000000001ll // Rounding flag
+ };
+
+enum du_cycle2_e
+ {
+ du2_ALD1 = 01000000000000ll, // Alphanumeric operand one load gate
+ du2_ALD2 = 00400000000000ll, // Alphanumeric operand two load gate
+ du2_NLD1 = 00200000000000ll, // Numeric operand one load gate
+ du2_NLD2 = 00100000000000ll, // Numeric operand two load gate
+ du2_LWT1 = 00040000000000ll, // Load rewrite register one gate
+ du2_LWT2 = 00020000000000ll, // Load rewrite register two gate
+ du2_ASTR = 00010000000000ll, // Alphanumeric store gate
+ du2_ANPK = 00004000000000ll, // Alphanumeric packing cycle gate
+ du2_FGCH = 00002000000000ll, // Character operation gate
+ du2_XMOP = 00001000000000ll, // Execute MOP
+ du2_BLNK = 00000400000000ll, // Blanking gate
+ du2_U11 = 00000200000000ll, //
+ du2_U12 = 00000100000000ll, //
+ du2_CS_0 = 00000040000000ll, //
+ du2_CU_0 = 00000020000000ll, // CS=0
+ du2_FI_0 = 00000010000000ll, // CU=0
+ du2_CU_V = 00000004000000ll, // CU=V
+ du2_UM_V = 00000002000000ll, // UM<V
+ du2_U18 = 00000001000000ll, // ?
+ du2_U19 = 00000000400000ll, // ?
+ du2_U20 = 00000000200000ll, // ?
+ du2_U21 = 00000000100000ll, // ?
+ du2_U22 = 00000000040000ll, // ?
+ du2_U23 = 00000000020000ll, // ?
+ du2_U24 = 00000000010000ll, // ?
+ du2_U25 = 00000000004000ll, // ?
+ du2_U26 = 00000000002000ll, // ?
+ du2_U27 = 00000000001000ll, // ?
+ du2_L128 = 00000000000400ll, // L<128 Length less than 128
+ du2_END_SEQ = 00000000000200ll, // End sequence flag
+ du2_U29 = 00000000000100ll, // ?
+ du2_U31 = 00000000000040ll, // ?
+ du2_U32 = 00000000000020ll, // ?
+ du2_U33 = 00000000000010ll, // ?
+ du2_U34 = 00000000000004ll, // ?
+ du2_U35 = 00000000000002ll, // ?
+ du2_U36 = 00000000000001ll // ?
+ };
+#endif
+#endif
+
+typedef struct du_unit_data_t
+ {
+ // Word 0
+
+ // 0- 8 9 Zeros
+ word1 Z; // 9 1 Z All bit-string instruction results
+ // are zero
+ word1 NOP; // 10 1 0 Negative overpunch found in 6-4
+ // expanded move
+ word24 CHTALLY; // 12-35 24 CHTALLY The number of characters examined
+ // by the scm, scmr, scd,
+ // scdr, tct, or tctr instructions
+ // (up to the interrupt or match)
+
+ // Word 1
+
+ // 0-35 26 Zeros
+
+ // Word 2
+
+ // word24 D1_PTR; // 0-23 24 D1 PTR Address of the last double-word
+ // accessed by operand descriptor 1;
+ // bits 17-23 (bit-address) valid
+ // only for initial access
+ // 24 1 Zero
+ // word2 TA1; // 25-26 2 TA1 Alphanumeric type of operand
+ // descriptor 1
+ // 27-29 3 Zeroes
+ // 30 1 I Decimal unit interrupted flag; a
+ // copy of the mid-instruction
+ // interrupt fault indicator
+ // word1 F1; // 31 1 F1 First time; data in operand
+ // descriptor 1 is valid
+ // word1 A1; // 32 1 A1 Operand descriptor 1 is active
+ // 33-35 3 Zeroes
+
+ // Word 3
+
+ word10 LEVEL1; // 0- 9 10 LEVEL 1 Difference in the count of
+ // characters loaded into the and
+ // processor characters not acted
+ // upon
+ // word24 D1_RES; // 12-35 24 D1 RES Count of characters remaining in
+ // operand descriptor 1
+
+ // Word 4
+
+ // word24 D2_PTR; // 0-23 24 D2 PTR Address of the last double-word
+ // accessed by operand descriptor 2;
+ // bits 17-23 (bit-address) valid
+ // only for initial access
+ // 24 1 Zero
+ // word2 TA2; // 25-26 2 TA2 Alphanumeric type of operand
+ // descriptor 2
+ // 27-29 3 Zeroes
+ word1 R; // 30 1 R Last cycle performed must be
+ // repeated
+ // word1 F2; // 31 1 F2 First time; data in operand
+ // descriptor 2 is valid
+ // word1 A2; // 32 1 A2 Operand descriptor 2 is active
+ // 33-35 3 Zeroes
+
+ // Word 5
+
+ word10 LEVEL2; // 0- 9 10 LEVEL 2 Same as LEVEL 1, but used mainly
+ // for OP 2 information
+ // word24 D2_RES; // 12-35 24 D2 RES Count of characters remaining in
+ // operand descriptor 2
+
+ // Word 6
+
+ // word24 D3_PTR; // 0-23 24 D3 PTR Address of the last double-word
+ // accessed by operand descriptor 3;
+ // bits 17-23 (bit-address) valid
+ // only for initial access
+ // 24 1 Zero
+ // word2 TA3; // 25-26 2 TA3 Alphanumeric type of operand
+ // descriptor 3
+ // 27-29 3 Zeroes
+ // 30 1 R Last cycle performed must be
+ // repeated
+ // [XXX: what is the difference between
+ // this and word4.R]
+ // word1 F3; // 31 1 F3 First time; data in operand
+ // descriptor 3 is valid
+ // word1 A3; // 32 1 A3 Operand descriptor 3 is active
+ word3 JMP; // 33-35 3 JMP Descriptor count; number of words
+ // to skip to find the next
+ // instruction following this
+ // multiword instruction
+
+ // Word 7
+
+ // 0-12 12 Zeroes
+ // word24 D3_RES; // 12-35 24 D3 RES Count of characters remaining in
+ // operand descriptor 3
+
+ // Fields from above reorganized for generality
+ word2 TAk [3];
+
+// D_PTR is a word24 divided into a 18 bit address, and a 6-bit bitno/char
+// field
+
+ word18 Dk_PTR_W [3];
+#define D1_PTR_W Dk_PTR_W [0]
+#define D2_PTR_W Dk_PTR_W [1]
+#define D3_PTR_W Dk_PTR_W [2]
+
+ word6 Dk_PTR_B [3];
+#define D1_PTR_B Dk_PTR_B [0]
+#define D2_PTR_B Dk_PTR_B [1]
+#define D3_PTR_B Dk_PTR_B [2]
+
+ word24 Dk_RES [3];
+#define D_RES Dk_RES
+#define D1_RES Dk_RES [0]
+#define D2_RES Dk_RES [1]
+#define D3_RES Dk_RES [2]
+
+ word1 Fk [3];
+//#define F Fk
+#define F1 Fk [0]
+#define F2 Fk [0]
+#define F3 Fk [0]
+
+ word1 Ak [3];
+
+ // Working storage for EIS instruction processing.
+
+ // These values must be restored on instruction restart
+ word7 MF [3]; // Modifier fields for each instruction.
+
+#ifdef ISOLTS
+ // Image of LPL/SPL for ISOLTS compliance
+ word36 image [8];
+#endif
+
+#ifdef PANEL
+ word37 cycle1;
+ word37 cycle2;
+ word1 POL; // Prepare operand length
+ word1 POP; // Prepare operand pointer
+#endif
+ } du_unit_data_t;
+
+#ifdef PANEL
+// prepare_state bits
+enum
+ {
+ ps_PIA = 0200,
+ ps_POA = 0100,
+ ps_RIW = 0040,
+ ps_SIW = 0020,
+ ps_POT = 0010,
+ ps_PON = 0004,
+ ps_RAW = 0002,
+ ps_SAW = 0001
+ };
+#endif
+
+// History registers
+
+// CU History register flag2 field bit
+
+enum { CUH_XINT = 0100, CUH_IFT = 040, CUH_CRD = 020, CUH_MRD = 010,
+ CUH_MSTO = 04, CUH_PIB = 02 };
+
+#ifdef DPS8M
+#define N_WAM_ENTRIES 64
+#define N_WAM_MASK 077
+#endif
+#ifdef L68
+#define N_WAM_ENTRIES 16
+#define N_WAM_MASK 017
+#endif
+
+typedef struct
+ {
+ jmp_buf jmpMain; // This is the entry to the CPU state machine
+ cycles_e cycle;
+ unsigned long long cycleCnt;
+ unsigned long long instrCnt;
+ unsigned long long instrCntT0;
+ unsigned long long instrCntT1;
+ unsigned long long lockCnt;
+ unsigned long long lockImmediate;
+ unsigned long long lockWait;
+ unsigned long long lockWaitMax;
+ unsigned long long lockYield;
+ unsigned long faultCnt [N_FAULTS];
+
+ // The following are all from the control unit history register:
+
+ bool interrupt_flag; // an interrupt is pending in this cycle
+ bool g7_flag; // a g7 fault is pending in this cycle;
+ _fault faultNumber; // fault number saved by doFault
+ _fault_subtype subFault; // saved by doFault
+
+ bool wasXfer; // The previous instruction was a transfer
+
+ bool wasInhibited; // One or both of the previous instruction
+ // pair was interrupr inhibited.
+
+ bool isExec; // The instruction being executed is the target of
+ // an XEC or XED instruction
+ bool isXED; // The instruction being executed is the target of an
+ // XEC instruction
+
+ DCDstruct currentInstruction;
+ EISstruct currentEISinstruction;
+
+ events_t events;
+ switches_t switches;
+ ctl_unit_data_t cu;
+ du_unit_data_t du;
+ ou_unit_data_t ou;
+ apu_unit_data_t apu;
+ word36 faultRegister [2];
+
+ word36 itxPair [2];
+
+ word36 rA; // accumulator
+ word36 rQ; // quotient
+ word8 rE; // exponent [map: rE, 28 0's]
+
+ word18 rX [8]; // index
+ word27 rTR; // timer [map: TR, 9 0's]
+#if defined(THREADZ) || defined(LOCKLESS)
+ struct timespec rTRTime; // time when rTR was set
+ uint rTRsample;
+#endif
+ word24 rY; // address operand
+ word6 rTAG; // instruction tag
+ word3 rRALR; // ring alarm [3b] [map: 33 0's, RALR]
+ word3 RSDWH_R1; // Track the ring number of the last SDW
+ fault_acv_subtype_ acvFaults; // pending ACV faults
+
+ word18 lnk; // rpl link value
+
+ struct tpr_s TPR; // Temporary Pointer Register
+ struct ppr_s PPR; // Procedure Pointer Register
+ struct par_s PAR [8]; // pointer/address resisters
+ struct bar_s BAR; // Base Address Register
+ struct dsbr_s DSBR; // Descriptor Segment Base Register
+#ifdef WAM
+ sdw_s SDWAM [N_WAM_ENTRIES]; // Segment Descriptor Word Associative Memory
+#endif
+#ifdef L68
+ word4 SDWAMR;
+#endif
+#ifdef DPS8M
+ word6 SDWAMR;
+#endif
+ sdw_s * SDW; // working SDW
+ sdw_s SDW0; // a SDW not in SDWAM
+ sdw_s _s;
+#ifdef PANEL
+ // Intermediate data collection for APU SCROLL
+ word18 lastPTWOffset;
+// The L68 APU SCROLL 4U has an entry "ACSD"; I am interpreting it as
+// on: lastPTRAddr was a DSPTW
+// off: lastPTRAddr was a PTW
+ bool lastPTWIsDS;
+ word18 APUDataBusOffset;
+ word24 APUDataBusAddr;
+ word24 APUMemAddr;
+ word1 panel4_red_ready_light_state;
+ word1 panel7_enabled_light_state;
+// The state of the panel switches
+ volatile word15 APU_panel_segno_sw;
+ volatile word1 APU_panel_enable_match_ptw_sw; // lock
+ volatile word1 APU_panel_enable_match_sdw_sw; // lock
+ volatile word1 APU_panel_scroll_select_ul_sw;
+ volatile word4 APU_panel_scroll_select_n_sw;
+ volatile word4 APU_panel_scroll_wheel_sw;
+ //volatile word18 APU_panel_addr_sw;
+ volatile word18 APU_panel_enter_sw;
+ volatile word18 APU_panel_display_sw;
+ volatile word4 CP_panel_wheel_sw;
+ volatile word4 DATA_panel_ds_sw;
+ volatile word4 DATA_panel_d1_sw;
+ volatile word4 DATA_panel_d2_sw;
+ volatile word4 DATA_panel_d3_sw;
+ volatile word4 DATA_panel_d4_sw;
+ volatile word4 DATA_panel_d5_sw;
+ volatile word4 DATA_panel_d6_sw;
+ volatile word4 DATA_panel_d7_sw;
+ volatile word4 DATA_panel_wheel_sw;
+ volatile word4 DATA_panel_addr_stop_sw;
+ volatile word1 DATA_panel_enable_sw;
+ volatile word1 DATA_panel_validate_sw;
+ volatile word1 DATA_panel_auto_fast_sw; // lock
+ volatile word1 DATA_panel_auto_slow_sw; // lock
+ volatile word4 DATA_panel_cycle_sw; // lock
+ volatile word1 DATA_panel_step_sw; // lock
+ volatile word1 DATA_panel_s_trig_sw;
+ volatile word1 DATA_panel_execute_sw; // lock
+ volatile word1 DATA_panel_scope_sw;
+ volatile word1 DATA_panel_init_sw; // lock
+ volatile word1 DATA_panel_exec_sw; // lock
+ volatile word4 DATA_panel_hr_sel_sw;
+ volatile word4 DATA_panel_trackers_sw;
+ volatile bool panelInitialize;
+
+ // Intermediate data collection for DATA SCROLL
+ bool portBusy;
+ word2 portSelect;
+ word36 portAddr [N_CPU_PORTS];
+ word36 portData [N_CPU_PORTS];
+ // Intermediate data collection for CU
+ word36 IWRAddr;
+ word7 dataMode; // 0100 9 bit
+ // 0040 6 bit
+ // 0020 4 bit
+ // 0010 1 bit
+ // 0004 36 bit
+ // 0002 alphanumeric
+ // 0001 numeric
+ word8 prepare_state;
+ bool DACVpDF;
+ bool AR_F_E;
+ bool INS_FETCH;
+ // Control Points data acquisition
+ word1 cpt [28] [36];
+#endif
+#define cpt1U 0 // Instruction processing tracking
+#define cpt1L 1 // Instruction processing tracking
+#define cpt2U 2 // Instruction execution tracking
+#define cpt2L 3 // Instruction execution tracking
+#define cpt3U 4 // Register usage
+#define cpt3L 5 // Register usage
+#define cpt4U 6
+#define cpt4L 7
+#define cpt5U 8
+#define cpt5L 9
+#define cpt6U 10
+#define cpt6L 11
+#define cpt7U 12
+#define cpt7L 13
+#define cpt8U 14
+#define cpt8L 15
+#define cpt9U 16
+#define cpt9L 17
+#define cpt10U 18
+#define cpt10L 19
+#define cpt11U 20
+#define cpt11L 21
+#define cpt12U 22
+#define cpt12L 23
+#define cpt13U 24
+#define cpt13L 25
+#define cpt14U 26
+#define cpt14L 27
+
+#define cptUseE 0
+#define cptUseBAR 1
+#define cptUseTR 2
+#define cptUseRALR 3
+#define cptUsePRn 4 // 4 - 11
+#define cptUseDSBR 12
+#define cptUseFR 13
+#define cptUseMR 14
+#define cptUseCMR 15
+#define cptUseIR 16
+
+
+ // Address Modification tally
+ word12 AM_tally;
+
+ // Zone mask
+ word36 zone;
+ bool useZone;
+
+#ifdef WAM
+ ptw_s PTWAM [N_WAM_ENTRIES];
+#endif
+#ifdef L68
+ word4 PTWAMR;
+#endif
+#ifdef DPS8M
+ word6 PTWAMR;
+#endif
+ ptw_s * PTW;
+ ptw0_s PTW0; // a PTW not in PTWAM (PTWx1)
+ cache_mode_register_s CMR;
+ mode_register_s MR;
+
+ // G7 faults
+
+ bool bTroubleFaultCycle;
+ uint g7FaultsPreset;
+ uint g7Faults;
+ _fault_subtype g7SubFaults [N_FAULTS];
+
+#ifdef L68
+ // FFV faults
+
+ uint FFV_faults_preset;
+ uint FFV_faults;
+ uint FFV_fault_number;
+ bool is_FFV;
+#endif
+
+ word24 iefpFinalAddress;
+ word36 CY; // C(Y) operand data from memory
+ word36 Ypair[2]; // 2-words
+ word36 Yblock8[8]; // 8-words
+ word36 Yblock16[16]; // 16-words
+ word36 Yblock32[32]; // 32-words
+ word36 scu_data[8]; // For SCU instruction
+ struct
+ {
+ word15 PSR;
+ word3 PRR;
+ word18 IC;
+ } cu_data; // For STCD instruction
+ uint rTRticks;
+#ifdef ISOLTS
+ uint rTRlsb;
+#endif
+ uint64 lufCounter;
+ bool lufOccurred;
+ bool secret_addressing_mode;
+ //bool went_appending; // we will go....
+#ifdef ROUND_ROBIN
+ bool isRunning;
+#endif
+ // Map memory to port
+ int scbank_map [N_SCBANKS];
+ word24 scbank_base [N_SCBANKS];
+ // scu_unit_idx * 4u * 1024u * 1024u + scpg * SCBANK
+ int scbank_pg_os [N_SCBANKS];
+
+ uint history_cyclic [N_HIST_SETS]; // 0..63
+ word36 history [N_HIST_SETS] [N_HIST_SIZE] [2];
+
+ // Used by LCPR to prevent the LCPR instruction from being recorded
+ // in the CU.
+ bool skip_cu_hist;
+ // Changes to the mode register history bits do not take affect until
+ // the next instruction (ISOLTS 700 2a). Cache the values here so
+ // that post register updates can see the old values.
+ mode_register_s MR_cache;
+
+ // If the instruction wants overflow thrown after operand write
+ bool dlyFlt;
+
+ // Arguments for delayed overflow fault
+
+ _fault dlyFltNum;
+ _fault_subtype dlySubFltNum;
+ const char * dlyCtx;
+
+#ifdef ISOLTS
+ uint shadowTR;
+ uint TR0; // The value that the TR was set to.
+#endif
+ word18 last_write;
+#ifdef LOCKLESS
+ word24 locked_addr;
+ word24 char_word_address;
+#endif
+ word24 rmw_address;
+ word24 pad[16];
+//#ifdef THREADZ
+// // Set if this thread has set memlock
+// bool havelock; // Vetinari
+//bool have_tst_lock;
+//#endif
+#ifdef AFFINITY
+ bool set_affinity;
+ uint affinity;
+#endif
+ bool restart;
+ uint restart_address;
+
+
+ // Caching some cabling data for interrupt handling.
+ // When a CPU calls get_highest_intr(), it needs to know
+ // what port on the SCU it is attached to. Because of port
+ // exapanders several CPUs can be attached to an SCU port,
+ // mapping from the CPU to the SCU is easier to query
+ uint scu_port[N_SCU_UNITS_MAX];
+
+ } cpu_state_t;
+
+#ifdef M_SHARED
+extern cpu_state_t * cpus;
+#else
+extern cpu_state_t cpus [N_CPU_UNITS_MAX];
+#endif
+
+#if defined(THREADZ) || defined(LOCKLESS)
+extern __thread cpu_state_t * restrict cpup;
+#else
+extern cpu_state_t * restrict cpup;
+#endif
+#define cpu (* cpup)
+
+
+#define N_STALL_POINTS 4
+struct stall_point_s
+ {
+ word15 segno;
+ word18 offset;
+ unsigned int/*useconds_t*/ time;
+ };
+extern struct stall_point_s stall_points [N_STALL_POINTS];
+extern bool stall_point_active;
+
+uint set_cpu_idx (uint cpuNum);
+#if defined(THREADZ) || defined(LOCKLESS)
+extern __thread uint current_running_cpu_idx;
+extern bool bce_dis_called;
+#else
+#ifdef ROUND_ROBIN
+extern uint current_running_cpu_idx;
+#else
+#define current_running_cpu_idx 0
+#endif
+#endif
+
+// Support code to access ARn.BITNO, ARn.CHAR, PRn.BITNO
+
+#define GET_PR_BITNO(n) (cpu.PAR[n].PR_BITNO)
+#define GET_AR_BITNO(n) (cpu.PAR[n].AR_BITNO)
+#define GET_AR_CHAR(n) (cpu.PAR[n].AR_CHAR)
+static inline void SET_PR_BITNO (uint n, word6 b)
+ {
+ cpu.PAR[n].PR_BITNO = b;
+ cpu.PAR[n].AR_BITNO = (b % 9) & MASK4;
+ cpu.PAR[n].AR_CHAR = (b / 9) & MASK2;
+ }
+static inline void SET_AR_CHAR_BITNO (uint n, word2 c, word4 b)
+ {
+ cpu.PAR[n].PR_BITNO = c * 9 + b;
+ cpu.PAR[n].AR_BITNO = b & MASK4;
+ cpu.PAR[n].AR_CHAR = c & MASK2;
+ }
+
+
+bool sample_interrupts (void);
+t_stat simh_hooks (void);
+int operand_size (void);
+t_stat read_operand (word18 addr, processor_cycle_type cyctyp);
+t_stat write_operand (word18 addr, processor_cycle_type acctyp);
+
+#ifdef PANEL
+static inline void trackport (word24 a, word36 d)
+ {
+ // Simplifying assumption: 4 * 4MW SCUs
+ word2 port = (a >> 22) & MASK2;
+ cpu.portSelect = port;
+ cpu.portAddr [port] = a;
+ cpu.portData [port] = d;
+ cpu.portBusy = false;
+ }
+#endif
+
+#ifdef THREADZ
+// Ugh. Circular dependencies XXX
+void lock_mem_rd (void);
+void lock_mem_wr (void);
+void unlock_mem (void);
+#define LOCK_MEM_RD lock_mem_rd ();
+#define LOCK_MEM_WR lock_mem_wr ();
+#define UNLOCK_MEM unlock_mem ();
+#else // ! THREADZ
+#ifdef TEST_FENCE
+#define LOCK_MEM_RD fence ();
+#define LOCK_MEM_WR fence ();
+#define UNLOCK_MEM fence ();
+#else
+#define LOCK_MEM_RD
+#define LOCK_MEM_WR
+#define UNLOCK_MEM
+#endif
+#endif // ! THREADZ
+
+#if defined(SPEED) && defined(INLINE_CORE)
+// Ugh. Circular dependencies XXX
+void doFault (_fault faultNumber, _fault_subtype faultSubtype,
+ const char * faultMsg) NO_RETURN;
+extern const _fault_subtype fst_str_nea;
+#ifdef SCUMEM
+// Stupid dependency order
+int lookup_cpu_mem_map (word24 addr, word24 * offset);
+#endif
+
+static inline int core_read (word24 addr, word36 *data, UNUSED const char * ctx)
+ {
+ PNL (cpu.portBusy = true;)
+#ifdef ISOLTS
+ if (cpu.switches.useMap)
+ {
+ uint pgnum = addr / SCBANK;
+ int os = cpu.scbank_pg_os [pgnum];
+ if (os < 0)
+ {
+ doFault (FAULT_STR, fst_str_nea, __func__);
+ }
+ addr = (uint) os + addr % SCBANK;
+ }
+#endif
+#if 0 // XXX Controlled by TEST/NORMAL switch
+#ifdef ISOLTS
+ if (cpu.MR.sdpap)
+ {
+ sim_warn ("failing to implement sdpap\n");
+ cpu.MR.sdpap = 0;
+ }
+ if (cpu.MR.separ)
+ {
+ sim_warn ("failing to implement separ\n");
+ cpu.MR.separ = 0;
+ }
+#endif
+#endif
+#ifdef SCUMEM
+ word24 offset;
+ int cpu_port_num = lookup_cpu_mem_map (addr, & offset);
+ if (! get_scu_in_use (current_running_cpu_idx, cpu_port_num))
+ {
+ sim_warn ("%s %012o has no SCU; faulting\n", __func__, addr);
+ doFault (FAULT_STR, fst_str_nea, __func__);
+ }
+ uint scuUnitIdx = get_scu_idx (current_running_cpu_idx, cpu_port_num);
+ LOCK_MEM_RD;
+ *data = scu [scuUnitIdx].M[offset] & DMASK;
+ UNLOCK_MEM;
+#else
+ LOCK_MEM_RD;
+ *data = M[addr] & DMASK;
+ UNLOCK_MEM;
+#endif
+#ifdef TR_WORK_MEM
+ cpu.rTRticks ++;
+#endif
+ PNL (trackport (addr, * data);)
+ return 0;
+ }
+
+static inline int core_write (word24 addr, word36 data, UNUSED const char * ctx)
+ {
+ PNL (cpu.portBusy = true;)
+#ifdef ISOLTS
+ if (cpu.switches.useMap)
+ {
+ uint pgnum = addr / SCBANK;
+ int os = cpu.scbank_pg_os [pgnum];
+ if (os < 0)
+ {
+ doFault (FAULT_STR, fst_str_nea, __func__);
+ }
+ addr = (uint) os + addr % SCBANK;
+ }
+#endif
+#ifdef ISOLTS
+ if (cpu.MR.sdpap)
+ {
+ sim_warn ("failing to implement sdpap\n");
+ cpu.MR.sdpap = 0;
+ }
+ if (cpu.MR.separ)
+ {
+ sim_warn ("failing to implement separ\n");
+ cpu.MR.separ = 0;
+ }
+#endif
+#ifdef SCUMEM
+ word24 offset;
+ int cpu_port_num = lookup_cpu_mem_map (addr, & offset);
+ if (! get_scu_in_use (current_running_cpu_idx, cpu_port_num))
+ {
+ sim_warn ("%s %012o has no SCU; faulting\n", __func__, addr);
+ doFault (FAULT_STR, fst_str_nea, __func__);
+ }
+ uint scuUnitIdx = get_scu_idx (current_running_cpu_idx, cpu_port_num);
+ LOCK_MEM_WR;
+ scu[scuUnitIdx].M[offset] = data & DMASK;
+ UNLOCK_MEM;
+#else
+ LOCK_MEM_WR;
+ M[addr] = data & DMASK;
+ UNLOCK_MEM;
+#endif
+#ifdef TR_WORK_MEM
+ cpu.rTRticks ++;
+#endif
+ PNL (trackport (addr, data);)
+ return 0;
+ }
+
+static inline int core_write_zone (word24 addr, word36 data, UNUSED const char * ctx)
+ {
+ PNL (cpu.portBusy = true;)
+#ifdef ISOLTS
+ if (cpu.switches.useMap)
+ {
+ uint pgnum = addr / SCBANK;
+ int os = cpu.scbank_pg_os [pgnum];
+ if (os < 0)
+ {
+ doFault (FAULT_STR, fst_str_nea, __func__);
+ }
+ addr = (uint) os + addr % SCBANK;
+ }
+#endif
+#ifdef ISOLTS
+ if (cpu.MR.sdpap)
+ {
+ sim_warn ("failing to implement sdpap\n");
+ cpu.MR.sdpap = 0;
+ }
+ if (cpu.MR.separ)
+ {
+ sim_warn ("failing to implement separ\n");
+ cpu.MR.separ = 0;
+ }
+#endif
+#ifdef SCUMEM
+ word24 offset;
+ int cpu_port_num = lookup_cpu_mem_map (addr, & offset);
+ if (! get_scu_in_use (current_running_cpu_idx, cpu_port_num))
+ {
+ sim_warn ("%s %012o has no SCU; faulting\n", __func__, addr);
+ doFault (FAULT_STR, fst_str_nea, __func__);
+ }
+ uint scuUnitIdx = get_scu_idx (current_running_cpu_idx, cpu_port_num);
+ LOCK_MEM_WR;
+ scu[scuUnitIdx].M[offset] = (scu[scuUnitIdx].M[offset] & ~cpu.zone) |
+ (data & cpu.zone);
+ cpu.useZone = false; // Safety
+ UNLOCK_MEM;
+#else
+ LOCK_MEM_WR;
+ M[addr] = (M[addr] & ~cpu.zone) | (data & cpu.zone);
+ cpu.useZone = false; // Safety
+ UNLOCK_MEM;
+#endif
+#ifdef TR_WORK_MEM
+ cpu.rTRticks ++;
+#endif
+ PNL (trackport (addr, data);)
+ return 0;
+ }
+
+static inline int core_read2 (word24 addr, word36 *even, word36 *odd,
+ UNUSED const char * ctx)
+ {
+ PNL (cpu.portBusy = true;)
+#ifdef ISOLTS
+ if (cpu.switches.useMap)
+ {
+ uint pgnum = addr / SCBANK;
+ int os = cpu.scbank_pg_os [pgnum];
+ if (os < 0)
+ {
+ doFault (FAULT_STR, fst_str_nea, __func__);
+ }
+ addr = (uint) os + addr % SCBANK;
+ }
+#endif
+#if 0 // XXX Controlled by TEST/NORMAL switch
+#ifdef ISOLTS
+ if (cpu.MR.sdpap)
+ {
+ sim_warn ("failing to implement sdpap\n");
+ cpu.MR.sdpap = 0;
+ }
+ if (cpu.MR.separ)
+ {
+ sim_warn ("failing to implement separ\n");
+ cpu.MR.separ = 0;
+ }
+#endif
+#endif
+#ifdef SCUMEM
+ word24 offset;
+ int cpu_port_num = lookup_cpu_mem_map (addr, & offset);
+ if (! get_scu_in_use (current_running_cpu_idx, cpu_port_num))
+ {
+ sim_warn ("%s %012o has no SCU; faulting\n", __func__, addr);
+ doFault (FAULT_STR, fst_str_nea, __func__);
+ }
+ uint scuUnitIdx = get_scu_idx (current_running_cpu_idx, cpu_port_num);
+ LOCK_MEM_WR;
+ *even = scu [scuUnitIdx].M[offset++] & DMASK;
+ *odd = scu [scuUnitIdx].M[offset] & DMASK;
+ UNLOCK_MEM;
+#else
+ LOCK_MEM_WR;
+ *even = M[addr++] & DMASK;
+ *odd = M[addr] & DMASK;
+ UNLOCK_MEM;
+#endif
+#ifdef TR_WORK_MEM
+ cpu.rTRticks ++;
+#endif
+ PNL (trackport (addr - 1, * even);)
+ return 0;
+ }
+
+static inline int core_write2 (word24 addr, word36 even, word36 odd,
+ UNUSED const char * ctx)
+ {
+ PNL (cpu.portBusy = true;)
+#ifdef ISOLTS
+ if (cpu.switches.useMap)
+ {
+ uint pgnum = addr / SCBANK;
+ int os = cpu.scbank_pg_os [pgnum];
+ if (os < 0)
+ {
+ doFault (FAULT_STR, fst_str_nea, __func__);
+ }
+ addr = (uint) os + addr % SCBANK;
+ }
+#endif
+#ifdef ISOLTS
+ if (cpu.MR.sdpap)
+ {
+ sim_warn ("failing to implement sdpap\n");
+ cpu.MR.sdpap = 0;
+ }
+ if (cpu.MR.separ)
+ {
+ sim_warn ("failing to implement separ\n");
+ cpu.MR.separ = 0;
+ }
+#endif
+#ifdef SCUMEM
+ word24 offset;
+ int cpu_port_num = lookup_cpu_mem_map (addr, & offset);
+ if (! get_scu_in_use (current_running_cpu_idx, cpu_port_num))
+ {
+ sim_warn ("%s %012o has no SCU; faulting\n", __func__, addr);
+ doFault (FAULT_STR, fst_str_nea, __func__);
+ }
+ uint scuUnitIdx = get_scu_idx (current_running_cpu_idx, cpu_port_num);
+ LOCK_MEM_WR;
+ scu [scuUnitIdx].M[offset++] = even & DMASK;
+ scu [scuUnitIdx].M[offset] = odd & DMASK;
+ UNLOCK_MEM;
+#else
+ LOCK_MEM_WR;
+ M[addr++] = even;
+ M[addr] = odd;
+ UNLOCK_MEM;
+#endif
+ PNL (trackport (addr - 1, even);)
+#ifdef TR_WORK_MEM
+ cpu.rTRticks ++;
+#endif
+ return 0;
+ }
+#else // defined(SPEED) && defined(INLINE_CORE)
+int core_read (word24 addr, word36 *data, const char * ctx);
+int core_write (word24 addr, word36 data, const char * ctx);
+int core_write_zone (word24 addr, word36 data, const char * ctx);
+int core_read2 (word24 addr, word36 *even, word36 *odd, const char * ctx);
+int core_write2 (word24 addr, word36 even, word36 odd, const char * ctx);
+#endif // defined(SPEED) && defined(INLINE_CORE)
+
+#ifdef LOCKLESS
+int core_read_lock (word24 addr, word36 *data, const char * ctx);
+int core_write_unlock (word24 addr, word36 data, const char * ctx);
+int core_unlock_all();
+
+#define DEADLOCK_DETECT 0x40000000U
+#define MEM_LOCKED_BIT 61
+#define MEM_LOCKED (1LLU<<MEM_LOCKED_BIT)
+
+#if defined(__FreeBSD__) && !defined(USE_COMPILER_ATOMICS)
+#include <machine/atomic.h>
+
+#define LOCK_CORE_WORD(addr) \
+ do \
+ { \
+ unsigned int i = DEADLOCK_DETECT; \
+ while ( atomic_testandset_64((volatile u_long *)&M[addr], MEM_LOCKED_BIT) == 1 && i > 0) \
+ { \
+ i--; \
+ if ((i & 0xff) == 0) { \
+ pthread_yield(); \
+ cpu.lockYield++; \
+ } \
+ } \
+ if (i == 0) \
+ { \
+ sim_warn ("%s: locked %x addr %x deadlock\n", __FUNCTION__, cpu.locked_addr, addr); \
+ } \
+ cpu.lockCnt++; \
+ if (i == DEADLOCK_DETECT) \
+ cpu.lockImmediate++; \
+ cpu.lockWait += (DEADLOCK_DETECT-i); \
+ cpu.lockWaitMax = ((DEADLOCK_DETECT-i) > cpu.lockWaitMax) ? (DEADLOCK_DETECT-i) : cpu.lockWaitMax; \
+ } \
+ while (0)
+
+#define LOAD_ACQ_CORE_WORD(res, addr) \
+ do \
+ { \
+ res = atomic_load_acq_64((volatile u_long *)&M[addr]); \
+ } \
+ while (0)
+
+#define STORE_REL_CORE_WORD(addr, data) \
+ do \
+ { \
+ atomic_store_rel_64((volatile u_long *)&M[addr], data & DMASK); \
+ } \
+ while (0)
+
+#else // defined(__FreeBSD__) && !defined(USE_COMPILER_ATOMICS)
+
+#ifdef MEMORY_ACCESS_NOT_STRONGLY_ORDERED
+#define MEM_BARRIER() do { __sync_synchronize(); } while (0)
+#else
+#define MEM_BARRIER() do {} while (0)
+#endif
+
+#define LOCK_CORE_WORD(addr) \
+ do \
+ { \
+ unsigned int i = DEADLOCK_DETECT; \
+ while ((__sync_fetch_and_or((volatile u_long *)&M[addr], MEM_LOCKED) & MEM_LOCKED) \
+ && i > 0) \
+ { \
+ i--; \
+ if ((i & 0xff) == 0) { \
+ pthread_yield(); \
+ cpu.lockYield++; \
+ } \
+ } \
+ if (i == 0) \
+ { \
+ sim_warn ("%s: locked %x addr %x deadlock\n", __FUNCTION__, cpu.locked_addr, addr); \
+ } \
+ cpu.lockCnt++; \
+ if (i == DEADLOCK_DETECT) \
+ cpu.lockImmediate++; \
+ cpu.lockWait += (DEADLOCK_DETECT-i); \
+ cpu.lockWaitMax = ((DEADLOCK_DETECT-i) > cpu.lockWaitMax) ? (DEADLOCK_DETECT-i) : cpu.lockWaitMax; \
+ } \
+ while (0)
+
+#define LOAD_ACQ_CORE_WORD(res, addr) \
+ do \
+ { \
+ res = M[addr]; \
+ MEM_BARRIER(); \
+ } \
+ while (0)
+
+#define STORE_REL_CORE_WORD(addr, data) \
+ do \
+ { \
+ MEM_BARRIER(); \
+ M[addr] = data & DMASK; \
+ } \
+ while (0)
+
+#endif // defined(__FreeBSD__) && !defined(USE_COMPILER_ATOMICS)
+#endif // LOCKLESS
+
+static inline void core_readN (word24 addr, word36 * data, uint n,
+ UNUSED const char * ctx)
+ {
+ for (uint i = 0; i < n; i ++)
+ {
+ core_read (addr + i, data + i, ctx);
+ HDBGMRead (addr + i, * (data + i));
+ }
+ }
+
+static inline void core_writeN (word24 addr, word36 * data, uint n,
+ UNUSED const char * ctx)
+ {
+ for (uint i = 0; i < n; i ++)
+ {
+ core_write (addr + i, data [i], ctx);
+ }
+ }
+
+int is_priv_mode (void);
+//void set_went_appending (void);
+//void clr_went_appending (void);
+//bool get_went_appending (void);
+bool get_bar_mode (void);
+addr_modes_e get_addr_mode (void);
+void set_addr_mode (addr_modes_e mode);
+void decode_instruction (word36 inst, DCDstruct * p);
+#ifndef SPEED
+t_stat set_mem_watch (int32 arg, const char * buf);
+#endif
+char *str_SDW0 (char * buf, sdw_s *SDW);
+#ifdef SCUMEM
+int lookup_cpu_mem_map (word24 addr, word24 * offset);
+#else
+int lookup_cpu_mem_map (word24 addr);
+#endif
+void cpu_init (void);
+void setup_scbank_map (void);
+#ifdef DPS8M
+void add_CU_history (void);
+void add_DUOU_history (word36 flags, word18 ICT, word9 RS_REG, word9 flags2);
+void add_APU_history (word15 ESN, word21 flags, word24 RMA, word3 RTRR,
+ word9 flags2);
+void add_EAPU_history (word18 ZCA, word18 opcode);
+#endif
+#ifdef L68
+void add_CU_history (void);
+void add_OU_history (void);
+void add_DU_history (void);
+void add_APU_history (enum APUH_e op);
+#endif
+void add_history_force (uint hset, word36 w0, word36 w1);
+word18 get_BAR_address(word18 addr);
+#if defined(THREADZ) || defined(LOCKLESS)
+t_stat threadz_sim_instr (void);
+void * cpu_thread_main (void * arg);
+#endif
+void cpu_reset_unit_idx (UNUSED uint cpun, bool clear_mem);
--- /dev/null
+/*
+ Copyright 2012-2016 by Harry Reed
+ Copyright 2013-2016 by Charles Anthony
+ Copyright 2017 by Michal Tomek
+
+ All rights reserved.
+
+ This software is made available under the terms of the
+ ICU License -- ICU 1.8.1 and later.
+ See the LICENSE file at the top-level directory of this distribution and
+ at https://sourceforge.net/p/dps8m/code/ci/master/tree/LICENSE
+ */
+
+//
+// dps8_decimal.c
+// dps8
+//
+// Created by Harry Reed on 2/9/13.
+// Original portions Copyright (c) 2013 Harry Reed. All rights reserved.
+//
+// decimal arithmetic support code for dps8 simulator.
+//
+// portions based off of the 'decNumber' decimal arithmetic library
+// Copyright (c) IBM Corporation, 2000, 2009. All rights reserved.
+//
+
+#include <stdio.h>
+
+#include "dps8.h"
+#include "dps8_sys.h"
+#include "dps8_faults.h"
+//#include "dps8_scu.h"
+//#include "dps8_iom.h"
+//#include "dps8_cable.h"
+#include "dps8_cpu.h"
+#include "dps8_decimal.h"
+#include "dps8_eis.h"
+#include "dps8_utils.h"
+
+/* ------------------------------------------------------------------ */
+/* HWR 6/28/14 18:54 derived from ...... */
+/* decContextDefault(...) */
+/* */
+/* decContextDefaultDPS8 -- initialize a context structure */
+/* */
+/* Similar to decContextDefault EXCEPT digits are set to 65 for our */
+/* dps8 simulator (add additional features as required */
+/* */
+/* ------------------------------------------------------------------ */
+decContext * decContextDefaultDPS8(decContext *context)
+{
+ decContextDefault(context, DEC_INIT_BASE);
+ context->traps=0;
+
+ context->digits = 65;
+
+ return context;
+}
+#if 1
+/* ------------------------------------------------------------------ */
+/* HWR 3/21/16 19:54 derived from ...... */
+/* decContextDefault(...) */
+/* */
+/* decContextDefaultDPS8 -- initialize a context structure */
+/* */
+/* Similar to decContextDefault EXCEPT digits are set to 126 for our */
+/* dps8 simulators mpXd instructions */
+/* */
+/* ------------------------------------------------------------------ */
+decContext * decContextDefaultDPS8Mul(decContext *context)
+{
+ decContextDefault(context, DEC_INIT_BASE);
+ context->traps=0;
+
+ context->digits = 63 + 63; // worse case for multiply
+
+ return context;
+}
+#else
+/* ------------------------------------------------------------------ */
+/* HWR 6/28/14 18:54 derived from ...... */
+/* decContextDefault(...) */
+/* */
+/* decContextDefaultDPS8 -- initialize a context structure */
+/* */
+/* Similar to decContextDefault EXCEPT digits are set to 80 for our */
+/* dps8 simulator (add additional features as required */
+/* */
+/* ------------------------------------------------------------------ */
+decContext * decContextDefaultDPS8_80(decContext *context)
+{
+ decContextDefault(context, DEC_INIT_BASE);
+ context->traps=0;
+
+ context->digits = 80; //63 * 63; // worse case for multiply
+
+ return context;
+}
+#endif
+
+
+decNumber * decBCD9ToNumber(const word9 *bcd, Int length, const Int scale, decNumber *dn)
+{
+ const word9 *last=bcd+length-1; // -> last byte
+ const word9 *first; // -> first non-zero byte
+ uInt nib; // work nibble
+ Unit *up=dn->lsu; // output pointer
+ Int digits; // digits count
+ Int cut=0; // phase of output
+
+ decNumberZero(dn); // default result
+ //last = &bcd[length-1];
+ //nib = *last & 0x0f; // get the sign
+ //if (nib==DECPMINUS || nib==DECPMINUSALT) dn->bits=DECNEG;
+ //else if (nib<=9) return NULL; // not a sign nibble
+
+ // skip leading zero bytes [final byte is always non-zero, due to sign]
+ //for (first=bcd; *first==0;) first++;
+
+ //Also, a bug in decBCD9ToNumber; in the input is all zeros, the skip leading zeros code wanders off the end of the input buffer....
+ for (first=bcd; *first==0 && first <= last;)
+ first++;
+
+ digits=(Int)(last-first)+1; // calculate digits ..
+ //if ((*first & 0xf0)==0) digits--; // adjust for leading zero nibble
+ if (digits!=0)
+ dn->digits=digits; // count of actual digits [if 0,
+ // leave as 1]
+
+ // check the adjusted exponent; note that scale could be unbounded
+ dn->exponent=-scale; // set the exponent
+ if (scale>=0) // usual case
+ {
+ if ((dn->digits-scale-1)<-DECNUMMAXE) // underflow
+ {
+ decNumberZero(dn);
+ //return NULL;
+ // XXX check subfault
+ doFault (FAULT_IPR, fst_ill_proc, "decBCD9ToNumber underflow");
+ }
+ }
+ else // -ve scale; +ve exponent
+ {
+ // need to be careful to avoid wrap, here, also BADINT case
+ if ((scale<-DECNUMMAXE) // overflow even without digits
+ || ((dn->digits-scale-1)>DECNUMMAXE)) // overflow
+ {
+ decNumberZero(dn);
+ //return NULL;
+ // XXX check subfault
+ doFault (FAULT_IPR, fst_ill_proc, "decBCD9ToNumber overflow");
+ }
+ }
+ if (digits==0)
+ return dn; // result was zero
+
+ // copy the digits to the number's units, starting at the lsu
+ // [unrolled]
+ for (;last >= bcd;) // forever
+ {
+ nib=(unsigned)(*last & 0x0f);
+ // got a digit, in nib
+ //if (nib>9) {decNumberZero(dn); return NULL;} // bad digit
+ if (nib > 9)
+ doFault (FAULT_IPR, fst_ill_dig, "decBCD9ToNumber ill digit");
+
+ if (cut==0)
+ *up=(Unit)nib;
+ else
+ *up=(Unit)(*up+nib*DECPOWERS[cut]);
+ digits--;
+ if (digits==0)
+ break; // got them all
+ cut++;
+ if (cut==DECDPUN)
+ {
+ up++;
+ cut=0;
+ }
+ last--; // ready for next
+
+ // nib = *last & 0x0f; // get right nibble
+ // if (nib>9) {decNumberZero(dn); return NULL;}
+ //
+ // // got a digit, in nib
+ // if (cut==0) *up=(Unit)nib;
+ // else *up=(Unit)(*up+nib*DECPOWERS[cut]);
+ // digits--;
+ // if (digits==0) break; // got them all
+ // cut++;
+ // if (cut==DECDPUN) {
+ // up++;
+ // cut=0;
+ // }
+ } // forever
+
+ return dn;
+} // decBCD9ToNumber
+
+#if 0
+/* ------------------------------------------------------------------ */
+/* HWR 2/07 15:49 derived from ...... */
+/* */
+/* decPackedFromNumber -- convert decNumber to BCD Packed Decimal */
+/* */
+/* bcd is the BCD bytes */
+/* length is the length of the BCD array */
+/* scale is the scale result */
+/* dn is the decNumber */
+/* returns bcd, or NULL if error */
+/* */
+/* The number is converted to a BCD decimal byte array, */
+/* right aligned in the bcd array, whose length is indicated by the */
+/* second parameter. */
+/* scale is set to the scale of the number (this is the exponent, */
+/* negated). To force the number to a specified scale, first use the */
+/* decNumberRescale routine, which will round and change the exponent */
+/* as necessary. */
+/* */
+/* If there is an error (that is, the decNumber has too many digits */
+/* to fit in length bytes, or it is a NaN or Infinity), NULL is */
+/* returned and the bcd and scale results are unchanged. Otherwise */
+/* bcd is returned. */
+/* ------------------------------------------------------------------ */
+static uint8_t * decBCDFromNumber(uint8_t *bcd, int length, int *scale, const decNumber *dn) {
+
+ //PRINTDEC("decBCDFromNumber()", dn);
+
+ const Unit *up=dn->lsu; // Unit array pointer
+ uByte obyte=0, *out; // current output byte, and where it goes
+ Int indigs=dn->digits; // digits processed
+ uInt cut=DECDPUN; // downcounter per Unit
+ uInt u=*up; // work
+ uInt nib; // ..
+#if DECDPUN<=4
+ uInt temp; // ..
+#endif
+
+ if (dn->digits>length // too long ..
+ ||(dn->bits & DECSPECIAL)) return NULL; // .. or special -- hopeless
+
+ //if (dn->bits&DECNEG) obyte=DECPMINUS; // set the sign ..
+ //else obyte=DECPPLUS;
+ *scale=-dn->exponent; // .. and scale
+
+ // loop from lowest (rightmost) byte
+ out=bcd+length-1; // -> final byte
+ for (; out>=bcd; out--) {
+ if (indigs>0) {
+ if (cut==0) {
+ up++;
+ u=*up;
+ cut=DECDPUN;
+ }
+#if DECDPUN<=4
+ temp=(u*6554)>>16; // fast /10
+ nib=u-X10(temp);
+ u=temp;
+#else
+ nib=u%10; // cannot use *6554 trick :-(
+ u=u/10;
+#endif
+ //obyte|=(nib<<4);
+ obyte=nib & 255U;
+ indigs--;
+ cut--;
+ }
+ *out=obyte;
+ obyte=0; // assume 0
+ // if (indigs>0) {
+ // if (cut==0) {
+ // up++;
+ // u=*up;
+ // cut=DECDPUN;
+ // }
+ //#if DECDPUN<=4
+ // temp=(u*6554)>>16; // as above
+ // obyte=(uByte)(u-X10(temp));
+ // u=temp;
+ //#else
+ // obyte=(uByte)(u%10);
+ // u=u/10;
+ //#endif
+ // indigs--;
+ // cut--;
+ // }
+ } // loop
+
+ return bcd;
+} // decBCDFromNumber
+
+
+
+static unsigned char *getBCD(uint8_t bcd [256], decNumber *a)
+{
+ memset(bcd, 0, sizeof(bcd));
+ int scale;
+
+ decBCDFromNumber(bcd, a->digits, &scale, a);
+ for(int i = 0 ; i < a->digits ; i += 1 )
+ bcd[i] += '0';
+
+ return (unsigned char *) bcd;
+}
+
+
+
+static const char *CS[] = {"CSFL", "CSLS", "CSTS", "CSNS"};
+static const char *CTN[] = {"CTN9", "CTN4"};
+
+
+
+
+
+
+char *formatDecimal(decContext *set, decNumber *r, int tn, int n, int s, int sf, bool R, bool *OVR, bool *TRUNC)
+{
+ uint8_t bcd [256];
+#if 1
+ /*
+ * this is for mp3d ISOLTS error (and perhaps others)
+ */
+ if (r->digits > 63 || r->digits > n)
+ {
+ static char out1 [132];
+ memset (out1, 0, sizeof (out1));
+
+ static char out2 [132];
+ memset (out2, 0, sizeof (out2));
+
+ int scale, adjLen = n;
+
+ switch (s)
+ {
+ case CSFL: // we have a leading sign and a trailing exponent.
+ if (tn == CTN9)
+ adjLen -= 2; // a sign and an 1 9-bit exponent
+ else
+ adjLen -= 3; // a sign and 2 4-bit digits making up the exponent
+ break; // until we have an example of what to do here, let's just ignore it and hope it goes away
+ case CSLS:
+ case CSTS: // take sign into assount. One less char to play with
+ adjLen -= 1;
+ break; // until we have an example of what to do here, let's just ignore it and hope it goes away (again)
+ case CSNS: // no sign to worry about. Use everything
+ decBCDFromNumber((uint8_t *)out1, r->digits, &scale, r);
+ for(int i = 0 ; i < r->digits ; i += 1)
+ out1[i] += '0';
+ // now copy the lower n chars to out2
+// for(int i = 0 ; i < n ; i += 1)
+// {
+// out2[i] = out1[i + r->digits - n];
+// }
+ // memcpy
+ memcpy(out2, out1 + r->digits - n, (unsigned long) n);
+
+ *OVR = true;
+ return (char *) out2;
+ }
+ }
+#else
+ /*
+ * this is for mp3d ISOLTS error (and perhaps others)
+ */
+ if (r->digits > 63 || r->digits > n)
+ {
+ static char out1 [132];
+ memset (out1, 0, sizeof (out1));
+
+ static char out2 [132];
+ memset (out2, 0, sizeof (out2));
+
+ int scale, adjLen = n;
+
+ switch (s)
+ {
+ case CSFL: // we have a leading sign and a trailing exponent.
+ if (tn == CTN9)
+ adjLen -= 2; // a sign and an 1 9-bit exponent
+ else
+ adjLen -= 3; // a sign and 2 4-bit digits making up the exponent
+ break; // until we have an example of what to do here, let's just ignore it and hope it goes away
+ case CSLS:
+ case CSTS: // take sign into assount. One less char to play with
+ adjLen -= 1;
+ break; // until we have an example of what to do here, let's just ignore it and hope it goes away (again)
+ case CSNS: // no sign to worry about. Use everything
+ decBCDFromNumber((uint8_t *)out1, r->digits, &scale, r);
+ for(int i = 0 ; i < r->digits ; i += 1)
+ out1[i] += '0';
+ // now copy the lower n chars to out2
+// for(int i = 0 ; i < n ; i += 1)
+// {
+// out2[i] = out1[i + r->digits - n];
+// sim_printf("out2[%d]:%s\n", i, out2);
+// }
+ // memcpy
+ memcpy(out2, out1 + r->digits - n, n);
+
+ *OVR = true;
+ }
+ return (char *) out2;
+ }
+#endif
+
+
+ if (s == CSFL)
+ sf = 0;
+
+ // XXX what happens if we try to write a negative number to an unsigned field?????
+ // Detection of a character outside the range [0,11]8 in a digit position or a character outside the range [12,17]8 in a sign position causes an illegal procedure fault.
+
+ // adjust output length according to type ....
+ //This implies that an unsigned fixed-point receiving field has a minimum length of 1 character; a signed fixed-point field, 2 characters; and a floating-point field, 3 characters.
+
+ int adjLen = n; // adjLen is the adjusted allowed length of the result taking into account signs and/or exponent
+ switch (s)
+ {
+ case CSFL: // we have a leading sign and a trailing exponent.
+ if (tn == CTN9)
+ adjLen -= 2; // a sign and an 1 9-bit exponent
+ else
+ adjLen -= 3; // a sign and 2 4-bit digits making up the exponent
+ break;
+ case CSLS:
+ case CSTS: // take sign into assount. One less char to play with
+ adjLen -= 1;
+ break;
+ case CSNS:
+ break; // no sign to worry about. Use everything
+ }
+
+ sim_debug (DBG_TRACEEXT, & cpu_dev,
+ "\nformatDecimal: adjLen=%d SF=%d S=%s TN=%s\n", adjLen, sf, CS[s], CTN[tn]);
+ sim_debug (DBG_TRACEEXT, & cpu_dev,
+ "formatDecimal: %s r->digits=%d r->exponent=%d\n", getBCD (bcd, r), r->digits, r->exponent);
+
+ if (adjLen < 1)
+ {
+ // adjusted length is too small for anything but sign and/or exponent
+ //*OVR = 1;
+
+ // XXX what do we fill in here? Sign and exp?
+ *OVR = true;
+ return (char *)"";
+ }
+
+ // scale result (if not floating)
+
+ decNumber _r2;
+ decNumberZero(&_r2);
+
+ decNumber *r2 = &_r2;
+
+ decNumber _sf; // scaling factor
+ {
+ //decNumberTrim(r); // clean up any trailing 0's
+
+#ifndef SPEED
+ int scale;
+ char out[256], out2[256];
+
+ if_sim_debug (DBG_TRACEEXT, & cpu_dev)
+ {
+ memset (out, 0, sizeof (out));
+ memset (out2, 0, sizeof (out2));
+
+ decBCDFromNumber((uint8_t *)out, r->digits, &scale, r);
+ for(int i = 0 ; i < r->digits ; i += 1 )
+ out[i] += '0';
+ sim_printf("formatDecimal(DEBUG): out[]: '%s'\n", out);
+ }
+#endif
+
+ if (s != CSFL)// && sf != 0)
+ {
+ decNumberFromInt32(&_sf, sf);
+ sim_debug (DBG_TRACEEXT, & cpu_dev,
+ "formatDecimal(s != CSFL a): %s r->digits=%d r->exponent=%d\n", getBCD (bcd, r), r->digits, r->exponent);
+ r2 = decNumberRescale(&_r2, r, &_sf, set);
+ sim_debug (DBG_TRACEEXT, & cpu_dev,
+ "formatDecimal(s != CSFL b): %s r2->digits=%d r2->exponent=%d\n", getBCD (bcd, r2), r2->digits, r2->exponent);
+ }
+ else
+ //*r2 = *r;
+ decNumberCopy(r2, r);
+
+#ifndef SPEED
+ if_sim_debug (DBG_TRACEEXT, & cpu_dev)
+ {
+ decBCDFromNumber((uint8_t *)out2, r2->digits, &scale, r2);
+ for(int i = 0 ; i < r2->digits ; i += 1 )
+ out2[i] += '0';
+
+ sim_debug (DBG_TRACEEXT, & cpu_dev,
+ "formatDecimal: adjLen=%d E=%d SF=%d S=%s TN=%s digits(r2)=%s E2=%d\n", adjLen, r->exponent, sf, CS[s], CTN[tn],out2, r2->exponent);
+ }
+#endif
+ }
+
+ int scale;
+
+ static uint8_t out[256];
+
+ memset (out, 0, sizeof (out));
+
+ //bool ovr = (r->digits-sf) > adjLen; // is integer portion too large to fit?
+ bool ovr = r2->digits > adjLen; // is integer portion too large to fit?
+ bool trunc = r->digits > r2->digits; // did we loose something along the way?
+
+ // now let's check for overflows
+ if (!ovr && !trunc)
+ {
+ sim_debug (DBG_TRACEEXT, & cpu_dev,
+ "formatDecimal(OK): r->digits(%d) <= adjLen(%d) r2->digits(%d)\n", r->digits, adjLen, r2->digits);
+ if (s == CSFL)
+ if (r2->digits < adjLen)
+ {
+ PRINTDEC("Value 1", r2)
+
+ decNumber _s, *sc;
+ int rescaleFactor = r2->exponent - (adjLen - r2->digits);
+ sc = decNumberFromInt32(&_s, rescaleFactor);
+
+ PRINTDEC("Value sc", sc)
+ if (rescaleFactor > (adjLen - r2->digits))
+ r2 = decNumberRescale(r2, r2, sc, set);
+
+ PRINTDEC("Value 2", r2)
+ }
+ decBCDFromNumber(out, adjLen, &scale, r2);
+ for(int i = 0 ; i < adjLen ; i += 1 )
+ out[i] += '0';
+ //sim_printf("out[ot]='%s'\n", out);
+ }
+ else
+ {
+ ovr = false;
+ trunc = false;
+
+ // if we get here then we have either overflow or truncation....
+
+ sim_debug (DBG_TRACEEXT, & cpu_dev,
+ "formatDecimal(!OK%s): r2->digits %d adjLen %d\n", R ? " R" : "", r2->digits, adjLen);
+
+ // so, what do we do?
+ if (R)
+ {
+ // NB even with rounding you can have an overflow...
+
+ // if we're in rounding mode then we just make things fit and everything is OK - except if we have an overflow.
+
+ decNumber *ro = r2; //(s == CSFL ? r : r2);
+
+ int safe = set->digits;
+
+ if (ro->digits > adjLen) //(adjLen + 1))
+ {
+ //set->digits = ro->digits + sf + 1;
+ sim_debug (DBG_TRACEEXT, & cpu_dev,
+ "formatDecimal(!OK R1): ro->digits %d adjLen %d\n", ro->digits, adjLen);
+
+ set->digits = adjLen;
+ decNumberPlus(ro, ro, set);
+
+ decBCDFromNumber(out, set->digits, &scale, ro);
+ for(int i = 0 ; i < set->digits ; i += 1 )
+ out[i] += '0';
+
+ // HWR 24 Oct 2013
+ char temp[256];
+ strcpy(temp, (char *) out+set->digits-adjLen);
+ strcpy((char *) out, temp);
+
+ //strcpy(out, out+set->digits-adjLen); // this generates a SIGABRT - probably because of overlapping strings.
+
+ //sim_debug (DBG_TRACEEXT, & cpu_dev, "R OVR\n");
+ //ovr = true; breaks ET MVN 5
+ }
+ else
+ {
+ sim_debug (DBG_TRACEEXT, & cpu_dev,
+ "formatDecimal(!OK R2): ro->digits %d adjLen %d\n", ro->digits, adjLen);
+
+ if (s==CSFL)
+ {
+
+ set->digits = adjLen;
+ decNumberPlus(ro, ro, set);
+
+ decBCDFromNumber(out, adjLen, &scale, ro);
+ for(int i = 0 ; i < adjLen ; i += 1 )
+ out[i] += '0';
+ out[adjLen] = 0;
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "formatDecimal(!OK R2a): %s\n", out);
+
+ }
+ else
+ {
+ int dig = set->digits;
+ set->digits = adjLen;
+ ro = decNumberPlus(ro, ro, set); // round to adjLen digits
+ decBCDFromNumber((uint8_t *)out, adjLen, &scale, ro);
+ set->digits = dig;
+
+
+// decNumber _i;
+// decNumber *i = decNumberToIntegralValue(&_i, ro, set);
+// decBCDFromNumber((uint8_t *)out, adjLen, &scale, i);
+
+ for(int j = 0 ; j < adjLen; j += 1 )
+ out[j] += '0';
+
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "formatDecimal(!OK R2b): %s\n", out);
+ }
+ ovr = false; // since we've rounded we can have no overflow ?????
+ }
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "formatDecimal(R3): digits:'%s'\n", out);
+
+ set->digits = safe;
+
+ // display int of number
+
+#ifndef SPEED
+ if_sim_debug (DBG_TRACEEXT, & cpu_dev)
+ {
+ decNumber _i;
+ decNumber *i = decNumberToIntegralValue(&_i, ro, set);
+ char outi[256];
+ memset (outi, 0, sizeof (outi));
+ decBCDFromNumber((uint8_t *)outi, adjLen, &scale, i);
+ for(int j = 0 ; j < adjLen; j += 1 )
+ outi[j] += '0';
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "i=%s\n", outi);
+ }
+#endif
+ }
+ else
+ {
+ // if we're not in rounding mode then we can either have a truncation or an overflow
+
+ if (s == CSFL)
+ {
+ enum rounding safeR = decContextGetRounding(set); // save rounding mode
+ decContextSetRounding(set, DEC_ROUND_DOWN); // Round towards 0 (truncation).
+
+ int safe = set->digits;
+ set->digits = adjLen;
+ decNumberPlus(r2, r2, set);
+
+ decBCDFromNumber(out, r2->digits, &scale, r2);
+ for(int i = 0 ; i < adjLen ; i += 1 )
+ out[i] += '0';
+ out[adjLen] = 0;
+
+ set->digits = safe;
+ decContextSetRounding(set, safeR); // restore rounding mode
+
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "CSFL TRUNC\n");
+ trunc = true;
+ }
+ else
+ {
+ if (r2->digits < r->digits)
+ {
+ enum rounding safeR = decContextGetRounding(set); // save rounding mode
+ decContextSetRounding(set, DEC_ROUND_DOWN); // Round towards 0 (truncation).
+
+ // re-rescale r with an eye towards truncation notrounding
+
+ r2 = decNumberRescale(r2, r, &_sf, set);
+
+ if (r2->digits <= adjLen)
+ decBCDFromNumber(out, adjLen, &scale, r2);
+ else
+ decBCDFromNumber(out, r2->digits, &scale, r2);
+ for(int i = 0 ; i < adjLen; i += 1 )
+ out[i] += '0';
+ out[adjLen] = 0;
+
+ decContextSetRounding(set, safeR); // restore rounding mode
+
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "TRUNC\n");
+ trunc = true;
+
+// } else if ((r2->digits-sf) > adjLen) // HWR 18 July 2014 was (r->digits > adjLen)
+ } else if ((r2->digits) > adjLen) // HWR 18 July 2014 was (r->digits > adjLen)
+ {
+ // OVR
+ decBCDFromNumber(out, r2->digits, &scale, r2);
+ for(int i = 0 ; i < r2->digits ; i += 1 )
+ out[i] += '0';
+ out[r2->digits] = 0;
+
+ // HWR 24 Oct 2013
+ char temp[256];
+ strcpy(temp, (char *) out+r2->digits-adjLen);
+ strcpy((char *) out, temp);
+ //strcpy(out, out+r->digits-adjLen); // this generates a SIGABRT - probably because of overlapping strings.
+
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "OVR\n");
+ ovr = true;
+ }
+ else
+ sim_printf("formatDecimal(?): How'd we get here?\n");
+ }
+ }
+ }
+ sim_debug (DBG_TRACEEXT, & cpu_dev,
+ "formatDecimal(END): ovrflow=%d trunc=%d R=%d out[]='%s'\n", ovr, trunc, R, out);
+ *OVR = ovr;
+ *TRUNC = trunc;
+
+ decNumberCopy(r, r2);
+ return (char *) out;
+}
+#endif
+
+char *formatDecimal (uint8_t * out, decContext *set, decNumber *r, int nout, int s, int sf, bool R, bool *OVR, bool *TRUNC)
+{
+ decNumber _sf;
+ decNumber _r2;
+ decNumber *r2 = &_r2;
+ enum rounding safeR = decContextGetRounding(set); // save rounding mode
+ int safe = set->digits;
+
+ //char pr[256];
+
+ *OVR = false;
+ *TRUNC = false;
+
+ // CSFL isn't rescaled and can't overflow
+ if (s != CSFL) {
+
+ // rescale to sf first
+ // AL39: If N2 is not large enough to hold the integer part of C(Y-charn1) _as rescaled by SF2_, an overflow condition exists
+ // ET 336
+ if (sf != r->exponent) {
+ if (!R) {
+ decContextSetRounding(set, DEC_ROUND_DOWN); // Round towards 0 (truncation). ISOLTS 815 09b
+ if (sf > r->exponent) // ET 330: truncation due to: output sf > input sf or exponent
+ *TRUNC = true;
+ }
+ decNumberFromInt32(&_sf, sf);
+ decNumberRescale(r, r, &_sf, set);
+
+ //decNumberToString(r,pr); sim_printf("rescaled: %s %d\n",pr,r->digits);
+
+ //decContextSetRounding(set, safeR); // restore rounding mode
+ }
+
+ // check for overflow and if it occurs, adjust the operand
+
+ decContextSetRounding(set, DEC_ROUND_DOWN); // Round towards 0 (truncation).
+ decNumberToIntegralValue(r2, r, set);
+
+ //decNumberToString(r2,pr); sim_printf("integral: %s\n",pr);
+
+ // if sf< 0, this reduces number of integer slots available
+ // if sf>=0, this doesn't change anything, nout integer slots are available
+ // ET 275, ET 336
+ if (nout + min(sf,0) < r2 -> digits) {
+
+ // discard overflowing digits
+ // note that this may set zero flag: ISOLTS-810 01l
+ set->digits = r2 -> digits - (nout + min(sf,0));
+ decNumberPlus(r2, r, set);
+ set->digits = safe;
+
+ //decNumberToString(r2,pr); sim_printf("subtracting: %s\n",pr);
+
+ decNumberSubtract(r, r, r2, set);
+
+ //decNumberToString(r,pr); sim_printf("discarded: %s\n",pr);
+
+ *OVR = true;
+
+ }
+ decContextSetRounding(set, safeR);
+
+ }
+ if (nout < r->digits) { // not enough space to move all digits
+ // round or truncate
+ if (!R) {
+ decContextSetRounding(set, DEC_ROUND_DOWN); // Round towards 0 (truncation).
+ // truncation flag is independent of whether overflow occurred: ISOLTS-810 01k, 815 09b
+ *TRUNC = true;
+ }
+ set->digits = nout;
+ decNumberPlus(r, r, set);
+ set->digits = safe;
+
+ //decNumberToString(r,pr); sim_printf("r/trunc: %s\n",pr);
+
+ decContextSetRounding(set, safeR); // restore rounding mode
+ }
+
+ // write out the digits
+ // note that even CSFL is aligned right - ISOLTS-810 05d
+ uint8_t tmp[256];
+ //int scale;
+ //decBCDFromNumber(out, nout, &scale, r);
+ decNumberGetBCD(r,tmp);
+ int justif = nout - r->digits;
+ //decNumberToString(r,pr); sim_printf("justif: %d %d %d\n",nout,r->digits,justif);
+ for (int i=0;i<nout;i++) {
+ if (i<justif)
+ out[i]='0';
+ else
+ out[i]=tmp[i-justif]+'0';
+ }
+ out[nout]=0;
+
+ return (char*)out;
+
+}
+
+
+#ifndef QUIET_UNUSED
+// If the lhs is less than the rhs in the total order then the number will be set to the value -1. If they are equal, then number is set to 0. If the lhs is greater than the rhs then the number will be set to the value 1.
+int decCompare(decNumber *lhs, decNumber *rhs, decContext *set)
+{
+ decNumber _cmp, *cmp;
+ cmp = decNumberCompareTotal(&_cmp, lhs, rhs, set);
+
+ if (decNumberIsZero(cmp))
+ return 0; // lhs == rhs
+
+ if (decNumberIsNegative(cmp))
+ return -1; // lhs < rhs
+
+ return 1; // lhs > rhs
+}
+#endif
+
+int decCompareMAG(decNumber *lhs, decNumber *rhs, decContext *set)
+{
+ decNumber _cmpm, *cmpm;
+ cmpm = decNumberCompareTotalMag(&_cmpm, lhs, rhs, set);
+
+ if (decNumberIsZero(cmpm))
+ return 0; // lhs == rhs
+
+ if (decNumberIsNegative(cmpm))
+ return -1; // lhs < rhs
+
+ return 1; // lhs > rhs
+}
+
+#if 0
+int findFirstDigit(unsigned char *bcd)
+{
+ int i = 0;
+ while (bcd[i] == '0' && bcd[i])
+ i += 1;
+
+ return i;
+}
+#endif
+
+
+
+
+
+
+
--- /dev/null
+/*
+ Copyright 2012-2016 by Harry Reed
+ Copyright 2013-2016 by Charles Anthony
+ Copyright 2017 by Michal Tomek
+
+ All rights reserved.
+
+ This software is made available under the terms of the
+ ICU License -- ICU 1.8.1 and later.
+ See the LICENSE file at the top-level directory of this distribution and
+ at https://sourceforge.net/p/dps8m/code/ci/master/tree/LICENSE
+ */
+
+#define DECUSE64 1
+#define DECSUBSET 1
+#define DECDPUN 8
+#define DECBUFFER 32
+
+#define DECNUMDIGITS 126
+
+#include "decNumber.h" // base number library
+#include "decNumberLocal.h" // decNumber local types, etc.
+
+#define PRINTDEC(msg, dn) \
+ { \
+ if_sim_debug (DBG_TRACEEXT, & cpu_dev) /**/ \
+ { \
+ char temp[256]; \
+ decNumberToString(dn, temp); \
+ sim_printf("%s:'%s'\n", msg, temp); \
+ } \
+ }
+#define PRINTALL(msg, dn, set) \
+ { \
+ if_sim_debug (DBG_TRACEEXT, & cpu_dev) /**/ \
+ sim_printf("%s:'%s E%d'\n", msg, getBCDn(dn, set->digits), dn->exponent); \
+ }
+
+
+decContext * decContextDefaultDPS8(decContext *context);
+decContext * decContextDefaultDPS8Mul(decContext *context);
+decNumber * decBCD9ToNumber(const word9 *bcd, Int length, const Int scale, decNumber *dn);
+char *formatDecimal(uint8_t * out, decContext *set, decNumber *r, int nout, int s, int sf, bool R, bool *OVR, bool *TRUNC);
+//uint8_t * decBCDFromNumber(uint8_t *bcd, int length, int *scale, const decNumber *dn);
+//unsigned char *getBCD(decNumber *a);
+//char *getBCDn(decNumber *a, int digits);
+//int decCompare(decNumber *lhs, decNumber *rhs, decContext *set);
+int decCompareMAG(decNumber *lhs, decNumber *rhs, decContext *set);
--- /dev/null
+/*
+ Copyright (c) 2007-2013 Michael Mondy
+ Copyright 2012-2016 by Harry Reed
+ Copyright 2013-2016 by Charles Anthony
+ Copyright 2015-2016 by Craig Ruff
+ Copyright 2016 by Michal Tomek
+
+ All rights reserved.
+
+ This software is made available under the terms of the
+ ICU License -- ICU 1.8.1 and later.
+ See the LICENSE file at the top-level directory of this distribution and
+ at https://sourceforge.net/p/dps8m/code/ci/master/tree/LICENSE
+ */
+
+/**
+ * file dps8_eis.c
+ * project dps8
+ * date 12/31/12
+ * copyright Copyright (c) 2012 Harry Reed. All rights reserved.
+ * brief EIS support code...
+*/
+
+#ifdef EIS_PTR
+// Cached operand data...
+// Alphanumeric Operand
+// Address 18 bits --> DkW
+// CN 3 bits --> DkB
+// TA 2 bits
+// Length 12 bits
+// Numeric Operand
+// Address 18 bits --> DkW
+// CN 3 bits --> DkB
+// TN 1 bits
+// S 2 bits
+// SF 6 bits
+// Length 6 bits
+// Bit-string Operand
+// Address 18 bits --> DkW
+// C 2 bits --> DkB
+// B 4 bits --> DkB
+// Length 12 bits
+#endif
+
+#include <ctype.h>
+
+#include "dps8.h"
+#include "dps8_sys.h"
+#include "dps8_faults.h"
+//#include "dps8_scu.h"
+//#include "dps8_iom.h"
+//#include "dps8_cable.h"
+#include "dps8_cpu.h"
+#include "dps8_iefp.h"
+#include "dps8_decimal.h"
+#include "dps8_ins.h"
+#include "dps8_eis.h"
+#include "dps8_utils.h"
+
+#define DBG_CTR cpu.cycleCnt
+
+// Restart status
+//
+// a6bd n/a
+// a4bd n/a
+// a9bd n/a
+// abd n/a
+// awd n/a
+// s4bd n/a
+// s6bd n/a
+// s9bd n/a
+// sbd n/a
+// swd n/a
+// cmpc done
+// scd done
+// scdr done
+// scm done
+// scmr done
+// tct done
+// tctr done
+// mlr done
+// mrl done
+// mve
+// mvne
+// mvt done
+// cmpn
+// mvn
+// csl done
+// csr done
+// cmpb
+// btd
+// dtb
+// ad2d
+// ad3d
+// sb2d
+// sb3d
+// mp2d
+// mp3d
+// dv2d
+// dv3d
+
+// Local optimization
+#define ABD_BITS
+
+// Enable EIS operand setup refactoring code -- crashes Multics late in boot.
+//#define EIS_SETUP
+
+// EISWriteCache -- flush the cache
+//
+//
+// EISWriteIdx (p, n, data, flush); -- write to cache at p->addr [n];
+// EISRead (p) -- read to cache from p->addr
+// EISReadIdx (p, n) -- read to cache from p->addr[n];
+// EISReadN (p, n, dst) -- read N words to dst;
+
+// EISget469 (k, i)
+// EISput469 (k, i, c)
+
+// EISget49 (k, *pos, tn) get p->addr[*pos++]
+
+// AL39, Figure 2-3
+static word4 get4 (word36 w, int pos)
+ {
+ switch (pos)
+ {
+ case 0:
+ return getbits36_4 (w, 1);
+
+ case 1:
+ return getbits36_4 (w, 5);
+
+ case 2:
+ return getbits36_4 (w, 10);
+
+ case 3:
+ return getbits36_4 (w, 14);
+
+ case 4:
+ return getbits36_4 (w, 19);
+
+ case 5:
+ return getbits36_4 (w, 23);
+
+ case 6:
+ return getbits36_4 (w, 28);
+
+ case 7:
+ return getbits36_4 (w, 32);
+
+ }
+ sim_printf ("get4(): How'd we get here?\n");
+ return 0;
+}
+
+// AL39, Figure 2-4
+static word4 get6 (word36 w, int pos)
+ {
+ switch (pos)
+ {
+ case 0:
+ return getbits36_6 (w, 0);
+
+ case 1:
+ return getbits36_6 (w, 6);
+
+ case 2:
+ return getbits36_6 (w, 12);
+
+ case 3:
+ return getbits36_6 (w, 18);
+
+ case 4:
+ return getbits36_6 (w, 24);
+
+ case 5:
+ return getbits36_6 (w, 30);
+
+ }
+ sim_printf ("get6(): How'd we get here?\n");
+ return 0;
+ }
+
+// AL39, Figure 2-5
+static word9 get9(word36 w, int pos)
+ {
+
+ switch (pos)
+ {
+ case 0:
+ return getbits36_9 (w, 0);
+
+ case 1:
+ return getbits36_9 (w, 9);
+
+ case 2:
+ return getbits36_9 (w, 18);
+
+ case 3:
+ return getbits36_9 (w, 27);
+
+ }
+ sim_printf ("get9(): How'd we get here?\n");
+ return 0;
+ }
+
+// AL39, Figure 2-3
+static word36 put4 (word36 w, int pos, word4 c)
+ {
+
+// AL-39 pg 13: "The 0 bits at it positions 0, 9, 18, and 27 are forced to be 0
+// by the processor on data transfers to main memory ..."
+//
+// The code uses 5 bit sets for the even bytes to force the 0 writes.
+
+ c &= MASK4;
+ switch (pos)
+ {
+ case 0:
+ //return setbits36_4 (w, 1, c);
+ return setbits36_5 (w, 0, c);
+
+ case 1:
+ return setbits36_4 (w, 5, c);
+
+ case 2:
+ //return setbits36_4 (w, 10, c);
+ return setbits36_5 (w, 9, c);
+
+ case 3:
+ return setbits36_4 (w, 14, c);
+
+ case 4:
+ //return setbits36_4 (w, 19, c);
+ return setbits36_5 (w, 18, c);
+
+ case 5:
+ return setbits36_4 (w, 23, c);
+
+ case 6:
+ //return setbits36_4 (w, 28, c);
+ return setbits36_5 (w, 27, c);
+
+ case 7:
+ return setbits36_4 (w, 32, c);
+ }
+ sim_printf ("put4(): How'd we get here?\n");
+ return 0;
+ }
+
+// AL39, Figure 2-4
+static word36 put6 (word36 w, int pos, word6 c)
+ {
+ switch (pos)
+ {
+ case 0:
+ //return bitfieldInsert36 (w, c, 30, 6);
+ return setbits36_6 (w, 0, c);
+
+ case 1:
+ //`return bitfieldInsert36 (w, c, 24, 6);
+ return setbits36_6 (w, 6, c);
+
+ case 2:
+ //return bitfieldInsert36 (w, c, 18, 6);
+ return setbits36_6 (w, 12, c);
+
+ case 3:
+ //return bitfieldInsert36 (w, c, 12, 6);
+ return setbits36_6 (w, 18, c);
+
+ case 4:
+ //return bitfieldInsert36 (w, c, 6, 6);
+ return setbits36_6 (w, 24, c);
+
+ case 5:
+ //return bitfieldInsert36 (w, c, 0, 6);
+ return setbits36_6 (w, 30, c);
+
+ }
+ sim_printf ("put6(): How'd we get here?\n");
+ return 0;
+ }
+
+// AL39, Figure 2-5
+static word36 put9 (word36 w, int pos, word9 c)
+ {
+
+ switch (pos)
+ {
+ case 0:
+ //return bitfieldInsert36 (w, c, 27, 9);
+ return setbits36_9 (w, 0, c);
+
+ case 1:
+ //return bitfieldInsert36 (w, c, 18, 9);
+ return setbits36_9 (w, 9, c);
+
+ case 2:
+ //return bitfieldInsert36 (w, c, 9, 9);
+ return setbits36_9 (w, 18, c);
+
+ case 3:
+ //return bitfieldInsert36 (w, c, 0, 9);
+ return setbits36_9 (w, 27, c);
+
+ }
+ sim_printf ("put9(): How'd we get here?\n");
+ return 0;
+ }
+
+/**
+ * get register value indicated by reg for Address Register operations
+ * (not for use with address modifications)
+ */
+
+static word36 getCrAR (word4 reg)
+ {
+ if (reg == 0)
+ return 0;
+
+ if (reg & 010) /* Xn */
+ return cpu.rX [X (reg)];
+
+ switch (reg)
+ {
+ case TD_N:
+ return 0;
+
+ case TD_AU: // C(A)0,17
+ return GETHI (cpu.rA);
+
+ case TD_QU: // C(Q)0,17
+ return GETHI (cpu.rQ);
+
+ case TD_IC: // C(PPR.IC)
+ return cpu.PPR.IC;
+
+ case TD_AL: // C(A)18,35
+ return cpu.rA; // See AL36, Table 4-1
+
+ case TD_QL: // C(Q)18,35
+ return cpu.rQ; // See AL36, Table 4-1
+ }
+ return 0;
+ }
+
+// getMFReg
+// RType reflects the AL-39 R-type and C(op. desc.)32,35 columns
+//
+// Table 4-1. R-type Modifiers for REG Fields
+//
+// Meaning as used in:
+//
+// Octal R-type MF.REG Indirect operand C(operand descriptor)32,35
+// Code decriptor-pointer
+// 00 n n n IPR
+// 01 au au au au
+// 02 qu qu qu qu
+// 03 du IPR IPR du (a)
+// 04 ic ic ic ic (b)
+// 05 al a (c) al a (c)
+// 06 ql q (c) ql a (c)
+// 07 dl IPR IPR IPR
+// 1n xn xn xn xn
+//
+
+static word18 getMFReg18 (uint n, bool allowDU, bool allowNIC, fault_ipr_subtype_ *mod_fault)
+ {
+ switch (n)
+ {
+ case 0: // n
+ if (! allowNIC)
+ {
+ //sim_printf ("getMFReg18 n\n");
+ *mod_fault |= FR_ILL_MOD;
+ //doFault (FAULT_IPR, fst_ill_mod, "getMFReg18 n");
+ }
+ return 0;
+
+ case 1: // au
+ return GETHI (cpu.rA);
+
+ case 2: // qu
+ return GETHI (cpu.rQ);
+
+ case 3: // du
+ // du is a special case for SCD, SCDR, SCM, and SCMR
+// XXX needs attention; doesn't work with old code; triggered by
+// XXX parseOperandDescriptor;
+ if (! allowDU)
+ {
+#if 0 // fixes first fail
+sim_printf ("getMFReg18 %012"PRIo64"\n", IWB_IRODD);
+ if (cpu.currentInstruction.opcode == 0305 && // dtb
+ cpu.currentInstruction.opcodeX == 1)
+ {
+ sim_printf ("dtb special case 2\n");
+ doFault (FAULT_IPR,
+ (_fault_subtype) (((_fault_subtype) {.fault_ipr_subtype=FR_ILL_MOD}).bits |
+ ((_fault_subtype) {.fault_ipr_subtype=FR_ILL_PROC}).bits),
+ "getMFReg18 du");
+ }
+#endif
+ //sim_printf ("getMFReg18 du\n");
+ *mod_fault |= FR_ILL_MOD;
+ //doFault (FAULT_IPR, fst_ill_mod, "getMFReg18 du");
+ }
+ return 0;
+
+ case 4: // ic - The ic modifier is permitted in
+ // C (od)32,35 only if MFk.RL = 0, that is, if the contents of
+ // the register is an address offset, not the designation of
+ // a register containing the operand length.
+ // Note that AL39 is wrong saying "is permitted in MFk.REG and C(od)32,35". Only C(od)32,35 is correct. cf RJ78
+ if (! allowNIC)
+ {
+ //sim_printf ("getMFReg18 n\n");
+ *mod_fault |= FR_ILL_MOD;
+ //doFault (FAULT_IPR, fst_ill_mod, "getMFReg18 ic");
+ }
+ return cpu.PPR.IC;
+
+ case 5: // al / a
+ return GETLO (cpu.rA);
+
+ case 6: // ql / a
+ return GETLO (cpu.rQ);
+
+ case 7: // dl
+ *mod_fault |= FR_ILL_MOD;
+ //doFault (FAULT_IPR, fst_ill_mod, "getMFReg18 dl");
+ return 0;
+
+ case 8:
+ case 9:
+ case 10:
+ case 11:
+ case 12:
+ case 13:
+ case 14:
+ case 15:
+ return cpu.rX [n - 8];
+ }
+ sim_printf ("getMFReg18(): How'd we get here? n=%d\n", n);
+ return 0;
+ }
+
+static word36 getMFReg36 (uint n, bool allowDU, bool allowNIC, fault_ipr_subtype_ *mod_fault)
+ {
+ switch (n)
+ {
+ case 0: // n
+ if (! allowNIC)
+ {
+ //sim_printf ("getMFReg36 n\n");
+ *mod_fault |= FR_ILL_MOD;
+ //doFault (FAULT_IPR, fst_ill_mod, "getMFReg36 n");
+ }
+ return 0;
+ case 1: // au
+ return GETHI (cpu.rA);
+
+ case 2: // qu
+ return GETHI (cpu.rQ);
+
+ case 3: // du
+ // du is a special case for SCD, SCDR, SCM, and SCMR
+ if (! allowDU)
+ *mod_fault |= FR_ILL_MOD;
+ //doFault (FAULT_IPR, fst_ill_mod, "getMFReg36 du");
+ return 0;
+
+ case 4: // ic - The ic modifier is permitted in MFk.REG and
+ // C (od)32,35 only if MFk.RL = 0, that is, if the contents of
+ // the register is an address offset, not the designation of
+ // a register containing the operand length.
+ // Note that AL39 is wrong saying "is permitted in MFk.REG and C(od)32,35". Only C(od)32,35 is correct. cf RJ78
+ if (! allowNIC)
+ {
+ //sim_printf ("getMFReg36 n\n");
+ *mod_fault |= FR_ILL_MOD;
+ //doFault (FAULT_IPR, fst_ill_mod, "getMFReg36 ic");
+ }
+ return cpu.PPR.IC;
+
+ case 5: // al / a
+ return cpu.rA;
+
+ case 6: // ql / a
+ return cpu.rQ;
+
+ case 7: // dl
+ *mod_fault |= FR_ILL_MOD;
+ //doFault (FAULT_IPR, fst_ill_mod, "getMFReg36 dl");
+ return 0;
+
+ case 8:
+ case 9:
+ case 10:
+ case 11:
+ case 12:
+ case 13:
+ case 14:
+ case 15:
+ return cpu.rX [n - 8];
+ }
+ sim_printf ("getMFReg36(): How'd we get here? n=%d\n", n);
+ return 0;
+ }
+
+#define EISADDR_IDX(p) ((p) - cpu.currentEISinstruction.addr)
+
+static void EISWriteCache (EISaddr * p)
+ {
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "EISWriteCache addr %06o\n", p->cachedAddr);
+ word3 saveTRR = cpu.TPR.TRR;
+
+ if (p -> cacheValid && p -> cacheDirty)
+ {
+ if (p -> mat == viaPR)
+ {
+ cpu.TPR.TRR = p -> RNR;
+ cpu.TPR.TSR = p -> SNR;
+ cpu.cu.XSF = 0;
+ if_sim_debug (DBG_TRACEEXT, & cpu_dev)
+ {
+ for (uint i = 0; i < 8; i ++)
+#ifdef CWO
+ if (p->wordDirty[i])
+ {
+#endif
+ sim_debug (DBG_TRACEEXT, & cpu_dev,
+ "%s: writeCache (PR) %012"PRIo64"@%o:%06o\n",
+ __func__, p -> cachedParagraph [i], p -> SNR, p -> cachedAddr + i);
+#ifdef CWO
+ }
+#endif
+ }
+{ long eisaddr_idx = EISADDR_IDX (p);
+sim_debug (DBG_TRACEEXT, & cpu_dev, "EIS %ld Write8 TRR %o TSR %05o\n", eisaddr_idx, cpu.TPR.TRR, cpu.TPR.TSR); }
+#ifdef CWO
+ for (uint i = 0; i < 8; i ++)
+ if (p->wordDirty[i])
+ {
+ Write1 (p->cachedAddr+i, p -> cachedParagraph[i], true);
+ p->wordDirty[i] = false;
+ }
+#else
+ Write8 (p->cachedAddr, p -> cachedParagraph, true);
+#endif
+ }
+ else
+ {
+ //if (get_addr_mode() == APPEND_mode)
+ //{
+ cpu.TPR.TRR = cpu.PPR.PRR;
+ cpu.TPR.TSR = cpu.PPR.PSR;
+ cpu.cu.XSF = 0;
+ //}
+
+ if_sim_debug (DBG_TRACEEXT, & cpu_dev)
+ {
+ for (uint i = 0; i < 8; i ++)
+#ifdef CWO
+ if (p->wordDirty[i])
+ {
+#endif
+ sim_debug (DBG_TRACEEXT, & cpu_dev,
+ "%s: writeCache %012"PRIo64"@%o:%06o\n",
+ __func__, p -> cachedParagraph [i], cpu.TPR.TSR, p -> cachedAddr + i);
+#ifdef CWO
+ }
+#endif
+ }
+{ long eisaddr_idx = EISADDR_IDX (p);
+sim_debug (DBG_TRACEEXT, & cpu_dev, "EIS %ld Write8 NO PR TRR %o TSR %05o\n", eisaddr_idx, cpu.TPR.TRR, cpu.TPR.TSR); }
+#ifdef CWO
+ for (uint i = 0; i < 8; i ++)
+ if (p->wordDirty[i])
+ {
+ Write1 (p->cachedAddr+i, p -> cachedParagraph[i], false);
+ p->wordDirty[i] = false;
+ }
+#else
+ Write8 (p->cachedAddr, p -> cachedParagraph, false);
+#endif
+ }
+ }
+ p -> cacheDirty = false;
+ cpu.TPR.TRR = saveTRR;
+ }
+
+static void EISReadCache (EISaddr * p, word18 address)
+ {
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "EISReadCache addr %06o\n", address);
+ word3 saveTRR = cpu.TPR.TRR;
+
+ address &= AMASK;
+
+ word18 paragraphAddress = address & paragraphMask;
+ //word3 paragraphOffset = address & paragraphOffsetMask;
+
+ if (p -> cacheValid && p -> cachedAddr == paragraphAddress)
+ {
+ return;
+ }
+
+ if (p -> cacheValid && p -> cacheDirty && p -> cachedAddr != paragraphAddress)
+ {
+ EISWriteCache (p);
+ }
+
+ if (p -> mat == viaPR)
+ {
+ cpu.TPR.TRR = p -> RNR;
+ cpu.TPR.TSR = p -> SNR;
+ cpu.cu.XSF = 0;
+{ long eisaddr_idx = EISADDR_IDX (p);
+sim_debug (DBG_TRACEEXT, & cpu_dev, "EIS %ld Read8 TRR %o TSR %05o\n", eisaddr_idx, cpu.TPR.TRR, cpu.TPR.TSR); }
+ Read8 (paragraphAddress, p -> cachedParagraph, true);
+
+ if_sim_debug (DBG_TRACEEXT, & cpu_dev)
+ {
+ for (uint i = 0; i < 8; i ++)
+ sim_debug (DBG_TRACEEXT, & cpu_dev,
+ "%s: readCache (PR) %012"PRIo64"@%o:%06o\n",
+ __func__, p -> cachedParagraph [i], p -> SNR, paragraphAddress + i);
+ }
+ }
+ else
+ {
+ //if (get_addr_mode() == APPEND_mode)
+ //{
+ cpu.TPR.TRR = cpu.PPR.PRR;
+ cpu.TPR.TSR = cpu.PPR.PSR;
+ cpu.cu.XSF = 0;
+ //}
+
+{ long eisaddr_idx = EISADDR_IDX (p);
+sim_debug (DBG_TRACEEXT, & cpu_dev, "EIS %ld Read8 NO PR TRR %o TSR %05o\n", eisaddr_idx, cpu.TPR.TRR, cpu.TPR.TSR); }
+ Read8 (paragraphAddress, p -> cachedParagraph, false);
+ if_sim_debug (DBG_TRACEEXT, & cpu_dev)
+ {
+ for (uint i = 0; i < 8; i ++)
+ sim_debug (DBG_TRACEEXT, & cpu_dev,
+ "%s: readCache %012"PRIo64"@%o:%06o\n",
+ __func__, p -> cachedParagraph [i], cpu.TPR.TSR, paragraphAddress + i);
+ }
+ }
+ p -> cacheValid = true;
+ p -> cacheDirty = false;
+#ifdef CWO
+ for (uint i = 0; i < 8; i ++)
+ p->wordDirty[i] = false;
+#endif
+ p -> cachedAddr = paragraphAddress;
+ cpu.TPR.TRR = saveTRR;
+ }
+
+static void EISWriteIdx (EISaddr *p, uint n, word36 data, bool flush)
+{
+#ifdef EIS_PTR
+ long eisaddr_idx = EISADDR_IDX (p);
+if (eisaddr_idx < 0 || eisaddr_idx > 2) { sim_warn ("IDX1"); return }
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "EISWriteIdx addr %06o n %u\n", cpu.du.Dk_PTR_W[eisaddr_idx], n);
+ word18 addressN = (cpu.du.Dk_PTR_W[eisaddr_idx] + n) & AMASK;
+#else
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "EISWriteIdx addr %06o n %u\n", p->address, n);
+ word18 addressN = p -> address + n;
+#endif
+ addressN &= AMASK;
+
+ word18 paragraphAddress = addressN & paragraphMask;
+ word3 paragraphOffset = addressN & paragraphOffsetMask;
+
+ if (p -> cacheValid && p -> cacheDirty && p -> cachedAddr != paragraphAddress)
+ {
+ EISWriteCache (p);
+ }
+ if ((! p -> cacheValid) || p -> cachedAddr != paragraphAddress)
+ {
+ EISReadCache (p, paragraphAddress);
+ }
+ p -> cacheDirty = true;
+#ifdef CWO
+ p -> wordDirty[paragraphOffset] = true;
+#endif
+ p -> cachedParagraph [paragraphOffset] = data;
+ p -> cachedAddr = paragraphAddress;
+// XXX ticket #31
+// This a little brute force; it we fault on the next read, the cached value
+// is lost. There might be a way to logic it up so that when the next read
+// word offset changes, then we write the cache before doing the read. For
+// right now, be pessimistic. Sadly, since this is a bit loop, it is very.
+ if (flush)
+ {
+ EISWriteCache (p);
+ }
+}
+
+static word36 EISReadIdx (EISaddr * p, uint n)
+ {
+#ifdef EIS_PTR
+ long eisaddr_idx = EISADDR_IDX (p);
+if (eisaddr_idx < 0 || eisaddr_idx > 2) { sim_warn ("IDX1"); return }
+
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "EISReadIdx addr %06o n %u\n", cpu.du.Dk_PTR_W[eisaddr_idx], n);
+ word18 addressN = (cpu.du.Dk_PTR_W[eisaddr_idx] + n) & AMASK;
+#else
+ long eisaddr_idx = EISADDR_IDX (p);
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "EISReadIdx %ld addr %06o n %u\n", eisaddr_idx, p->address, n);
+ word18 addressN = p -> address + n;
+#endif
+ addressN &= AMASK;
+
+ word18 paragraphAddress = addressN & paragraphMask;
+ word3 paragraphOffset = addressN & paragraphOffsetMask;
+
+ if (p -> cacheValid && p -> cachedAddr == paragraphAddress)
+ {
+ return p -> cachedParagraph [paragraphOffset];
+ }
+ if (p -> cacheValid && p -> cacheDirty)
+ {
+ EISWriteCache (p);
+ }
+ EISReadCache (p, paragraphAddress);
+ return p -> cachedParagraph [paragraphOffset];
+ }
+
+static word36 EISRead (EISaddr * p)
+ {
+#ifdef EIS_PTR
+ long eisaddr_idx = EISADDR_IDX (p);
+if (eisaddr_idx < 0 || eisaddr_idx > 2) { sim_warn ("IDX1"); return }
+
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "EISRead addr %06o\n", cpu.du.Dk_PTR_W[eisaddr_idx]);
+#else
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "EISRead addr %06o\n", p->address);
+#endif
+ return EISReadIdx (p, 0);
+ }
+
+#if 0
+static void EISReadN (EISaddr * p, uint N, word36 *dst)
+ {
+#ifdef EIS_PTR
+ long eisaddr_idx = EISADDR_IDX (p);
+if (eisaddr_idx < 0 || eisaddr_idx > 2) { sim_warn ("IDX1"); return }
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "EISReadN addr %06o N %u\n", cpu.du.Dk_PTR_W[eisaddr_idx], N);
+#else
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "EISReadN addr %06o N %u\n", p->address, N);
+#endif
+ for (uint n = 0; n < N; n ++)
+ {
+ * dst ++ = EISReadIdx (p, n);
+ }
+ }
+#endif
+
+static void EISReadPage (EISaddr * p, uint n, word36 * data)
+ {
+#ifdef EIS_PTR
+ long eisaddr_idx = EISADDR_IDX (p);
+if (eisaddr_idx < 0 || eisaddr_idx > 2) { sim_warn ("IDX1"); return }
+ word18 addressN = (cpu.du.Dk_PTR_W[eisaddr_idx] + n) & AMASK;
+#else
+ word18 addressN = p -> address + n;
+#endif
+ addressN &= AMASK;
+
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "%s addr %06o\n", __func__, addressN);
+ if ((addressN & PGMK) != 0)
+ {
+ sim_warn ("EISReadPage not aligned %06o\n", addressN);
+ addressN &= (word18) ~PGMK;
+ }
+
+ word3 saveTRR = cpu.TPR.TRR;
+
+ if (p -> mat == viaPR)
+ {
+ cpu.TPR.TRR = p -> RNR;
+ cpu.TPR.TSR = p -> SNR;
+ cpu.cu.XSF = 0;
+ ReadPage (addressN, data, true);
+
+ if_sim_debug (DBG_TRACEEXT, & cpu_dev)
+ {
+ for (uint i = 0; i < PGSZ; i ++)
+#ifdef EIS_PTR
+ sim_debug (DBG_TRACEEXT, & cpu_dev,
+ "%s: (PR) %012"PRIo64"@%o:%06o\n",
+ __func__, data [i], cpu.TPR.TSR, addressN + i);
+#else
+ sim_debug (DBG_TRACEEXT, & cpu_dev,
+ "%s: (PR) %012"PRIo64"@%o:%06o\n",
+ __func__, data [i], p -> SNR, addressN + i);
+#endif
+ }
+ }
+ else
+ {
+ //if (get_addr_mode() == APPEND_mode)
+ //{
+ cpu.TPR.TRR = cpu.PPR.PRR;
+ cpu.TPR.TSR = cpu.PPR.PSR;
+ cpu.cu.XSF = 0;
+ //}
+
+ ReadPage (addressN, data, false);
+ if_sim_debug (DBG_TRACEEXT, & cpu_dev)
+ {
+ for (uint i = 0; i < PGSZ; i ++)
+ sim_debug (DBG_TRACEEXT, & cpu_dev,
+ "%s: %012"PRIo64"@%o:%06o\n",
+ __func__, data [i], cpu.TPR.TSR, addressN + i);
+ }
+ }
+ cpu.TPR.TRR = saveTRR;
+ }
+
+static void EISWritePage (EISaddr * p, uint n, word36 * data)
+ {
+#ifdef EIS_PTR
+ long eisaddr_idx = EISADDR_IDX (p);
+if (eisaddr_idx < 0 || eisaddr_idx > 2) { sim_warn ("IDX1"); return }
+ word18 addressN = (cpu.du.Dk_PTR_W[eisaddr_idx] + n) & AMASK;
+#else
+ word18 addressN = p -> address + n;
+#endif
+ addressN &= AMASK;
+
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "%s addr %06o\n", __func__, addressN);
+ if ((addressN & PGMK) != 0)
+ {
+ sim_warn ("EISWritePage not aligned %06o\n", addressN);
+ addressN &= (uint) ~PGMK;
+ }
+
+ word3 saveTRR = cpu.TPR.TRR;
+
+ if (p -> mat == viaPR)
+ {
+ cpu.TPR.TRR = p -> RNR;
+ cpu.TPR.TSR = p -> SNR;
+ cpu.cu.XSF = 0;
+ WritePage (addressN, data, true);
+
+ if_sim_debug (DBG_TRACEEXT, & cpu_dev)
+ {
+ for (uint i = 0; i < PGSZ; i ++)
+#ifdef EIS_PTR
+ sim_debug (DBG_TRACEEXT, & cpu_dev,
+ "%s: (PR) %012"PRIo64"@%o:%06o\n",
+ __func__, data [i], cpu.TPR.TSR, addressN + i);
+#else
+ sim_debug (DBG_TRACEEXT, & cpu_dev,
+ "%s: (PR) %012"PRIo64"@%o:%06o\n",
+ __func__, data [i], p -> SNR, addressN + i);
+#endif
+ }
+ }
+ else
+ {
+ //if (get_addr_mode() == APPEND_mode)
+ //{
+ cpu.TPR.TRR = cpu.PPR.PRR;
+ cpu.TPR.TSR = cpu.PPR.PSR;
+ cpu.cu.XSF = 0;
+ //}
+
+ WritePage (addressN, data, false);
+ if_sim_debug (DBG_TRACEEXT, & cpu_dev)
+ {
+ for (uint i = 0; i < PGSZ; i ++)
+ sim_debug (DBG_TRACEEXT, & cpu_dev,
+ "%s: %012"PRIo64"@%o:%06o\n",
+ __func__, data [i], cpu.TPR.TSR, addressN + i);
+ }
+ }
+ cpu.TPR.TRR = saveTRR;
+ }
+
+static word9 EISget469 (int k, uint i)
+ {
+ EISstruct * e = & cpu.currentEISinstruction;
+
+ uint nPos = 4; // CTA9
+#ifdef EIS_PTR3
+ switch (cpu.du.TAk[k-1])
+#else
+ switch (e -> TA [k - 1])
+#endif
+ {
+ case CTA4:
+ nPos = 8;
+ break;
+
+ case CTA6:
+ nPos = 6;
+ break;
+ }
+
+ word18 address = e -> WN [k - 1];
+ uint nChars = i + e -> CN [k - 1];
+
+ address += nChars / nPos;
+ uint residue = nChars % nPos;
+
+ PNL (cpu.du.Dk_PTR_W[k-1] = address);
+#ifdef EIS_PTR
+ cpu.du.Dk_PTR_W[k-1] = address;
+#else
+ e -> addr [k - 1].address = address;
+#endif
+ word36 data = EISRead (& e -> addr [k - 1]); // read it from memory
+
+ word9 c = 0;
+#ifdef EIS_PTR3
+ switch (cpu.du.TAk[k-1])
+#else
+ switch (e -> TA [k - 1])
+#endif
+ {
+ case CTA4:
+ c = (word9) get4 (data, (int) residue);
+ break;
+
+ case CTA6:
+ c = (word9) get6 (data, (int) residue);
+ break;
+
+ case CTA9:
+ c = get9 (data, (int) residue);
+ break;
+ }
+#ifdef EIS_PTR3
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "EISGet469 : k: %u TAk %u coffset %u c %o \n", k, cpu.du.TAk[k - 1], residue, c);
+#else
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "EISGet469 : k: %u TAk %u coffset %u c %o \n", k, e -> TA [k - 1], residue, c);
+#endif
+
+ return c;
+ }
+
+static void EISput469 (int k, uint i, word9 c469)
+ {
+ EISstruct * e = & cpu.currentEISinstruction;
+
+ uint nPos = 4; // CTA9
+#ifdef EIS_PTR3
+ switch (cpu.du.TAk[k-1])
+#else
+ switch (e -> TA [k - 1])
+#endif
+ {
+ case CTA4:
+ nPos = 8;
+ break;
+
+ case CTA6:
+ nPos = 6;
+ break;
+ }
+
+ word18 address = e -> WN [k - 1];
+ uint nChars = i + e -> CN [k - 1];
+
+ address += nChars / nPos;
+ uint residue = nChars % nPos;
+
+ PNL (cpu.du.Dk_PTR_W[k-1] = address);
+#ifdef EIS_PTR
+ cpu.du.Dk_PTR_W[k-1] = address;
+#else
+ e -> addr [k - 1].address = address;
+#endif
+ word36 data = EISRead (& e -> addr [k - 1]); // read it from memory
+
+ word36 w = 0;
+#ifdef EIS_PTR3
+ switch (cpu.du.TAk[k-1])
+#else
+ switch (e -> TA [k - 1])
+#endif
+ {
+ case CTA4:
+ w = put4 (data, (int) residue, (word4) c469);
+ break;
+
+ case CTA6:
+ w = put6 (data, (int) residue, (word6) c469);
+ break;
+
+ case CTA9:
+ w = put9 (data, (int) residue, c469);
+ break;
+ }
+ EISWriteIdx (& e -> addr [k - 1], 0, w, true);
+ }
+
+/*
+ * return a 4- or 9-bit character at memory "*address" and position "*pos".
+ * Increment pos (and address if necesary)
+ */
+
+static word9 EISget49 (EISaddr * p, int * pos, int tn)
+ {
+ int maxPos = tn == CTN4 ? 7 : 3;
+
+ if (* pos > maxPos) // overflows to next word?
+ { // yep....
+ * pos = 0; // reset to 1st byte
+ // bump source to next address
+#ifdef EIS_PTR
+ long eisaddr_idx = EISADDR_IDX (p);
+if (eisaddr_idx < 0 || eisaddr_idx > 2) { sim_warn ("IDX1"); return }
+ cpu.du.Dk_PTR_W[eisaddr_idx] = (cpu.du.Dk_PTR_W[eisaddr_idx] + 1) & AMASK;
+#else
+ p -> address = (p -> address + 1) & AMASK;
+#endif
+ p -> data = EISRead (p); // read it from memory
+ }
+ else
+ {
+ p -> data = EISRead (p); // read data word from memory
+ }
+
+ word9 c = 0;
+ switch (tn)
+ {
+ case CTN4:
+ c = get4 (p -> data, * pos);
+ break;
+ case CTN9:
+ c = get9 (p -> data, * pos);
+ break;
+ }
+
+ (* pos) ++;
+ return c;
+ }
+
+static bool EISgetBitRWN (EISaddr * p, bool flush)
+ {
+#ifdef EIS_PTR
+ long eisaddr_idx = EISADDR_IDX (p);
+if (eisaddr_idx < 0 || eisaddr_idx > 2) { sim_warn ("IDX1"); return }
+
+#endif
+ int baseCharPosn = (p -> cPos * 9); // 9-bit char bit position
+ int baseBitPosn = baseCharPosn + p -> bPos;
+ baseBitPosn += (int) cpu.du.CHTALLY;
+
+ int bitPosn = baseBitPosn % 36;
+ int woff = baseBitPosn / 36;
+
+#ifdef EIS_PTR
+ word18 saveAddr = cpu.du.Dk_PTR_W[eisaddr_idx];
+ cpu.du.Dk_PTR_W[eisaddr_idx] += (uint) woff;
+ cpu.du.Dk_PTR_W[eisaddr_idx] &= AMASK;
+#else
+ word18 saveAddr = p -> address;
+ p -> address += (uint) woff;
+#endif
+
+ p -> data = EISRead (p); // read data word from memory
+
+ if (p -> mode == eRWreadBit)
+ {
+ p -> bit = getbits36_1 (p -> data, (uint) bitPosn);
+ }
+ else if (p -> mode == eRWwriteBit)
+ {
+ p -> data = setbits36_1 (p -> data, (uint) bitPosn, p -> bit);
+
+ EISWriteIdx (p, 0, p -> data, flush); // write data word to memory
+ }
+
+ p->last_bit_posn = bitPosn;
+
+#ifdef EIS_PTR
+ cpu.du.Dk_PTR_W[eisaddr_idx] = saveAddr;
+#else
+ p -> address = saveAddr;
+#endif
+ return p -> bit;
+ }
+
+static void setupOperandDescriptorCache (int k)
+ {
+ EISstruct * e = & cpu.currentEISinstruction;
+ e -> addr [k - 1]. cacheValid = false;
+ }
+
+//
+// 5.2.10.5 Operand Descriptor Address Preparation Flowchart
+//
+// A flowchart of the operations involved in operand descriptor address
+// preparation is shown in Figure 5-2. The chart depicts the address
+// preparation for operand descriptor 1 of a multiword instruction as described
+// by modification field 1 (MF1). A similar type address preparation would be
+// carried out for each operand descriptor as specified by its MF code.
+// (Bull Nova 9000 pg 5-40 67 A2 RJ78 REV02)
+//
+// 1. The multiword instruction is obtained from memory.
+//
+// 2. The indirect (ID) bit of MF1 is queried to determine if the descriptor
+// for operand 1 is present or is an indirect word.
+//
+// 3. This step is reached only if an indirect word was in the operand
+// descriptor location. Address modification for the indirect word is now
+// performed. If the AR bit of the indirect word is 1, address register
+// modification step 4 is performed.
+//
+// 4. The y field of the indirect word is added to the contents of the
+// specified address register.
+//
+// 5. A check is now made to determine if the REG field of the indirect word
+// specifies that a register type modification be performed.
+//
+// 6. The indirect address as modified by the address register is now modified
+// by the contents of the specified register, producing the effective address
+// of the operand descriptor.
+//
+// 7. The operand descriptor is obtained from the location determined by the
+// generated effective address in item 6.
+//
+
+static void setupOperandDescriptor (int k, fault_ipr_subtype_ *mod_fault)
+ {
+ EISstruct * e = & cpu.currentEISinstruction;
+ switch (k)
+ {
+ case 1:
+ PNL (L68_ (DU_CYCLE_FA_I1;))
+ PNL (L68_ (DU_CYCLE_GDLDA;))
+ e -> MF1 = getbits36_7 (cpu.cu.IWB, 29);
+ break;
+ case 2:
+ PNL (L68_ (DU_CYCLE_FA_I2;))
+ PNL (L68_ (DU_CYCLE_GDLDB;))
+ e -> MF2 = getbits36_7 (cpu.cu.IWB, 11);
+ break;
+ case 3:
+ PNL (L68_ (DU_CYCLE_FA_I3;))
+ PNL (L68_ (DU_CYCLE_GDLDC;))
+ e -> MF3 = getbits36_7 (cpu.cu.IWB, 2);
+ break;
+ }
+
+ word18 MFk = e -> MF [k - 1];
+
+ if (MFk & MFkID)
+ {
+ PNL (L68_ (if (k == 1)
+ DU_CYCLE_LDWRT1;
+ if (k == 2)
+ DU_CYCLE_LDWRT2;))
+
+ word36 opDesc = e -> op [k - 1];
+
+// 18-28 MBZ check breaks Multics; line 161 of sys_trouble.alm contains
+//
+// 000103 aa 040100 1006 20 160 mlr (id),(pr),fill(040) copy error message
+// 000104 0a 000126 0002 05 161 arg trouble_messages-1,al
+// 000105 aa 300063 200063 162 desc9a bb|fgbx.message+3(1),64-13
+//
+// bit 28 of 000104 is set
+//
+
+#if 0
+ // Bits 18-28,30, 31 MBZ
+ if (opDesc & 0000000777660)
+ {
+#if 0 // fix 2nd fail
+sim_printf ("setupOperandDescriptor %012"PRIo64"\n", opDesc);
+sim_printf ("setupOperandDescriptor %012"PRIo64"\n", IWB_IRODD);
+ if (cpu.currentInstruction.opcode == 0305 && // dtb
+ cpu.currentInstruction.opcodeX == 1)
+ {
+ sim_printf ("dtb special case\n");
+ doFault (FAULT_IPR,
+ (_fault_subtype) (((_fault_subtype) {.fault_ipr_subtype=FR_ILL_MOD}).bits |
+ ((_fault_subtype) {.fault_ipr_subtype=FR_ILL_PROC}).bits),
+ "setupOperandDescriptor 18-28,30, 31 MBZ");
+ }
+#endif
+ doFault (FAULT_IPR, fst_ill_mod, "setupOperandDescriptor 18-28,30, 31 MBZ");
+ }
+#endif
+
+ // Bits 30, 31 MBZ
+ // RJ78 p. 5-39, ISOLTS 840 07a,07b
+ if (opDesc & 060)
+ {
+ *mod_fault |= FR_ILL_MOD;
+ //doFault (FAULT_IPR, fst_ill_mod, "setupOperandDescriptor 30,31 MBZ");
+ }
+
+ // fill operand according to MFk....
+ word18 address = GETHI (opDesc);
+ PNL (cpu.du.Dk_PTR_W[k-1] = address);
+#ifdef EIS_PTR
+ cpu.du.Dk_PTR_W[k-1] = address;
+#else
+ e -> addr [k - 1].address = address;
+#endif
+
+ // Indirect descriptor control. If ID = 1 for Mfk, then the kth word
+ // following the instruction word is an indirect pointer to the operand
+ // descriptor for the kth operand; otherwise, that word is the operand
+ // descriptor.
+ //
+ // If MFk.ID = 1, then the kth word following an EIS multiword
+ // instruction word is not an operand descriptor, but is an indirect
+ // pointer to an operand descriptor and is interpreted as shown in
+ // Figure 4-5.
+
+
+ // Mike Mondy michael.mondy@coffeebird.net sez' ...
+ // EIS indirect pointers to operand descriptors use PR registers.
+ // However, operand descriptors use AR registers according to the
+ // description of the AR registers and the description of EIS operand
+ // descriptors. However, the description of the MF field
+ // claims that operands use PR registers. The AR doesn't have a
+ // segment field. Emulation confirms that operand descriptors
+ // need to be fetched via segments given in PR registers.
+
+ bool a = opDesc & (1 << 6);
+ if (a)
+ {
+ // A 3-bit pointer register number (n) and a 15-bit offset relative
+ // to C(PRn.WORDNO) if A = 1 (all modes)
+ word3 n = (word3) getbits18 (address, 0, 3);
+ CPTUR (cptUsePRn + n);
+ word15 offset = address & MASK15; // 15-bit signed number
+ address = (cpu.AR [n].WORDNO + SIGNEXT15_18 (offset)) & AMASK;
+
+ PNL (cpu.du.Dk_PTR_W[k-1] = address);
+#ifdef EIS_PTR
+ cpu.du.Dk_PTR_W[k-1] = address;
+#else
+ e -> addr [k - 1].address = address;
+#endif
+ cpu.cu.TSN_PRNO[k-1] = n;
+ cpu.cu.TSN_VALID[k-1] = 1;
+ e -> addr [k - 1].SNR = cpu.PR [n].SNR;
+ e -> addr [k - 1].RNR = max3 (cpu.PR [n].RNR,
+ cpu.TPR.TRR,
+ cpu.PPR.PRR);
+
+ e -> addr [k - 1].mat = viaPR; // ARs involved
+sim_debug (DBG_TRACEEXT, & cpu_dev, "AR n %u k %u\n", n, k - 1);
+ }
+ else
+ {
+ e->addr [k - 1].mat = OperandRead; // no ARs involved yet
+sim_debug (DBG_TRACEEXT, & cpu_dev, "No ARb %u\n", k - 1);
+ }
+
+ // Address modifier for ADDRESS. All register modifiers except du and
+ // dl may be used. If the ic modifier is used, then ADDRESS is an
+ // 18-bit offset relative to value of the instruction counter for the
+ // instruction word. C(REG) is always interpreted as a word offset. REG
+
+ uint reg = opDesc & 017;
+ // XXX RH03/RJ78 say a,q modifiers are also available here. AL39 says al/ql only
+ address += getMFReg18 (reg, false, true, mod_fault); // ID=1: disallow du, allow n,ic
+ address &= AMASK;
+
+ PNL (cpu.du.Dk_PTR_W[k-1] = address);
+
+#ifdef EIS_PTR
+ cpu.du.Dk_PTR_W[k-1] = address;
+#else
+ e -> addr [k - 1].address = address;
+#endif
+
+ // read EIS operand .. this should be an indirectread
+ e -> op [k - 1] = EISRead (& e -> addr [k - 1]);
+ }
+ else
+ {
+ e->addr [k - 1].mat = OperandRead; // no ARs involved yet
+sim_debug (DBG_TRACEEXT, & cpu_dev, "No ARa %u\n", k - 1);
+ }
+ setupOperandDescriptorCache (k);
+}
+
+void setupEISoperands (void)
+ {
+ PNL (cpu.du.POP = 0);
+ PNL (cpu.du.POL = 0);
+
+#ifdef EIS_SETUP
+ for (int i = 0; i < 3; i ++)
+ {
+ if (i < cpu.currentInstruction.info -> ndes)
+ setupOperandDescriptor (i + 1);
+ else
+ setupOperandDescriptorCache (i + 1);
+ }
+#endif
+ }
+
+static void parseAlphanumericOperandDescriptor (uint k, uint useTA, bool allowDU, fault_ipr_subtype_ *mod_fault)
+ {
+ EISstruct * e = & cpu.currentEISinstruction;
+ word18 MFk = e -> MF [k - 1];
+
+ PNL (L68_ (if (k == 1)
+ DU_CYCLE_ANLD1;
+ else if (k == 2)
+ DU_CYCLE_ANLD2;
+ else if (k == 3)
+ DU_CYCLE_ANSTR;))
+
+ PNL (cpu.du.POP = 1);
+
+ word36 opDesc = e -> op [k - 1];
+
+ word8 ARn_CHAR = 0;
+ word6 ARn_BITNO = 0;
+
+ word18 address = GETHI (opDesc);
+
+#ifdef EIS_PTR3
+ if (useTA != k)
+ cpu.du.TAk[k-1] = cpu.du.TAk[useTA-1];
+ else
+ cpu.du.TAk[k-1] = getbits36_2 (opDesc, 21); // type alphanumeric
+#else
+ if (useTA != k)
+ e -> TA [k - 1] = e -> TA [useTA - 1];
+ else
+ e -> TA [k - 1] = getbits36_2 (opDesc, 21); // type alphanumeric
+#endif
+
+#ifdef PANEL
+ if (k == 1) // Use data from first operand
+ {
+ switch (e->TA[0])
+ {
+ case CTA9:
+ cpu.dataMode = 0102; // 9 bit an
+ cpu.ou.opsz = is_9 >> 12;
+ break;
+ case CTA6:
+ cpu.dataMode = 0042; // 6 bit an
+ cpu.ou.opsz = is_6 >> 12;
+ break;
+ case CTA4:
+ cpu.dataMode = 0022; // 4 bit an
+ cpu.ou.opsz = is_4 >> 12;
+ break;
+ }
+ }
+#endif
+
+
+// 8. Modification of the operand descriptor address begins. This step is
+// reached directly from 2 if no indirection is involved. The AR bit of MF1 is
+// checked to determine if address register modification is specified.
+//
+// 9. Address register modification is performed on the operand descriptor as
+// described under "Address Modification with Address Registers" above. The
+// character and bit positions of the specified address register are used in
+// one of two ways, depending on the type of operand descriptor, i.e., whether
+// the type is a bit string, a numeric, or an alphanumeric descriptor.
+//
+// 10. The REG field of MF1 is checked for a legal code. If DU is specified in
+// the REG field of MF2 in one of the four multiword instructions (SCD, SCDR,
+// SCM, or SCMR) for which DU is legal, the CN field is ignored and the
+// character or characters are arranged within the 18 bits of the word address
+// portion of the operand descriptor.
+//
+// 11. The count contained in the register specified by the REG field code is
+// appropriately converted and added to the operand address.
+//
+// 12. The operand is retrieved from the calculated effective address location.
+//
+
+ if (MFk & MFkAR)
+ {
+ // if MKf contains ar then it Means Y-charn is not the memory address
+ // of the data but is a reference to a pointer register pointing to the
+ // data.
+ word3 n = (word3) getbits18 (address, 0, 3);
+ CPTUR (cptUsePRn + n);
+ word18 offset = SIGNEXT15_18 ((word15) address); // 15-bit signed number
+ address = (cpu.AR [n].WORDNO + offset) & AMASK;
+
+ ARn_CHAR = GET_AR_CHAR (n); // AR[n].CHAR;
+ ARn_BITNO = GET_AR_BITNO (n); // AR[n].BITNO;
+
+ cpu.cu.TSN_PRNO[k-1] = n;
+ cpu.cu.TSN_VALID[k-1] = 1;
+ e -> addr [k - 1].SNR = cpu.PR [n].SNR;
+ e -> addr [k - 1].RNR = max3 (cpu.PR [n].RNR, cpu.TPR.TRR, cpu.PPR.PRR);
+
+ e -> addr [k - 1].mat = viaPR; // ARs involved
+sim_debug (DBG_TRACEEXT, & cpu_dev, "AR n %u k %u\n", n, k - 1);
+ }
+
+ PNL (cpu.du.POL = 1);
+
+ uint CN = getbits36_3 (opDesc, 18); // character number
+
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "initial CN%u %u\n", k, CN);
+
+ if (MFk & MFkRL)
+ {
+ uint reg = opDesc & 017;
+// XXX Handle N too big intelligently....
+ e -> N [k - 1] = (uint) getMFReg36 (reg, false, false, mod_fault); // RL=1: disallow du,n,ic
+#ifdef EIS_PTR3
+ switch (cpu.du.TAk[k-1])
+#else
+ switch (e -> TA [k - 1])
+#endif
+ {
+ case CTA4:
+ e -> N [k - 1] &= 017777777; // 22-bits of length
+ break;
+
+ case CTA6:
+ case CTA9:
+ e -> N [k - 1] &= 07777777; // 21-bits of length.
+ break;
+
+ default:
+#ifdef L68
+ doFault (FAULT_IPR, fst_ill_proc, "parseAlphanumericOperandDescriptor TA 3");
+#else
+ *mod_fault |= FR_ILL_PROC;
+#endif
+ //sim_printf ("parseAlphanumericOperandDescriptor(ta=%d) How'd we get here 1?\n", e->TA[k-1]);
+ break;
+ }
+ }
+ else
+ e -> N [k - 1] = opDesc & 07777;
+
+ //if (e->N [k - 1] == 0)
+ //doFault (FAULT_IPR, FR_ILL_PROC, "parseAlphanumericOperandDescriptor N 0");
+
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "N%u %o\n", k, e->N[k-1]);
+
+ word36 r = getMFReg36 (MFk & 017, allowDU, true, mod_fault); // allow du based on instruction, allow n,ic
+
+ if ((MFk & 017) == 4) // reg == IC ?
+ {
+ address += r;
+ address &= AMASK;
+ r = 0;
+ }
+
+ // If seems that the effect address calcs given in AL39 p.6-27 are not
+ // quite right. E.g. For CTA4/CTN4 because of the 4 "slop" bits you need
+ // to do 32-bit calcs not 36-bit!
+ uint effBITNO = 0;
+ uint effCHAR = 0;
+ uint effWORDNO = 0;
+
+#ifdef EIS_PTR3
+ switch (cpu.du.TAk[k-1])
+#else
+ switch (e -> TA [k - 1])
+#endif
+ {
+ case CTA4:
+ {
+ // Calculate character number of ARn CHAR and BITNO
+ uint bitoffset = ARn_CHAR * 9u + ARn_BITNO;
+ uint arn_char4 = bitoffset * 2 / 9; // / 4.5
+ // 8 chars per word plus the number of chars in r, plus the
+ // number of chars in ARn CHAR/BITNO plus the CN from the operand
+// XXX Handle 'r' too big intelligently...
+ uint nchars = address * 8 + (uint) r + arn_char4 + CN;
+
+ effWORDNO = nchars / 8; // 8 chars/word
+ effCHAR = nchars % 8; // effCHAR is the 4 bit char number, not
+ // the 9-bit char no
+ effBITNO = (nchars & 1) ? 5 : 0;
+
+ effWORDNO &= AMASK;
+
+ e -> CN [k - 1] = effCHAR;
+ e -> WN [k - 1] = effWORDNO;
+
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "CN%d set to %d by CTA4\n",
+ k, e -> CN [k - 1]);
+ }
+ break;
+
+ case CTA6:
+ if (CN >= 6)
+#ifdef L68
+ doFault (FAULT_IPR, fst_ill_proc, "parseAlphanumericOperandDescriptor TAn CTA6 CN >= 6");
+#else
+ *mod_fault |= FR_ILL_PROC;
+#endif
+ effBITNO = (9u * ARn_CHAR + 6u * r + ARn_BITNO) % 9u;
+ effCHAR = ((6u * CN +
+ 9u * ARn_CHAR +
+ 6u * r + ARn_BITNO) % 36u) / 6u;//9;
+ effWORDNO = (uint) (address +
+ (6u * CN +
+ 9u * ARn_CHAR +
+ 6u * r +
+ ARn_BITNO) / 36u);
+ effWORDNO &= AMASK;
+
+ e -> CN [k - 1] = effCHAR; // ??????
+ e -> WN [k - 1] = effWORDNO;
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "CN%d set to %d by CTA6\n",
+ k, e -> CN [k - 1]);
+ break;
+
+ case CTA9:
+ if (CN & 01)
+#ifdef L68
+ doFault(FAULT_IPR, fst_ill_proc, "parseAlphanumericOperandDescriptor CTA9 & CN odd");
+#else
+ *mod_fault |= FR_ILL_PROC;
+#endif
+ CN = (CN >> 1);
+
+ effBITNO = 0;
+ effCHAR = (CN + ARn_CHAR + r) % 4;
+ sim_debug (DBG_TRACEEXT, & cpu_dev,
+ "effCHAR %d = (CN %d + ARn_CHAR %d + r %"PRId64") %% 4)\n",
+ effCHAR, CN, ARn_CHAR, r);
+ effWORDNO = (uint) (address +
+ ((9u * CN +
+ 9u * ARn_CHAR +
+ 9u * r +
+ ARn_BITNO) / 36u));
+ effWORDNO &= AMASK;
+
+ e -> CN [k - 1] = effCHAR; // ??????
+ e -> WN [k - 1] = effWORDNO;
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "CN%d set to %d by CTA9\n",
+ k, e -> CN [k - 1]);
+ break;
+
+ default:
+#ifdef L68
+ doFault (FAULT_IPR, fst_ill_proc, "parseAlphanumericOperandDescriptor TA1 3");
+#else
+ *mod_fault |= FR_ILL_PROC;
+#endif
+ //sim_printf ("parseAlphanumericOperandDescriptor(ta=%d) How'd we get here 2?\n", e->TA[k-1]);
+ break;
+ }
+
+ EISaddr * a = & e -> addr [k - 1];
+ PNL (cpu.du.Dk_PTR_W[k-1] = effWORDNO);
+#ifdef EIS_PTR
+ cpu.du.Dk_PTR_W[k-1] = effWORDNO;
+#else
+ a -> address = effWORDNO;
+#endif
+ a -> cPos= (int) effCHAR;
+ a -> bPos = (int) effBITNO;
+
+#ifndef EIS_PTR3
+ // a->_type = eisTA;
+ a -> TA = (int) e -> TA [k - 1];
+#endif
+ }
+
+static void parseArgOperandDescriptor (uint k, fault_ipr_subtype_ *mod_fault)
+ {
+ PNL (L68_ (if (k == 1)
+ DU_CYCLE_NLD1;
+ else if (k == 2)
+ DU_CYCLE_NLD2;
+ else if (k == 3)
+ DU_CYCLE_GSTR;))
+
+ EISstruct * e = & cpu.currentEISinstruction;
+ word36 opDesc = e -> op [k - 1];
+ word18 y = GETHI (opDesc);
+ word1 yA = GET_A (opDesc);
+
+ uint yREG = opDesc & 0xf;
+
+ word36 r = getMFReg36 (yREG, false, true, mod_fault); // disallow du, allow n,ic
+
+ word8 ARn_CHAR = 0;
+ word6 ARn_BITNO = 0;
+
+ PNL (cpu.du.POP = 1);
+
+ if (yA)
+ {
+ // if operand contains A (bit-29 set) then it Means Y-char9n is not
+ // the memory address of the data but is a reference to a pointer
+ // register pointing to the data.
+ word3 n = GET_ARN (opDesc);
+ CPTUR (cptUsePRn + n);
+ word15 offset = y & MASK15; // 15-bit signed number
+ y = (cpu.AR [n].WORDNO + SIGNEXT15_18 (offset)) & AMASK;
+
+ ARn_CHAR = GET_AR_CHAR (n); // AR[n].CHAR;
+ ARn_BITNO = GET_AR_BITNO (n); // AR[n].BITNO;
+
+ cpu.cu.TSN_PRNO[k-1] = n;
+ cpu.cu.TSN_VALID[k-1] = 1;
+ e -> addr [k - 1].SNR = cpu.PR[n].SNR;
+ e -> addr [k - 1].RNR = max3 (cpu.PR [n].RNR, cpu.TPR.TRR, cpu.PPR.PRR);
+ e -> addr [k - 1].mat = viaPR;
+ }
+
+ y += ((9u * ARn_CHAR + 36u * r + ARn_BITNO) / 36u);
+ y &= AMASK;
+
+ PNL (cpu.du.Dk_PTR_W[k-1] = y);
+
+#ifdef EIS_PTR
+ cpu.du.Dk_PTR_W[k-1] = y;
+#else
+ e -> addr [k - 1].address = y;
+#endif
+ }
+
+static void parseNumericOperandDescriptor (int k, fault_ipr_subtype_ *mod_fault)
+{
+ PNL (L68_ (if (k == 1)
+ DU_CYCLE_NLD1;
+ else if (k == 2)
+ DU_CYCLE_NLD2;
+ else if (k == 3)
+ DU_CYCLE_GSTR;))
+
+ EISstruct * e = & cpu.currentEISinstruction;
+ word18 MFk = e->MF[k-1];
+
+ PNL (cpu.du.POP = 1);
+
+ word36 opDesc = e->op[k-1];
+
+ word8 ARn_CHAR = 0;
+ word6 ARn_BITNO = 0;
+
+ word18 address = GETHI(opDesc);
+ if (MFk & MFkAR)
+ {
+ // if MKf contains ar then it Means Y-charn is not the memory address
+ // of the data but is a reference to a pointer register pointing to the
+ // data.
+ word3 n = (word3) getbits18 (address, 0, 3);
+ CPTUR (cptUsePRn + n);
+ word15 offset = address & MASK15; // 15-bit signed number
+ address = (cpu.AR[n].WORDNO + SIGNEXT15_18(offset)) & AMASK;
+
+ ARn_CHAR = GET_AR_CHAR (n); // AR[n].CHAR;
+ ARn_BITNO = GET_AR_BITNO (n); // AR[n].BITNO;
+
+ cpu.cu.TSN_PRNO[k-1] = n;
+ cpu.cu.TSN_VALID[k-1] = 1;
+ e->addr[k-1].SNR = cpu.PR[n].SNR;
+ e->addr[k-1].RNR = max3(cpu.PR[n].RNR, cpu.TPR.TRR, cpu.PPR.PRR);
+
+ e->addr[k-1].mat = viaPR; // ARs involved
+ }
+
+ PNL (cpu.du.POL = 1);
+
+ word3 CN = getbits36_3 (opDesc, 18); // character number
+ e->TN[k-1] = getbits36_1 (opDesc, 21); // type numeric
+
+#ifdef PANEL
+ if (k == 1)
+ {
+ if (e->TN[0])
+ cpu.dataMode = 0021; // 4 bit numeric
+ else
+ cpu.dataMode = 0101; // 9 bit numeric
+ }
+#endif
+
+ e->S[k-1] = getbits36_2 (opDesc, 22); // Sign and decimal type of data
+ e->SF[k-1] = SIGNEXT6_int (getbits36_6 (opDesc, 24)); // Scaling factor.
+
+ // Operand length. If MFk.RL = 0, this field contains the operand length in
+ // digits. If MFk.RL = 1, it contains the REG code for the register holding
+ // the operand length and C(REG) is treated as a 0 modulo 64 number. See
+ // Table 4-1 and EIS modification fields (MF) above for a discussion of
+ // register codes.
+
+ if (MFk & MFkRL)
+ {
+ uint reg = opDesc & 017;
+ e->N[k-1] = getMFReg18(reg, false, false, mod_fault) & 077; // RL=1: disallow du,n,ic
+ }
+ else
+ e->N[k-1] = opDesc & 077;
+
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "parseNumericOperandDescriptor(): N%u %0o\n", k, e->N[k-1]);
+
+ word36 r = getMFReg36(MFk & 017, false, true, mod_fault); // disallow du, allow n, ic
+ if ((MFk & 017) == 4) // reg == IC ?
+ {
+ address += r;
+ address &= AMASK;
+ r = 0;
+ }
+
+#ifdef ISOLTS
+#if 0
+ uint TN = e->TN[k-1];
+ uint S = e->S[k-1]; // This is where MVNE gets really nasty.
+#endif
+#endif
+// handled in numeric instructions
+#if 0
+ uint N = e->N[k-1]; // number of chars in string
+ // I spit on the designers of this instruction set (and of COBOL.) >Ptui!<
+
+ if (N == 0)
+ {
+ doFault (FAULT_IPR, fst_ill_proc, "parseNumericOperandDescriptor N=0");
+ }
+#endif
+
+// Causes:
+//DBG(662088814)> CPU0 FAULT: Fault 10(012), sub 4294967296(040000000000), dfc N, 'parseNumericOperandDescriptor N=1 S=0|1|2'^M
+//DBG(662088814)> CPU0 FAULT: 00257:004574 bound_process_env_:command_query_+04574^M
+//DBG(662088814)> CPU0 FAULT: 664 end print_question;^M
+//DBG(662088814)> CPU0 FAULT: 00257:004574 4 000100301500 (BTD PR0|100) 000100 301(1) 0 0 0 00^M
+
+#ifdef ISOLTS
+#if 0
+// This test does not hold true for BTD operand 1; the S field is ignored by
+// the instruction
+
+ if (N == 1 && (S == 0 || S == 1 || S == 2))
+ {
+sim_printf ("k %d N %d S %d\n", k, N, S);
+ doFault (FAULT_IPR, fst_ill_proc, "parseNumericOperandDescriptor N=1 S=0|1|2");
+ }
+#endif
+
+// This breaks eis_tester 631 dtb; the S field in OP2 is ignored by the instruction.
+#if 0
+ if (N == 2 && S == 0)
+ {
+ doFault (FAULT_IPR, fst_ill_proc, "parseNumericOperandDescriptor N=2 S=0");
+ }
+#endif
+
+// handled in numeric instructions
+#if 0
+ if (N == 3 && S == 0 && TN == 1)
+ {
+ doFault (FAULT_IPR, fst_ill_proc, "parseNumericOperandDescriptor N=3 S=0 TN 1");
+ }
+#endif
+#endif
+
+
+ uint effBITNO = 0;
+ uint effCHAR = 0;
+ uint effWORDNO = 0;
+
+ // If seems that the effect address calcs given in AL39 p.6-27 are not
+ // quite right.
+ // E.g. For CTA4/CTN4 because of the 4 "slop" bits you need to do 32-bit
+ // calcs not 36-bit!
+
+ switch (e->TN[k-1])
+ {
+ case CTN4:
+ {
+ // Calculate character number of ARn CHAR and BITNO
+ uint bitoffset = ARn_CHAR * 9u + ARn_BITNO;
+ uint arn_char4 = bitoffset * 2u / 9u; // / 4.5
+ //// The odd chars start at the 6th bit, not the 5th
+ //if (bitoffset & 1) // if odd
+ // arn_char4 ++;
+ // 8 chars per word plus the number of chars in r, plus the number of chars in ARn CHAR/BITNO
+// XXX Handle 'r' too big intelligently...
+ uint nchars = (uint) (address * 8u + r + arn_char4 + CN);
+
+ effWORDNO = nchars / 8u; // 8 chars/word
+ effCHAR = nchars % 8u; // effCHAR is the 4 bit char number, not the 9-bit char no
+ effBITNO = (nchars & 1u) ? 5u : 0u;
+ effWORDNO &= AMASK;
+
+ e->CN[k-1] = effCHAR; // ?????
+ }
+ break;
+
+ case CTN9:
+ if (CN & 1u)
+#ifdef L68
+ doFault(FAULT_IPR, fst_ill_proc, "parseNumericOperandDescriptor CTA9 & CN odd");
+#else
+ *mod_fault |= FR_ILL_PROC;
+#endif
+ CN = (CN >> 1u) & 03u;
+
+ effBITNO = 0;
+ effCHAR = ((word36) CN + (word36) ARn_CHAR + r) % 4u;
+ effWORDNO = (uint) (address + (9u*CN + 9u*ARn_CHAR + 9u*r + ARn_BITNO) / 36);
+ effWORDNO &= AMASK;
+
+ e->CN[k-1] = effCHAR; // ?????
+
+ break;
+ default:
+#ifdef EIS_PTR3
+ sim_printf ("parseNumericOperandDescriptor(ta=%d) How'd we get here 2?\n", cpu.du.TAk[k-1]);
+#else
+ sim_printf ("parseNumericOperandDescriptor(ta=%d) How'd we get here 2?\n", e->TA[k-1]);
+#endif
+ break;
+ }
+
+ EISaddr *a = &e->addr[k-1];
+ PNL (cpu.du.Dk_PTR_W[k-1] = effWORDNO);
+#ifdef EIS_PTR
+ cpu.du.Dk_PTR_W[k-1] = effWORDNO;
+#else
+ a->address = effWORDNO;
+#endif
+ a->cPos = (int) effCHAR;
+ a->bPos = (int) effBITNO;
+
+ // a->_type = eisTN;
+ a->TN = (int) e->TN[k-1];
+
+#ifdef EIS_PTR
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "parseNumericOperandDescriptor(): address:%06o cPos:%d bPos:%d N%u %u\n", cpu.du.Dk_PTR_W[k-1], a->cPos, a->bPos, k, e->N[k-1]);
+#else
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "parseNumericOperandDescriptor(): address:%06o cPos:%d bPos:%d N%u %u\n", a->address, a->cPos, a->bPos, k, e->N[k-1]);
+#endif
+
+}
+
+static void parseBitstringOperandDescriptor (int k, fault_ipr_subtype_ *mod_fault)
+{
+ PNL (L68_ (if (k == 1)
+ DU_CYCLE_ANLD1;
+ else if (k == 2)
+ DU_CYCLE_ANLD2;
+ else if (k == 3)
+ DU_CYCLE_ANSTR;))
+
+ EISstruct * e = & cpu.currentEISinstruction;
+ word18 MFk = e->MF[k-1];
+ word36 opDesc = e->op[k-1];
+
+#ifdef PANEL
+ if (k == 1)
+ cpu.dataMode = 0010; // 1 bit not alpha, not alpha numeric
+#endif
+ word8 ARn_CHAR = 0;
+ word6 ARn_BITNO = 0;
+
+ PNL (cpu.du.POP = 1);
+
+ word18 address = GETHI(opDesc);
+ if (MFk & MFkAR)
+ {
+ // if MKf contains ar then it Means Y-charn is not the memory address
+ // of the data but is a reference to a pointer register pointing to the
+ // data.
+ word3 n = (word3) getbits18 (address, 0, 3);
+ CPTUR (cptUsePRn + n);
+ word15 offset = address & MASK15; // 15-bit signed number
+ address = (cpu.AR[n].WORDNO + SIGNEXT15_18(offset)) & AMASK;
+
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "bitstring k %d AR%d\n", k, n);
+
+ ARn_CHAR = GET_AR_CHAR (n); // AR[n].CHAR;
+ ARn_BITNO = GET_AR_BITNO (n); // AR[n].BITNO;
+ cpu.cu.TSN_PRNO[k-1] = n;
+ cpu.cu.TSN_VALID[k-1] = 1;
+ e->addr[k-1].SNR = cpu.PR[n].SNR;
+ e->addr[k-1].RNR = max3(cpu.PR[n].RNR, cpu.TPR.TRR, cpu.PPR.PRR);
+ e->addr[k-1].mat = viaPR; // ARs involved
+ }
+ PNL (cpu.du.POL = 1);
+
+ //Operand length. If MFk.RL = 0, this field contains the string length of
+ //the operand. If MFk.RL = 1, this field contains the code for a register
+ //holding the operand string length. See Table 4-1 and EIS modification
+ //fields (MF) above for a discussion of register codes.
+ if (MFk & MFkRL)
+ {
+ uint reg = opDesc & 017;
+ e->N[k-1] = getMFReg36(reg, false, false, mod_fault) & 077777777; // RL=1: disallow du,n,ic
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "bitstring k %d RL reg %u val %"PRIo64"\n", k, reg, (word36)e->N[k-1]);
+ }
+ else
+ {
+ e ->N[k-1] = opDesc & 07777;
+ }
+
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "bitstring k %d opdesc %012"PRIo64"\n", k, opDesc);
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "N%u %u\n", k, e->N[k-1]);
+
+
+ word4 B = getbits36_4(opDesc, 20); // bit# from descriptor
+ word2 C = getbits36_2 (opDesc, 18); // char# from descriptor
+
+ if (B >= 9)
+#ifdef L68
+ doFault (FAULT_IPR, fst_ill_proc, "parseBitstringOperandDescriptor B >= 9");
+#else
+ *mod_fault |= FR_ILL_PROC;
+#endif
+
+ word36 r = getMFReg36(MFk & 017, false, true, mod_fault); // disallow du, allow n,ic
+ if ((MFk & 017) == 4) // reg == IC ?
+ {
+ // If reg == IC, then R is in words, not bits.
+ //r *= 36;
+ address += r;
+ address &= AMASK;
+ r = 0;
+ }
+
+ uint effBITNO = (9u*ARn_CHAR + r + ARn_BITNO + B + 9u*C) % 9u;
+ uint effCHAR = ((9u*ARn_CHAR + r + ARn_BITNO + B + 9u*C) % 36u) / 9u;
+ uint effWORDNO = (uint) (address + (9u*ARn_CHAR + r + ARn_BITNO + B + 9u*C) / 36u);
+ effWORDNO &= AMASK;
+
+ e->B[k-1] = effBITNO;
+ e->C[k-1] = effCHAR;
+
+ EISaddr *a = &e->addr[k-1];
+ PNL (cpu.du.Dk_PTR_W[k-1] = effWORDNO);
+#ifdef EIS_PTR
+ cpu.du.Dk_PTR_W[k-1] = effWORDNO;
+#else
+ a->address = effWORDNO;
+#endif
+ a->cPos = (int) effCHAR;
+ a->bPos = (int) effBITNO;
+}
+
+static void cleanupOperandDescriptor (int k)
+ {
+ EISstruct * e = & cpu.currentEISinstruction;
+ if (e -> addr [k - 1].cacheValid && e -> addr [k - 1].cacheDirty)
+ {
+ EISWriteCache(& e -> addr [k - 1]);
+ }
+ e -> addr [k - 1].cacheDirty = false;
+ }
+
+// For a4bd/s4bd, the world is made of 32 bit words, so the address space
+// is 2^18 * 32 bits
+#define n4bits (1 << 23)
+// For a4bd/s4bd, the world is made of 8 4-bitcharacter words, so the address space
+// is 2^18 * 8 characters
+#define n4chars (1 << 21)
+// For axbd/sxbd, the world is made of 36 bits words, so the address space
+// is 2^18 * 36 bits
+#define nxbits ((1 << 18) * 36)
+
+// 2 * (s->BITNO / 9) + (s->BITNO % 9) / 4;
+static unsigned int cntFromBit[36] = {
+ 0, 0, 0, 0, 0, 1, 1, 1, 1,
+ 2, 2, 2, 2, 2, 3, 3, 3, 3,
+ 4, 4, 4, 4, 4, 5, 5, 5, 5,
+ 6, 6, 6, 6, 6, 7, 7, 7, 7
+};
+
+static word6 bitFromCnt[8] = {1, 5, 10, 14, 19, 23, 28, 32};
+
+void a4bd (void)
+ {
+ // 8 4-bit characters/word
+
+ uint ARn = GET_ARN (cpu.cu.IWB);
+ CPTUR (cptUsePRn + ARn);
+ int32_t address = SIGNEXT15_32 (GET_OFFSET (cpu.cu.IWB));
+//if (current_running_cpu_idx)
+//sim_printf ("a4bd address %o %d.\n", address, address);
+
+ word4 reg = GET_TD (cpu.cu.IWB); // 4-bit register modification (None except
+ // au, qu, al, ql, xn)
+ // r is the count of 4bit characters
+ word36 ur = getCrAR (reg);
+ int32 r = SIGNEXT22_32 ((word22) ur);
+//if (current_running_cpu_idx)
+//sim_printf ("a4bd r %o %d.\n", r, r);
+
+ uint augend = 0; // in 4bit characters
+ if (GET_A (cpu.cu.IWB))
+ {
+//if (current_running_cpu_idx)
+//sim_printf ("a4bd AR%d WORDNO %o %d. CHAR %o BITNO %o\n", cpu.AR[ARn].WORDNO, cpu.AR[ARn].WORDNO, cpu.AR[ARn].WORDNO, cpu.AR[ARn].CHAR, cpu.AR[ARn].BITNO);
+
+ //augend = cpu.AR[ARn].WORDNO * 32u + cntFromBit [GET_AR_BITNO (ARn)];
+ // force to 4 bit character boundary
+ //augend = augend & ~3;
+ //augend = cpu.AR[ARn].WORDNO * 8 + cpu.AR[ARn].CHAR * 2;
+ augend = cpu.AR[ARn].WORDNO * 8u + GET_AR_CHAR (ARn) * 2u;
+
+ //if (cpu.AR[ARn].BITNO >= 5)
+ if (GET_AR_BITNO (ARn) >= 5u)
+ augend ++;
+ }
+
+//if (current_running_cpu_idx)
+//sim_printf ("a4bd augend %o %d.\n", augend, augend);
+
+ int32_t addend = address * 8 + r; // in characters
+
+//if (current_running_cpu_idx)
+//sim_printf ("a4bd addend %o %d.\n", addend, addend);
+
+ int32_t sum = (int32_t) augend + addend;
+//if (current_running_cpu_idx)
+//sim_printf ("a4bd sum %o %d.\n", sum, sum);
+
+
+ // Handle over/under flow
+ while (sum < 0)
+ sum += n4chars;
+ sum = sum % n4chars;
+//if (current_running_cpu_idx)
+//sim_printf ("a4bd sum %o %d.\n", sum, sum);
+
+
+ cpu.AR[ARn].WORDNO = (word18) (sum / 8) & AMASK;
+//if (current_running_cpu_idx)
+//sim_printf ("a4bd WORDNO %o %d.\n", cpu.AR[ARn].WORDNO, cpu.AR[ARn].WORDNO);
+
+// // 0aaaabbbb0ccccdddd0eeeeffff0gggghhhh
+// // 111111 11112222 22222233
+// // 01234567 89012345 67890123 45678901 // 4 bit notation offset
+// static int tab [32] = { 1, 2, 3, 4, 5, 6, 7, 8,
+// 10, 11, 12, 13, 14, 15, 16, 17,
+// 19, 20, 21, 22, 23, 24, 25, 26,
+// 28, 29, 30, 31, 32, 33, 34, 35};
+//
+ //uint bitno = sum % 32;
+// AR [ARn].BITNO = tab [bitno];
+ //cpu.AR [ARn].BITNO = bitFromCnt[bitno % 8];
+ //SET_PR_BITNO (ARn, bitFromCnt[bitno % 8]);
+ uint char4no = (uint) (sum % 8);
+//if (current_running_cpu_idx)
+//sim_printf ("a4bd char4no %d.\n", char4no);
+
+ SET_AR_CHAR_BITNO (ARn, (word2) (char4no / 2), (char4no % 2) ? 5 : 0);
+ HDBGRegAR (ARn);
+//if (current_running_cpu_idx)
+//sim_printf ("a4bd CHAR %o %d.\n", cpu.AR[ARn].CHAR, cpu.AR[ARn].CHAR);
+//if (current_running_cpu_idx)
+//sim_printf ("a4bd BITNO %o %d.\n", cpu.AR[ARn].BITNO, cpu.AR[ARn].BITNO);
+ }
+
+
+void s4bd (void)
+ {
+ uint ARn = GET_ARN (cpu.cu.IWB);
+ CPTUR (cptUsePRn + ARn);
+ int32_t address = SIGNEXT15_32 (GET_OFFSET (cpu.cu.IWB));
+ word4 reg = GET_TD (cpu.cu.IWB); // 4-bit register modification (None except
+ // au, qu, al, ql, xn)
+ // r is the count of characters
+ word36 ur = getCrAR (reg);
+ int32 r = SIGNEXT22_32 ((word22) ur);
+
+ uint minuend = 0;
+ if (GET_A (cpu.cu.IWB))
+ {
+ //minuend = cpu.AR [ARn].WORDNO * 32 + cntFromBit [GET_PR_BITNO (ARn)];
+ minuend = cpu.AR [ARn].WORDNO * 32 + cntFromBit [GET_AR_CHAR (ARn) * 9 + GET_AR_BITNO (ARn)];
+ // force to 4 bit character boundary
+ minuend = minuend & (unsigned int) ~3;
+ }
+ int32_t subtractend = address * 32 + r * 4;
+ int32_t difference = (int32_t) minuend - subtractend;
+
+ // Handle over/under flow
+ while (difference < 0)
+ difference += n4bits;
+ difference = difference % n4bits;
+
+ cpu.AR [ARn].WORDNO = (word18) (difference / 32) & AMASK;
+
+// // 0aaaabbbb0ccccdddd0eeeeffff0gggghhhh
+// // 111111 11112222 22222233
+// // 01234567 89012345 67890123 45678901 // 4 bit notation offset
+// static int tab [32] = { 1, 2, 3, 4, 5, 6, 7, 8,
+// 10, 11, 12, 13, 14, 15, 16, 17,
+// 19, 20, 21, 22, 23, 24, 25, 26,
+// 28, 29, 30, 31, 32, 33, 34, 35};
+//
+
+ uint bitno = (uint) (difference % 32);
+// cpu.AR [ARn].BITNO = tab [bitno];
+ // SET_PR_BITNO (ARn, bitFromCnt[bitno % 8]);
+ SET_AR_CHAR_BITNO (ARn, bitFromCnt[bitno % 8] / 9, bitFromCnt[bitno % 8] % 9);
+ HDBGRegAR (ARn);
+ }
+
+void axbd (uint sz)
+ {
+ uint ARn = GET_ARN (cpu.cu.IWB);
+ CPTUR (cptUsePRn + ARn);
+ int32_t address = SIGNEXT15_32 (GET_OFFSET (cpu.cu.IWB));
+ word6 reg = GET_TD (cpu.cu.IWB); // 4-bit register modification (None except
+ // au, qu, al, ql, xn)
+ // r is the count of characters
+ word36 rcnt = getCrAR (reg);
+ int32_t r;
+
+ if (sz == 1)
+ r = SIGNEXT24_32 ((word24) rcnt);
+ else if (sz == 4)
+ r = SIGNEXT22_32 ((word22) rcnt);
+ else if (sz == 6)
+ r = SIGNEXT21_32 ((word21) rcnt);
+ else if (sz == 9)
+ r = SIGNEXT21_32 ((word21) rcnt);
+ else // if (sz == 36)
+ r = SIGNEXT18_32 ((word18) rcnt);
+
+ sim_debug (DBG_TRACEEXT|DBG_CAC, & cpu_dev, "axbd sz %d ARn 0%o address 0%o reg 0%o r 0%o\n", sz, ARn, address, reg, r);
+
+
+ uint augend = 0;
+ if (GET_A (cpu.cu.IWB))
+ {
+ sim_debug (DBG_TRACEEXT|DBG_CAC, & cpu_dev, "axbd ARn %d WORDNO %o CHAR %o BITNO %0o %d.\n", ARn, cpu.PAR[ARn].WORDNO, GET_AR_CHAR (ARn), GET_AR_BITNO (ARn), GET_AR_BITNO (ARn));
+ augend = cpu.AR[ARn].WORDNO * 36u + GET_AR_CHAR (ARn) * 9u + GET_AR_BITNO (ARn);
+ }
+ sim_debug (DBG_TRACEEXT|DBG_CAC, & cpu_dev, "axbd augend 0%o\n", augend);
+ // force to character boundary
+ //if (sz == 9 || sz == 36 || GET_A (cpu.cu.IWB))
+ if (sz == 9 || GET_A (cpu.cu.IWB))
+ {
+ augend = (augend / sz) * sz;
+ sim_debug (DBG_TRACEEXT|DBG_CAC, & cpu_dev, "axbd force augend 0%o\n", augend);
+ }
+// If sz == 9, this is an a9bd instruction; ISOLTS says that r is in characters, not bits.
+// wow. That breaks the boot bad.
+// if (sz == 9)
+// {
+// r *= 9;
+//if (current_running_cpu_idx)
+//sim_printf ("axbd force chars 0%o %d. bits\n", r, r);
+// }
+
+ int32_t addend = address * 36 + r * (int32_t) sz;
+ int32_t sum = (int32_t) augend + addend;
+
+ // Handle over/under flow
+ while (sum < 0)
+ sum += nxbits;
+ sum = sum % nxbits;
+
+ sim_debug (DBG_TRACEEXT|DBG_CAC, & cpu_dev, "axbd augend 0%o addend 0%o sum 0%o\n", augend, addend, sum);
+
+ cpu.AR [ARn].WORDNO = (word18) (sum / 36) & AMASK;
+ //SET_PR_BITNO (ARn, sum % 36);
+ SET_AR_CHAR_BITNO (ARn, (sum % 36) / 9, sum % 9);
+ HDBGRegAR (ARn);
+ }
+
+#if 1
+void abd (void)
+ {
+ uint ARn = GET_ARN (cpu.cu.IWB);
+ CPTUR (cptUsePRn + ARn);
+
+ word18 address = SIGNEXT15_18 (GET_OFFSET (cpu.cu.IWB));
+//if (current_running_cpu_idx)
+//sim_printf ("address %o\n", address);
+ word4 reg = (word4) GET_TD (cpu.cu.IWB);
+ // r is the count of bits (0 - 2^18 * 36 -1); 24 bits
+ word24 r = getCrAR ((word4) reg) & MASK24;
+//if (current_running_cpu_idx)
+//sim_printf ("r 0%o %d.\n", r, r);
+//if (current_running_cpu_idx)
+//sim_printf ("abd WORDNO 0%o %d. CHAR %o BITNO 0%o %d.\n", cpu.AR[ARn].WORDNO, cpu.AR[ARn].WORDNO, cpu.AR[ARn].CHAR, cpu.AR[ARn].BITNO, cpu.AR[ARn].BITNO);
+
+ //if (cpu.AR[ARn].BITNO > 8)
+ //cpu.AR[ARn].BITNO = 8;
+ if (GET_AR_BITNO (ARn) > 8)
+ SET_AR_CHAR_BITNO (ARn, GET_AR_CHAR (ARn), 8);
+
+ if (GET_A (cpu.cu.IWB))
+ {
+//if (current_running_cpu_idx)
+//sim_printf ("A 1\n");
+ //word24 bits = 9 * cpu.AR[ARn].CHAR + cpu.AR[ARn].BITNO + r;
+ word24 bits = 9u * GET_AR_CHAR (ARn) + GET_AR_BITNO (ARn) + r;
+//if (current_running_cpu_idx)
+//sim_printf ("bits 0%o %d.\n", bits, bits);
+ cpu.AR[ARn].WORDNO = (cpu.AR[ARn].WORDNO + address +
+ bits / 36) & MASK18;
+ if (r % 36)
+ {
+ //cpu.AR[ARn].CHAR = (bits % 36) / 9;
+ //cpu.AR[ARn].BITNO = bits % 9;
+ SET_AR_CHAR_BITNO (ARn, (bits % 36) / 9,
+ bits % 9);
+ }
+ }
+ else
+ {
+//if (current_running_cpu_idx)
+//sim_printf ("A 0\n");
+ cpu.AR[ARn].WORDNO = (address + r / 36) & MASK18;
+ if (r % 36)
+ {
+ //cpu.AR[ARn].CHAR = (r % 36) / 9;
+ //cpu.AR[ARn].BITNO = r % 9;
+ SET_AR_CHAR_BITNO (ARn, (r % 36) / 9,
+ r % 9);
+ }
+ }
+ HDBGRegAR (ARn);
+
+//if (current_running_cpu_idx)
+//sim_printf ("abd WORDNO 0%o %d. CHAR %o BITNO 0%o %d.\n", cpu.AR[ARn].WORDNO, cpu.AR[ARn].WORDNO, cpu.AR[ARn].CHAR, cpu.AR[ARn].BITNO, cpu.AR[ARn].BITNO);
+ }
+#else
+void abd (void)
+ {
+ uint ARn = GET_ARN (cpu.cu.IWB);
+ CPTUR (cptUsePRn + ARn);
+ int32_t address = SIGNEXT15_32 (GET_OFFSET (cpu.cu.IWB));
+
+if (current_running_cpu_idx)
+sim_printf ("abd address 0%o %d.\n", address, address);
+
+ // 4-bit register modification (None except
+ // au, qu, al, ql, xn)
+ uint reg = GET_TD (cpu.cu.IWB);
+
+ // r is the count of bits
+ int32_t r = getCrAR (reg);
+
+if (current_running_cpu_idx)
+sim_printf ("abd r 0%o %d.\n", r, r);
+
+ r = SIGNEXT24_32 (r);
+
+if (current_running_cpu_idx)
+sim_printf ("abd r 0%o %d.\n", r, r);
+
+#define SEPARATE
+
+ uint augend = 0; // in bits
+#ifdef SEPARATE
+ uint bitno = 0;
+#endif
+
+ if (GET_A (cpu.cu.IWB))
+ {
+
+if (current_running_cpu_idx)
+sim_printf ("abd ARn %d WORDNO %o CHAR %o BITNO %0o %d. PR_BITNO %0o %d.\n", ARn, cpu.PAR[ARn].WORDNO, cpu.PAR[ARn].CHAR, cpu.PAR[ARn].BITNO, cpu.PAR[ARn].BITNO, GET_AR_BITNO (ARn), GET_AR_BITNO (ARn));
+ sim_debug (DBG_TRACEEXT|DBG_CAC, & cpu_dev, "abd ARn %d WORDNO %o BITNO %0o %d.\n", ARn, cpu.PAR[ARn].WORDNO, GET_AR_BITNO (ARn), GET_AR_BITNO (ARn));
+
+#ifdef SEPARATE
+ //augend = cpu.AR[ARn].WORDNO * 36 + cpu.AR[ARn].CHAR * 9;
+ //bitno = cpu.AR[ARn].BITNO;
+ augend = cpu.AR[ARn].WORDNO * 36 + GET_AR_CHAR (ARn) * 9;
+ bitno = GET_AR_BITNO (ARn);
+#else
+ augend = cpu.AR [ARn].WORDNO * 36 + GET_AR_CHARNO (ARn) * 9 + GET_AR_BITNO (ARn);
+#endif
+ }
+
+if (current_running_cpu_idx)
+sim_printf ("abd augend 0%o %d.\n", augend, augend);
+
+#ifdef SEPARATE
+ if (GET_A (cpu.cu.IWB))
+ {
+
+if (current_running_cpu_idx)
+sim_printf ("abd bitno 0%o %d.\n", bitno, bitno);
+
+ int32_t rBitcnt = r % 36;
+
+if (current_running_cpu_idx)
+sim_printf ("abd rBitcnt 0%o %d.\n", rBitcnt, rBitcnt);
+
+ r -= rBitcnt;
+
+if (current_running_cpu_idx)
+sim_printf ("abd r 0%o %d.\n", r, r);
+ sim_debug (DBG_TRACEEXT|DBG_CAC, & cpu_dev, "abd augend 0%o\n", augend);
+
+
+ // BITNO overflows oddly; handle separately
+
+ int32_t deltaBits = rBitcnt + bitno;
+
+if (current_running_cpu_idx)
+sim_printf ("abd deltaBits 0%o %d.\n", deltaBits, deltaBits);
+
+ while (deltaBits < 0)
+ {
+ deltaBits += 9;
+ r -= 9;
+ }
+ while (deltaBits > 15)
+ {
+ deltaBits -= 9;
+ r += 9;
+ }
+ cpu.AR[ARn].BITNO = deltaBits;
+
+if (current_running_cpu_idx)
+sim_printf ("abd deltaBits 0%o %d.\n", deltaBits, deltaBits);
+if (current_running_cpu_idx)
+sim_printf ("abd r 0%o %d.\n", r, r);
+
+ }
+ else
+ {
+ cpu.AR[ARn].BITNO = (r % 9) & MASK4;
+ }
+#endif
+
+ int32_t addend = address * 36 + r;
+
+if (current_running_cpu_idx)
+sim_printf ("abd addend 0%o %d.\n", addend, addend);
+
+ int32_t sum = augend + addend;
+
+if (current_running_cpu_idx)
+sim_printf ("abd sum 0%o %d.\n", sum, sum);
+
+
+
+ // Handle over/under flow
+ while (sum < 0)
+ sum += nxbits;
+ sum = sum % nxbits;
+
+if (current_running_cpu_idx)
+sim_printf ("abd sum 0%o %d.\n", sum, sum);
+
+ sim_debug (DBG_TRACEEXT|DBG_CAC, & cpu_dev, "abd augend 0%o addend 0%o sum 0%o\n", augend, addend, sum);
+
+ cpu.AR[ARn].WORDNO = (sum / 36) & AMASK;
+#ifdef SEPARATE
+ //cpu.AR[ARn].CHAR = (sum / 9) & MASK2;
+ SET_AR_CHAR_BITNO (ARn, (sum / 9) & MASK2, GET_AR_BITNO (ARn));
+#else
+ // Fails ISOLTS
+ //SET_PR_BITNO (ARn, sum % 36);
+ SET_AR_CHAR_BITNO (ARn, (sum % 36) / 9, sum % 9);
+#endif
+ HDBGRegAR (ARn);
+
+ // Fails boot
+ //uint bitno = sum % 36;
+ //cpu.AR[ARn].CHAR = (bitno >> 4) & MASK2;
+ //cpu.AR[ARn].BITNO = bitno & MASK4;
+
+
+if (current_running_cpu_idx)
+sim_printf ("abd WORDNO 0%o %d. CHAR %o BITNO 0%o %d.\n", cpu.AR[ARn].WORDNO, cpu.AR[ARn].WORDNO, cpu.AR[ARn].CHAR, cpu.AR[ARn].BITNO, cpu.AR[ARn].BITNO);
+ }
+#endif
+
+void awd (void)
+ {
+ uint ARn = GET_ARN (cpu.cu.IWB);
+ CPTUR (cptUsePRn + ARn);
+ int32_t address = SIGNEXT15_32 (GET_OFFSET (cpu.cu.IWB));
+ // 4-bit register modification (None except
+ // au, qu, al, ql, xn)
+ word4 reg = (word4) GET_TD (cpu.cu.IWB);
+ // r is the count of characters
+// XXX This code is assuming that 'r' has 18 bits of data....
+ int32_t r = (int32_t) (getCrAR (reg) & MASK18);
+ r = SIGNEXT18_32 ((word18) r);
+
+ sim_debug (DBG_TRACEEXT|DBG_CAC, & cpu_dev,
+ "awd ARn 0%o address 0%o reg 0%o r 0%o\n", ARn, address, reg, r);
+
+
+ uint augend = 0;
+ if (GET_A (cpu.cu.IWB))
+ {
+ sim_debug (DBG_TRACEEXT|DBG_CAC, & cpu_dev, "awd ARn %d WORDNO %o CHAR %o BITNO %0o %d.\n", ARn, cpu.PAR[ARn].WORDNO, GET_AR_CHAR (ARn), GET_AR_BITNO (ARn), GET_AR_BITNO (ARn));
+
+ //augend = cpu.AR [ARn].WORDNO * 36 + GET_AR_CHAR (ARn) * 9 + GET_AR_BITNO (ARn);
+ augend = cpu.AR [ARn].WORDNO;
+ }
+
+ sim_debug (DBG_TRACEEXT|DBG_CAC, & cpu_dev, "awd augend 0%o\n", augend);
+
+ int32_t addend = address + r;
+ int32_t sum = (int32_t) augend + addend;
+
+ sim_debug (DBG_TRACEEXT|DBG_CAC, & cpu_dev, "awd augend 0%o addend 0%o sum 0%o\n", augend, addend, sum);
+
+ cpu.AR[ARn].WORDNO = (word18) sum & AMASK;
+ SET_AR_CHAR_BITNO (ARn, 0, 0);
+ HDBGRegAR (ARn);
+ }
+
+void sbd (void)
+ {
+ uint ARn = GET_ARN (cpu.cu.IWB);
+
+ word18 address = SIGNEXT15_18 (GET_OFFSET (cpu.cu.IWB));
+ word4 reg = (word4) GET_TD (cpu.cu.IWB);
+ // r is the count of bits (0 - 2^18 * 36 -1); 24 bits
+ word24 r = getCrAR ((word4) reg) & MASK24;
+ if (GET_AR_BITNO (ARn) > 8)
+ SET_AR_CHAR_BITNO (ARn, GET_AR_CHAR (ARn), 8);
+
+ if (GET_A (cpu.cu.IWB))
+ {
+ word24 bits = 9u * GET_AR_CHAR (ARn) + GET_AR_BITNO (ARn) - r;
+ cpu.AR[ARn].WORDNO = (cpu.AR[ARn].WORDNO -
+ address + bits / 36) & MASK18;
+ if (r % 36)
+ {
+ SET_AR_CHAR_BITNO (ARn, (- ((bits % 36) / 9)) & MASK2,
+ (- (bits % 9)) & MASK4);
+ }
+ }
+ else
+ {
+ cpu.AR[ARn].WORDNO = (- (address + r / 36)) & MASK18;
+ if (r % 36)
+ {
+ SET_AR_CHAR_BITNO (ARn, (- ((r % 36) / 9)) & MASK2,
+ (- (r % 9)) & MASK4);
+ }
+ }
+ HDBGRegAR (ARn);
+ }
+
+void swd (void)
+ {
+ uint ARn = GET_ARN (cpu.cu.IWB);
+ CPTUR (cptUsePRn + ARn);
+ int32_t address = SIGNEXT15_32 (GET_OFFSET (cpu.cu.IWB));
+ // 4-bit register modification (None except
+ // au, qu, al, ql, xn)
+ word4 reg = (word4) GET_TD (cpu.cu.IWB);
+ // r is the count of characters
+// XXX This code is assuming that 'r' has 18 bits of data....
+ int32_t r = (int32_t) (getCrAR (reg) & MASK18);
+ r = SIGNEXT18_32 ((word18) r);
+
+ sim_debug (DBG_TRACEEXT|DBG_CAC, & cpu_dev, "swd ARn 0%o address 0%o reg 0%o r 0%o\n", ARn, address, reg, r);
+
+ uint minued = 0;
+ if (GET_A (cpu.cu.IWB))
+ {
+ sim_debug (DBG_TRACEEXT|DBG_CAC, & cpu_dev, "swd ARn %d WORDNO %o CHAR %o BITNO %0o %d.\n", ARn, cpu.PAR[ARn].WORDNO, GET_AR_CHAR (ARn), GET_AR_BITNO (ARn), GET_AR_BITNO (ARn));
+
+ //minued = cpu.AR [ARn].WORDNO * 36 + GET_AR_BITNO (ARn);
+ minued = cpu.AR [ARn].WORDNO;
+ }
+
+ sim_debug (DBG_TRACEEXT|DBG_CAC, & cpu_dev, "swd minued 0%o\n", minued);
+
+ int32_t subtractend = address + r;
+ int32_t difference = (int32_t) minued - subtractend;
+
+ sim_debug (DBG_TRACEEXT|DBG_CAC, & cpu_dev, "swd minued 0%o subtractend 0%o difference 0%o\n", minued, subtractend, difference);
+
+ cpu.AR [ARn].WORDNO = (word18) difference & AMASK;
+ SET_AR_CHAR_BITNO (ARn, 0, 0);
+ HDBGRegAR (ARn);
+ }
+
+void s9bd (void)
+ {
+ uint ARn = GET_ARN (cpu.cu.IWB);
+ CPTUR (cptUsePRn + ARn);
+ word18 address = SIGNEXT15_18 (GET_OFFSET (cpu.cu.IWB));
+ // 4-bit register modification (None except
+ // au, qu, al, ql, xn)
+ word4 reg = (word4) GET_TD (cpu.cu.IWB);
+
+ // r is the count of 9-bit characters
+ word21 r = getCrAR (reg) & MASK21;;
+
+ sim_debug (DBG_TRACEEXT|DBG_CAC, & cpu_dev, "s9bd r 0%o\n", r);
+
+ sim_debug (DBG_TRACEEXT|DBG_CAC, & cpu_dev, "s9bd ARn 0%o address 0%o reg 0%o r 0%o\n", ARn, address, reg, r);
+
+ if (GET_A (cpu.cu.IWB))
+ {
+ //cpu.AR[ARn].WORDNO = (cpu.AR[ARn].WORDNO -
+ // address +
+ // (cpu.AR[ARn].CHAR - r) / 4) & MASK18;
+ cpu.AR[ARn].WORDNO = (cpu.AR[ARn].WORDNO -
+ address +
+ (GET_AR_CHAR (ARn) - r) / 4) & MASK18;
+ //if (r % 36)
+ //{
+ //cpu.AR[ARn].CHAR = ((cpu.AR[ARn].CHAR - r) % 4) & MASK2;
+ //cpu.AR[ARn].CHAR = (cpu.AR[ARn].CHAR - r) & MASK2;
+ SET_AR_CHAR_BITNO (ARn, (GET_AR_CHAR (ARn) - r) & MASK2, 0);
+ //}
+ }
+ else
+ {
+ cpu.AR[ARn].WORDNO = (- (address + (r + 3) / 4)) & MASK18;
+ //if (r % 36)
+ //{
+ //cpu.AR[ARn].CHAR = (-r) & MASK2;
+ SET_AR_CHAR_BITNO (ARn, (-r) & MASK2, 0);
+ //}
+ }
+ //cpu.AR[ARn].BITNO = 0;
+ HDBGRegAR (ARn);
+
+//if (current_running_cpu_idx)
+//sim_printf ("s9bd WORDNO 0%o %d. CHAR %o BITNO 0%o %d.\n", cpu.AR[ARn].WORDNO, cpu.AR[ARn].WORDNO, cpu.AR[ARn].CHAR, cpu.AR[ARn].BITNO, cpu.AR[ARn].BITNO);
+ }
+
+
+//
+// Address Register arithmetic
+//
+// This code handles Address Register arithmetic
+//
+// asxbd ( , )
+// ABD 1 false
+// A4BD 4 false
+// A6BD 6 false
+// A9BD 9 false
+// AWD 36 false
+// SBD 1 true
+// S4BD 4 true
+// S6BD 6 true
+// S9BD 9 true
+// SWD 36 true
+//
+
+// The general approach is do all of the math as unsigned number of bits,
+// modulo 2^18 * 36 (the number of bits in a segment).
+//
+// To handle subtraction underflow, a preemptive borrow is done if the
+// the operation will underflow.
+//
+// Notes:
+// According to ISOLTS 805, WORDNO is unsigned; this disagrees with AL-39
+
+void asxbd (uint sz, bool sub)
+ {
+ // Map charno:bitno to bit offset for 4 bit char set
+ uint map4 [64] =
+ { // 9-bit 4-bit
+ 0, // 0 0 0
+ 0, // 0 1 0
+ 0, // 0 2 0
+ 0, // 0 3 0
+ 0, // 0 4 0
+ 5, // 0 5 1
+ 5, // 0 6 1
+ 5, // 0 7 1
+ 5, // 0 8 1
+ 5, // 0 9 ill guess
+ 5, // 0 10 ill guess
+ 5, // 0 11 ill guess
+ 5, // 0 12 ill guess
+ 5, // 0 13 ill guess
+ 5, // 0 14 ill guess
+ 5, // 0 15 ill guess
+ 9, // 1 0 2
+ 9, // 1 1 2
+ 9, // 1 2 2
+ 9, // 1 3 2
+ 9, // 1 4 2
+ 14, // 1 5 3
+ 14, // 1 6 3
+ 14, // 1 7 3
+ 14, // 1 8 3
+ 14, // 1 9 ill guess
+ 14, // 1 10 ill ISOLTS 805 loop point 010226 sxbd test no 28 (4bit)
+ 14, // 1 11 ill guess
+ 14, // 1 12 ill guess
+ 14, // 1 13 ill guess
+ 14, // 1 14 ill guess
+ 14, // 1 15 ill guess
+ 18, // 2 0 4
+ 18, // 2 1 4
+ 18, // 2 2 4
+ 18, // 2 3 4
+ 18, // 2 4 4
+ 23, // 2 5 5
+ 23, // 2 6 5
+ 23, // 2 7 5
+ 23, // 2 8 5
+ 23, // 2 9 ill guess
+ 23, // 2 10 ill guess
+ 23, // 2 11 ill guess
+ 23, // 2 12 ill ISOLTS 805 loop point 010226 sxbd test no 26 (4bit)
+ 23, // 2 13 ill guess
+ 23, // 2 14 ill guess
+ 23, // 2 15 ill guess
+ 27, // 3 0 6
+ 27, // 3 1 6
+ 27, // 3 2 6
+ 27, // 3 3 6
+ 27, // 3 4 6
+ 32, // 3 5 7
+ 32, // 3 6 7
+ 32, // 3 7 7
+ 32, // 3 8 7
+ 32, // 3 9 ill guess
+ 32, // 3 10 ill guess
+ 32, // 3 11 ill guess
+ 32, // 3 12 ill guess
+ 32, // 3 13 ill guess
+ 32, // 3 14 ill ISOLTS 805 loop point 010226 sxbd test no 24 (4bit)
+ 32 // 3 15 ill guess
+ };
+ // Map charno:bitno to bit offset for 6 bit char set
+ uint map6 [64] =
+ { // 9-bit 6-bit
+ 0, // 0 0 0
+ 1, // 0 1 0
+ 2, // 0 2 0
+ 3, // 0 3 0
+ 4, // 0 4 0
+ 5, // 0 5 0
+ 6, // 0 6 1
+ 7, // 0 7 1
+ 8, // 0 8 1
+ 6, // 0 9 ill ISOLTS 805 loop point 010100 sxbd test no 12
+ 6, // 0 10 ill guess
+ 6, // 0 11 ill guess
+ 6, // 0 12 ill guess
+ 6, // 0 13 ill guess
+ 6, // 0 14 ill guess
+ 6, // 0 15 ill guess
+ 9, // 1 0 1
+ 10, // 1 1 1
+ 11, // 1 2 1
+ 12, // 1 3 2
+ 13, // 1 4 2
+ 14, // 1 5 2
+ 15, // 1 6 2
+ 16, // 1 7 2
+ 17, // 1 8 2
+ 12, // 1 9 ill guess
+ 12, // 1 10 ill guess
+ 12, // 1 11 ill ISOLTS 805 loop point 010100 sxbd test no 12
+ 12, // 1 12 ill guess
+ 12, // 1 13 ill guess
+ 12, // 1 14 ill guess
+ 12, // 1 15 ill guess
+ 18, // 2 0 3
+ 19, // 2 1 3
+ 20, // 2 2 3
+ 21, // 2 3 3
+ 22, // 2 4 3
+ 23, // 2 5 3
+ 24, // 2 6 4
+ 25, // 2 7 4
+ 26, // 2 8 4
+ 24, // 2 9 ill guess
+ 24, // 2 10 ill guess
+ 24, // 2 11 ill guess
+ 24, // 2 12 ill guess
+ 24, // 2 13 ill ISOLTS 805 loop point 010100 sxbd test no 10
+ 24, // 2 14 ill guess
+ 24, // 2 15 ill guess
+ 27, // 3 0 4
+ 28, // 3 1 4
+ 29, // 3 2 4
+ 30, // 3 3 5
+ 31, // 3 4 5
+ 32, // 3 5 5
+ 33, // 3 6 5
+ 34, // 3 7 5
+ 35, // 3 8 5
+ 30, // 3 9 ill guess
+ 30, // 3 10 ill guess
+ 30, // 3 11 ill guess
+ 30, // 3 12 ill guess
+ 30, // 3 13 ill guess
+ 30, // 3 14 ill guess
+ 30 // 3 15 ill ISOLTS 805 loop point 010100 sxbd test no 8 (6bit)
+ };
+ // Map charno:bitno to 1 and 9 bit offset
+ uint map9 [64] =
+ { // 9-bit
+ 0, // 0 0
+ 1, // 0 1
+ 2, // 0 2
+ 3, // 0 3
+ 4, // 0 4
+ 5, // 0 5
+ 6, // 0 6
+ 7, // 0 7
+ 8, // 0 8
+ 8, // 0 9 ill guess
+ 8, // 0 10 ill guess
+ 8, // 0 11 ill guess
+ 8, // 0 12 ill guess
+ 8, // 0 13 ill guess
+ 8, // 0 14 ill guess
+ 8, // 0 15 ill guess
+ 9, // 1 0
+ 10, // 1 1
+ 11, // 1 2
+ 12, // 1 3
+ 13, // 1 4
+ 14, // 1 5
+ 15, // 1 6
+ 16, // 1 7
+ 17, // 1 8
+ 17, // 1 9 ill guess
+ 17, // 1 10 ill guess
+ 17, // 1 11 ill guess
+ 17, // 1 12 ill guess
+ 17, // 1 13 ill guess
+ 17, // 1 14 ill guess
+ 17, // 1 15 ill guess
+ 18, // 2 0
+ 19, // 2 1
+ 20, // 2 2
+ 21, // 2 3
+ 22, // 2 4
+ 23, // 2 5
+ 24, // 2 6
+ 25, // 2 7
+ 26, // 2 8
+ 26, // 2 9 ill guess
+ 26, // 2 10 ill guess
+ 26, // 2 11 ill guess
+ 26, // 2 12 ill guess
+ 26, // 2 13 ill guess
+ 26, // 2 14 ill guess
+ 26, // 2 15 ill guess
+ 27, // 3 0
+ 28, // 3 1
+ 29, // 3 2
+ 30, // 3 3
+ 31, // 3 4
+ 32, // 3 5
+ 33, // 3 6
+ 34, // 3 7
+ 35, // 3 8
+ 35, // 3 9 ill guess
+ 35, // 3 10 ill guess
+ 35, // 3 11 ill guess
+ 35, // 3 12 ill guess
+ 35, // 3 13 ill guess
+ 35, // 3 14 ill guess
+ 35 // 3 15 ill guess
+ };
+
+//
+// Extract the operand data from the instruction
+//
+
+ uint ARn = GET_ARN (cpu.cu.IWB);
+ uint address = SIGNEXT15_18 (GET_OFFSET (cpu.cu.IWB));
+ word4 reg = (word4) GET_TD (cpu.cu.IWB); // 4-bit register modification (None except
+ // au, qu, al, ql, xn)
+
+//
+// Calculate r
+//
+
+ // r is the count of characters (or bits if sz is 1; words if sz == 36)
+ word36 rcnt = getCrAR (reg);
+
+ sim_debug (DBG_TRACEEXT|DBG_CAC, & cpu_dev, "asxbd sz %d r 0%"PRIo64"\n", sz, rcnt);
+
+ // Crop rcnt into r based on the operand size.
+ uint r = 0;
+
+ if (sz == 1)
+ r = (uint) (rcnt & MASK24);
+ else if (sz == 4)
+ r = (uint) (rcnt & MASK22);
+ else if (sz == 6)
+ r = (uint) (rcnt & MASK21);
+ else if (sz == 9)
+ r = (uint) (rcnt & MASK21);
+ else // if (sz == 36)
+ r = (uint) (rcnt & MASK18);
+
+ sim_debug (DBG_TRACEEXT|DBG_CAC, & cpu_dev, "asxbd sz %d ARn 0%o address 0%o reg 0%o r 0%o\n", sz, ARn, address, reg, r);
+
+//
+// Calculate augend
+//
+
+ // If A is set, the instruction is AR = AR op operand; if not, AR = 0 op operand.
+ uint augend = 0;
+ if (GET_A (cpu.cu.IWB))
+ {
+ // For AWD/SWD, leave CHAR/BITNO alone
+ if (sz == 36)
+ {
+ augend = cpu.AR[ARn].WORDNO * 36u;
+ }
+ else
+ {
+ uint bitno = GET_AR_BITNO (ARn);
+ uint charno = GET_AR_CHAR (ARn);
+
+ // The behavior of the DU for cases of CHAR > 9 is not defined; some values
+ // are tested by ISOLTS, and have been recorded in the mapx tables; the
+ // missing values are guessed at.
+
+ uint * map;
+ if (sz == 4)
+ map = map4;
+ else if (sz == 6)
+ map = map6;
+ else
+ map = map9;
+
+ augend = cpu.AR[ARn].WORDNO * 36u + map [charno * 16 + bitno];
+ augend = augend % nxbits;
+ }
+ }
+
+//
+// Calculate addend
+//
+
+ uint addend = 0;
+ if (sz == 4)
+ {
+ // r is the number of 4 bit characters; each character is actually
+ // 4.5 bits long
+ addend = address * 36u + (r * 9) / 2;
+
+ // round the odd character up one bit
+ // (the odd character starts at bit n/2 + 1, not n/2.)
+ if ((! sub) && r % 2) // r is odd
+ addend ++;
+ }
+ else
+ addend = address * 36u + r * sz;
+
+ // Handle overflow
+ addend = addend % nxbits;
+
+//
+// Calculate sum or difference
+//
+
+ uint sum = 0;
+ if (sub)
+ {
+ // Prevent underflow
+ if (addend > augend)
+ augend += nxbits;
+ sum = augend - addend;
+ }
+ else
+ {
+ sum = augend + addend;
+ sum %= nxbits;
+ }
+
+ sim_debug (DBG_TRACEEXT|DBG_CAC, & cpu_dev, "asxbd augend 0%o addend 0%o sum 0%o\n", augend, addend, sum);
+
+//
+// Adjust to character boundary
+//
+
+ if (sz == 6 || sz == 9)
+ {
+ sum = (sum / sz) * sz;
+ }
+
+//
+// Convert sum to WORDNO/CHAR/BITNO
+//
+
+ cpu.AR [ARn].WORDNO = (word18) (sum / 36u) & AMASK;
+
+ // If AWD/SWD clear CHAR/BITNO
+
+ if (sz == 36)
+ {
+ SET_AR_CHAR_BITNO (ARn, 0, 0);
+ }
+ else
+ {
+ if (sz == 4)
+ {
+ static uint tab [36] [2] =
+ {
+ // char bitno offset 4-bit charno
+ { 0, 0 }, // 0 0
+ { 0, 0 }, // 1
+ { 0, 0 }, // 2
+ { 0, 0 }, // 3
+ { 0, 0 }, // 4
+
+ { 0, 5 }, // 5 1
+ { 0, 5 }, // 6
+ { 0, 5 }, // 7
+ { 0, 5 }, // 8
+
+ { 1, 0 }, // 9 2
+ { 1, 0 }, // 10
+ { 1, 0 }, // 11
+ { 1, 0 }, // 12
+ { 1, 0 }, // 13
+
+ { 1, 5 }, // 15 3
+ { 1, 5 }, // 15
+ { 1, 5 }, // 16
+ { 1, 5 }, // 17
+
+ { 2, 0 }, // 18 4
+ { 2, 0 }, // 19
+ { 2, 0 }, // 20
+ { 2, 0 }, // 21
+ { 2, 0 }, // 22
+
+ { 2, 5 }, // 23 5
+ { 2, 5 }, // 24
+ { 2, 5 }, // 25
+ { 2, 5 }, // 26
+
+ { 3, 0 }, // 27 6
+ { 3, 0 }, // 28
+ { 3, 0 }, // 29
+ { 3, 0 }, // 30
+ { 3, 0 }, // 31
+
+ { 3, 5 }, // 32 7
+ { 3, 5 }, // 33
+ { 3, 5 }, // 34
+ { 3, 5 } // 35
+ };
+ uint charno = tab [sum % 36u] [0];
+ uint bitno = tab [sum % 36u] [1];
+ SET_AR_CHAR_BITNO (ARn, (word2) charno, (word4) bitno);
+ }
+ else
+ {
+ uint charno = (sum % 36u) / 9;
+ uint bitno = sum % 9;
+ SET_AR_CHAR_BITNO (ARn, (word2) charno, (word4) bitno);
+ }
+ }
+ HDBGRegAR (ARn);
+ }
+
+void cmpc (void)
+ {
+ EISstruct * e = & cpu.currentEISinstruction;
+
+ // For i = 1, 2, ..., minimum (N1,N2)
+ // C(Y-charn1)i-1 :: C(Y-charn2)i-1
+ // If N1 < N2, then for i = N1+1, N1+2, ..., N2
+ // C(FILL) :: C(Y-charn2)i-1
+ // If N1 > N2, then for i = N2+1, N2+2, ..., N1
+ // C(Y-charn1)i-1 :: C(FILL)
+ //
+ // Indicators:
+ // Zero: If C(Y-charn1)i-1 = C(Y-charn2)i-1 for all i, then ON;
+ // otherwise, OFF
+ // Carry: If C(Y-charn1)i-1 < C(Y-charn2)i-1 for any i, then OFF;
+ // otherwise ON
+
+ // Both strings are treated as the data type given for the left-hand
+ // string, TA1. A data type given for the right-hand string, TA2, is
+ // ignored.
+ //
+ // Comparison is made on full 9-bit fields. If the given data type is not
+ // 9-bit (TA1 ≠ 0), then characters from C(Y-charn1) and C(Y-charn2) are
+ // high- order zero filled. All 9 bits of C(FILL) are used.
+ //
+ // Instruction execution proceeds until an inequality is found or the
+ // larger string length count is exhausted.
+
+ fault_ipr_subtype_ mod_fault = 0;
+
+#ifndef EIS_SETUP
+ setupOperandDescriptor (1, &mod_fault);
+ setupOperandDescriptor (2, &mod_fault);
+#endif
+ parseAlphanumericOperandDescriptor (1, 1, false, &mod_fault);
+ parseAlphanumericOperandDescriptor (2, 1, false, &mod_fault);
+
+#ifdef L68
+ // L68 raises it immediately
+ if (mod_fault)
+ {
+ doFault (FAULT_IPR,
+ (_fault_subtype) {.fault_ipr_subtype=mod_fault},
+ "Illegal modifier");
+ }
+#endif
+
+ // Bits 9-10 MBZ
+ if (IWB_IRODD & 0000600000000)
+ doFault (FAULT_IPR, (_fault_subtype) {.fault_ipr_subtype=FR_ILL_OP|mod_fault}, "cmpc 9-10 MBZ");
+
+ // Bit 23 of OP1 MBZ
+ if (!(e->MF[0] & MFkID) && e -> op [0] & 0000000010000)
+ doFault (FAULT_IPR, (_fault_subtype) {.fault_ipr_subtype=FR_ILL_PROC|mod_fault}, "cmpc op1 23 MBZ");
+
+// ISOLTS ps846 test-07a dec add test
+// Sets TA2 to the same as TA1. AL39 says TA2 ignored.
+// Try only check bit 23.
+// ISOLTS 880 test-04c sets bit 23.
+#if 0
+ // // Bits 21-23 of OP2 MBZ
+ // if (e -> op [1] & 0000000070000)
+ // Bit 23 of OP2 MBZ
+ if (!(e->MF[1] & MFkID) && e -> op [1] & 0000000010000)
+ doFault (FAULT_IPR, (_fault_subtype) {.fault_ipr_subtype=FR_ILL_PROC|mod_fault}, "cmpc op2 23 MBZ");
+#endif
+
+#ifdef DPS8M
+ // DPS8M raises it delayed
+ if (mod_fault)
+ {
+ doFault (FAULT_IPR,
+ (_fault_subtype) {.fault_ipr_subtype=mod_fault},
+ "Illegal modifier");
+ }
+#endif
+
+ word9 fill = getbits36_9 (cpu.cu.IWB, 0);
+
+ SET_I_ZERO; // set ZERO flag assuming strings are equal ...
+ SET_I_CARRY; // set CARRY flag assuming strings are equal ...
+
+ PNL (L68_ (if (max (e->N1, e->N2) < 128)
+ DU_CYCLE_FLEN_128;))
+
+ for (; cpu.du.CHTALLY < min (e->N1, e->N2); cpu.du.CHTALLY ++)
+ {
+ word9 c1 = EISget469 (1, cpu.du.CHTALLY); // get Y-char1n
+ word9 c2 = EISget469 (2, cpu.du.CHTALLY); // get Y-char2n
+sim_debug (DBG_TRACEEXT, & cpu_dev, "cmpc tally %d c1 %03o c2 %03o\n", cpu.du.CHTALLY, c1, c2);
+ if (c1 != c2)
+ {
+ CLR_I_ZERO; // an inequality found
+ SC_I_CARRY (c1 > c2);
+ cleanupOperandDescriptor (1);
+ cleanupOperandDescriptor (2);
+ return;
+ }
+ }
+
+ if (e -> N1 < e -> N2)
+ {
+ for( ; cpu.du.CHTALLY < e->N2; cpu.du.CHTALLY ++)
+ {
+ word9 c1 = fill; // use fill for Y-char1n
+ word9 c2 = EISget469 (2, cpu.du.CHTALLY); // get Y-char2n
+
+ if (c1 != c2)
+ {
+ CLR_I_ZERO; // an inequality found
+ SC_I_CARRY (c1 > c2);
+ cleanupOperandDescriptor (1);
+ cleanupOperandDescriptor (2);
+ return;
+ }
+ }
+ }
+ else if (e->N1 > e->N2)
+ {
+ for ( ; cpu.du.CHTALLY < e->N1; cpu.du.CHTALLY ++)
+ {
+ word9 c1 = EISget469 (1, cpu.du.CHTALLY); // get Y-char1n
+ word9 c2 = fill; // use fill for Y-char2n
+
+ if (c1 != c2)
+ {
+ CLR_I_ZERO; // an inequality found
+ SC_I_CARRY (c1 > c2);
+ cleanupOperandDescriptor (1);
+ cleanupOperandDescriptor (2);
+ return;
+ }
+ }
+ }
+ // else ==
+ cleanupOperandDescriptor (1);
+ cleanupOperandDescriptor (2);
+ }
+
+
+/*
+ * SCD - Scan Characters Double
+ */
+
+void scd ()
+ {
+ EISstruct * e = & cpu.currentEISinstruction;
+
+ // For i = 1, 2, ..., N1-1
+ // C(Y-charn1)i-1,i :: C(Y-charn2)0,1
+ // On instruction completion, if a match was found:
+ // 00...0 → C(Y3)0,11
+ // i-1 → C(Y3)12,35
+ // If no match was found:
+ // 00...0 → C(Y3)0,11
+ // N1-1→ C(Y3)12,35
+ //
+
+ // The REG field of MF1 is checked for a legal code. If DU is specified in
+ // the REG field of MF2 in one of the four multiword instructions (SCD,
+ // SCDR, SCM, or SCMR) for which DU is legal, the CN field is ignored and
+ // the character or characters are arranged within the 18 bits of the word
+ // address portion of the operand descriptor.
+
+ fault_ipr_subtype_ mod_fault = 0;
+
+#ifndef EIS_SETUP
+ setupOperandDescriptor (1, &mod_fault);
+ setupOperandDescriptor (2, &mod_fault);
+ setupOperandDescriptorCache (3);
+#endif
+
+ parseAlphanumericOperandDescriptor (1, 1, false, &mod_fault);
+ parseAlphanumericOperandDescriptor (2, 1, true, &mod_fault); // use TA1
+ parseArgOperandDescriptor (3, &mod_fault);
+
+#ifdef L68
+ // L68 raises it immediately
+ if (mod_fault)
+ {
+ doFault (FAULT_IPR,
+ (_fault_subtype) {.fault_ipr_subtype=mod_fault},
+ "Illegal modifier");
+ }
+#endif
+
+ // Bits 0-10 MBZ
+ if (IWB_IRODD & 0777600000000)
+ {
+ //sim_printf ("scd %12"PRIo64"\n", IWB_IRODD);
+ doFault (FAULT_IPR, (_fault_subtype) {.fault_ipr_subtype=FR_ILL_OP|mod_fault}, "scd 0-10 MBZ");
+ }
+
+ // Bit 23 of OP1 MBZ
+ if (!(e->MF[0] & MFkID) && e -> op [0] & 0000000010000)
+ doFault (FAULT_IPR, (_fault_subtype) {.fault_ipr_subtype=FR_ILL_PROC|mod_fault}, "scd op1 23 MBZ");
+
+ // Bits 18-28. 30-31 of OP3 MBZ
+ if (!(e->MF[2] & MFkID) && e -> op [2] & 0000000777660)
+ doFault (FAULT_IPR, (_fault_subtype) {.fault_ipr_subtype=FR_ILL_PROC|mod_fault}, "scd op3 18-28. 30-31 MBZ");
+
+#ifdef DPS8M
+ // DPS8M raises it delayed
+ if (mod_fault)
+ {
+ doFault (FAULT_IPR,
+ (_fault_subtype) {.fault_ipr_subtype=mod_fault},
+ "Illegal modifier");
+ }
+#endif
+
+ // Both the string and the test character pair are treated as the data type
+ // given for the string, TA1. A data type given for the test character
+ // pair, TA2, is ignored.
+
+ // fetch 'test' char - double
+ // If MF2.ID = 0 and MF2.REG = du, then the second word following the
+ // instruction word does not contain an operand descriptor for the test
+ // character; instead, it contains the test character as a direct upper
+ // operand in bits 0,8.
+
+ word9 c1 = 0;
+ word9 c2 = 0;
+
+ if (! (e -> MF2 & MFkID) && ((e -> MF2 & MFkREGMASK) == 3)) // MF2.du
+ {
+ // per Bull RJ78, p. 5-45
+#ifdef EIS_PTR3
+ switch (TA1) // Use TA1, not TA2
+#else
+ switch (e -> TA1) // Use TA1, not TA2
+#endif
+ {
+ case CTA4:
+#ifdef EIS_PTR
+ c1 = (cpu.du.D2_PTR_W >> 13) & 017;
+ c2 = (cpu.du.D2_PTR_W >> 9) & 017;
+#else
+ c1 = (e -> ADDR2.address >> 13) & 017;
+ c2 = (e -> ADDR2.address >> 9) & 017;
+#endif
+ break;
+
+ case CTA6:
+#ifdef EIS_PTR
+ c1 = (cpu.du.D2_PTR_W >> 12) & 077;
+ c2 = (cpu.du.D2_PTR_W >> 6) & 077;
+#else
+ c1 = (e -> ADDR2.address >> 12) & 077;
+ c2 = (e -> ADDR2.address >> 6) & 077;
+#endif
+ break;
+
+ case CTA9:
+#ifdef EIS_PTR
+ c1 = (cpu.du.D2_PTR_W >> 9) & 0777;
+ c2 = (cpu.du.D2_PTR_W ) & 0777;
+#else
+ c1 = (e -> ADDR2.address >> 9) & 0777;
+ c2 = (e -> ADDR2.address ) & 0777;
+#endif
+ break;
+ }
+ }
+ else
+ {
+ c1 = EISget469 (2, 0);
+ c2 = EISget469 (2, 1);
+ }
+
+#ifdef EIS_PTR3
+ switch (TA1) // Use TA1, not TA2
+#else
+ switch (e -> TA1) // Use TA1, not TA2
+#endif
+ {
+ case CTA4:
+ c1 &= 017; // keep 4-bits
+ c2 &= 017; // keep 4-bits
+ break;
+
+ case CTA6:
+ c1 &= 077; // keep 6-bits
+ c2 &= 077; // keep 6-bits
+ break;
+
+ case CTA9:
+ c1 &= 0777; // keep 9-bits
+ c2 &= 0777; // keep 9-bits
+ break;
+ }
+
+
+ PNL (L68_ (if (e->N1 < 128)
+ DU_CYCLE_FLEN_128;))
+
+ word9 yCharn11;
+ word9 yCharn12;
+ if (e -> N1)
+ {
+ uint limit = e -> N1 - 1;
+ for ( ; cpu.du.CHTALLY < limit; cpu.du.CHTALLY ++)
+ {
+ yCharn11 = EISget469 (1, cpu.du.CHTALLY);
+ yCharn12 = EISget469 (1, cpu.du.CHTALLY + 1);
+ if (yCharn11 == c1 && yCharn12 == c2)
+ break;
+ }
+ SC_I_TALLY (cpu.du.CHTALLY == limit);
+ }
+ else
+ {
+ SET_I_TALLY;
+ }
+
+ //word36 CY3 = bitfieldInsert36 (0, cpu.du.CHTALLY, 0, 24);
+ word36 CY3 = setbits36_24 (0, 12, cpu.du.CHTALLY);
+ EISWriteIdx (& e -> ADDR3, 0, CY3, true);
+
+ cleanupOperandDescriptor (1);
+ cleanupOperandDescriptor (2);
+ cleanupOperandDescriptor (3);
+ }
+
+/*
+ * SCDR - Scan Characters Double Reverse
+ */
+
+void scdr (void)
+ {
+ EISstruct * e = & cpu.currentEISinstruction;
+
+ // For i = 1, 2, ..., N1-1
+ // C(Y-charn1)N1-i-1,N1-i :: C(Y-charn2)0,1
+ // On instruction completion, if a match was found:
+ // 00...0 → C(Y3)0,11
+ // i-1 → C(Y3)12,35
+ // If no match was found:
+ // 00...0 → C(Y3)0,11
+ // N1-1→ C(Y3)12,35
+ //
+
+ // The REG field of MF1 is checked for a legal code. If DU is specified in
+ // the REG field of MF2 in one of the four multiword instructions (SCD,
+ // SCDR, SCM, or SCMR) for which DU is legal, the CN field is ignored and
+ // the character or characters are arranged within the 18 bits of the word
+ // address portion of the operand descriptor.
+
+ fault_ipr_subtype_ mod_fault = 0;
+
+#ifndef EIS_SETUP
+ setupOperandDescriptor(1, &mod_fault);
+ setupOperandDescriptor(2, &mod_fault);
+ setupOperandDescriptorCache(3);
+#endif
+
+ parseAlphanumericOperandDescriptor(1, 1, false, &mod_fault);
+ parseAlphanumericOperandDescriptor(2, 1, true, &mod_fault); // Use TA1
+ parseArgOperandDescriptor (3, &mod_fault);
+
+#ifdef L68
+ // L68 raises it immediately
+ if (mod_fault)
+ {
+ doFault (FAULT_IPR,
+ (_fault_subtype) {.fault_ipr_subtype=mod_fault},
+ "Illegal modifier");
+ }
+#endif
+
+ // Bits 0-10 MBZ
+ if (IWB_IRODD & 0777600000000)
+ {
+ //sim_printf ("scdr %12"PRIo64"\n", IWB_IRODD);
+ doFault (FAULT_IPR, (_fault_subtype) {.fault_ipr_subtype=FR_ILL_OP|mod_fault}, "scdr 0-10 MBZ");
+ }
+
+ // Bit 23 of OP1 MBZ
+ if (!(e->MF[0] & MFkID) && e -> op [0] & 0000000010000)
+ doFault (FAULT_IPR, (_fault_subtype) {.fault_ipr_subtype=FR_ILL_PROC|mod_fault}, "scdr op1 23 MBZ");
+
+ // Bits 18-28. 30-31 of OP3 MBZ
+ if (!(e->MF[2] & MFkID) && e -> op [2] & 0000000777660)
+ doFault (FAULT_IPR, (_fault_subtype) {.fault_ipr_subtype=FR_ILL_PROC|mod_fault}, "scdr op3 18-28. 30-31 MBZ");
+
+#ifdef DPS8M
+ // DPS8M raises it delayed
+ if (mod_fault)
+ {
+ doFault (FAULT_IPR,
+ (_fault_subtype) {.fault_ipr_subtype=mod_fault},
+ "Illegal modifier");
+ }
+#endif
+
+ // Both the string and the test character pair are treated as the data type
+ // given for the string, TA1. A data type given for the test character
+ // pair, TA2, is ignored.
+
+ // fetch 'test' char - double
+ // If MF2.ID = 0 and MF2.REG = du, then the second word following the
+ // instruction word does not contain an operand descriptor for the test
+ // character; instead, it contains the test character as a direct upper
+ // operand in bits 0,8.
+
+ word9 c1 = 0;
+ word9 c2 = 0;
+
+ if (! (e -> MF2 & MFkID) && ((e -> MF2 & MFkREGMASK) == 3)) // MF2.du
+ {
+ // per Bull RJ78, p. 5-45
+#ifdef EIS_PTR3
+ switch (TA1) // Use TA1, not TA2
+#else
+ switch (e -> TA1)
+#endif
+ {
+ case CTA4:
+#ifdef EIS_PTR
+ c1 = (cpu.du.D2_PTR_W >> 13) & 017;
+ c2 = (cpu.du.D2_PTR_W >> 9) & 017;
+#else
+ c1 = (e -> ADDR2.address >> 13) & 017;
+ c2 = (e -> ADDR2.address >> 9) & 017;
+#endif
+ break;
+
+ case CTA6:
+#ifdef EIS_PTR
+ c1 = (cpu.du.D2_PTR_W >> 12) & 077;
+ c2 = (cpu.du.D2_PTR_W >> 6) & 077;
+#else
+ c1 = (e -> ADDR2.address >> 12) & 077;
+ c2 = (e -> ADDR2.address >> 6) & 077;
+#endif
+ break;
+
+ case CTA9:
+#ifdef EIS_PTR
+ c1 = (cpu.du.D2_PTR_W >> 9) & 0777;
+ c2 = (cpu.du.D2_PTR_W ) & 0777;
+#else
+ c1 = (e -> ADDR2.address >> 9) & 0777;
+ c2 = (e -> ADDR2.address ) & 0777;
+#endif
+ break;
+ }
+ }
+ else
+ {
+ c1 = EISget469 (2, 0);
+ c2 = EISget469 (2, 1);
+ }
+
+#ifdef EIS_PTR3
+ switch (TA1) // Use TA1, not TA2
+#else
+ switch (e -> TA1) // Use TA1, not TA2
+#endif
+ {
+ case CTA4:
+ c1 &= 017; // keep 4-bits
+ c2 &= 017; // keep 4-bits
+ break;
+
+ case CTA6:
+ c1 &= 077; // keep 6-bits
+ c2 &= 077; // keep 6-bits
+ break;
+
+ case CTA9:
+ c1 &= 0777; // keep 9-bits
+ c2 &= 0777; // keep 9-bits
+ break;
+ }
+
+ word9 yCharn11;
+ word9 yCharn12;
+
+ PNL (L68_ (if (e->N1 < 128)
+ DU_CYCLE_FLEN_128;))
+
+ if (e -> N1)
+ {
+ uint limit = e -> N1 - 1;
+
+ for ( ; cpu.du.CHTALLY < limit; cpu.du.CHTALLY ++)
+ {
+ yCharn11 = EISget469 (1, limit - cpu.du.CHTALLY - 1);
+ yCharn12 = EISget469 (1, limit - cpu.du.CHTALLY);
+
+ if (yCharn11 == c1 && yCharn12 == c2)
+ break;
+ }
+ SC_I_TALLY (cpu.du.CHTALLY == limit);
+ }
+ else
+ {
+ SET_I_TALLY;
+ }
+
+ //word36 CY3 = bitfieldInsert36(0, cpu.du.CHTALLY, 0, 24);
+ word36 CY3 = setbits36_24 (0, 12, cpu.du.CHTALLY);
+ EISWriteIdx (& e -> ADDR3, 0, CY3, true);
+
+ cleanupOperandDescriptor (1);
+ cleanupOperandDescriptor (2);
+ cleanupOperandDescriptor (3);
+ }
+
+
+/*
+ * SCM - Scan with Mask
+ */
+
+void scm (void)
+ {
+ EISstruct * e = & cpu.currentEISinstruction;
+
+ // For characters i = 1, 2, ..., N1
+ // For bits j = 0, 1, ..., 8
+ // C(Z)j = ~C(MASK)j & ((C(Y-charn1)i-1 )j ⊕ (C(Y-charn2)0)j)
+ // If C(Z)0,8 = 00...0, then
+ // 00...0 → C(Y3)0,11
+ // i-1 → C(Y3)12,35
+ // otherwise, continue scan of C(Y-charn1)
+ // If a masked character match was not found, then
+ // 00...0 → C(Y3)0,11
+ // N1 → C(Y3)12,35
+
+ // Starting at location YC1, the L1 type TA1 characters are masked and
+ // compared with the assumed type TA1 character contained either in
+ // location YC2 or in bits 0-8 or 0-5 of the address field of operand
+ // descriptor 2 (when the REG field of MF2 specifies DU modification). The
+ // mask is right-justified in bit positions 0-8 of the instruction word.
+ // Each bit position of the mask that is a 1 prevents that bit position in
+ // the two characters from entering into the compare.
+
+ // The masked compare operation continues until either a match is found or
+ // the tally (L1) is exhausted. For each unsuccessful match, a count is
+ // incremented by 1. When a match is found or when the L1 tally runs out,
+ // this count is stored right-justified in bits 12-35 of location Y3 and
+ // bits 0-11 of Y3 are zeroed. The contents of location YC2 and the source
+ // string remain unchanged. The RL bit of the MF2 field is not used.
+
+ // The REG field of MF1 is checked for a legal code. If DU is specified in
+ // the REG field of MF2 in one of the four multiword instructions (SCD,
+ // SCDR, SCM, or SCMR) for which DU is legal, the CN field is ignored and
+ // the character or characters are arranged within the 18 bits of the word
+ // address portion of the operand descriptor.
+
+ fault_ipr_subtype_ mod_fault = 0;
+
+#ifndef EIS_SETUP
+ setupOperandDescriptor (1, &mod_fault);
+ setupOperandDescriptor (2, &mod_fault);
+ setupOperandDescriptorCache (3);
+#endif
+
+ parseAlphanumericOperandDescriptor (1, 1, false, &mod_fault);
+ parseAlphanumericOperandDescriptor (2, 1, true, &mod_fault);
+ parseArgOperandDescriptor (3, &mod_fault);
+
+#ifdef L68
+ // L68 raises it immediately
+ if (mod_fault)
+ {
+ doFault (FAULT_IPR,
+ (_fault_subtype) {.fault_ipr_subtype=mod_fault},
+ "Illegal modifier");
+ }
+#endif
+
+ // Bits 9-10 MBZ
+ if (IWB_IRODD & 0000600000000)
+ doFault (FAULT_IPR, (_fault_subtype) {.fault_ipr_subtype=FR_ILL_OP|mod_fault}, "scm 9-10 MBZ");
+
+ // Bit 23 of OP1 MBZ
+ if (!(e->MF[0] & MFkID) && e -> op [0] & 0000000010000)
+ doFault (FAULT_IPR, (_fault_subtype) {.fault_ipr_subtype=FR_ILL_PROC|mod_fault}, "scm op1 23 MBZ");
+
+ // Bits 18-28, 39-31 of OP3 MBZ
+ if (!(e->MF[2] & MFkID) && e -> op [2] & 0000000777660)
+ doFault (FAULT_IPR, (_fault_subtype) {.fault_ipr_subtype=FR_ILL_PROC|mod_fault}, "scm op3 18-28, 39-31 MBZ");
+
+#ifdef DPS8M
+ // DPS8M raises it delayed
+ if (mod_fault)
+ {
+ doFault (FAULT_IPR,
+ (_fault_subtype) {.fault_ipr_subtype=mod_fault},
+ "Illegal modifier");
+ }
+#endif
+
+ // Both the string and the test character pair are treated as the data type
+ // given for the string, TA1. A data type given for the test character
+ // pair, TA2, is ignored.
+
+ // get 'mask'
+ uint mask = (uint) getbits36_9 (cpu.cu.IWB, 0);
+
+ // fetch 'test' char
+ // If MF2.ID = 0 and MF2.REG = du, then the second word following the
+ // instruction word does not contain an operand descriptor for the test
+ // character; instead, it contains the test character as a direct upper
+ // operand in bits 0,8.
+
+ word9 ctest = 0;
+ if (! (e -> MF2 & MFkID) && ((e -> MF2 & MFkREGMASK) == 3)) // MF2.du
+ {
+ word18 duo = GETHI (e -> OP2);
+ // per Bull RJ78, p. 5-45
+#ifdef EIS_PTR3
+ switch (TA1) // Use TA1, not TA2
+#else
+ switch (e -> TA1)
+#endif
+ {
+ case CTA4:
+ ctest = (duo >> 13) & 017;
+ break;
+ case CTA6:
+ ctest = (duo >> 12) & 077;
+ break;
+ case CTA9:
+ ctest = (duo >> 9) & 0777;
+ break;
+ }
+ }
+ else
+ {
+ ctest = EISget469 (2, 0);
+ }
+
+#ifdef EIS_PTR3
+ switch (TA1) // Use TA1, not TA2
+#else
+ switch (e -> TA1) // use TA1, not TA2
+#endif
+ {
+ case CTA4:
+ ctest &= 017; // keep 4-bits
+ break;
+ case CTA6:
+ ctest &= 077; // keep 6-bits
+ break;
+ case CTA9:
+ ctest &= 0777; // keep 9-bits
+ }
+
+ PNL (L68_ (if (e->N1 < 128)
+ DU_CYCLE_FLEN_128;))
+
+ uint limit = e -> N1;
+
+ for ( ; cpu.du.CHTALLY < limit; cpu.du.CHTALLY ++)
+ {
+ word9 yCharn1 = EISget469 (1, cpu.du.CHTALLY);
+ word9 c = ((~mask) & (yCharn1 ^ ctest)) & 0777;
+ if (c == 0)
+ {
+ //00...0 → C(Y3)0,11
+ //i-1 → C(Y3)12,35
+ //Y3 = bitfieldInsert36(Y3, cpu.du.CHTALLY, 0, 24);
+ break;
+ }
+ }
+ //word36 CY3 = bitfieldInsert36 (0, cpu.du.CHTALLY, 0, 24);
+ word36 CY3 = setbits36_24 (0, 12, cpu.du.CHTALLY);
+
+ SC_I_TALLY (cpu.du.CHTALLY == limit);
+
+ EISWriteIdx (& e -> ADDR3, 0, CY3, true);
+
+ cleanupOperandDescriptor (1);
+ cleanupOperandDescriptor (2);
+ cleanupOperandDescriptor (3);
+ }
+/*
+ * SCMR - Scan with Mask in Reverse
+ */
+
+void scmr (void)
+ {
+ EISstruct * e = & cpu.currentEISinstruction;
+
+ // For characters i = 1, 2, ..., N1
+ // For bits j = 0, 1, ..., 8
+ // C(Z)j = ~C(MASK)j & ((C(Y-charn1)i-1 )j ⊕ (C(Y-charn2)0)j)
+ // If C(Z)0,8 = 00...0, then
+ // 00...0 → C(Y3)0,11
+ // i-1 → C(Y3)12,35
+ // otherwise, continue scan of C(Y-charn1)
+ // If a masked character match was not found, then
+ // 00...0 → C(Y3)0,11
+ // N1 → C(Y3)12,35
+
+ // Starting at location YC1, the L1 type TA1 characters are masked and
+ // compared with the assumed type TA1 character contained either in
+ // location YC2 or in bits 0-8 or 0-5 of the address field of operand
+ // descriptor 2 (when the REG field of MF2 specifies DU modification). The
+ // mask is right-justified in bit positions 0-8 of the instruction word.
+ // Each bit position of the mask that is a 1 prevents that bit position in
+ // the two characters from entering into the compare.
+
+ // The masked compare operation continues until either a match is found or
+ // the tally (L1) is exhausted. For each unsuccessful match, a count is
+ // incremented by 1. When a match is found or when the L1 tally runs out,
+ // this count is stored right-justified in bits 12-35 of location Y3 and
+ // bits 0-11 of Y3 are zeroed. The contents of location YC2 and the source
+ // string remain unchanged. The RL bit of the MF2 field is not used.
+
+ // The REG field of MF1 is checked for a legal code. If DU is specified in
+ // the REG field of MF2 in one of the four multiword instructions (SCD,
+ // SCDR, SCM, or SCMR) for which DU is legal, the CN field is ignored and
+ // the character or characters are arranged within the 18 bits of the word
+ // address portion of the operand descriptor.
+
+ fault_ipr_subtype_ mod_fault = 0;
+
+#ifndef EIS_SETUP
+ setupOperandDescriptor (1, &mod_fault);
+ setupOperandDescriptor (2, &mod_fault);
+ setupOperandDescriptorCache (3);
+#endif
+
+ parseAlphanumericOperandDescriptor (1, 1, false, &mod_fault);
+ parseAlphanumericOperandDescriptor (2, 1, true, &mod_fault);
+ parseArgOperandDescriptor (3, &mod_fault);
+
+#ifdef L68
+ // L68 raises it immediately
+ if (mod_fault)
+ {
+ doFault (FAULT_IPR,
+ (_fault_subtype) {.fault_ipr_subtype=mod_fault},
+ "Illegal modifier");
+ }
+#endif
+
+ // Bits 9-10 MBZ
+ if (IWB_IRODD & 0000600000000)
+ doFault (FAULT_IPR, (_fault_subtype) {.fault_ipr_subtype=FR_ILL_OP|mod_fault}, "scmr 9-10 MBZ");
+
+ // Bit 23 of OP1 MBZ
+ if (!(e->MF[0] & MFkID) && e -> op [0] & 0000000010000)
+ doFault (FAULT_IPR, (_fault_subtype) {.fault_ipr_subtype=FR_ILL_PROC|mod_fault}, "scmr op1 23 MBZ");
+
+ // Bits 18 of OP3 MBZ
+ //if (!(e->MF[2] & MFkID) && e -> op [2] & 0000000400000)
+ // doFault (FAULT_IPR, (_fault_subtype) {.fault_ipr_subtype=FR_ILL_PROC|mod_fault}, "scmr op3 18 MBZ");
+
+ // Bits 18-28, 39-31 of OP3 MBZ
+ if (!(e->MF[2] & MFkID) && e -> op [2] & 0000000777660)
+ doFault (FAULT_IPR, (_fault_subtype) {.fault_ipr_subtype=FR_ILL_PROC|mod_fault}, "scmr op3 18-28, 39-31 MBZ");
+
+#ifdef DPS8M
+ // DPS8M raises it delayed
+ if (mod_fault)
+ {
+ doFault (FAULT_IPR,
+ (_fault_subtype) {.fault_ipr_subtype=mod_fault},
+ "Illegal modifier");
+ }
+#endif
+
+ // Both the string and the test character pair are treated as the data type
+ // given for the string, TA1. A data type given for the test character
+ // pair, TA2, is ignored.
+
+ // get 'mask'
+ uint mask = (uint) getbits36_9 (cpu.cu.IWB, 0);
+
+ // fetch 'test' char
+ // If MF2.ID = 0 and MF2.REG = du, then the second word following the
+ // instruction word does not contain an operand descriptor for the test
+ // character; instead, it contains the test character as a direct upper
+ // operand in bits 0,8.
+
+ word9 ctest = 0;
+ if (! (e -> MF2 & MFkID) && ((e -> MF2 & MFkREGMASK) == 3)) // MF2.du
+ {
+ word18 duo = GETHI (e -> OP2);
+ // per Bull RJ78, p. 5-45
+#ifdef EIS_PTR3
+ switch (TA1) // Use TA1, not TA2
+#else
+ switch (e -> TA1)
+#endif
+ {
+ case CTA4:
+ ctest = (duo >> 13) & 017;
+ break;
+ case CTA6:
+ ctest = (duo >> 12) & 077;
+ break;
+ case CTA9:
+ ctest = (duo >> 9) & 0777;
+ break;
+ }
+ }
+ else
+ {
+ ctest = EISget469 (2, 0);
+ }
+
+#ifdef EIS_PTR3
+ switch (TA1) // Use TA1, not TA2
+#else
+ switch (e -> TA1) // use TA1, not TA2
+#endif
+ {
+ case CTA4:
+ ctest &= 017; // keep 4-bits
+ break;
+ case CTA6:
+ ctest &= 077; // keep 6-bits
+ break;
+ case CTA9:
+ ctest &= 0777; // keep 9-bits
+ }
+
+ PNL (L68_ (if (e->N1 < 128)
+ DU_CYCLE_FLEN_128;))
+
+ uint limit = e -> N1;
+ for ( ; cpu.du.CHTALLY < limit; cpu.du.CHTALLY ++)
+ {
+ word9 yCharn1 = EISget469 (1, limit - cpu.du.CHTALLY - 1);
+ word9 c = ((~mask) & (yCharn1 ^ ctest)) & 0777;
+ if (c == 0)
+ {
+ //00...0 → C(Y3)0,11
+ //i-1 → C(Y3)12,35
+ //Y3 = bitfieldInsert36(Y3, cpu.du.CHTALLY, 0, 24);
+ break;
+ }
+ }
+ //word36 CY3 = bitfieldInsert36 (0, cpu.du.CHTALLY, 0, 24);
+ word36 CY3 = setbits36_24 (0, 12, cpu.du.CHTALLY);
+
+ SC_I_TALLY (cpu.du.CHTALLY == limit);
+
+ EISWriteIdx (& e -> ADDR3, 0, CY3, true);
+
+ cleanupOperandDescriptor (1);
+ cleanupOperandDescriptor (2);
+ cleanupOperandDescriptor (3);
+ }
+
+/*
+ * TCT - Test Character and Translate
+ */
+
+#if 0
+static word9 xlate (word36 * xlatTbl, uint dstTA, uint c)
+ {
+ uint idx = (c / 4) & 0177; // max 128-words (7-bit index)
+ word36 entry = xlatTbl [idx];
+
+ uint pos9 = c % 4; // lower 2-bits
+ word9 cout = GETBYTE (entry, pos9);
+ switch (dstTA)
+ {
+ case CTA4:
+ return cout & 017;
+ case CTA6:
+ return cout & 077;
+ case CTA9:
+ return cout;
+ }
+ return 0;
+ }
+#endif
+
+static word9 xlate (EISaddr * xlatTbl, uint dstTA, uint c)
+ {
+ uint idx = (c / 4) & 0177; // max 128-words (7-bit index)
+ word36 entry = EISReadIdx(xlatTbl, idx);
+
+ uint pos9 = c % 4; // lower 2-bits
+ word9 cout = GETBYTE (entry, pos9);
+ switch (dstTA)
+ {
+ case CTA4:
+ return cout & 017;
+ case CTA6:
+ return cout & 077;
+ case CTA9:
+ return cout;
+ }
+ return 0;
+ }
+
+
+void tct (void)
+ {
+ EISstruct * e = & cpu.currentEISinstruction;
+
+ // For i = 1, 2, ..., N1
+ // m = C(Y-charn1)i-1
+ // If C(Y-char92)m ≠ 00...0, then
+ // C(Y-char92)m → C(Y3)0,8
+ // 000 → C(Y3)9,11
+ // i-1 → C(Y3)12,35
+ // otherwise, continue scan of C(Y-charn1)
+ // If a non-zero table entry was not found, then 00...0 → C(Y3)0,11
+ // N1 → C(Y3)12,35
+ //
+ // Indicators: Tally Run Out. If the string length count exhausts, then ON;
+ // otherwise, OFF
+ //
+ // If the data type of the string to be scanned is not 9-bit (TA1 ≠ 0),
+ // then characters from C(Y-charn1) are high-order zero filled in forming
+ // the table index, m.
+ // Instruction execution proceeds until a non-zero table entry is found or
+ // the string length count is exhausted.
+ // The character number of Y-char92 must be zero, i.e., Y-char92 must start
+ // on a word boundary.
+
+
+ fault_ipr_subtype_ mod_fault = 0;
+
+#ifndef EIS_SETUP
+ setupOperandDescriptor (1, &mod_fault);
+ setupOperandDescriptorCache (2);
+ setupOperandDescriptorCache (3);
+#endif
+
+ parseAlphanumericOperandDescriptor (1, 1, false, &mod_fault);
+ parseArgOperandDescriptor (2, &mod_fault);
+ parseArgOperandDescriptor (3, &mod_fault);
+
+#ifdef L68
+ // L68 raises it immediately
+ if (mod_fault)
+ {
+ doFault (FAULT_IPR,
+ (_fault_subtype) {.fault_ipr_subtype=mod_fault},
+ "Illegal modifier");
+ }
+#endif
+
+ // Bits 0-17 MBZ
+ if (IWB_IRODD & 0777777000000)
+ doFault (FAULT_IPR, (_fault_subtype) {.fault_ipr_subtype=FR_ILL_OP|mod_fault}, "tct 0-17 MBZ");
+
+ // Bit 23 of OP1 MBZ
+ if (!(e->MF[0] & MFkID) && e -> op [0] & 0000000010000)
+ doFault (FAULT_IPR, (_fault_subtype) {.fault_ipr_subtype=FR_ILL_PROC|mod_fault}, "tct op1 23 MBZ");
+
+ // Bits 18-28, 39-31 of OP2 MBZ
+ if (!(e->MF[1] & MFkID) && e -> op [1] & 0000000777660)
+ doFault (FAULT_IPR, (_fault_subtype) {.fault_ipr_subtype=FR_ILL_PROC|mod_fault}, "tct op2 18-28, 39-31 MBZ");
+
+ // Bits 18-28, 39-31 of OP3 MBZ
+ if (!(e->MF[2] & MFkID) && e -> op [2] & 0000000777660)
+ doFault (FAULT_IPR, (_fault_subtype) {.fault_ipr_subtype=FR_ILL_PROC|mod_fault}, "tct op3 18-28, 39-31 MBZ");
+
+#ifdef DPS8M
+ // DPS8M raises it delayed
+ if (mod_fault)
+ {
+ doFault (FAULT_IPR,
+ (_fault_subtype) {.fault_ipr_subtype=mod_fault},
+ "Illegal modifier");
+ }
+#endif
+
+#ifdef EIS_PTR3
+ sim_debug (DBG_TRACEEXT, & cpu_dev,
+ "TCT CN1: %d TA1: %d\n", e -> CN1, TA1);
+#else
+ sim_debug (DBG_TRACEEXT, & cpu_dev,
+ "TCT CN1: %d TA1: %d\n", e -> CN1, e -> TA1);
+#endif
+
+ uint srcSZ = 0;
+
+#ifdef EIS_PTR3
+ switch (TA1)
+#else
+ switch (e -> TA1)
+#endif
+ {
+ case CTA4:
+ srcSZ = 4;
+ break;
+ case CTA6:
+ srcSZ = 6;
+ break;
+ case CTA9:
+ srcSZ = 9;
+ break;
+ }
+
+ // ISOLTS-878 01h asserts no prepaging
+#if 0
+ // Prepage Check in a Multiword Instruction
+ // The MVT, TCT, TCTR, and CMPCT instruction have a prepage check. The
+ // size of the translate table is determined by the TA1 data type as shown
+ // in the table below. Before the instruction is executed, a check is made
+ // for allocation in memory for the page for the translate table. If the
+ // page is not in memory, a Missing Page fault occurs before execution of
+ // the instruction. (Bull RJ78 p.7-75)
+
+ // TA1 TRANSLATE TABLE SIZE
+ // 4-BIT CHARACTER 4 WORDS
+ // 6-BIT CHARACTER 16 WORDS
+ // 9-BIT CHARACTER 128 WORDS
+
+ uint xlatSize = 0; // size of xlation table in words .....
+#ifdef EIS_PTR3
+ switch (TA1)
+#else
+ switch(e -> TA1)
+#endif
+ {
+ case CTA4:
+ xlatSize = 4;
+ break;
+ case CTA6:
+ xlatSize = 16;
+ break;
+ case CTA9:
+ xlatSize = 128;
+ break;
+ }
+
+ word36 xlatTbl [128];
+ memset (xlatTbl, 0, sizeof (xlatTbl)); // 0 it out just in case
+
+ EISReadN (& e -> ADDR2, xlatSize, xlatTbl);
+#endif
+
+ word36 CY3 = 0;
+
+ sim_debug (DBG_TRACEEXT, & cpu_dev,
+ "TCT N1 %d\n", e -> N1);
+
+ PNL (L68_ (if (e->N1 < 128)
+ DU_CYCLE_FLEN_128;))
+
+ for ( ; cpu.du.CHTALLY < e -> N1; cpu.du.CHTALLY ++)
+ {
+ word9 c = EISget469 (1, cpu.du.CHTALLY); // get src char
+
+ uint m = 0;
+
+ switch (srcSZ)
+ {
+ case 4:
+ m = c & 017; // truncate upper 2-bits
+ break;
+ case 6:
+ m = c & 077; // truncate upper 3-bits
+ break;
+ case 9:
+ m = c; // keep all 9-bits
+ break; // should already be 0-filled
+ }
+
+ word9 cout = xlate (&e->ADDR2, CTA9, m);
+
+ sim_debug (DBG_TRACEEXT, & cpu_dev,
+ "TCT c %03o %c cout %03o %c\n",
+ m, isprint ((int) m) ? '?' : (char) m,
+ cout, isprint ((int) cout) ? '?' : (char) cout);
+
+ if (cout)
+ {
+ // CY3 = bitfieldInsert36 (0, cout, 27, 9); // C(Y-char92)m -> C(Y3)0,8
+ CY3 = setbits36_9 (0, 0, cout);
+ break;
+ }
+ }
+
+ SC_I_TALLY (cpu.du.CHTALLY == e -> N1);
+
+ //CY3 = bitfieldInsert36 (CY3, cpu.du.CHTALLY, 0, 24);
+ putbits36_24 (& CY3, 12, cpu.du.CHTALLY);
+ EISWriteIdx (& e -> ADDR3, 0, CY3, true);
+
+ cleanupOperandDescriptor (1);
+ cleanupOperandDescriptor (2);
+ cleanupOperandDescriptor (3);
+ }
+
+void tctr (void)
+ {
+ EISstruct * e = & cpu.currentEISinstruction;
+
+ // For i = 1, 2, ..., N1
+ // m = C(Y-charn1)N1-i
+ // If C(Y-char92)m ≠ 00...0, then
+ // C(Y-char92)m → C(Y3)0,8
+ // 000 → C(Y3)9,11
+ // i-1 → C(Y3)12,35
+ // otherwise, continue scan of C(Y-charn1) If a non-zero table entry was
+ // not found, then
+ // 00...0 → C(Y3)0,11
+ // N1 → C(Y3)12,35
+ //
+ // Indicators: Tally Run Out. If the string length count exhausts, then ON;
+ // otherwise, OFF
+ //
+ // If the data type of the string to be scanned is not 9-bit (TA1 ≠ 0),
+ // then characters from C(Y-charn1) are high-order zero filled in forming
+ // the table index, m.
+
+ // Instruction execution proceeds until a non-zero table entry is found or
+ // the string length count is exhausted.
+
+ // The character number of Y-char92 must be zero, i.e., Y-char92 must start
+ // on a word boundary.
+
+
+ fault_ipr_subtype_ mod_fault = 0;
+
+#ifndef EIS_SETUP
+ setupOperandDescriptor (1, &mod_fault);
+ setupOperandDescriptorCache (2);
+ setupOperandDescriptorCache (3);
+#endif
+
+ parseAlphanumericOperandDescriptor (1, 1, false, &mod_fault);
+ parseArgOperandDescriptor (2, &mod_fault);
+ parseArgOperandDescriptor (3, &mod_fault);
+
+#ifdef L68
+ // L68 raises it immediately
+ if (mod_fault)
+ {
+ doFault (FAULT_IPR,
+ (_fault_subtype) {.fault_ipr_subtype=mod_fault},
+ "Illegal modifier");
+ }
+#endif
+
+ // Bits 0-17 MBZ
+ if (IWB_IRODD & 0777777000000)
+ doFault (FAULT_IPR, (_fault_subtype) {.fault_ipr_subtype=FR_ILL_OP|mod_fault}, "tctr 0-17 MBZ");
+
+ // Bit 23 of OP1 MBZ
+ if (!(e->MF[0] & MFkID) && e -> op [0] & 0000000010000)
+ doFault (FAULT_IPR, (_fault_subtype) {.fault_ipr_subtype=FR_ILL_PROC|mod_fault}, "tctr op1 23 MBZ");
+
+ // Bits 18-28, 39-31 of OP2 MBZ
+ if (!(e->MF[1] & MFkID) && e -> op [1] & 0000000777660)
+ doFault (FAULT_IPR, (_fault_subtype) {.fault_ipr_subtype=FR_ILL_PROC|mod_fault}, "tctr op2 18-28, 39-31 MBZ");
+
+ // Bits 18-28, 39-31 of OP3 MBZ
+ if (!(e->MF[2] & MFkID) && e -> op [2] & 0000000777660)
+ doFault (FAULT_IPR, (_fault_subtype) {.fault_ipr_subtype=FR_ILL_PROC|mod_fault}, "tctr op3 18-28, 39-31 MBZ");
+
+#ifdef DPS8M
+ // DPS8M raises it delayed
+ if (mod_fault)
+ {
+ doFault (FAULT_IPR,
+ (_fault_subtype) {.fault_ipr_subtype=mod_fault},
+ "Illegal modifier");
+ }
+#endif
+
+#ifdef EIS_PTR3
+ sim_debug (DBG_TRACEEXT, & cpu_dev,
+ "TCTR CN1: %d TA1: %d\n", e -> CN1, TA1);
+#else
+ sim_debug (DBG_TRACEEXT, & cpu_dev,
+ "TCTR CN1: %d TA1: %d\n", e -> CN1, e -> TA1);
+#endif
+
+ uint srcSZ = 0;
+
+#ifdef EIS_PTR3
+ switch (TA1)
+#else
+ switch (e -> TA1)
+#endif
+ {
+ case CTA4:
+ srcSZ = 4;
+ break;
+ case CTA6:
+ srcSZ = 6;
+ break;
+ case CTA9:
+ srcSZ = 9;
+ break;
+ }
+
+ // ISOLTS-878 01i asserts no prepaging
+#if 0
+ // Prepage Check in a Multiword Instruction
+ // The MVT, TCT, TCTR, and CMPCT instruction have a prepage check. The
+ // size of the translate table is determined by the TA1 data type as shown
+ // in the table below. Before the instruction is executed, a check is made
+ // for allocation in memory for the page for the translate table. If the
+ // page is not in memory, a Missing Page fault occurs before execution of
+ // the instruction. (Bull RJ78 p.7-75)
+
+ // TA1 TRANSLATE TABLE SIZE
+ // 4-BIT CHARACTER 4 WORDS
+ // 6-BIT CHARACTER 16 WORDS
+ // 9-BIT CHARACTER 128 WORDS
+
+ uint xlatSize = 0; // size of xlation table in words .....
+#ifdef EIS_PTR3
+ switch (TA1)
+#else
+ switch(e -> TA1)
+#endif
+ {
+ case CTA4:
+ xlatSize = 4;
+ break;
+ case CTA6:
+ xlatSize = 16;
+ break;
+ case CTA9:
+ xlatSize = 128;
+ break;
+ }
+
+ word36 xlatTbl [128];
+ memset (xlatTbl, 0, sizeof (xlatTbl)); // 0 it out just in case
+
+ EISReadN (& e -> ADDR2, xlatSize, xlatTbl);
+#endif
+
+ word36 CY3 = 0;
+
+ sim_debug (DBG_TRACEEXT, & cpu_dev,
+ "TCT N1 %d\n", e -> N1);
+
+ PNL (L68_ (if (e->N1 < 128)
+ DU_CYCLE_FLEN_128;))
+
+ uint limit = e -> N1;
+ for ( ; cpu.du.CHTALLY < limit; cpu.du.CHTALLY ++)
+ {
+ word9 c = EISget469 (1, limit - cpu.du.CHTALLY - 1); // get src char
+
+ uint m = 0;
+
+ switch (srcSZ)
+ {
+ case 4:
+ m = c & 017; // truncate upper 2-bits
+ break;
+ case 6:
+ m = c & 077; // truncate upper 3-bits
+ break;
+ case 9:
+ m = c; // keep all 9-bits
+ break; // should already be 0-filled
+ }
+
+ word9 cout = xlate (&e->ADDR2, CTA9, m);
+
+ sim_debug (DBG_TRACEEXT, & cpu_dev,
+ "TCT c %03o %c cout %03o %c\n",
+ m, isprint ((int) m) ? '?' : (char) m,
+ cout, isprint ((int) cout) ? '?' : (char) cout);
+
+ if (cout)
+ {
+ //CY3 = bitfieldInsert36 (0, cout, 27, 9); // C(Y-char92)m -> C(Y3)0,8
+ CY3 = setbits36_9 (0, 0, cout);
+ break;
+ }
+ }
+
+ SC_I_TALLY (cpu.du.CHTALLY == e -> N1);
+
+ //CY3 = bitfieldInsert36 (CY3, cpu.du.CHTALLY, 0, 24);
+ putbits36_24 (& CY3, 12, cpu.du.CHTALLY);
+ EISWriteIdx (& e -> ADDR3, 0, CY3, true);
+
+ cleanupOperandDescriptor (1);
+ cleanupOperandDescriptor (2);
+ cleanupOperandDescriptor (3);
+ }
+
+/*
+ * MLR - Move Alphanumeric Left to Right
+ *
+ * (Nice, simple instruction if it weren't for the stupid overpunch stuff that ruined it!!!!)
+ */
+
+/*
+ * does 6-bit char represent a GEBCD negative overpuch? if so, whice numeral?
+ * Refer to Bull NovaScale 9000 RJ78 Rev2 p11-178
+ */
+
+#if 0
+static bool isOvp (uint c, word9 * on)
+ {
+ // look for GEBCD -' 'A B C D E F G H I (positive overpunch)
+ // look for GEBCD - ^ J K L M N O P Q R (negative overpunch)
+
+ word9 c2 = c & 077; // keep to 6-bits
+ * on = 0;
+
+ if (c2 >= 020 && c2 <= 031) // positive overpunch
+ {
+ * on = c2 - 020; // return GEBCD number 0-9 (020 is +0)
+ return false; // well, it's not a negative overpunch is it?
+ }
+ if (c2 >= 040 && c2 <= 052) // negative overpunch
+ {
+ * on = c2 - 040; // return GEBCD number 0-9
+ // (052 is just a '-' interpreted as -0)
+ return true;
+ }
+ return false;
+}
+#endif
+
+// RJ78 p.11-178,D-6
+static bool isGBCDOvp (uint c, bool * isNeg)
+ {
+ if (c & 020)
+ {
+ * isNeg = false;
+ return true;
+ }
+ if (c & 040)
+ {
+ * isNeg = true;
+ return true;
+ }
+ return false;
+ }
+
+// Applies to both MLR and MRL
+//
+// If L1 is greater than L2, the least significant (L1-L2) characters are not moved and
+// the Truncation indicator is set. If L1 is less than L2, bits 0-8, 3-8, or 5-8 of the FILL
+// character (depending on TA2) are inserted as the least significant (L2-L1)
+// characters. If L1 is less than L2, bit 0 of C(FILL) = 1, TA1 = 01, and TA2 = 10
+// (6-4 move); the hardware looks for a 6-bit overpunched sign. If a negative
+// overpunch sign is found, a negative sign (octal 15) is inserted as the last FILL
+// character. If a negative overpunch sign is not found, a positive sign (octal 14) is
+// inserted as the last FILL character
+
+void mlr (void)
+ {
+ EISstruct * e = & cpu.currentEISinstruction;
+
+ // For i = 1, 2, ..., minimum (N1,N2)
+ // C(Y-charn1)N1-i → C(Y-charn2)N2-i
+ // If N1 < N2, then for i = N1+1, N1+2, ..., N2
+ // C(FILL) → C(Y-charn2)N2-i
+ // Indicators: Truncation. If N1 > N2 then ON; otherwise OFF
+
+ fault_ipr_subtype_ mod_fault = 0;
+
+#ifndef EIS_SETUP
+ setupOperandDescriptor (1, &mod_fault);
+ setupOperandDescriptor (2, &mod_fault);
+ //setupOperandDescriptorCache (3);
+#endif
+
+ parseAlphanumericOperandDescriptor(1, 1, false, &mod_fault);
+ parseAlphanumericOperandDescriptor(2, 2, false, &mod_fault);
+
+#ifdef L68
+ // L68 raises it immediately
+ if (mod_fault)
+ {
+ doFault (FAULT_IPR,
+ (_fault_subtype) {.fault_ipr_subtype=mod_fault},
+ "Illegal modifier");
+ }
+#endif
+
+ // Bit 10 MBZ
+ if (IWB_IRODD & 0000200000000)
+ doFault (FAULT_IPR, (_fault_subtype) {.fault_ipr_subtype=FR_ILL_OP|mod_fault}, "mlr 10 MBZ");
+
+ // Bit 23 of OP1 MBZ
+ if (!(e->MF[0] & MFkID) && e -> op [0] & 0000000010000)
+ doFault (FAULT_IPR, (_fault_subtype) {.fault_ipr_subtype=FR_ILL_PROC|mod_fault}, "mlr op1 23 MBZ");
+
+ // Bit 23 of OP2 MBZ
+ if (!(e->MF[1] & MFkID) && e -> op [1] & 0000000010000)
+ doFault (FAULT_IPR, (_fault_subtype) {.fault_ipr_subtype=FR_ILL_PROC|mod_fault}, "mlr op2 23 MBZ");
+
+#ifdef DPS8M
+ // DPS8M raises it delayed
+ if (mod_fault)
+ {
+ doFault (FAULT_IPR,
+ (_fault_subtype) {.fault_ipr_subtype=mod_fault},
+ "Illegal modifier");
+ }
+#endif
+
+ int srcSZ = 0, dstSZ = 0;
+
+#ifdef EIS_PTR3
+ switch (TA1)
+#else
+ switch (e -> TA1)
+#endif
+ {
+ case CTA4:
+ srcSZ = 4;
+ break;
+ case CTA6:
+ srcSZ = 6;
+ break;
+ case CTA9:
+ srcSZ = 9;
+ break;
+ }
+
+#ifdef EIS_PTR3
+ switch (TA2)
+#else
+ switch (e -> TA2)
+#endif
+ {
+ case CTA4:
+ dstSZ = 4;
+ break;
+ case CTA6:
+ dstSZ = 6;
+ break;
+ case CTA9:
+ dstSZ = 9;
+ break;
+ }
+
+ word1 T = getbits36_1 (cpu.cu.IWB, 9);
+
+ word9 fill = getbits36_9 (cpu.cu.IWB, 0);
+ word9 fillT = fill; // possibly truncated fill pattern
+
+ // play with fill if we need to use it
+ switch (dstSZ)
+ {
+ case 4:
+ fillT = fill & 017; // truncate upper 5-bits
+ break;
+ case 6:
+ fillT = fill & 077; // truncate upper 3-bits
+ break;
+ }
+
+ // If N1 > N2, then (N1-N2) leading characters of C(Y-charn1) are not moved
+ // and the truncation indicator is set ON.
+
+ // If N1 < N2 and TA2 = 2 (4-bit data) or 1 (6-bit data), then FILL
+ // characters are high-order truncated as they are moved to C(Y-charn2). No
+ // character conversion takes place.
+
+ // The user of string replication or overlaying is warned that the decimal
+ // unit addresses the main memory in unaligned (not on modulo 8 boundary)
+ // units of Y-block8 words and that the overlayed string, C(Y-charn2), is
+ // not returned to main memory until the unit of Y-block8 words is filled or
+ // the instruction completes.
+
+ // If T = 1 and the truncation indicator is set ON by execution of the
+ // instruction, then a truncation (overflow) fault occurs.
+
+ // Attempted execution with the xed instruction causes an illegal procedure
+ // fault.
+
+ // Attempted repetition with the rpt, rpd, or rpl instructions causes an
+ // illegal procedure fault.
+
+ PNL (L68_ (if (max (e->N1, e->N2) < 128)
+ DU_CYCLE_FLEN_128;))
+
+#ifdef EIS_PTR3
+ bool ovp = (e -> N1 < e -> N2) && (fill & 0400) && (TA1 == 1) &&
+ (TA2 == 2); // (6-4 move)
+#else
+ bool ovp = (e -> N1 < e -> N2) && (fill & 0400) && (e -> TA1 == 1) &&
+ (e -> TA2 == 2); // (6-4 move)
+#endif
+ //word9 on; // number overpunch represents (if any)
+ bool isNeg = false;
+ //bool bOvp = false; // true when a negative overpunch character has been
+ // found @ N1-1
+
+
+#ifdef EIS_PTR3
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "MLR TALLY %u TA1 %u TA2 %u N1 %u N2 %u CN1 %u CN2 %u\n", cpu.du.CHTALLY, TA1, TA2, e -> N1, e -> N2, e -> CN1, e -> CN2);
+#else
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "MLR TALLY %u TA1 %u TA2 %u N1 %u N2 %u CN1 %u CN2 %u\n", cpu.du.CHTALLY, e -> TA1, e -> TA2, e -> N1, e -> N2, e -> CN1, e -> CN2);
+#endif
+
+//
+// Multics frequently uses certain code sequences which are easily detected
+// and optimized; eg. it uses the MLR instruction to copy or zero segments.
+//
+// The MLR implementation is correct, not efficent. Copy invokes 12 append
+// cycles per word, and fill 8.
+//
+
+
+//
+// Page copy
+//
+
+ if ((cpu.du.CHTALLY % PGSZ) == 0 &&
+#ifdef EIS_PTR3
+ TA1 == CTA9 && // src and dst are both char 9
+ TA2 == CTA9 &&
+#else
+ e -> TA1 == CTA9 && // src and dst are both char 9
+ e -> TA2 == CTA9 &&
+#endif
+ (e -> N1 % (PGSZ * 4)) == 0 && // a page
+ e -> N2 == e -> N1 && // the src is the same size as the dest.
+ e -> CN1 == 0 && // and it starts at a word boundary // BITNO?
+ e -> CN2 == 0 &&
+#ifdef EIS_PTR
+ (cpu.du.D1_PTR_W & PGMK) == 0 &&
+ (cpu.du.D2_PTR_W & PGMK) == 0)
+#else
+ (e -> ADDR1.address & PGMK) == 0 &&
+ (e -> ADDR2.address & PGMK) == 0)
+#endif
+ {
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "MLR special case #3\n");
+ while (cpu.du.CHTALLY < e -> N1)
+ {
+ word36 pg [PGSZ];
+ EISReadPage (& e -> ADDR1, cpu.du.CHTALLY / 4, pg);
+ EISWritePage (& e -> ADDR2, cpu.du.CHTALLY / 4, pg);
+ cpu.du.CHTALLY += PGSZ * 4;
+ }
+ cleanupOperandDescriptor (1);
+ cleanupOperandDescriptor (2);
+ // truncation fault check does need to be checked for here since
+ // it is known that N1 == N2
+ CLR_I_TRUNC;
+ return;
+ }
+
+//
+// Page zero
+//
+
+ if ((cpu.du.CHTALLY % PGSZ) == 0 &&
+#ifdef EIS_PTR3
+ TA1 == CTA9 && // src and dst are both char 9
+ TA2 == CTA9 &&
+#else
+ e -> TA1 == CTA9 && // src and dst are both char 9
+ e -> TA2 == CTA9 &&
+#endif
+ e -> N1 == 0 && // the source is entirely fill
+ (e -> N2 % (PGSZ * 4)) == 0 && // a page
+ e -> CN1 == 0 && // and it starts at a word boundary // BITNO?
+ e -> CN2 == 0 &&
+#ifdef EIS_PTR
+ (cpu.du.D1_PTR_W & PGMK) == 0 &&
+ (cpu.du.D2_PTR_W& PGMK) == 0)
+#else
+ (e -> ADDR1.address & PGMK) == 0 &&
+ (e -> ADDR2.address & PGMK) == 0)
+#endif
+ {
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "MLR special case #4\n");
+ word36 pg [PGSZ];
+ if (fill)
+ {
+ word36 w = (word36) fill | ((word36) fill << 9) | ((word36) fill << 18) | ((word36) fill << 27);
+ for (uint i = 0; i < PGSZ; i ++)
+ pg [i] = w;
+ }
+ else
+ {
+ memset (pg, 0, sizeof (pg));
+ }
+ while (cpu.du.CHTALLY < e -> N2)
+ {
+ EISWritePage (& e -> ADDR2, cpu.du.CHTALLY / 4, pg);
+ cpu.du.CHTALLY += PGSZ * 4;
+ }
+ cleanupOperandDescriptor (1);
+ cleanupOperandDescriptor (2);
+ // truncation fault check does need to be checked for here since
+ // it is known that N1 == N2
+ CLR_I_TRUNC;
+ return;
+ }
+
+// Test for the case of aligned word move; and do things a word at a time,
+// instead of a byte at a time...
+
+#ifdef EIS_PTR3
+ if (TA1 == CTA9 && // src and dst are both char 9
+ TA2 == CTA9 &&
+#else
+ if (e -> TA1 == CTA9 && // src and dst are both char 9
+ e -> TA2 == CTA9 &&
+#endif
+ e -> N1 % 4 == 0 && // a whole number of words in the src
+ e -> N2 == e -> N1 && // the src is the same size as the dest.
+ e -> CN1 == 0 && // and it starts at a word boundary // BITNO?
+ e -> CN2 == 0)
+ {
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "MLR special case #1\n");
+ for ( ; cpu.du.CHTALLY < e -> N2; cpu.du.CHTALLY += 4)
+ {
+ uint n = cpu.du.CHTALLY / 4;
+ word36 w = EISReadIdx (& e -> ADDR1, n);
+ EISWriteIdx (& e -> ADDR2, n, w, true);
+ }
+ cleanupOperandDescriptor (1);
+ cleanupOperandDescriptor (2);
+ // truncation fault check does need to be checked for here since
+ // it is known that N1 == N2
+ CLR_I_TRUNC;
+ return;
+ }
+
+// Test for the case of aligned word fill; and do things a word at a time,
+// instead of a byte at a time...
+
+#ifdef EIS_PTR3
+ if (TA1 == CTA9 && // src and dst are both char 9
+ TA2 == CTA9 &&
+#else
+ if (e -> TA1 == CTA9 && // src and dst are both char 9
+ e -> TA2 == CTA9 &&
+#endif
+ e -> N1 == 0 && // the source is entirely fill
+ e -> N2 % 4 == 0 && // a whole number of words in the dest
+ e -> CN1 == 0 && // and it starts at a word boundary // BITNO?
+ e -> CN2 == 0)
+ {
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "MLR special case #2\n");
+ word36 w = (word36) fill | ((word36) fill << 9) | ((word36) fill << 18) | ((word36) fill << 27);
+ for ( ; cpu.du.CHTALLY < e -> N2; cpu.du.CHTALLY += 4)
+ {
+ uint n = cpu.du.CHTALLY / 4;
+ EISWriteIdx (& e -> ADDR2, n, w, true);
+ }
+ cleanupOperandDescriptor (1);
+ cleanupOperandDescriptor (2);
+ // truncation fault check does need to be checked for here since
+ // it is known that N1 <= N2
+ CLR_I_TRUNC;
+ return;
+ }
+
+ for ( ; cpu.du.CHTALLY < min (e->N1, e->N2); cpu.du.CHTALLY ++)
+ {
+ word9 c = EISget469 (1, cpu.du.CHTALLY); // get src char
+ word9 cout = 0;
+
+#ifdef EIS_PTR3
+ if (TA1 == TA2)
+#else
+ if (e -> TA1 == e -> TA2)
+#endif
+ EISput469 (2, cpu.du.CHTALLY, c);
+ else
+ {
+ // If data types are dissimilar (TA1 ≠ TA2), each character is
+ // high-order truncated or zero filled, as appropriate, as it is
+ // moved. No character conversion takes place.
+ cout = c;
+ switch (srcSZ)
+ {
+ case 6:
+ switch(dstSZ)
+ {
+ case 4:
+ cout = c & 017; // truncate upper 2-bits
+ break;
+ case 9:
+ break; // should already be 0-filled
+ }
+ break;
+ case 9:
+ switch(dstSZ)
+ {
+ case 4:
+ cout = c & 017; // truncate upper 5-bits
+ break;
+ case 6:
+ cout = c & 077; // truncate upper 3-bits
+ break;
+ }
+ break;
+ }
+
+ // If N1 < N2, C(FILL)0 = 1, TA1 = 1, and TA2 = 2 (6-4 move), then
+ // C(Y-charn1)N1-1 is examined for a GBCD overpunch sign. If a
+ // negative overpunch sign is found, then the minus sign character
+ // is placed in C(Y-charn2)N2-1; otherwise, a plus sign character
+ // is placed in C(Y-charn2)N2-1.
+
+ if (ovp && (cpu.du.CHTALLY == e -> N1 - 1))
+ {
+ // C(FILL)0 = 1 means that there *is* an overpunch char here.
+ // ISOLTS-838 01e, RJ78 p. 11-126
+ isGBCDOvp (c, & isNeg);
+ }
+ EISput469 (2, cpu.du.CHTALLY, cout);
+ }
+ }
+
+ // If N1 < N2, then for i = N1+1, N1+2, ..., N2
+ // C(FILL) → C(Y-charn2)N2-i
+ // If N1 < N2 and TA2 = 2 (4-bit data) or 1 (6-bit data), then FILL
+ // characters are high-order truncated as they are moved to C(Y-charn2). No
+ // character conversion takes place.
+
+ if (e -> N1 < e -> N2)
+ {
+ for ( ; cpu.du.CHTALLY < e -> N2 ; cpu.du.CHTALLY ++)
+ {
+ // if there's an overpunch then the sign will be the last of the fill
+ if (ovp && (cpu.du.CHTALLY == e -> N2 - 1))
+ {
+ if (isNeg)
+ EISput469 (2, cpu.du.CHTALLY, 015); // 015 is decimal -
+ else
+ EISput469 (2, cpu.du.CHTALLY, 014); // 014 is decimal +
+ }
+ else
+ EISput469 (2, cpu.du.CHTALLY, fillT);
+ }
+ }
+ cleanupOperandDescriptor (1);
+ cleanupOperandDescriptor (2);
+
+ if (e -> N1 > e -> N2)
+ {
+ SET_I_TRUNC;
+ if (T && ! TST_I_OMASK)
+ doFault (FAULT_OFL, fst_zero, "mlr truncation fault");
+ }
+ else
+ CLR_I_TRUNC;
+ }
+
+
+void mrl (void)
+ {
+ EISstruct * e = & cpu.currentEISinstruction;
+
+ // For i = 1, 2, ..., minimum (N1,N2)
+ // C(Y-charn1)N1-i → C(Y-charn2)N2-i
+ // If N1 < N2, then for i = N1+1, N1+2, ..., N2
+ // C(FILL) → C(Y-charn2)N2-i
+ // Indicators: Truncation. If N1 > N2 then ON; otherwise OFF
+
+ fault_ipr_subtype_ mod_fault = 0;
+
+#ifndef EIS_SETUP
+ setupOperandDescriptor (1, &mod_fault);
+ setupOperandDescriptor (2, &mod_fault);
+ //setupOperandDescriptorCache (3);
+#endif
+
+ parseAlphanumericOperandDescriptor(1, 1, false, &mod_fault);
+ parseAlphanumericOperandDescriptor(2, 2, false, &mod_fault);
+
+#ifdef L68
+ // L68 raises it immediately
+ if (mod_fault)
+ {
+ doFault (FAULT_IPR,
+ (_fault_subtype) {.fault_ipr_subtype=mod_fault},
+ "Illegal modifier");
+ }
+#endif
+
+ // Bit 10 MBZ
+ if (IWB_IRODD & 0000200000000)
+ doFault (FAULT_IPR, (_fault_subtype) {.fault_ipr_subtype=FR_ILL_OP|mod_fault}, "mrl 10 MBZ");
+
+ // Bit 23 of OP1 MBZ
+ if (!(e->MF[0] & MFkID) && e -> op [0] & 0000000010000)
+ doFault (FAULT_IPR, (_fault_subtype) {.fault_ipr_subtype=FR_ILL_PROC|mod_fault}, "mrl op1 23 MBZ");
+
+ // Bit 23 of OP2 MBZ
+ if (!(e->MF[1] & MFkID) && e -> op [1] & 0000000010000)
+ doFault (FAULT_IPR, (_fault_subtype) {.fault_ipr_subtype=FR_ILL_PROC|mod_fault}, "mrl op2 23 MBZ");
+
+#ifdef DPS8M
+ // DPS8M raises it delayed
+ if (mod_fault)
+ {
+ doFault (FAULT_IPR,
+ (_fault_subtype) {.fault_ipr_subtype=mod_fault},
+ "Illegal modifier");
+ }
+#endif
+
+ int srcSZ = 0, dstSZ = 0;
+
+#ifdef EIS_PTR3
+ switch (TA1)
+#else
+ switch (e -> TA1)
+#endif
+ {
+ case CTA4:
+ srcSZ = 4;
+ break;
+ case CTA6:
+ srcSZ = 6;
+ break;
+ case CTA9:
+ srcSZ = 9;
+ break;
+ }
+
+#ifdef EIS_PTR3
+ switch (TA2)
+#else
+ switch (e -> TA2)
+#endif
+ {
+ case CTA4:
+ dstSZ = 4;
+ break;
+ case CTA6:
+ dstSZ = 6;
+ break;
+ case CTA9:
+ dstSZ = 9;
+ break;
+ }
+
+ word1 T = getbits36_1 (cpu.cu.IWB, 9);
+
+ word9 fill = getbits36_9 (cpu.cu.IWB, 0);
+ word9 fillT = fill; // possibly truncated fill pattern
+
+ // play with fill if we need to use it
+ switch (dstSZ)
+ {
+ case 4:
+ fillT = fill & 017; // truncate upper 5-bits
+ break;
+ case 6:
+ fillT = fill & 077; // truncate upper 3-bits
+ break;
+ }
+
+ // If N1 > N2, then (N1-N2) leading characters of C(Y-charn1) are not moved
+ // and the truncation indicator is set ON.
+
+ // If N1 < N2 and TA2 = 2 (4-bit data) or 1 (6-bit data), then FILL
+ // characters are high-order truncated as they are moved to C(Y-charn2). No
+ // character conversion takes place.
+
+ // The user of string replication or overlaying is warned that the decimal
+ // unit addresses the main memory in unaligned (not on modulo 8 boundary)
+ // units of Y-block8 words and that the overlayed string, C(Y-charn2), is
+ // not returned to main memory until the unit of Y-block8 words is filled or
+ // the instruction completes.
+
+ // If T = 1 and the truncation indicator is set ON by execution of the
+ // instruction, then a truncation (overflow) fault occurs.
+
+ // Attempted execution with the xed instruction causes an illegal procedure
+ // fault.
+
+ // Attempted repetition with the rpt, rpd, or rpl instructions causes an
+ // illegal procedure fault.
+
+#ifdef EIS_PTR3
+ bool ovp = (e -> N1 < e -> N2) && (fill & 0400) && (TA1 == 1) &&
+ (TA2 == 2); // (6-4 move)
+#else
+ bool ovp = (e -> N1 < e -> N2) && (fill & 0400) && (e -> TA1 == 1) &&
+ (e -> TA2 == 2); // (6-4 move)
+#endif
+ bool isNeg = false;
+ //bool bOvp = false; // true when a negative overpunch character has been
+ // found @ N1-1
+
+ PNL (L68_ (if (max (e->N1, e->N2) < 128)
+ DU_CYCLE_FLEN_128;))
+
+//
+// Test for the case of aligned word move; and do things a word at a time,
+// instead of a byte at a time...
+
+#ifdef EIS_PTR3
+ if (TA1 == CTA9 && // src and dst are both char 9
+ TA2 == CTA9 &&
+#else
+ if (e -> TA1 == CTA9 && // src and dst are both char 9
+ e -> TA2 == CTA9 &&
+#endif
+ e -> N1 % 4 == 0 && // a whole number of words in the src
+ e -> N2 == e -> N1 && // the src is the same size as the dest.
+ e -> CN1 == 0 && // and it starts at a word boundary // BITNO?
+ e -> CN2 == 0)
+ {
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "MRL special case #1\n");
+ uint limit = e -> N2;
+ for ( ; cpu.du.CHTALLY < limit; cpu.du.CHTALLY += 4)
+ {
+ uint n = (limit - cpu.du.CHTALLY - 1) / 4;
+ word36 w = EISReadIdx (& e -> ADDR1, n);
+ EISWriteIdx (& e -> ADDR2, n, w, true);
+ }
+ cleanupOperandDescriptor (1);
+ cleanupOperandDescriptor (2);
+ // truncation fault check does need to be checked for here since
+ // it is known that N1 == N2
+ CLR_I_TRUNC;
+ return;
+ }
+
+// Test for the case of aligned word fill; and do things a word at a time,
+// instead of a byte at a time...
+
+#ifdef EIS_PTR3
+ if (TA1 == CTA9 && // src and dst are both char 9
+ TA2 == CTA9 &&
+#else
+ if (e -> TA1 == CTA9 && // src and dst are both char 9
+ e -> TA2 == CTA9 &&
+#endif
+ e -> N1 == 0 && // the source is entirely fill
+ e -> N2 % 4 == 0 && // a whole number of words in the dest
+ e -> CN1 == 0 && // and it starts at a word boundary // BITNO?
+ e -> CN2 == 0)
+ {
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "MRL special case #2\n");
+ word36 w = (word36) fill |
+ ((word36) fill << 9) |
+ ((word36) fill << 18) |
+ ((word36) fill << 27);
+ uint limit = e -> N2;
+ for ( ; cpu.du.CHTALLY < e -> N2; cpu.du.CHTALLY += 4)
+ {
+ uint n = (limit - cpu.du.CHTALLY - 1) / 4;
+ EISWriteIdx (& e -> ADDR2, n, w, true);
+ }
+ cleanupOperandDescriptor (1);
+ cleanupOperandDescriptor (2);
+ // truncation fault check does need to be checked for here since
+ // it is known that N1 <= N2
+ CLR_I_TRUNC;
+ return;
+ }
+
+ for ( ; cpu.du.CHTALLY < min (e -> N1, e -> N2); cpu.du.CHTALLY ++)
+ {
+ word9 c = EISget469 (1, e -> N1 - cpu.du.CHTALLY - 1); // get src char
+ word9 cout = 0;
+
+#ifdef EIS_PTR3
+ if (TA1 == TA2)
+#else
+ if (e -> TA1 == e -> TA2)
+#endif
+ EISput469 (2, e -> N2 - cpu.du.CHTALLY - 1, c);
+ else
+ {
+ // If data types are dissimilar (TA1 ≠ TA2), each character is
+ // high-order truncated or zero filled, as appropriate, as it is
+ // moved. No character conversion takes place.
+ cout = c;
+ switch (srcSZ)
+ {
+ case 6:
+ switch(dstSZ)
+ {
+ case 4:
+ cout = c & 017; // truncate upper 2-bits
+ break;
+ case 9:
+ break; // should already be 0-filled
+ }
+ break;
+ case 9:
+ switch(dstSZ)
+ {
+ case 4:
+ cout = c & 017; // truncate upper 5-bits
+ break;
+ case 6:
+ cout = c & 077; // truncate upper 3-bits
+ break;
+ }
+ break;
+ }
+
+ // If N1 < N2, C(FILL)0 = 1, TA1 = 1, and TA2 = 2 (6-4 move), then
+ // C(Y-charn1)N1-1 is examined for a GBCD overpunch sign. If a
+ // negative overpunch sign is found, then the minus sign character
+ // is placed in C(Y-charn2)N2-1; otherwise, a plus sign character
+ // is placed in C(Y-charn2)N2-1.
+
+// ISOLTS 838 01f, RJ78 p.11-154 - the rightmost digit is examined for overpunch.
+ if (ovp && (cpu.du.CHTALLY == 0))
+ {
+ // C(FILL)0 = 1 means that there *is* an overpunch char here.
+ isGBCDOvp (c, & isNeg);
+ }
+ EISput469 (2, e -> N2 - cpu.du.CHTALLY - 1, cout);
+ }
+ }
+
+ // If N1 < N2, then for i = N1+1, N1+2, ..., N2
+ // C(FILL) → C(Y-charn2)N2-i
+ // If N1 < N2 and TA2 = 2 (4-bit data) or 1 (6-bit data), then FILL
+ // characters are high-order truncated as they are moved to C(Y-charn2). No
+ // character conversion takes place.
+
+ if (e -> N1 < e -> N2)
+ {
+ for ( ; cpu.du.CHTALLY < e -> N2 ; cpu.du.CHTALLY ++)
+ {
+ // if there's an overpunch then the sign will be the last of the fill
+ if (ovp && (cpu.du.CHTALLY == e -> N2 - 1))
+ {
+ if (isNeg)
+ EISput469 (2, e -> N2 - cpu.du.CHTALLY - 1, 015); // 015 is decimal -
+ else
+ EISput469 (2, e -> N2 - cpu.du.CHTALLY - 1, 014); // 014 is decimal +
+ }
+ else
+ {
+ EISput469 (2, e -> N2 - cpu.du.CHTALLY - 1, fillT);
+ }
+ }
+ }
+ cleanupOperandDescriptor (1);
+ cleanupOperandDescriptor (2);
+
+ if (e -> N1 > e -> N2)
+ {
+ SET_I_TRUNC;
+ if (T && ! TST_I_OMASK)
+ doFault (FAULT_OFL, fst_zero, "mrl truncation fault");
+ }
+ else
+ CLR_I_TRUNC;
+ }
+
+// decimalZero
+//
+// Try 1:
+//
+// This makes MVE 1-28 and all of MVNE work
+// Fails MVE 29-37 (6->6)
+// #define decimalZero (e->srcTA != CTA4 ? '0' : 0)
+//
+// Try 2
+// This makes MVE 1-10 and 20-37 and all of MVNE work
+// Fails MVE 11-19 (6->9)
+// #define decimalZero (e->srcTA == CTA9 ? '0' : 0)
+//
+// Try 4
+//
+#define isDecimalZero(c) ((e->srcTA == CTA9) ? \
+ ((c) == '0') : \
+ (((c) & 017) == 0))
+
+/*
+ * Load the entire sending string number (maximum length 63 characters) into
+ * the decimal unit input buffer as 4-bit digits (high-order truncating 9-bit
+ * data). Strip the sign and exponent characters (if any), put them aside into
+ * special holding registers and decrease the input buffer count accordingly.
+ */
+
+static void EISloadInputBufferNumeric (int k)
+{
+ EISstruct * e = & cpu.currentEISinstruction;
+
+ word9 *p = e->inBuffer; // p points to position in inBuffer where 4-bit chars are stored
+ memset(e->inBuffer, 0, sizeof(e->inBuffer)); // initialize to all 0's
+
+ int pos = (int) e->CN[k-1];
+
+ int TN = (int) e->TN[k-1];
+ int S = (int) e->S[k-1]; // This is where MVNE gets really nasty.
+ // I spit on the designers of this instruction set (and of COBOL.) >Ptui!<
+
+ int N = (int) e->N[k-1]; // number of chars in src string
+
+ EISaddr *a = &e->addr[k-1];
+
+ e->sign = 1;
+ e->exponent = 0;
+
+ for(int n = 0 ; n < N ; n += 1)
+ {
+ word9 c = EISget49(a, &pos, TN);
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "src: %d: %o\n", n, c);
+
+ /*
+ * Here we need to distinguish between 4 type of numbers.
+ *
+ * CSFL - Floating-point, leading sign
+ * CSLS - Scaled fixed-point, leading sign
+ * CSTS - Scaled fixed-point, trailing sign
+ * CSNS - Scaled fixed-point, unsigned
+ */
+ switch(S)
+ {
+ case CSFL: // this is the real evil one ....
+ // Floating-point:
+ // [sign=c0] c1×10(n-3) + c2×10(n-4) + ... + c(n-3) [exponent=8
+ // bits]
+ //
+ // where:
+ //
+ // ci is the decimal value of the byte in the ith byte
+ // position.
+ //
+ // [sign=ci] indicates that ci is interpreted as a sign byte.
+ //
+ // [exponent=8 bits] indicates that the exponent value is
+ // taken from the last 8 bits of the string. If the data is in
+ // 9-bit bytes, the exponent is bits 1-8 of c(n-1). If the
+ // data is in 4- bit bytes, the exponent is the binary value
+ // of the concatenation of c(n-2) and c(n-1).
+
+ if (n == 0) // first had better be a sign ....
+ {
+ c &= 0xf; // hack off all but lower 4 bits
+
+ if (c < 012 || c > 017)
+ doFault (FAULT_IPR,
+ fst_ill_dig,
+ "loadInputBufferNumeric(1): illegal char in "
+ "input");
+
+ if (c == 015) // '-'
+ e->sign = -1;
+
+ e->srcTally -= 1; // 1 less source char
+ }
+ else if (TN == CTN9 && n == N-1) // the 9-bit exponent (of which only 8-bits are used)
+ {
+ e->exponent = (signed char)(c & 0377); // want to do a sign extend
+ e->srcTally -= 1; // 1 less source char
+ }
+ else if (TN == CTN4 && n == N-2) // the 1st 4-chars of the 8-bit exponent
+ {
+ e->exponent = (c & 0xf);// << 4;
+ e->exponent <<= 4;
+ e->srcTally -= 1; // 1 less source char
+ }
+ else if (TN == CTN4 && n == N-1) // the 2nd 4-chars of the 8-bit exponent
+ {
+ e->exponent |= (c & 0xf);
+
+ signed char ce = (signed char) (e->exponent & 0xff);
+ e->exponent = ce;
+
+ e->srcTally -= 1; // 1 less source char
+ }
+ else
+ {
+ c &= 0xf; // hack off all but lower 4 bits
+ if (c > 011)
+ doFault(FAULT_IPR, fst_ill_dig, "loadInputBufferNumeric(2): illegal char in input"); // TODO: generate ill proc fault
+
+ *p++ = c; // store 4-bit char in buffer
+ }
+ break;
+
+ case CSLS:
+ // Only the byte values [0,11]8 are legal in digit positions and only the byte values [12,17]8 are legal in sign positions. Detection of an illegal byte value causes an illegal procedure fault
+ c &= 0xf; // hack off all but lower 4 bits
+
+ if (n == 0) // first had better be a sign ....
+ {
+ if (c < 012 || c > 017)
+ doFault(FAULT_IPR, fst_ill_dig, "loadInputBufferNumeric(3): illegal char in input"); // TODO: generate ill proc fault
+
+ if (c == 015) // '-'
+ e->sign = -1;
+ e->srcTally -= 1; // 1 less source char
+ }
+ else
+ {
+ if (c > 011)
+ doFault(FAULT_IPR, fst_ill_dig, "loadInputBufferNumeric(4): illegal char in input");
+ *p++ = c; // store 4-bit char in buffer
+ }
+ break;
+
+ case CSTS:
+ c &= 0xf; // hack off all but lower 4 bits
+
+ if (n == N-1) // last had better be a sign ....
+ {
+ if (c < 012 || c > 017)
+ doFault(FAULT_IPR, fst_ill_dig, "loadInputBufferNumeric(5): illegal char in input");
+ if (c == 015) // '-'
+ e->sign = -1;
+ e->srcTally -= 1; // 1 less source char
+ }
+ else
+ {
+ if (c > 011)
+ doFault(FAULT_IPR, fst_ill_dig, "loadInputBufferNumeric(6): illegal char in input");
+ *p++ = c; // store 4-bit char in buffer
+ }
+ break;
+
+ case CSNS:
+ c &= 0xf; // hack off all but lower 4 bits
+
+ *p++ = c; // the "easy" one
+ break;
+ }
+ }
+ if_sim_debug (DBG_TRACEEXT, & cpu_dev)
+ {
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "inBuffer:");
+ for (word9 *q = e->inBuffer; q < p; q ++)
+ sim_debug (DBG_TRACEEXT, & cpu_dev, " %02o", * q);
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "\n");
+ }
+}
+
+
+/*
+ * Load decimal unit input buffer with sending string characters. Data is read
+ * from main memory in unaligned units (not modulo 8 boundary) of Y-block8
+ * words. The number of characters loaded is the minimum of the remaining
+ * sending string count, the remaining receiving string count, and 64.
+ */
+
+static void EISloadInputBufferAlphnumeric (int k)
+ {
+ EISstruct * e = & cpu.currentEISinstruction;
+ // p points to position in inBuffer where 4-bit chars are stored
+ word9 * p = e -> inBuffer;
+ memset (e -> inBuffer, 0, sizeof (e -> inBuffer));// initialize to all 0's
+
+ // minimum of the remaining sending string count, the remaining receiving
+ // string count, and 64.
+ // uint N = min3 (e -> N1, e -> N3, 64);
+ // The above AL39 rule doesn't work with IGN and possibly other MOPs as
+ // well. Workaround: Load all source, but max 63 characters (as RJ78
+ // suggests).
+ uint N = min (e-> N1, 63);
+
+ for (uint n = 0 ; n < N ; n ++)
+ {
+ word9 c = EISget469 (k, n);
+ * p ++ = c;
+ }
+}
+
+static void EISwriteOutputBufferToMemory (int k)
+ {
+ EISstruct * e = & cpu.currentEISinstruction;
+
+ for (uint n = 0 ; n < (uint) e -> dstTally; n ++)
+ {
+ word9 c49 = e -> outBuffer [n];
+ EISput469 (k, n, c49);
+ }
+ }
+
+
+static void writeToOutputBuffer (word9 **dstAddr, int szSrc, int szDst, word9 c49)
+{
+ EISstruct * e = & cpu.currentEISinstruction;
+ // 4. If an edit insertion table entry or MOP insertion character is to be
+ // stored, ANDed, or ORed into a receiving string of 4- or 6-bit
+ // characters, high-order truncate the character accordingly.
+ // 5. (MVNE) If the receiving string is 9-bit characters, high-order fill
+ // the (4-bit) digits from the input buffer with bits 0-4 of character 8 of
+ // the edit insertion table. If the receiving string is 6-bit characters,
+ // high-order fill the digits with "00"b.
+
+ if (e -> mvne)
+ {
+ switch (szSrc) // XXX according to rule 1 of Numeric Edit,
+ // input should be 4bits. this needs cleaning
+ {
+ case 4:
+ switch (szDst)
+ {
+ case 4:
+ ** dstAddr = c49 & 0xf;
+ break;
+ case 6:
+ ** dstAddr = c49 & 077; // high-order fill the digits with "00"b.
+ break;
+ case 9:
+ ** dstAddr = c49 | (e -> editInsertionTable [7] & 0760);
+ break;
+ }
+ break;
+ case 6:
+ switch (szDst)
+ {
+ case 4:
+ ** dstAddr = c49 & 0xf; // write only-4-bits
+ break;
+ case 6:
+ ** dstAddr = c49; // XXX is this safe? shouldn't it be & 077?
+ break;
+ case 9:
+ ** dstAddr = c49;
+ break;
+ }
+ break;
+ // Note: case szSrc == 9 is also used for writing edit table
+ // entries and MOP insertion characters which shall NOT be
+ // transformed by rule 5
+ case 9:
+ switch(szDst)
+ {
+ case 4:
+ ** dstAddr = c49 & 0xf; // write only-4-bits
+ break;
+ case 6:
+ ** dstAddr = c49 & 077; // write only 6-bits
+ break;
+ case 9:
+ ** dstAddr = c49;
+ break;
+ }
+ break;
+ }
+ }
+ else // mve
+ {
+ switch(szDst)
+ {
+ case 4:
+ ** dstAddr = c49 & 0xf; // write only-4-bits
+ break;
+ case 6:
+ ** dstAddr = c49 & 077; // write only 6-bits
+ break;
+ case 9:
+ ** dstAddr = c49;
+ break;
+ }
+ }
+ e->dstTally -= 1;
+ *dstAddr += 1;
+}
+
+/*!
+ * This is the Micro Operation Executor/Interpreter
+ */
+
+static char* defaultEditInsertionTable = " *+-$,.0";
+
+// Edit Flags
+//
+// The processor provides the following four edit flags for use by the micro
+// operations.
+//
+// bool mopES = false; // End Suppression flag; initially OFF, set ON by a
+// micro operation when zero-suppression ends.
+//
+// bool mopSN = false;
+// Sign flag; initially set OFF if the sending string has an alphanumeric
+// descriptor or an unsigned numeric descriptor. If the sending string has a
+// signed numeric descriptor, the sign is initially read from the sending
+// string from the digit position defined by the sign and the decimal type
+// field (S); SN is set OFF if positive, ON if negative. If all digits are
+// zero, the data is assumed positive and the SN flag is set OFF, even when the
+// sign is negative.
+//
+//bool mopBZ = false; i
+// Blank-when-zero flag; initially set OFF and set ON by either the ENF or SES
+// micro operation. If, at the completion of a move (L1 exhausted), both the Z
+// and BZ flags are ON, the receiving string is filled with character 1 of the
+// edit insertion table.
+
+/*!
+ * CHT Micro Operation - Change Table
+ * EXPLANATION: The edit insertion table is replaced by the string of eight
+ * 9-bit characters immediately following the CHT micro operation.
+ * FLAGS: None affected
+ * NOTE: C(IF) is not interpreted for this operation.
+ */
+
+static int mopCHT (void)
+{
+ EISstruct * e = & cpu.currentEISinstruction;
+ memset(&e->editInsertionTable, 0, sizeof(e->editInsertionTable)); // XXX do we really need this?
+ for(int i = 0 ; i < 8 ; i += 1)
+ {
+ if (e->mopTally == 0)
+ {
+ e->_faults |= FAULT_IPR;
+ break;
+ }
+#ifdef EIS_PTR2
+ word9 entry = EISget49(&e->ADDR2, &e->mopPos, CTN9); // get mop table entries
+#else
+ word9 entry = EISget49(e->mopAddress, &e->mopPos, CTN9); // get mop table entries
+#endif
+ e->editInsertionTable[i] = entry & 0777; // keep to 9-bits
+ e->mopTally -= 1;
+ }
+ return 0;
+}
+
+/*!
+ * ENF Micro Operation - End Floating Suppression
+ * EXPLANATION:
+ * Bit 0 of IF, IF(0), specifies the nature of the floating suppression. Bit 1
+ * of IF, IF(1), specifies if blank when zero option is used.
+ * For IF(0) = 0 (end floating-sign operation),
+ * − If ES is OFF and SN is OFF, then edit insertion table entry 3 is moved to
+ * the receiving field and ES is set ON.
+ * − If ES is OFF and SN is ON, then edit insertion table entry 4 is moved to
+ * the receiving field and ES is set ON.
+ * − If ES is ON, no action is taken.
+ * For IF(0) = 1 (end floating currency symbol operation),
+ * − If ES is OFF, then edit insertion table entry 5 is moved to the receiving
+ * field and ES is set ON.
+ * − If ES is ON, no action is taken.
+ * For IF(1) = 1 (blank when zero): the BZ flag is set ON. For IF(1) = 0 (no
+ * blank when zero): no action is taken.
+ * FLAGS: (Flags not listed are not affected)
+ * ES - If OFF, then set ON
+ * BZ - If bit 1 of C(IF) = 1, then set ON; otherwise, unchanged
+ */
+
+static int mopENF (void)
+{
+ EISstruct * e = & cpu.currentEISinstruction;
+ // For IF(0) = 0 (end floating-sign operation),
+ if (!(e->mopIF & 010))
+ {
+ // If ES is OFF and SN is OFF, then edit insertion table entry 3 is moved to the receiving field and ES is set ON.
+ if (!e->mopES && !e->mopSN)
+ {
+ writeToOutputBuffer(&e->out, 9, e->dstSZ, e->editInsertionTable[2]);
+ e->mopES = true;
+ }
+ // If ES is OFF and SN is ON, then edit insertion table entry 4 is moved to the receiving field and ES is set ON.
+ if (!e->mopES && e->mopSN)
+ {
+ writeToOutputBuffer(&e->out, 9, e->dstSZ, e->editInsertionTable[3]);
+ e->mopES = true;
+ }
+ // If ES is ON, no action is taken.
+ } else { // IF(0) = 1 (end floating currency symbol operation),
+ if (!e->mopES)
+ {
+ // If ES is OFF, then edit insertion table entry 5 is moved to the receiving field and ES is set ON.
+ writeToOutputBuffer(&e->out, 9, e->dstSZ, e->editInsertionTable[4]);
+ e->mopES = true;
+ }
+ // If ES is ON, no action is taken.
+ }
+
+ // For IF(1) = 1 (blank when zero): the BZ flag is set ON. For IF(1) = 0 (no blank when zero): no action is taken.
+ if (e->mopIF & 04)
+ e->mopBZ = true;
+
+ return 0;
+}
+
+/*!
+ * IGN Micro Operation - Ignore Source Characters
+ * EXPLANATION:
+ * IF specifies the number of characters to be ignored, where IF = 0 specifies
+ * 16 characters.
+ * The next IF characters in the source data field are ignored and the sending
+ * tally is reduced accordingly.
+ * FLAGS: None affected
+ */
+
+static int mopIGN (void)
+{
+ EISstruct * e = & cpu.currentEISinstruction;
+// AL-39 dosen't specify the == 0 test, but NovaScale does;
+// also ISOLTS ps830 test-04a seems to rely on it.
+ if (e->mopIF == 0)
+ e->mopIF = 16;
+
+ for(int n = 0 ; n < e->mopIF ; n += 1)
+ {
+ if (e->dstTally == 0)
+ break;
+ if (e->srcTally == 0)
+ {
+ // IGN doesn't allow BZ, raise IPR straight away
+ e->_faults |= FAULT_IPR;
+ break;
+ }
+
+ e->srcTally -= 1;
+ e->in += 1;
+ }
+ return 0;
+}
+
+/*!
+ * INSA Micro Operation - Insert Asterisk on Suppression
+ * EXPLANATION:
+ * This MOP is the same as INSB except that if ES is OFF, then edit insertion
+ * table entry 2 is moved to the receiving field.
+ * FLAGS: None affected
+ * NOTE: If C(IF) = 9-15, an IPR fault occurs.
+ */
+
+static int mopINSA (void)
+{
+ EISstruct * e = & cpu.currentEISinstruction;
+ // If C(IF) = 9-15, an IPR fault occurs.
+ if (e->mopIF >= 9 && e->mopIF <= 15)
+ {
+ e->_faults |= FAULT_IPR;
+ return 0;
+ }
+
+#if 1
+ // If ES is OFF, then edit insertion table entry 1 is moved to the
+ // receiving field. If IF = 0, then the next 9 bits are also skipped. If IF
+ // is not 0, the next 9 bits are treated as a MOP.
+
+ if (!e->mopES)
+ {
+ writeToOutputBuffer(&e->out, 9, e->dstSZ, e->editInsertionTable[1]);
+
+ if (e->mopIF == 0)
+ {
+ if (e->mopTally == 0)
+ {
+ e->_faults |= FAULT_IPR;
+ return 0;
+ }
+#ifdef EIS_PTR2
+ EISget49(&e->ADDR2, &e->mopPos, CTN9);
+#else
+ EISget49(e->mopAddress, &e->mopPos, CTN9);
+#endif
+ e->mopTally -= 1;
+ }
+ }
+
+ // If ES is ON and IF = 0, then the 9-bit character immediately following
+ // the INSB micro-instruction is moved to the receiving field
+ else
+ {
+ if (e->mopIF == 0)
+ {
+ if (e->mopTally == 0)
+ {
+ e->_faults |= FAULT_IPR;
+ return 0;
+ }
+#ifdef EIS_PTR2
+ word9 c = EISget49(&e->ADDR2, &e->mopPos, CTN9);
+#else
+ word9 c = EISget49(e->mopAddress, &e->mopPos, CTN9);
+#endif
+ writeToOutputBuffer(&e->out, 9, e->dstSZ, c);
+ e->mopTally -= 1;
+ }
+ // If ES is ON and IF<>0, then IF specifies which edit insertion table
+ // entry (1-8) is to be moved to the receiving field.
+ else
+ {
+ writeToOutputBuffer(&e->out, 9, e->dstSZ, e->editInsertionTable[e->mopIF-1]);
+ }
+ }
+#else
+ // If IF = 0, the 9 bits immediately following the INSB micro operation are
+ // treated as a 9-bit character (not a MOP) and are moved or skipped
+ // according to ES.
+ if (e->mopIF == 0)
+ {
+ // If ES is OFF, then edit insertion table entry 2 is moved to the
+ // receiving field. If IF = 0, then the next 9 bits are also skipped.
+ // If IF is not 0, the next 9 bits are treated as a MOP.
+ if (!e->mopES)
+ {
+ writeToOutputBuffer(&e->out, 9, e->dstSZ, e->editInsertionTable[1]);
+
+#ifdef EIS_PTR2
+ EISget49(&e->ADDR2, &e->mopPos, CTN9);
+#else
+ EISget49(e->mopAddress, &e->mopPos, CTN9);
+#endif
+ e->mopTally -= 1;
+ } else {
+ // If ES is ON and IF = 0, then the 9-bit character immediately
+ // following the INSB micro-instruction is moved to the receiving
+ // field.
+#if 1
+#ifdef EIS_PTR2
+ word9 c = EISget49(&e->ADDR2, &e->mopPos, CTN9);
+#else
+ word9 c = EISget49(e->mopAddress, &e->mopPos, CTN9);
+#endif
+ writeToOutputBuffer(&e->out, 9, e->dstSZ, c);
+#else
+#ifdef EIS_PTR2
+ writeToOutputBuffer(&e->out, 9, e->dstSZ, EISget49(&e->ADDR2, &e->mopPos, CTN9));
+#else
+ writeToOutputBuffer(&e->out, 9, e->dstSZ, EISget49(e->mopAddress, &e->mopPos, CTN9));
+#endif
+#endif
+ e->mopTally -= 1;
+ }
+
+ } else {
+ // If ES is ON and IF<>0, then IF specifies which edit insertion table
+ // entry (1-8) is to be moved to the receiving field.
+ if (e->mopES)
+ {
+ writeToOutputBuffer(&e->out, 9, e->dstSZ, e->editInsertionTable[e->mopIF-1]);
+ }
+ }
+#endif
+ return 0;
+}
+
+/*!
+ * INSB Micro Operation - Insert Blank on Suppression
+ * EXPLANATION:
+ * IF specifies which edit insertion table entry is inserted.
+ * If IF = 0, the 9 bits immediately following the INSB micro operation are
+ * treated as a 9-bit character (not a MOP) and are moved or skipped according
+ * to ES.
+ * − If ES is OFF, then edit insertion table entry 1 is moved to the receiving
+ * field. If IF = 0, then the next 9 bits are also skipped. If IF is not 0, the
+ * next 9 bits are treated as a MOP.
+ * − If ES is ON and IF = 0, then the 9-bit character immediately following the
+ * INSB micro-instruction is moved to the receiving field.
+ * − If ES is ON and IF<>0, then IF specifies which edit insertion table entry
+ * (1-8) is to be moved to the receiving field.
+ * FLAGS: None affected
+ * NOTE: If C(IF) = 9-15, an IPR fault occurs.
+ */
+
+static int mopINSB (void)
+{
+ EISstruct * e = & cpu.currentEISinstruction;
+ // If C(IF) = 9-15, an IPR fault occurs.
+ if (e->mopIF >= 9 && e->mopIF <= 15)
+ {
+ e->_faults |= FAULT_IPR;
+ return 0;
+ }
+
+ if (!e->mopES)
+ {
+ // If ES is OFF, then edit insertion table entry 1 is moved to the
+ // receiving field. If IF = 0, then the next 9 bits are also skipped.
+ // If IF is not 0, the next 9 bits are treated as a MOP.
+ writeToOutputBuffer(&e->out, 9, e->dstSZ, e->editInsertionTable[0]);
+
+ if (e->mopIF == 0)
+ {
+ if (e->mopTally == 0)
+ {
+ e->_faults |= FAULT_IPR;
+ return 0;
+ }
+#ifdef EIS_PTR2
+ EISget49(&e->ADDR2, &e->mopPos, CTN9);
+#else
+ EISget49(e->mopAddress, &e->mopPos, CTN9);
+#endif
+ e->mopTally -= 1;
+ }
+
+ } else {
+
+ // ES is ON
+
+ // If C(IF) != 0
+ if (e->mopIF)
+ {
+ // If ES is ON and IF<>0, then IF specifies which edit
+ // insertion table entry (1-8) is to be moved to the receiving
+ // field.
+ writeToOutputBuffer(&e->out, 9, e->dstSZ, e->editInsertionTable[e->mopIF - 1]);
+ } else {
+ // If ES is ON and IF = 0, then the 9-bit character immediately
+ // following the INSB micro-instruction is moved to the
+ // receiving field.
+ if (e->mopTally == 0)
+ {
+ e->_faults |= FAULT_IPR;
+ return 0;
+ }
+#ifdef EIS_PTR2
+ writeToOutputBuffer(&e->out, 9, e->dstSZ, EISget49(&e->ADDR2, &e->mopPos, CTN9));
+ //EISget49(&e->ADDR2, &e->mopPos, CTN9);
+#else
+ writeToOutputBuffer(&e->out, 9, e->dstSZ, EISget49(e->mopAddress, &e->mopPos, CTN9));
+ //EISget49(e->mopAddress, &e->mopPos, CTN9);
+#endif
+ e->mopTally -= 1;
+
+ }
+ }
+ return 0;
+}
+
+/*!
+ * INSM Micro Operation - Insert Table Entry One Multiple
+ * EXPLANATION:
+ * IF specifies the number of receiving characters affected, where IF = 0
+ * specifies 16 characters.
+ * Edit insertion table entry 1 is moved to the next IF (1-16) receiving field
+ * characters.
+ * FLAGS: None affected
+ */
+
+static int mopINSM (void)
+{
+ EISstruct * e = & cpu.currentEISinstruction;
+ if (e->mopIF == 0)
+ e->mopIF = 16;
+ for(int n = 0 ; n < e->mopIF ; n += 1)
+ {
+ if (e->dstTally == 0)
+ break;
+ writeToOutputBuffer(&e->out, 9, e->dstSZ, e->editInsertionTable[0]);
+ }
+ return 0;
+}
+
+/*!
+ * INSN Micro Operation - Insert on Negative
+ * EXPLANATION:
+ * IF specifies which edit insertion table entry is inserted. If IF = 0, the 9
+ * bits immediately following the INSN micro operation are treated as a 9-bit
+ * character (not a MOP) and are moved or skipped according to SN.
+ * − If SN is OFF, then edit insertion table entry 1 is moved to the receiving
+ * field. If IF = 0, then the next 9 bits are also skipped. If IF is not 0, the
+ * next 9 bits are treated as a MOP.
+ * − If SN is ON and IF = 0, then the 9-bit character immediately following the
+ * INSN micro-instruction is moved to the receiving field.
+ * − If SN is ON and IF <> 0, then IF specifies which edit insertion table
+ * entry (1-8) is to be moved to the receiving field.
+ * FLAGS: None affected
+ * NOTE: If C(IF) = 9-15, an IPR fault occurs.
+ */
+
+static int mopINSN (void)
+{
+ EISstruct * e = & cpu.currentEISinstruction;
+ // If C(IF) = 9-15, an IPR fault occurs.
+ if (e->mopIF >= 9 && e->mopIF <= 15)
+ {
+ e->_faults |= FAULT_IPR;
+ return 0;
+ }
+
+ // If IF = 0, the 9 bits immediately following the INSN micro operation are
+ // treated as a 9-bit character (not a MOP) and are moved or skipped
+ // according to SN.
+
+ if (e->mopIF == 0)
+ {
+ if (e->mopTally == 0)
+ {
+ e->_faults |= FAULT_IPR;
+ return 0;
+ }
+ if (!e->mopSN)
+ {
+ //If SN is OFF, then edit insertion table entry 1 is moved to the
+ //receiving field. If IF = 0, then the next 9 bits are also
+ //skipped. If IF is not 0, the next 9 bits are treated as a MOP.
+#ifdef EIS_PTR2
+ EISget49(&e->ADDR2, &e->mopPos, CTN9);
+#else
+ EISget49(e->mopAddress, &e->mopPos, CTN9);
+#endif
+ writeToOutputBuffer(&e->out, 9, e->dstSZ, e->editInsertionTable[0]);
+ e->mopTally -= 1;
+ } else {
+ // If SN is ON and IF = 0, then the 9-bit character immediately
+ // following the INSN micro-instruction is moved to the receiving
+ // field.
+#ifdef EIS_PTR2
+ writeToOutputBuffer(&e->out, 9, e->dstSZ, EISget49(&e->ADDR2, &e->mopPos, CTN9));
+#else
+ writeToOutputBuffer(&e->out, 9, e->dstSZ, EISget49(e->mopAddress, &e->mopPos, CTN9));
+#endif
+
+ e->mopTally -= 1;
+ }
+ }
+ else
+ {
+ if (e->mopSN)
+ {
+ //If SN is ON and IF <> 0, then IF specifies which edit insertion
+ //table entry (1-8) is to be moved to the receiving field.
+ writeToOutputBuffer(&e->out, 9, e->dstSZ, e->editInsertionTable[e->mopIF - 1]);
+ } else {
+ writeToOutputBuffer(&e->out, 9, e->dstSZ, e->editInsertionTable[0]);
+ }
+ }
+ return 0;
+}
+
+/*!
+ * INSP Micro Operation - Insert on Positive
+ * EXPLANATION:
+ * INSP is the same as INSN except that the responses for the SN values are
+ * reversed.
+ * FLAGS: None affected
+ * NOTE: If C(IF) = 9-15, an IPR fault occurs.
+ */
+
+static int mopINSP (void)
+{
+ EISstruct * e = & cpu.currentEISinstruction;
+ // If C(IF) = 9-15, an IPR fault occurs.
+ if (e->mopIF >= 9 && e->mopIF <= 15)
+ {
+ e->_faults |= FAULT_IPR;
+ return 0;
+ }
+
+ if (e->mopIF == 0)
+ {
+ if (e->mopTally == 0)
+ {
+ e->_faults |= FAULT_IPR;
+ return 0;
+ }
+ if (e->mopSN)
+ {
+#ifdef EIS_PTR2
+ EISget49(&e->ADDR2, &e->mopPos, CTN9);
+#else
+ EISget49(e->mopAddress, &e->mopPos, CTN9);
+#endif
+ writeToOutputBuffer(&e->out, 9, e->dstSZ, e->editInsertionTable[0]);
+ e->mopTally -= 1;
+ } else {
+#ifdef EIS_PTR2
+ writeToOutputBuffer(&e->out, 9, e->dstSZ, EISget49(&e->ADDR2, &e->mopPos, CTN9));
+#else
+ writeToOutputBuffer(&e->out, 9, e->dstSZ, EISget49(e->mopAddress, &e->mopPos, CTN9));
+#endif
+ e->mopTally -= 1;
+ }
+ }
+ else
+ {
+ if (!e->mopSN)
+ {
+ writeToOutputBuffer(&e->out, 9, e->dstSZ, e->editInsertionTable[e->mopIF - 1]);
+ } else {
+ writeToOutputBuffer(&e->out, 9, e->dstSZ, e->editInsertionTable[0]);
+ }
+ }
+
+ return 0;
+}
+
+/*!
+ * LTE Micro Operation - Load Table Entry
+ * EXPLANATION:
+ * IF specifies the edit insertion table entry to be replaced.
+ * The edit insertion table entry specified by IF is replaced by the 9-bit
+ * character immediately following the LTE microinstruction.
+ * FLAGS: None affected
+ * NOTE: If C(IF) = 0 or C(IF) = 9-15, an Illegal Procedure fault occurs.
+ */
+
+static int mopLTE (void)
+{
+ EISstruct * e = & cpu.currentEISinstruction;
+ if (e->mopIF == 0 || (e->mopIF >= 9 && e->mopIF <= 15))
+ {
+ e->_faults |= FAULT_IPR;
+ return 0;
+ }
+ if (e->mopTally == 0)
+ {
+ e->_faults |= FAULT_IPR;
+ return 0;
+ }
+#ifdef EIS_PTR2
+ word9 next = EISget49(&e->ADDR2, &e->mopPos, CTN9);
+#else
+ word9 next = EISget49(e->mopAddress, &e->mopPos, CTN9);
+#endif
+ e->mopTally -= 1;
+
+ e->editInsertionTable[e->mopIF - 1] = next;
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "LTE IT[%d]<=%d\n", e -> mopIF - 1, next);
+ return 0;
+}
+
+/*!
+ * MFLC Micro Operation - Move with Floating Currency Symbol Insertion
+ * EXPLANATION:
+ * IF specifies the number of characters of the sending field upon which the
+ * operation is performed, where IF = 0 specifies 16 characters.
+ * Starting with the next available sending field character, the next IF
+ * characters are individually fetched and the following conditional actions
+ * occur.
+ * − If ES is OFF and the character is zero, edit insertion table entry 1 is
+ * moved to the receiving field in place of the character.
+ * − If ES is OFF and the character is not zero, then edit insertion table
+ * entry 5 is moved to the receiving field, the character is also moved to the
+ * receiving field, and ES is set ON.
+ * − If ES is ON, the character is moved to the receiving field.
+ * The number of characters placed in the receiving field is data-dependent. If
+ * the entire sending field is zero, IF characters are placed in the receiving
+ * field. However, if the sending field contains a nonzero character, IF+1
+ * characters (the insertion character plus the characters from the sending
+ * field) are placed in the receiving field.
+ * An IPR fault occurs when the sending field is exhausted before the receiving
+ * field is filled. In order to provide space in the receiving field for an
+ * inserted currency symbol, the receiving field must have a string length one
+ * character longer than the sending field. When the sending field is all
+ * zeros, no currency symbol is inserted by the MFLC micro operation and the
+ * receiving field is not filled when the sending field is exhausted. The user
+ * should provide an ENF (ENF,12) micro operation after a MFLC micro operation
+ * that has as its character count the number of characters in the sending
+ * field. The ENF micro operation is engaged only when the MFLC micro operation
+ * fails to fill the receiving field. Then it supplies a currency symbol to
+ * fill the receiving field and blanks out the entire field.
+ * FLAGS: (Flags not listed are not affected.)
+ * ES If OFF and any of C(Y) is less than decimal zero, then ON; otherwise, it
+ * is unchanged.
+ * NOTE: Since the number of characters moved to the receiving string is
+ * data-dependent, a possible IPR fault may be avoided by ensuring that the Z
+ * and BZ flags are ON.
+ */
+
+static int mopMFLC (void)
+{
+ EISstruct * e = & cpu.currentEISinstruction;
+ if (e->mopIF == 0)
+ e->mopIF = 16;
+
+ // Starting with the next available sending field character, the next IF
+ // characters are individually fetched and the following conditional
+ // actions occur.
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "MFLC IF %d, srcTally %d, dstTally %d\n", e->mopIF, e->srcTally, e->dstTally);
+ for(int n = 0 ; n < e->mopIF ; n += 1)
+ {
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "MFLC n %d, srcTally %d, dstTally %d\n", n, e->srcTally, e->dstTally);
+ if (e->dstTally == 0)
+ break;
+ if (e->srcTally == 0)
+ return -1;
+ // If ES is OFF and the character is zero, edit insertion table entry 1
+ // is moved to the receiving field in place of the character.
+ // If ES is OFF and the character is not zero, then edit insertion
+ // table entry 5 is moved to the receiving field, the character is also
+ // moved to the receiving field, and ES is set ON.
+
+ word9 c = *(e->in);
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "MFLC c %d (0%o)\n", c, c);
+ if (!e->mopES) { // e->mopES is OFF
+
+
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "MFLC ES off\n");
+ if (isDecimalZero (c)) {
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "MFLC is zero\n");
+ // edit insertion table entry 1 is moved to the receiving field
+ // in place of the character.
+ writeToOutputBuffer(&e->out, 9, e->dstSZ, e->editInsertionTable[0]);
+ e->in += 1;
+ e->srcTally -= 1;
+ } else {
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "MFLC is not zero\n");
+ // then edit insertion table entry 5 is moved to the receiving
+ // field, the character is also moved to the receiving field,
+ // and ES is set ON.
+ writeToOutputBuffer(&e->out, 9, e->dstSZ, e->editInsertionTable[4]);
+
+ writeToOutputBuffer(&e->out, e->srcSZ, e->dstSZ, c);
+ e->mopZ = false; // iszero() tested above.
+ e->in += 1;
+ e->srcTally -= 1;
+
+ e->mopES = true;
+ }
+ } else {
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "MFLC ES on\n");
+ // If ES is ON, the character is moved to the receiving field.
+ writeToOutputBuffer(&e->out, e->srcSZ, e->dstSZ, c);
+
+ if (! isDecimalZero (c))
+ e->mopZ = false;
+ e->in += 1;
+ e->srcTally -= 1;
+ }
+ }
+
+ return 0;
+}
+
+/*!
+ * MFLS Micro Operation - Move with Floating Sign Insertion
+ * EXPLANATION:
+ * IF specifies the number of characters of the sending field upon which the
+ * operation is performed, where IF = 0 specifies 16 characters.
+ * Starting with the next available sending field character, the next IF
+ * characters are individually fetched and the following conditional actions
+ * occur.
+ * − If ES is OFF and the character is zero, edit insertion table entry 1 is
+ * moved to the receiving field in place of the character.
+ * − If ES is OFF, the character is not zero, and SN is OFF; then edit
+ * insertion table entry 3 is moved to the receiving field; the character is
+ * also moved to the receiving field, and ES is set ON.
+ * − If ES is OFF, the character is nonzero, and SN is ON; edit insertion table
+ * entry 4 is moved to the receiving field; the character is also moved to the
+ * receiving field, and ES is set ON.
+ * − If ES is ON, the character is moved to the receiving field.
+ * The number of characters placed in the receiving field is data-dependent. If
+ * the entire sending field is zero, IF characters are placed in the receiving
+ * field. However, if the sending field contains a nonzero character, IF+1
+ * characters (the insertion character plus the characters from the sending
+ * field) are placed in the receiving field.
+ * An IPR fault occurs when the sending field is exhausted before the receiving
+ * field is filled. In order to provide space in the receiving field for an
+ * inserted sign, the receiving field must have a string length one character
+ * longer than the sending field. When the sending field is all zeros, no sign
+ * is inserted by the MFLS micro operation and the receiving field is not
+ * filled when the sending field is exhausted. The user should provide an ENF
+ * (ENF,4) micro operation after a MFLS micro operation that has as its
+ * character count the number of characters in the sending field. The ENF micro
+ * operation is engaged only when the MFLS micro operation fails to fill the
+ * receiving field; then, it supplies a sign character to fill the receiving
+ * field and blanks out the entire field.
+ *
+ * FLAGS: (Flags not listed are not affected.)
+ * ES If OFF and any of C(Y) is less than decimal zero, then ON; otherwise,
+ * it is unchanged.
+ * NOTE: Since the number of characters moved to the receiving string is
+ * data-dependent, a possible Illegal Procedure fault may be avoided by
+ * ensuring that the Z and BZ flags are ON.
+ */
+
+static int mopMFLS (void)
+{
+ EISstruct * e = & cpu.currentEISinstruction;
+ if (e->mopIF == 0)
+ e->mopIF = 16;
+
+ for(int n = 0 ; n < e->mopIF; n += 1)
+ {
+ if (e->dstTally == 0)
+ break;
+ if (e->srcTally == 0)
+ return -1;
+
+ word9 c = *(e->in);
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "MFLS n %d c %o\n", n, c);
+ if (!e->mopES) { // e->mopES is OFF
+ if (isDecimalZero (c))
+ {
+ // edit insertion table entry 1 is moved to the receiving field
+ // in place of the character.
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "ES is off, c is zero; edit insertion table entry 1 is moved to the receiving field in place of the character.\n");
+ writeToOutputBuffer(&e->out, 9, e->dstSZ, e->editInsertionTable[0]);
+ e->in += 1;
+ e->srcTally -= 1;
+ } else {
+ // c is non-zero
+ if (!e->mopSN)
+ {
+ // then edit insertion table entry 3 is moved to the
+ // receiving field; the character is also moved to the
+ // receiving field, and ES is set ON.
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "ES is off, c is non-zero, SN is off; edit insertion table entry 3 is moved to the receiving field; the character is also moved to the receiving field, and ES is set ON.\n");
+ writeToOutputBuffer(&e->out, 9, e->dstSZ, e->editInsertionTable[2]);
+
+ e->in += 1;
+ e->srcTally -= 1;
+ e->mopZ = false; // iszero tested above
+#if 0
+ if (e->srcTally == 0 && e->dstTally > 1)
+ {
+ e->_faults |= FAULT_IPR;
+ return -1;
+ }
+#endif
+
+ writeToOutputBuffer(&e->out, e->srcSZ, e->dstSZ, c);
+
+ e->mopES = true;
+ } else {
+ // SN is ON; edit insertion table entry 4 is moved to the
+ // receiving field; the character is also moved to the
+ // receiving field, and ES is set ON.
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "ES is off, c is non-zero, SN is OFF; edit insertion table entry 4 is moved to the receiving field; the character is also moved to the receiving field, and ES is set ON.\n");
+ writeToOutputBuffer(&e->out, 9, e->dstSZ, e->editInsertionTable[3]);
+
+ e->in += 1;
+ e->srcTally -= 1;
+ e->mopZ = false; // iszero tested above
+
+ writeToOutputBuffer(&e->out, e->srcSZ, e->dstSZ, c);
+
+ e->mopES = true;
+ }
+ }
+ } else {
+ // If ES is ON, the character is moved to the receiving field.
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "ES is ON, the character is moved to the receiving field.\n");
+ writeToOutputBuffer(&e->out, e->srcSZ, e->dstSZ, c);
+
+ if (! isDecimalZero (c))
+ e->mopZ = false;
+ e->in += 1;
+ e->srcTally -= 1;
+ }
+ }
+
+ // NOTE: Since the number of characters moved to the receiving string is
+ // data-dependent, a possible Illegal Procedure fault may be avoided by
+ // ensuring that the Z and BZ flags are ON.
+
+ return 0;
+}
+
+/*!
+ * MORS Micro Operation - Move and OR Sign
+ * EXPLANATION:
+ * IF specifies the number of characters of the sending field upon which the
+ * operation is performed, where IF = 0 specifies 16 characters.
+ * Starting with the next available sending field character, the next IF
+ * characters are individually fetched and the following conditional actions
+ * occur.
+ * − If SN is OFF, the next IF characters in the source data field are moved to
+ * the receiving data field and, during the move, edit insertion table entry 3
+ * is ORed to each character.
+ * − If SN is ON, the next IF characters in the source data field are moved to
+ * the receiving data field and, during the move, edit insertion table entry 4
+ * is ORed to each character.
+ * MORS can be used to generate a negative overpunch for a receiving field to
+ * be used later as a sending field.
+ * FLAGS: None affected
+ */
+
+static int mopMORS (void)
+{
+ EISstruct * e = & cpu.currentEISinstruction;
+ if (e->mopIF == 0)
+ e->mopIF = 16;
+
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "MORS mopIF %d src %d dst %d\n", e->mopIF, e->srcTally, e->dstTally);
+ for(int n = 0 ; n < e->mopIF ; n += 1)
+ {
+// The micro operation sequence is terminated normally when the receiving
+// string length becomes exhausted. The micro operation sequence is terminated
+// abnormally (with an illegal procedure fault) if a move from an exhausted
+// sending string or the use of an exhausted MOP string is attempted.
+
+ if (e->dstTally == 0)
+ break;
+ if (e->srcTally == 0)
+ return -1;
+
+ // XXX this is probably wrong regarding the ORing, but it's a start ....
+ word9 c = (*e->in | (!e->mopSN ? e->editInsertionTable[2] : e->editInsertionTable[3]));
+ if (! isDecimalZero (*e->in))
+ e->mopZ = false;
+ e->in += 1;
+ e->srcTally -= 1;
+
+ writeToOutputBuffer(&e->out, e->srcSZ, e->dstSZ, c);
+ }
+
+ return 0;
+}
+
+/*!
+ * MVC Micro Operation - Move Source Characters
+ * EXPLANATION:
+ * IF specifies the number of characters to be moved, where IF = 0 specifies 16
+ * characters.
+ * The next IF characters in the source data field are moved to the receiving
+ * data field.
+ * FLAGS: None affected
+ */
+
+static int mopMVC (void)
+{
+ EISstruct * e = & cpu.currentEISinstruction;
+ if (e->mopIF == 0)
+ e->mopIF = 16;
+
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "MVC mopIF %d\n", e->mopIF);
+
+ for(int n = 0 ; n < e->mopIF ; n += 1)
+ {
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "MVC n %d srcTally %d dstTally %d\n", n, e->srcTally, e->dstTally);
+// GD's test_float shows that data exhaustion is not a fault.
+//#if 0
+ if (e->dstTally == 0)
+ break;
+ if (e->srcTally == 0)
+ return -1;
+
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "MVC write to output buffer %o\n", *e->in);
+ writeToOutputBuffer(&e->out, e->srcSZ, e->dstSZ, *e->in);
+ if (! isDecimalZero (*e->in))
+ e->mopZ = false;
+ e->in += 1;
+
+ e->srcTally -= 1;
+ }
+
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "MVC done\n");
+ return 0;
+}
+
+/*!
+ * MSES Micro Operation - Move and Set Sign
+ * EXPLANATION:
+ * IF specifies the number of characters of the sending field upon which the
+ * operation is performed, where IF = 0 specifies 16 characters. For MVE,
+ * starting with the next available sending field character, the next IF
+ * characters are individually fetched and the following conditional actions
+ * occur.
+ * Starting with the first character during the move, a comparative AND is made
+ * first with edit insertion table entry 3. If the result is nonzero, the first
+ * character and the rest of the characters are moved without further
+ * comparative ANDs. If the result is zero, a comparative AND is made between
+ * the character being moved and edit insertion table entry 4 If that result is
+ * nonzero, the SN indicator is set ON (indicating negative) and the first
+ * character and the rest of the characters are moved without further
+ * comparative ANDs. If the result is zero, the second character is treated
+ * like the first. This process continues until one of the comparative AND
+ * results is nonzero or until all characters are moved.
+ * For MVNE instruction, the sign (SN) flag is already set and IF characters
+ * are moved to the destination field (MSES is equivalent to the MVC
+ * instruction).
+ * FLAGS: (Flags not listed are not affected.)
+ * SN If edit insertion table entry 4 is found in C(Y-1), then ON; otherwise,
+ * it is unchanged.
+ */
+
+static int mopMSES (void)
+{
+ EISstruct * e = & cpu.currentEISinstruction;
+ if (e->mvne == true)
+ return mopMVC (); // XXX I think!
+
+
+ if (e->mopIF == 0)
+ e->mopIF = 16;
+
+ int overpunch = false;
+
+ for(int n = 0 ; n < e->mopIF ; n += 1)
+ {
+ if (e->dstTally == 0)
+ break;
+ if (e->srcTally == 0)
+ return -1;
+
+ //Starting with the first character during the move, a comparative AND
+ //is made first with edit insertion table entry 3. If the result is
+ //nonzero, the first character and the rest of the characters are moved
+ //without further comparative ANDs. If the result is zero, a
+ //comparative AND is made between the character being moved and edit
+ //insertion table entry 4 If that result is nonzero, the SN indicator
+ //is set ON (indicating negative) and the first character and the rest
+ //of the characters are moved without further comparative ANDs. If the
+ //result is zero, the second character is treated like the first. This
+ //process continues until one of the comparative AND results is nonzero
+ //or until all characters are moved.
+
+ word9 c = *(e->in);
+
+ if (!overpunch) {
+ if (c & e->editInsertionTable[2]) // XXX only lower 4-bits are considered
+ overpunch = true;
+
+ else if (c & e->editInsertionTable[3]) // XXX only lower 4-bits are considered
+ {
+ e->mopSN = true;
+ overpunch = true;
+ }
+ }
+
+ e->in += 1;
+ e->srcTally -= 1; // XXX is this correct? No chars have been consumed, but ......
+ if (! isDecimalZero (c))
+ e->mopZ = false;
+ writeToOutputBuffer(&e->out, e->srcSZ, e->dstSZ, c);
+ }
+
+ return 0;
+}
+
+/*!
+ * MVZA Micro Operation - Move with Zero Suppression and Asterisk Replacement
+ * EXPLANATION:
+ * MVZA is the same as MVZB except that if ES is OFF and the character is zero,
+ * then edit insertion table entry 2 is moved to the receiving field.
+ * FLAGS: (Flags not listed are not affected.)
+ * ES If OFF and any of C(Y) is less than decimal zero, then ON; otherwise, it
+ * is unchanged.
+ */
+
+static int mopMVZA (void)
+{
+ EISstruct * e = & cpu.currentEISinstruction;
+ if (e->mopIF == 0)
+ e->mopIF = 16;
+
+ for(int n = 0 ; n < e->mopIF ; n += 1)
+ {
+ if (e->dstTally == 0)
+ break;
+ if (e->srcTally == 0)
+ return -1;
+
+ word9 c = *e->in;
+ e->in += 1;
+ e->srcTally -= 1;
+
+ //if (!e->mopES && c == 0)
+ // XXX See srcTA comment in MVNE
+ if (!e->mopES && isDecimalZero (c))
+ {
+ //If ES is OFF and the character is zero, then edit insertion table
+ //entry 2 is moved to the receiving field in place of the
+ //character.
+ writeToOutputBuffer(&e->out, 9, e->dstSZ, e->editInsertionTable[1]);
+ //} else if (!e->mopES && c != 0)
+ // XXX See srcTA comment in MVNE
+ }
+ else if ((! e->mopES) && (! isDecimalZero (c)))
+ {
+ //If ES is OFF and the character is not zero, then the character is
+ //moved to the receiving field and ES is set ON.
+ e->mopZ = false;
+ writeToOutputBuffer(&e->out, e->srcSZ, e->dstSZ, c);
+
+ e->mopES = true;
+ } else if (e->mopES)
+ {
+ //If ES is ON, the character is moved to the receiving field.
+ if (! isDecimalZero (c))
+ e->mopZ = false;
+ writeToOutputBuffer(&e->out, e->srcSZ, e->dstSZ, c);
+ }
+ }
+
+ return 0;
+}
+
+/*!
+ * MVZB Micro Operation - Move with Zero Suppression and Blank Replacement
+ * EXPLANATION:
+ * IF specifies the number of characters of the sending field upon which the
+ * operation is performed, where IF = 0 specifies 16 characters.
+ * Starting with the next available sending field character, the next IF
+ * characters are individually fetched and the following conditional actions
+ * occur.
+ * − If ES is OFF and the character is zero, then edit insertion table entry 1
+ * is moved to the receiving field in place of the character.
+ * − If ES is OFF and the character is not zero, then the character is moved to
+ * the receiving field and ES is set ON.
+ * − If ES is ON, the character is moved to the receiving field.
+ * FLAGS: (Flags not listed are not affected.)
+ * ES If OFF and any of C(Y) is less than decimal zero, then ON; otherwise,
+ * it is unchanged.
+ */
+
+static int mopMVZB (void)
+{
+ EISstruct * e = & cpu.currentEISinstruction;
+ if (e->mopIF == 0)
+ e->mopIF = 16;
+
+ for(int n = 0 ; n < e->mopIF ; n += 1)
+ {
+ if (e->dstTally == 0)
+ break;
+ if (e->srcTally == 0)
+ return -1;
+
+ word9 c = *e->in;
+ e->srcTally -= 1;
+ e->in += 1;
+
+ //if (!e->mopES && c == 0)
+ // XXX See srcTA comment in MVNE
+ if ((!e->mopES) && isDecimalZero (c))
+ {
+ //If ES is OFF and the character is zero, then edit insertion table
+ //entry 1 is moved to the receiving field in place of the
+ //character.
+ writeToOutputBuffer(&e->out, 9, e->dstSZ, e->editInsertionTable[0]);
+ //} else if (!e->mopES && c != 0)
+ // XXX See srcTA comment in MVNE
+ }
+ if ((! e->mopES) && (! isDecimalZero (c)))
+ {
+ //If ES is OFF and the character is not zero, then the character is
+ //moved to the receiving field and ES is set ON.
+ e->mopZ = false;
+ writeToOutputBuffer(&e->out, e->srcSZ, e->dstSZ, c);
+
+ e->mopES = true;
+ } else if (e->mopES)
+ {
+ //If ES is ON, the character is moved to the receiving field.
+ if (! isDecimalZero (c))
+ e->mopZ = false;
+ writeToOutputBuffer(&e->out, e->srcSZ, e->dstSZ, c);
+ }
+ }
+
+ return 0;
+}
+
+/*!
+ * SES Micro Operation - Set End Suppression
+ * EXPLANATION:
+ * Bit 0 of IF (IF(0)) specifies the setting of the ES switch.
+ * If IF(0) = 0, the ES flag is set OFF. If IF(0) = 1, the ES flag is set ON.
+ * Bit 1 of IF (IF(1)) specifies the setting of the blank-when-zero option.
+ * If IF(1) = 0, no action is taken.
+ * If IF(1) = 1, the BZ flag is set ON.
+ * FLAGS: (Flags not listed are not affected.)
+ * ES set by this micro operation
+ * BZ If bit 1 of C(IF) = 1, then ON; otherwise, it is unchanged.
+ */
+
+static int mopSES (void)
+{
+ EISstruct * e = & cpu.currentEISinstruction;
+ if (e->mopIF & 010)
+ e->mopES = true;
+ else
+ e->mopES = false;
+
+ if (e->mopIF & 04)
+ e->mopBZ = true;
+
+ return 0;
+}
+
+// Table 4-9. Micro Operation Code Assignment Map
+#ifndef QUIET_UNUSED
+static char * mopCodes [040] =
+ {
+ // 0 1 2 3 4 5 6 7
+ /* 00 */ 0, "insm", "enf", "ses", "mvzb", "mvza", "mfls", "mflc",
+ /* 10 */ "insb", "insa", "insn", "insp", "ign", "mvc", "mses", "mors",
+ /* 20 */ "lte", "cht", 0, 0, 0, 0, 0, 0,
+ /* 30 */ 0, 0, 0, 0, 0, 0, 0, 0
+ };
+#endif
+
+
+static MOP_struct mopTab[040] = {
+ {NULL, 0},
+ {"insm", mopINSM },
+ {"enf", mopENF },
+ {"ses", mopSES },
+ {"mvzb", mopMVZB },
+ {"mvza", mopMVZA },
+ {"mfls", mopMFLS },
+ {"mflc", mopMFLC },
+ {"insb", mopINSB },
+ {"insa", mopINSA },
+ {"insn", mopINSN },
+ {"insp", mopINSP },
+ {"ign", mopIGN },
+ {"mvc", mopMVC },
+ {"mses", mopMSES },
+ {"mors", mopMORS },
+ {"lte", mopLTE },
+ {"cht", mopCHT },
+ {NULL, 0},
+ {NULL, 0},
+ {NULL, 0},
+ {NULL, 0},
+ {NULL, 0},
+ {NULL, 0},
+ {NULL, 0},
+ {NULL, 0},
+ {NULL, 0},
+ {NULL, 0},
+ {NULL, 0},
+ {NULL, 0},
+ {NULL, 0},
+ {NULL, 0}
+};
+
+
+/*!
+ * fetch MOP from e->mopAddr/e->mopPos ...
+ */
+
+static MOP_struct* EISgetMop (void)
+{
+ EISstruct * e = & cpu.currentEISinstruction;
+ //static word18 lastAddress; // try to keep memory access' down
+ //static word36 data;
+
+
+ if (e == NULL)
+ //{
+ // p->lastAddress = -1;
+ // p->data = 0;
+ return NULL;
+ //}
+
+#ifdef EIS_PTR2
+ EISaddr *p = &e->ADDR2;
+#else
+ EISaddr *p = e->mopAddress;
+#endif
+
+ //if (p->lastAddress != p->address) // read from memory if different address
+ p->data = EISRead(p); // read data word from memory
+
+ if (e->mopPos > 3) // overflows to next word?
+ { // yep....
+ e->mopPos = 0; // reset to 1st byte
+#ifdef EIS_PTR2
+ cpu.du.Dk_PTR_W[KMOP] = (cpu.du.Dk_PTR_W[KMOP] + 1) & AMASK; // bump source to next address
+ p->data = EISRead(&e->ADDR2); // read it from memory
+#else
+ PNL (cpu.du.Dk_PTR_W[1] = (cpu.du.Dk_PTR_W[1] + 1) & AMASK); // bump source to next address
+ PNL (p->data = EISRead(e->mopAddress)); // read it from memory
+#ifdef EIS_PTR
+ cpu.du.Dk_PTR_W[1] = (cpu.du.Dk_PTR_W[1] + 1) & AMASK; // bump source to next address
+ p->data = EISRead(e->mopAddress); // read it from memory
+#else
+ e->mopAddress->address = (e->mopAddress->address + 1) & AMASK; // bump source to next address
+ p->data = EISRead(e->mopAddress); // read it from memory
+#endif
+#endif
+ }
+
+ word9 mop9 = (word9) get9 (p -> data, e -> mopPos); // get 9-bit mop
+ word5 mop = (mop9 >> 4) & 037;
+ e->mopIF = mop9 & 0xf;
+
+ MOP_struct *m = &mopTab[mop];
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "MOP %s(%o) %o\n", m -> mopName, mop, e->mopIF);
+ e->m = m;
+ if (e->m == NULL || e->m->f == NULL)
+ {
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "getMop(e->m == NULL || e->m->f == NULL): mop:%d IF:%d\n", mop, e->mopIF);
+ return NULL;
+ }
+
+ e->mopPos += 1;
+ e->mopTally -= 1;
+
+ //p->lastAddress = p->address;
+
+ return m;
+}
+
+
+#ifdef EIS_PTR2
+static void mopExecutor (void)
+#else
+static void mopExecutor (int kMop)
+#endif
+ {
+ EISstruct * e = & cpu.currentEISinstruction;
+ PNL (L68_ (DU_CYCLE_FEXOP;))
+#ifdef EIS_PTR2
+ e->mopTally = (int) e->N[KMOP]; // number of micro-ops
+ e->mopPos = (int) e->CN[KMOP]; // starting at char pos CN
+#else
+ e->mopAddress = &e->addr[kMop-1];
+ e->mopTally = (int) e->N[kMop-1]; // number of micro-ops
+ e->mopPos = (int) e->CN[kMop-1]; // starting at char pos CN
+#endif
+
+ word9 *p9 = e->editInsertionTable; // re-initilize edit insertion table
+ char *q = defaultEditInsertionTable;
+ while((*p9++ = (word9) (*q++)))
+ ;
+
+ e->in = e->inBuffer; // reset input buffer pointer
+ e->out = e->outBuffer; // reset output buffer pointer
+
+ e->_faults = 0; // No faults (yet!)
+
+ // execute dstTally micro operations
+ // The micro operation sequence is terminated normally when the receiving
+ // string length becomes exhausted. The micro operation sequence is
+ // terminated abnormally (with an illegal procedure fault) if a move from
+ // an exhausted sending string or the use of an exhausted MOP string is
+ // attempted.
+
+ while (e->dstTally)
+ {
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "mopExecutor srcTally %d dstTally %d mopTally %d\n", e->srcTally, e->dstTally, e->mopTally);
+ MOP_struct *m = EISgetMop();
+ if (! m)
+ {
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "mopExecutor EISgetMop forced break\n");
+ e->_faults |= FAULT_IPR; // XXX ill proc fault
+ break;
+ }
+ int mres = m->f(); // execute mop
+
+ if (e->_faults & FAULT_IPR) // hard IPR raised by a MOP
+ break;
+
+ // RJ78 specifies "if at completion of a move (L1 exhausted)", AL39
+ // doesn't define "completion of a move".
+ // But ISOLTS-845 asserts a case where L1 is NOT exhausted. Therefore I
+ // assume "L2 exhausted" or "L1 or L2 exhausted" is the correct
+ // interpretation. Both these options pass ISOLTS-845.
+ // "L3 exhausted" is also an option but unlikely since that would fire
+ // the BZ check even upon normal termination.
+ // XXX DH03 7-295 suggests that there might be a difference between MVE
+ // and MVNE. It might well be that DPS88/9000 behaves differently than
+ // DPS8.
+
+#if 0
+ // delayed (L2) srcTally test
+ if (e->mopTally == 0)
+ {
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "mopExecutor N2 exhausted\n");
+#else
+ // immediate (L1 or L2) srcTally test
+ // perhaps more adherent to documentation
+ if (e->mopTally == 0 || mres)
+ {
+ sim_debug (DBG_TRACEEXT, & cpu_dev,
+ "mopExecutor N1 or N2 exhausted\n");
+#endif
+ if (e->mopZ && e->mopBZ)
+ {
+ e->out = e->outBuffer; // reset output buffer pointer
+ e->dstTally = (int) e->N3; // number of chars in dst (max 63)
+ while (e->dstTally)
+ {
+ writeToOutputBuffer(&e->out, 9, e->dstSZ, e->editInsertionTable[0]);
+ }
+ }
+ else if (mres || e->dstTally)
+ { // N1 or N2 exhausted and BZ wasn't enabled
+ e->_faults |= FAULT_IPR;
+ } // otherwise normal termination
+ break;
+ }
+ }
+
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "mop faults %o src %d dst %d mop %d\n", e->_faults, e->srcTally, e->dstTally, e->mopTally);
+
+//"The micro-operation sequence is terminated normally when the receiving string
+// length is exhausted. The micro-operation sequence is terminated abnormally (with
+// an IPR fault) if an attempt is made to move from an exhausted sending string or to
+// use an exhausted MOP string.
+
+// ISOLTS 845 is happy with no check at all
+#if 0
+// ISOLTS ps841
+// testing for ipr fault when micro-op tally runs out
+// prior to the sending or receiving field tally.
+// ISOLTS ps830 test-04a
+// mopTally 0 srcTally 32 dstTally 0 is not a fault
+ //if (e->mopTally < e->srcTally || e->mopTally < e->dstTally)
+ if (e->mopTally < e->dstTally)
+ {
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "mop executor IPR fault; mopTally %d srcTally %d dstTally %d\n", e->mopTally, e->srcTally, e->dstTally);
+ e->_faults |= FAULT_IPR; // XXX ill proc fault
+ }
+#endif
+#if 0
+ // dst string not exhausted?
+ if (e->dstTally != 0)
+ {
+ e->_faults |= FAULT_IPR; // XXX ill proc fault
+ }
+#endif
+
+#if 0
+ // mop string not exhausted?
+ if (e->mopTally != 0)
+ {
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "mop executor IPR fault; mopTally %d\n", e->mopTally);
+ e->_faults |= FAULT_IPR; // XXX ill proc fault
+ }
+#endif
+
+#if 0
+ // src string not exhausted?
+ if (e->srcTally != 0)
+ {
+ e->_faults |= FAULT_IPR; // XXX ill proc fault
+ }
+#endif
+
+ if (e -> _faults)
+ doFault (FAULT_IPR, fst_ill_proc, "mopExecutor");
+}
+
+
+void mve (void)
+ {
+ EISstruct * e = & cpu.currentEISinstruction;
+
+ sim_debug(DBG_TRACEEXT, & cpu_dev, "mve\n");
+
+ fault_ipr_subtype_ mod_fault = 0;
+
+#ifndef EIS_SETUP
+ setupOperandDescriptor(1, &mod_fault);
+ setupOperandDescriptor(2, &mod_fault);
+ setupOperandDescriptor(3, &mod_fault);
+#endif
+
+ parseAlphanumericOperandDescriptor(1, 1, false, &mod_fault);
+ parseAlphanumericOperandDescriptor(2, 2, false, &mod_fault);
+ parseAlphanumericOperandDescriptor(3, 3, false, &mod_fault);
+
+#ifdef L68
+ // L68 raises it immediately
+ if (mod_fault)
+ {
+ doFault (FAULT_IPR,
+ (_fault_subtype) {.fault_ipr_subtype=mod_fault},
+ "Illegal modifier");
+ }
+#endif
+
+ // Bits 0, 1, 9, and 10 MBZ
+ // According to RJ78, bit 9 is T, but is not mentioned in the text.
+ if (IWB_IRODD & 0600600000000)
+ doFault (FAULT_IPR, (_fault_subtype) {.fault_ipr_subtype=FR_ILL_OP|mod_fault}, "mve: 0, 1, 9, 10 MBZ");
+
+ // Bit 23 of OP1 MBZ
+ if (!(e->MF[0] & MFkID) && e -> op [0] & 0000000010000)
+ doFault (FAULT_IPR, (_fault_subtype) {.fault_ipr_subtype=FR_ILL_PROC|mod_fault}, "mve op1 23 MBZ");
+
+#if 0
+ // Bits 21-23 of OP1 MBZ
+ if (!(e->MF[1] & MFkID) && e -> op [1] & 0000000070000)
+ doFault (FAULT_IPR, (_fault_subtype) {.fault_ipr_subtype=FR_ILL_PROC|mod_fault}, "mve op2 21-23 MBZ");
+#endif
+ // only bit 23 according to RH03. this was fixed in DPS9000
+ if (!(e->MF[1] & MFkID) && e -> op [1] & 0000000010000)
+ doFault (FAULT_IPR, (_fault_subtype) {.fault_ipr_subtype=FR_ILL_PROC|mod_fault}, "mve op2 23 MBZ");
+
+ // Bit 23 of OP3 MBZ
+ if (!(e->MF[2] & MFkID) && e -> op [2] & 0000000010000)
+ doFault (FAULT_IPR, (_fault_subtype) {.fault_ipr_subtype=FR_ILL_PROC|mod_fault}, "mve op3 23 MBZ");
+
+#ifdef DPS8M
+ // DPS8M raises it delayed
+ if (mod_fault)
+ {
+ doFault (FAULT_IPR,
+ (_fault_subtype) {.fault_ipr_subtype=mod_fault},
+ "Illegal modifier");
+ }
+#endif
+
+ // initialize mop flags. Probably best done elsewhere.
+ e->mopES = false; // End Suppression flag
+ e->mopSN = false; // Sign flag
+ e->mopBZ = false; // Blank-when-zero flag
+ e->mopZ = true; // Zero flag
+
+ e->srcTally = (int) e->N1; // number of chars in src (max 63)
+ e->dstTally = (int) e->N3; // number of chars in dst (max 63)
+
+#ifdef EIS_PTR3
+ e->srcTA = (int) TA1; // type of chars in src
+#else
+ e->srcTA = (int) e->TA1; // type of chars in src
+#endif
+
+ switch (e -> srcTA)
+ {
+ case CTA4:
+ e -> srcSZ = 4;
+ break;
+ case CTA6:
+ e -> srcSZ = 6;
+ break;
+ case CTA9:
+ e -> srcSZ = 9;
+ break;
+ }
+
+#ifdef EIS_PTR3
+ uint dstTA = TA3; // type of chars in dst
+#else
+ uint dstTA = e -> TA3; // type of chars in dst
+#endif
+
+ switch (dstTA)
+ {
+ case CTA4:
+ e -> dstSZ = 4;
+ break;
+ case CTA6:
+ e -> dstSZ = 6;
+ break;
+ case CTA9:
+ e -> dstSZ = 9;
+ break;
+ }
+
+ // 1. load sending string into inputBuffer
+ EISloadInputBufferAlphnumeric (1); // according to MF1
+
+ // 2. Execute micro operation string, starting with first (4-bit) digit.
+ e -> mvne = false;
+
+#ifdef EIS_PTR2
+ mopExecutor ();
+#else
+ mopExecutor (2);
+#endif
+
+ e -> dstTally = (int) e -> N3; // restore dstTally for output
+
+ EISwriteOutputBufferToMemory (3);
+ cleanupOperandDescriptor (1);
+ cleanupOperandDescriptor (2);
+ cleanupOperandDescriptor (3);
+ }
+
+void mvne (void)
+ {
+ EISstruct * e = & cpu.currentEISinstruction;
+
+ fault_ipr_subtype_ mod_fault = 0;
+
+#ifndef EIS_SETUP
+ setupOperandDescriptor (1, &mod_fault);
+ setupOperandDescriptor (2, &mod_fault);
+ setupOperandDescriptor (3, &mod_fault);
+#endif
+
+ parseNumericOperandDescriptor (1, &mod_fault);
+ parseAlphanumericOperandDescriptor (2, 2, false, &mod_fault);
+ parseAlphanumericOperandDescriptor (3, 3, false, &mod_fault);
+
+#ifdef L68
+ // L68 raises it immediately
+ if (mod_fault)
+ {
+ doFault (FAULT_IPR,
+ (_fault_subtype) {.fault_ipr_subtype=mod_fault},
+ "Illegal modifier");
+ }
+#endif
+
+ // Bits 0, 1, 9, and 10 MBZ
+ if (IWB_IRODD & 0600600000000)
+ doFault (FAULT_IPR, (_fault_subtype) {.fault_ipr_subtype=FR_ILL_OP|mod_fault}, "mvne: 0, 1, 9, 10 MBZ");
+
+
+ // Bit 24-29 of OP1 MBZ
+ // Multics has been observed to use 600162017511, cf RJ78
+ //if (!(e->MF[0] & MFkID) && e -> op [0] & 0000000007700)
+ //doFault (FAULT_IPR, (_fault_subtype) {.fault_ipr_subtype=FR_ILL_PROC|mod_fault}, "mvne op1 24-29 MBZ");
+
+#if 0
+ // Bits 21-29 of OP1 MBZ
+ if (!(e->MF[1] & MFkID) && e -> op [1] & 0000000077700)
+ doFault (FAULT_IPR, (_fault_subtype) {.fault_ipr_subtype=FR_ILL_PROC|mod_fault}, "mvne op2 21-29 MBZ");
+#endif
+ // only bits 21-23 according to RJ78, maybe even less on DPS8
+ if (!(e->MF[1] & MFkID) && e -> op [1] & 0000000070000)
+ doFault (FAULT_IPR, (_fault_subtype) {.fault_ipr_subtype=FR_ILL_PROC|mod_fault}, "mvne op2 21-23 MBZ");
+
+#if 0
+ // Bits 23-29 of OP3 MBZ
+ if (!(e->MF[2] & MFkID) && e -> op [2] & 0000000017700)
+ doFault (FAULT_IPR, (_fault_subtype) {.fault_ipr_subtype=FR_ILL_PROC|mod_fault}, "mvne op3 23-29 MBZ");
+#endif
+ // only bit 23 according to RJ78
+ if (!(e->MF[2] & MFkID) && e -> op [2] & 0000000010000)
+ doFault (FAULT_IPR, (_fault_subtype) {.fault_ipr_subtype=FR_ILL_PROC|mod_fault}, "mvne op3 23 MBZ");
+
+#ifdef DPS8M
+ // DPS8M raises it delayed
+ if (mod_fault)
+ {
+ doFault (FAULT_IPR,
+ (_fault_subtype) {.fault_ipr_subtype=mod_fault},
+ "Illegal modifier");
+ }
+#endif
+
+ uint srcTN = e -> TN1; // type of chars in src
+
+ int n1 = 0;
+
+ /*
+ * Here we need to distinguish between 4 type of numbers.
+ *
+ * CSFL - Floating-point, leading sign
+ * CSLS - Scaled fixed-point, leading sign
+ * CSTS - Scaled fixed-point, trailing sign
+ * CSNS - Scaled fixed-point, unsigned
+ */
+
+ // determine precision
+ switch(e->S1)
+ {
+ case CSFL:
+ n1 = (int) e->N1 - 1; // need to account for the - sign
+ if (srcTN == CTN4)
+ n1 -= 2; // 2 4-bit digits exponent
+ else
+ n1 -= 1; // 1 9-bit digit exponent
+ break;
+
+ case CSLS:
+ case CSTS:
+ n1 = (int) e->N1 - 1; // only 1 sign
+ break;
+
+ case CSNS:
+ n1 = (int) e->N1; // no sign
+ break; // no sign wysiwyg
+ }
+
+ if (n1 < 1)
+ doFault (FAULT_IPR, fst_ill_proc, "mvne adjusted n1<1");
+
+ // Putting this check in pAOD breaks Multics boot
+ // From DH03 it seems that DPS8 does not check this explicitly, but L2 exhaust occurs which raises IPR anyway
+ // So we may as well keep it here.
+ if (e->N[1] == 0)
+ doFault (FAULT_IPR, fst_ill_proc, "mvne N2 0");
+
+ // this is a flaw in DPS8/70 which was corrected in DPS88 and later
+ // ISOLTS-841 07h, RH03 p.7-295
+ if (e->N[2] == 0)
+ doFault (FAULT_IPR, fst_ill_proc, "mvne N3 0");
+
+//if ((e -> op [0] & 0000000007700) ||
+// (e -> op [1] & 0000000077700) ||
+// (e -> op [2] & 0000000017700))
+//sim_printf ("%012"PRIo64"\n%012"PRIo64"\n%012"PRIo64"\n%012"PRIo64"\n", cpu.cu.IWB, e->op[0], e->op[1], e-> op[2]);
+//if (e -> op [0] & 0000000007700) sim_printf ("op1\n");
+//if (e -> op [1] & 0000000077700) sim_printf ("op2\n");
+//if (e -> op [2] & 0000000017700) sim_printf ("op3\n");
+//000140 aa 100 004 024 500 mvne (pr),(ic),(pr)
+//000141 aa 6 00162 01 7511 desc9ls pr6|114,9,-3
+//000142 aa 000236 00 0007 desc9a 158,7 000376 = 403040144040
+//000143 aa 6 00134 00 0012 desc9a pr6|92,10 vcpu
+//
+// The desc8ls is sign-extending the -3.
+
+
+
+
+ // initialize mop flags. Probably best done elsewhere.
+ e->mopES = false; // End Suppression flag
+ e->mopSN = false; // Sign flag
+ e->mopBZ = false; // Blank-when-zero flag
+ e->mopZ = true; // Zero flag
+
+ e -> srcTally = (int) e -> N1; // number of chars in src (max 63)
+ e -> dstTally = (int) e -> N3; // number of chars in dst (max 63)
+
+// XXX Temp hack to get MOP to work. Merge TA/TN?
+// The MOP operators look at srcTA to make 9bit/not 9-bit decisions about
+// the contents of inBuffer; parseNumericOperandDescriptor() always puts
+// 4 bit data in inBuffer, so signal the MOPS code of that.
+ e->srcTA = CTA4; // type of chars in src
+
+ switch(srcTN)
+ {
+ case CTN4:
+ //e->srcAddr = e->YChar41;
+ e->srcSZ = 4; // stored as 4-bit decimals
+ break;
+ case CTN9:
+ //e->srcAddr = e->YChar91;
+ e->srcSZ = 4; // 'cause everything is stored as 4-bit decimals
+ break;
+ }
+
+#ifdef EIS_PTR3
+ uint dstTA = TA3; // type of chars in dst
+#else
+ uint dstTA = e->TA3; // type of chars in dst
+#endif
+ switch(dstTA)
+ {
+ case CTA4:
+ //e->dstAddr = e->YChar43;
+ e->dstSZ = 4;
+ break;
+ case CTA6:
+ //e->dstAddr = e->YChar63;
+ e->dstSZ = 6;
+ break;
+ case CTA9:
+ //e->dstAddr = e->YChar93;
+ e->dstSZ = 9;
+ break;
+ }
+
+#ifdef EIS_PTR3
+ sim_debug (DBG_TRACEEXT, & cpu_dev,
+ "mvne N1 %d N2 %d N3 %d TN1 %d CN1 %d TA3 %d CN3 %d\n",
+ e->N1, e->N2, e->N3, e->TN1, e->CN1, TA3, e->CN3);
+#else
+ sim_debug (DBG_TRACEEXT, & cpu_dev,
+ "mvne N1 %d N2 %d N3 %d TN1 %d CN1 %d TA3 %d CN3 %d\n",
+ e->N1, e->N2, e->N3, e->TN1, e->CN1, e->TA3, e->CN3);
+#endif
+
+ // 1. load sending string into inputBuffer
+ EISloadInputBufferNumeric (1); // according to MF1
+
+ // 2. Test sign and, if required, set the SN flag. (Sign flag; initially
+ // set OFF if the sending string has an alphanumeric descriptor or an
+ // unsigned numeric descriptor. If the sending string has a signed numeric
+ // descriptor, the sign is initially read from the sending string from the
+ // digit position defined by the sign and the decimal type field (S); SN is
+ // set OFF if positive, ON if negative. If all digits are zero, the data is
+ // assumed positive and the SN flag is set OFF, even when the sign is
+ // negative.)
+
+ int sum = 0;
+ for(int n = 0 ; n < e -> srcTally ; n ++)
+ sum += e -> inBuffer [n];
+ if ((e -> sign == -1) && sum)
+ e -> mopSN = true;
+
+ // 3. Execute micro operation string, starting with first (4-bit) digit.
+ e -> mvne = true;
+
+#ifdef EIS_PTR2
+ mopExecutor ();
+#else
+ mopExecutor (2);
+#endif
+
+ e -> dstTally = (int) e -> N3; // restore dstTally for output
+
+ EISwriteOutputBufferToMemory (3);
+ cleanupOperandDescriptor (1);
+ cleanupOperandDescriptor (2);
+ cleanupOperandDescriptor (3);
+ }
+
+/*
+ * MVT - Move Alphanumeric with Translation
+ */
+
+void mvt (void)
+ {
+ EISstruct * e = & cpu.currentEISinstruction;
+
+ // For i = 1, 2, ..., minimum (N1,N2)
+ // m = C(Y-charn1)i-1
+ // C(Y-char93)m → C(Y-charn2)i-1
+ // If N1 < N2, then for i = N1+1, N1+2, ..., N2
+ // m = C(FILL)
+ // C(Y-char93)m → C(Y-charn2)i-1
+
+ // Indicators: Truncation. If N1 > N2 then ON; otherwise OFF
+
+ fault_ipr_subtype_ mod_fault = 0;
+
+#ifndef EIS_SETUP
+ setupOperandDescriptor (1, &mod_fault);
+ setupOperandDescriptor (2, &mod_fault);
+ setupOperandDescriptorCache (3);
+#endif
+
+ parseAlphanumericOperandDescriptor (1, 1, false, &mod_fault);
+ parseAlphanumericOperandDescriptor (2, 2, false, &mod_fault);
+ parseArgOperandDescriptor (3, &mod_fault);
+
+#ifdef L68
+ // L68 raises it immediately
+ if (mod_fault)
+ {
+ doFault (FAULT_IPR,
+ (_fault_subtype) {.fault_ipr_subtype=mod_fault},
+ "Illegal modifier");
+ }
+#endif
+
+// ISOLTS 808 test-03b sets bit 0, 1
+// ISOLTS 808 test-03b sets bit 0, 1, 9
+#if 1
+ // Bits 10 MBZ
+ if (IWB_IRODD & 0000200000000)
+ {
+ //sim_printf ("mvt %012"PRIo64"\n", IWB_IRODD);
+ doFault (FAULT_IPR, (_fault_subtype) {.fault_ipr_subtype=FR_ILL_OP|mod_fault}, "mvt 10 MBZ");
+ }
+#else
+ // Bits 0,1,9,10 MBZ
+ if (IWB_IRODD & 0600600000000)
+ {
+ //sim_printf ("mvt %012"PRIo64"\n", IWB_IRODD);
+ doFault (FAULT_IPR, (_fault_subtype) {.fault_ipr_subtype=FR_ILL_OP|mod_fault}, "mvt 0,1,9,10 MBZ");
+ }
+#endif
+
+ // Bit 23 of OP1 MBZ
+ if (!(e->MF[0] & MFkID) && e -> op [0] & 0000000010000)
+ doFault (FAULT_IPR, (_fault_subtype) {.fault_ipr_subtype=FR_ILL_PROC|mod_fault}, "mvt op1 23 MBZ");
+
+// This breaks eis_tester mvt 110
+#if 0
+ // Bits 18 of OP2 MBZ
+ if (!(e->MF[1] & MFkID) && e -> op [1] & 0000000400000)
+ doFault (FAULT_IPR, (_fault_subtype) {.fault_ipr_subtype=FR_ILL_PROC|mod_fault}, "mvt op2 18 MBZ");
+#endif
+
+ // Bits 18-28 of OP3 MBZ
+ if (!(e->MF[2] & MFkID) && e -> op [2] & 0000000777600)
+ doFault (FAULT_IPR, (_fault_subtype) {.fault_ipr_subtype=FR_ILL_PROC|mod_fault}, "mvt op3 18-28 MBZ");
+
+#ifdef DPS8M
+ // DPS8M raises it delayed
+ if (mod_fault)
+ {
+ doFault (FAULT_IPR,
+ (_fault_subtype) {.fault_ipr_subtype=mod_fault},
+ "Illegal modifier");
+ }
+#endif
+
+#ifdef EIS_PTR3
+ e->srcTA = (int) TA1;
+ uint dstTA = TA2;
+
+ switch (TA1)
+#else
+ e->srcTA = (int) e->TA1;
+ uint dstTA = e->TA2;
+
+ switch (e -> TA1)
+#endif
+ {
+ case CTA4:
+ e -> srcSZ = 4;
+ break;
+ case CTA6:
+ e -> srcSZ = 6;
+ break;
+ case CTA9:
+ e -> srcSZ = 9;
+ break;
+ }
+
+#ifdef EIS_PTR3
+ switch (TA2)
+#else
+ switch (e -> TA2)
+#endif
+ {
+ case CTA4:
+ e -> dstSZ = 4;
+ break;
+ case CTA6:
+ e -> dstSZ = 6;
+ break;
+ case CTA9:
+ e -> dstSZ = 9;
+ break;
+ }
+
+ // Prepage Check in a Multiword Instruction
+ // The MVT, TCT, TCTR, and CMPCT instruction have a prepage check. The
+ // size of the translate table is determined by the TA1 data type as shown
+ // in the table below. Before the instruction is executed, a check is made
+ // for allocation in memory for the page for the translate table. If the
+ // page is not in memory, a Missing Page fault occurs before execution of
+ // the instruction. (Bull RJ78 p.7-75)
+
+ // TA1 TRANSLATE TABLE SIZE
+ // 4-BIT CHARACTER 4 WORDS
+ // 6-BIT CHARACTER 16 WORDS
+ // 9-BIT CHARACTER 128 WORDS
+
+ uint xlatSize = 0; // size of xlation table in words .....
+#ifdef EIS_PTR3
+ switch(TA1)
+#else
+ switch(e->TA1)
+#endif
+ {
+ case CTA4:
+ xlatSize = 4;
+ break;
+ case CTA6:
+ xlatSize = 16;
+ break;
+ case CTA9:
+ xlatSize = 128;
+ break;
+ }
+
+#if 0
+ word36 xlatTbl[128];
+ memset(xlatTbl, 0, sizeof(xlatTbl)); // 0 it out just in case
+
+ // XXX here is where we probably need to to the prepage thang...
+ EISReadN(&e->ADDR3, xlatSize, xlatTbl);
+#endif
+
+ // ISOLTS 878 01c - op1 and xlate table are prepaged, in that order
+ // prepage op1
+ int lastpageidx = ((int)e->N1 + (int)e->CN1 -1) / e->srcSZ;
+ if (lastpageidx>0)
+ EISReadIdx(&e->ADDR1, (uint)lastpageidx);
+ // prepage xlate table
+ EISReadIdx(&e->ADDR3, 0);
+ EISReadIdx(&e->ADDR3, xlatSize-1);
+
+ word1 T = getbits36_1 (cpu.cu.IWB, 9);
+
+ word9 fill = getbits36_9 (cpu.cu.IWB, 0);
+ word9 fillT = fill; // possibly truncated fill pattern
+ // play with fill if we need to use it
+ switch(e->srcSZ)
+ {
+ case 4:
+ fillT = fill & 017; // truncate upper 5-bits
+ break;
+ case 6:
+ fillT = fill & 077; // truncate upper 3-bits
+ break;
+ }
+
+ sim_debug (DBG_TRACEEXT, & cpu_dev,
+ "%s srcCN:%d dstCN:%d srcSZ:%d dstSZ:%d T:%d fill:%03o/%03o N1:%d N2:%d\n",
+ __func__, e -> CN1, e -> CN2, e -> srcSZ, e -> dstSZ, T,
+ fill, fillT, e -> N1, e -> N2);
+
+ PNL (L68_ (if (max (e->N1, e->N2) < 128)
+ DU_CYCLE_FLEN_128;))
+
+ for ( ; cpu.du.CHTALLY < min(e->N1, e->N2); cpu.du.CHTALLY ++)
+ {
+ word9 c = EISget469(1, cpu.du.CHTALLY); // get src char
+ int cidx = 0;
+
+#ifdef EIS_PTR3
+ if (TA1 == TA2)
+#else
+ if (e->TA1 == e->TA2)
+#endif
+ EISput469(2, cpu.du.CHTALLY, xlate (&e->ADDR3, dstTA, c));
+ else
+ {
+ // If data types are dissimilar (TA1 ≠ TA2), each character is high-order truncated or zero filled, as appropriate, as it is moved. No character conversion takes place.
+ cidx = c;
+
+ word9 cout = xlate(&e->ADDR3, dstTA, (uint) cidx);
+
+// switch(e->dstSZ)
+// {
+// case 4:
+// cout &= 017; // truncate upper 5-bits
+// break;
+// case 6:
+// cout &= 077; // truncate upper 3-bits
+// break;
+// }
+
+ switch (e->srcSZ)
+ {
+ case 6:
+ switch(e->dstSZ)
+ {
+ case 4:
+ cout &= 017; // truncate upper 2-bits
+ break;
+ case 9:
+ break; // should already be 0-filled
+ }
+ break;
+ case 9:
+ switch(e->dstSZ)
+ {
+ case 4:
+ cout &= 017; // truncate upper 5-bits
+ break;
+ case 6:
+ cout &= 077; // truncate upper 3-bits
+ break;
+ }
+ break;
+ }
+
+ EISput469 (2, cpu.du.CHTALLY, cout);
+ }
+ }
+
+ // If N1 < N2, then for i = N1+1, N1+2, ..., N2
+ // m = C(FILL)
+ // C(Y-char93)m → C(Y-charn2)N2-i
+
+ if (e->N1 < e->N2)
+ {
+ word9 cfill = xlate(&e->ADDR3, dstTA, fillT);
+ switch (e->srcSZ)
+ {
+ case 6:
+ switch(e->dstSZ)
+ {
+ case 4:
+ cfill &= 017; // truncate upper 2-bits
+ break;
+ case 9:
+ break; // should already be 0-filled
+ }
+ break;
+ case 9:
+ switch(e->dstSZ)
+ {
+ case 4:
+ cfill &= 017; // truncate upper 5-bits
+ break;
+ case 6:
+ cfill &= 077; // truncate upper 3-bits
+ break;
+ }
+ break;
+ }
+
+ for( ; cpu.du.CHTALLY < e->N2 ; cpu.du.CHTALLY ++)
+ EISput469 (2, cpu.du.CHTALLY, cfill);
+ }
+
+ cleanupOperandDescriptor (1);
+ cleanupOperandDescriptor (2);
+ cleanupOperandDescriptor (3);
+
+ if (e->N1 > e->N2)
+ {
+ SET_I_TRUNC;
+ if (T && ! TST_I_OMASK)
+ doFault(FAULT_OFL, fst_zero, "mvt truncation fault");
+ }
+ else
+ CLR_I_TRUNC;
+ }
+
+
+/*
+ * cmpn - Compare Numeric
+ */
+
+void cmpn (void)
+{
+ EISstruct * e = & cpu.currentEISinstruction;
+
+ // C(Y-charn1) :: C(Y-charn2) as numeric values
+
+ // Zero If C(Y-charn1) = C(Y-charn2), then ON; otherwise OFF
+ // Negative If C(Y-charn1) > C(Y-charn2), then ON; otherwise OFF
+ // Carry If | C(Y-charn1) | > | C(Y-charn2) | , then OFF, otherwise ON
+
+ fault_ipr_subtype_ mod_fault = 0;
+
+#ifndef EIS_SETUP
+ setupOperandDescriptor(1, &mod_fault);
+ setupOperandDescriptor(2, &mod_fault);
+#endif
+
+ parseNumericOperandDescriptor(1, &mod_fault);
+ parseNumericOperandDescriptor(2, &mod_fault);
+
+#ifdef L68
+ // L68 raises it immediately
+ if (mod_fault)
+ {
+ doFault (FAULT_IPR,
+ (_fault_subtype) {.fault_ipr_subtype=mod_fault},
+ "Illegal modifier");
+ }
+#endif
+
+ // Bits 0-10 MBZ
+ if (IWB_IRODD & 0777600000000)
+ doFault (FAULT_IPR, (_fault_subtype) {.fault_ipr_subtype=FR_ILL_OP|mod_fault}, "cmpn 0-10 MBZ");
+
+#ifdef DPS8M
+ // DPS8M raises it delayed
+ if (mod_fault)
+ {
+ doFault (FAULT_IPR,
+ (_fault_subtype) {.fault_ipr_subtype=mod_fault},
+ "Illegal modifier");
+ }
+#endif
+
+ uint srcTN = e->TN1; // type of chars in src
+
+ int n1 = 0, n2 = 0, sc1 = 0, sc2 = 0;
+
+ /*
+ * Here we need to distinguish between 4 type of numbers.
+ *
+ * CSFL - Floating-point, leading sign
+ * CSLS - Scaled fixed-point, leading sign
+ * CSTS - Scaled fixed-point, trailing sign
+ * CSNS - Scaled fixed-point, unsigned
+ */
+
+ // determine precision
+ switch(e->S1)
+ {
+ case CSFL:
+ n1 = (int) e->N1 - 1; // need to account for the - sign
+ if (srcTN == CTN4)
+ n1 -= 2; // 2 4-bit digits exponent
+ else
+ n1 -= 1; // 1 9-bit digit exponent
+ sc1 = 0; // no scaling factor
+ break;
+
+ case CSLS:
+ case CSTS:
+ n1 = (int) e->N1 - 1; // only 1 sign
+ sc1 = -e->SF1;
+ break;
+
+ case CSNS:
+ n1 = (int) e->N1; // no sign
+ sc1 = -e->SF1;
+ break; // no sign wysiwyg
+ }
+
+ if (n1 < 1)
+ doFault (FAULT_IPR, fst_ill_proc, "cmpn adjusted n1<1");
+
+ switch(e->S2)
+ {
+ case CSFL:
+ n2 = (int) e->N2 - 1; // need to account for the sign
+ if (e->TN2 == CTN4)
+ n2 -= 2; // 2 4-bit digit exponent
+ else
+ n2 -= 1; // 1 9-bit digit exponent
+ sc2 = 0; // no scaling factor
+ break;
+
+ case CSLS:
+ case CSTS:
+ n2 = (int) e->N2 - 1; // 1 sign
+ sc2 = -e->SF2;
+ break;
+
+ case CSNS:
+ n2 = (int) e->N2; // no sign
+ sc2 = -e->SF2;
+ break; // no sign wysiwyg
+ }
+
+ if (n2 < 1)
+ doFault (FAULT_IPR, fst_ill_proc, "cmpn adjusted n2<1");
+
+
+ decContext set;
+ //decContextDefault(&set, DEC_INIT_BASE); // initialize
+ decContextDefaultDPS8(&set);
+
+ set.traps=0;
+
+ decNumber _1, _2, _3;
+
+ EISloadInputBufferNumeric (1); // according to MF1
+
+ decNumber *op1 = decBCD9ToNumber(e->inBuffer, n1, sc1, &_1);
+ if (e->sign == -1)
+ op1->bits |= DECNEG;
+ if (e->S1 == CSFL)
+ op1->exponent = e->exponent;
+
+ EISloadInputBufferNumeric (2); // according to MF2
+
+ decNumber *op2 = decBCD9ToNumber(e->inBuffer, n2, sc2, &_2);
+ if (e->sign == -1)
+ op2->bits |= DECNEG;
+ if (e->S2 == CSFL)
+ op2->exponent = e->exponent;
+
+ // signed-compare
+ decNumber *cmp = decNumberCompare(&_3, op1, op2, &set); // compare signed op1 :: op2
+ int cSigned = decNumberToInt32(cmp, &set);
+
+ // take absolute value of operands
+ op1 = decNumberAbs(op1, op1, &set);
+ op2 = decNumberAbs(op2, op2, &set);
+
+ // magnitude-compare
+ decNumber *mcmp = decNumberCompare(&_3, op1, op2, &set); // compare signed op1 :: op2
+ int cMag = decNumberToInt32(mcmp, &set);
+
+ // Zero If C(Y-charn1) = C(Y-charn2), then ON; otherwise OFF
+ // Negative If C(Y-charn1) > C(Y-charn2), then ON; otherwise OFF
+ // Carry If | C(Y-charn1) | > | C(Y-charn2) | , then OFF, otherwise ON
+
+ SC_I_ZERO (cSigned == 0);
+ SC_I_NEG (cSigned == 1);
+ SC_I_CARRY (cMag != 1);
+
+ cleanupOperandDescriptor (1);
+ cleanupOperandDescriptor (2);
+
+}
+
+/*
+ * mvn - move numeric (initial version was deleted by house gnomes)
+ */
+
+/*
+ * write 4-bit chars to memory @ pos ...
+ */
+
+static void EISwrite4(EISaddr *p, int *pos, word4 char4)
+{
+ word36 w;
+ if (*pos > 7) // out-of-range?
+ {
+ *pos = 0; // reset to 1st byte
+#ifdef EIS_PTR
+ long eisaddr_idx = EISADDR_IDX (p);
+if (eisaddr_idx < 0 || eisaddr_idx > 2) { sim_warn ("IDX1"); return }
+ cpu.du.Dk_PTR_W[eisaddr_idx] = (cpu.du.Dk_PTR_W[eisaddr_idx] + 1) & AMASK; // bump source to next address
+#else
+ p->address = (p->address + 1) & AMASK; // goto next dstAddr in memory
+#endif
+ }
+
+ w = EISRead(p); // read dst memory into w
+
+// AL39, Figure 2-3
+ switch (*pos)
+ {
+ case 0:
+ //w = bitfieldInsert36(w, char4, 31, 5);
+ w = setbits36_4 (w, 1, char4);
+ break;
+ case 1:
+ //w = bitfieldInsert36(w, char4, 27, 4);
+ w = setbits36_4 (w, 5, char4);
+ break;
+ case 2:
+ //w = bitfieldInsert36(w, char4, 22, 5);
+ w = setbits36_4 (w, 10, char4);
+ break;
+ case 3:
+ //w = bitfieldInsert36(w, char4, 18, 4);
+ w = setbits36_4 (w, 14, char4);
+ break;
+ case 4:
+ //w = bitfieldInsert36(w, char4, 13, 5);
+ w = setbits36_4 (w, 19, char4);
+ break;
+ case 5:
+ //w = bitfieldInsert36(w, char4, 9, 4);
+ w = setbits36_4 (w, 23, char4);
+ break;
+ case 6:
+ //w = bitfieldInsert36(w, char4, 4, 5);
+ w = setbits36_4 (w, 28, char4);
+ break;
+ case 7:
+ //w = bitfieldInsert36(w, char4, 0, 4);
+ w = setbits36_4 (w, 32, char4);
+ break;
+ }
+
+
+ EISWriteIdx(p, 0, w, true); // XXX this is the inefficient part!
+
+ *pos += 1; // to next char.
+}
+
+
+/*
+ * write 9-bit bytes to memory @ pos ...
+ */
+
+static void EISwrite9(EISaddr *p, int *pos, word9 char9)
+{
+ word36 w;
+ if (*pos > 3) // out-of-range?
+ {
+ *pos = 0; // reset to 1st byte
+#ifdef EIS_PTR
+ long eisaddr_idx = EISADDR_IDX (p);
+if (eisaddr_idx < 0 || eisaddr_idx > 2) { sim_warn ("IDX1"); return }
+ cpu.du.Dk_PTR_W[eisaddr_idx] = (cpu.du.Dk_PTR_W[eisaddr_idx] + 1) & AMASK; // bump source to next address
+#else
+ p->address = (p->address + 1) & AMASK; // goto next dstAddr in memory
+#endif
+ }
+
+ w = EISRead(p); // read dst memory into w
+
+// AL39, Figure 2-5
+ switch (*pos)
+ {
+ case 0:
+ //w = bitfieldInsert36(w, char9, 27, 9);
+ w = setbits36_9 (w, 0, char9);
+ break;
+ case 1:
+ //w = bitfieldInsert36(w, char9, 18, 9);
+ w = setbits36_9 (w, 9, char9);
+ break;
+ case 2:
+ //w = bitfieldInsert36(w, char9, 9, 9);
+ w = setbits36_9 (w, 18, char9);
+ break;
+ case 3:
+ //w = bitfieldInsert36(w, char9, 0, 9);
+ w = setbits36_9 (w, 27, char9);
+ break;
+ }
+
+ EISWriteIdx (p, 0, w, true); // XXX this is the inefficient part!
+
+ *pos += 1; // to next byte.
+}
+
+/*
+ * write a 4-, or 9-bit numeric char to dstAddr ....
+ */
+
+static void EISwrite49(EISaddr *p, int *pos, int tn, word9 c49)
+{
+ switch(tn)
+ {
+ case CTN4:
+ EISwrite4(p, pos, (word4) c49);
+ return;
+ case CTN9:
+ EISwrite9(p, pos, c49);
+ return;
+ }
+}
+
+
+void mvn (void)
+{
+ /*
+ * EXPLANATION:
+ * Starting at location YC1, the decimal number of data type TN1 and sign
+ * and decimal type S1 is moved, properly scaled, to the decimal number of
+ * data type TN2 and sign and decimal type S2 that starts at location YC2.
+ * If S2 indicates a fixed-point format, the results are stored as L2
+ * digits using scale factor SF2, and thereby may cause
+ * most-significant-digit overflow and/or least- significant-digit
+ * truncation.
+ * If P = 1, positive signed 4-bit results are stored using octal 13 as the
+ * plus sign. Rounding is legal for both fixed-point and floating-point
+ * formats. If P = 0, positive signed 4-bit results are stored using octal
+ * 14 as the plus sign.
+ * Provided that string 1 and string 2 are not overlapped, the contents of
+ * the decimal number that starts in location YC1 remain unchanged.
+ */
+
+ EISstruct * e = & cpu.currentEISinstruction;
+
+ fault_ipr_subtype_ mod_fault = 0;
+
+#ifndef EIS_SETUP
+ setupOperandDescriptor(1, &mod_fault);
+ setupOperandDescriptor(2, &mod_fault);
+#endif
+
+ parseNumericOperandDescriptor(1, &mod_fault);
+ parseNumericOperandDescriptor(2, &mod_fault);
+
+#ifdef L68
+ // L68 raises it immediately
+ if (mod_fault)
+ {
+ doFault (FAULT_IPR,
+ (_fault_subtype) {.fault_ipr_subtype=mod_fault},
+ "Illegal modifier");
+ }
+#endif
+
+ // Bits 2-8 MBZ
+ if (IWB_IRODD & 0377000000000)
+ doFault (FAULT_IPR,
+ (_fault_subtype) {.fault_ipr_subtype=FR_ILL_OP|mod_fault},
+ "mvn 2-8 MBZ");
+
+#ifdef DPS8M
+ // DPS8M raises it delayed
+ if (mod_fault)
+ {
+ doFault (FAULT_IPR,
+ (_fault_subtype) {.fault_ipr_subtype=mod_fault},
+ "Illegal modifier");
+ }
+#endif
+
+ e->P = getbits36_1 (cpu.cu.IWB, 0) != 0; // 4-bit data sign character
+ // control
+ word1 T = getbits36_1 (cpu.cu.IWB, 9);
+ bool R = getbits36_1 (cpu.cu.IWB, 10) != 0; // rounding bit
+ PNL (L68_ (if (R)
+ DU_CYCLE_FRND;))
+
+ uint srcTN = e->TN1; // type of chars in src
+
+ uint dstTN = e->TN2; // type of chars in dst
+ uint dstCN = e->CN2; // starting at char pos CN
+
+ sim_debug (DBG_CAC, & cpu_dev,
+ "mvn(1): TN1 %d CN1 %d N1 %d TN2 %d CN2 %d N2 %d\n",
+ e->TN1, e->CN1, e->N1, e->TN2, e->CN2, e->N2);
+ sim_debug (DBG_CAC, & cpu_dev,
+ "mvn(2): SF1 %d SF2 %d\n",
+ e->SF1, e->SF2);
+ sim_debug (DBG_CAC, & cpu_dev,
+ "mvn(3): OP1 %012"PRIo64" OP2 %012"PRIo64"\n",
+ e->OP1, e->OP2);
+
+ int n1 = 0, n2 = 0, sc1 = 0;
+
+ /*
+ * Here we need to distinguish between 4 type of numbers.
+ *
+ * CSFL - Floating-point, leading sign
+ * CSLS - Scaled fixed-point, leading sign
+ * CSTS - Scaled fixed-point, trailing sign
+ * CSNS - Scaled fixed-point, unsigned
+ */
+
+ // determine precision
+ switch(e->S1)
+ {
+ case CSFL:
+ n1 = (int) e->N1 - 1; // need to account for the - sign
+ if (srcTN == CTN4)
+ n1 -= 2; // 2 4-bit digits exponent
+ else
+ n1 -= 1; // 1 9-bit digit exponent
+ sc1 = 0; // no scaling factor
+ break;
+
+ case CSLS:
+ case CSTS:
+ n1 = (int) e->N1 - 1; // only 1 sign
+ sc1 = -e->SF1;
+ break;
+
+ case CSNS:
+ n1 = (int) e->N1; // no sign
+ sc1 = -e->SF1;
+ break; // no sign wysiwyg
+ }
+
+ sim_debug (DBG_CAC, & cpu_dev, "n1 %d sc1 %d\n", n1, sc1);
+
+ // RJ78: An Illegal Procedure fault occurs if:
+ // The values for the number of characters (N1 or N2) of the data
+ // descriptors are not large enough to hold the number of characters
+ // required for the specified sign and/or exponent, plus at least one
+ // digit.
+
+ if (n1 < 1)
+ doFault (FAULT_IPR, fst_ill_proc, "mvn adjusted n1<1");
+
+ switch(e->S2)
+ {
+ case CSFL:
+ n2 = (int) e->N2 - 1; // need to account for the sign
+ if (dstTN == CTN4)
+ n2 -= 2; // 2 4-bit digit exponent
+ else
+ n2 -= 1; // 1 9-bit digit exponent
+ break;
+
+ case CSLS:
+ case CSTS:
+ n2 = (int) e->N2 - 1; // 1 sign
+ break;
+
+ case CSNS:
+ n2 = (int) e->N2; // no sign
+ break; // no sign wysiwyg
+ }
+
+ sim_debug (DBG_CAC, & cpu_dev, "n2 %d\n", n2);
+
+ if (n2 < 1)
+ doFault (FAULT_IPR, fst_ill_proc, "mvn adjusted n2<1");
+
+ decContext set;
+ decContextDefaultDPS8(&set);
+ set.traps=0;
+
+ decNumber _1;
+
+ EISloadInputBufferNumeric (1); // according to MF1
+
+ decNumber *op1 = decBCD9ToNumber (e->inBuffer, n1, sc1, &_1);
+
+ if (e->sign == -1)
+ op1->bits |= DECNEG;
+ if (e->S1 == CSFL)
+ op1->exponent = e->exponent;
+ if (decNumberIsZero (op1))
+ op1->exponent = 127;
+
+ if_sim_debug (DBG_CAC, & cpu_dev)
+ {
+ PRINTDEC ("mvn input (op1)", op1);
+ }
+
+ bool Ovr = false, EOvr = false, Trunc = false;
+
+
+ uint8_t out [256];
+ char * res = formatDecimal (out, & set, op1, n2, (int) e->S2, e->SF2, R,
+ & Ovr, & Trunc);
+
+ sim_debug (DBG_CAC, & cpu_dev, "mvn res: '%s'\n", res);
+
+ // now write to memory in proper format.....
+
+ //word18 dstAddr = e->dstAddr;
+ int pos = (int) dstCN;
+
+ // 1st, take care of any leading sign .......
+ switch(e->S2)
+ {
+ case CSFL: // floating-point, leading sign.
+ case CSLS: // fixed-point, leading sign
+ switch(dstTN)
+ {
+ case CTN4:
+ // If TN2 and S2 specify a 4-bit signed number and P = 1, then the 13(8) plus
+ // sign character is placed appropriately if the result of the operation is
+ // positive.
+ if (e->P)
+ // special +
+ EISwrite49 (& e->ADDR2, & pos, (int) dstTN,
+ (decNumberIsNegative (op1) &&
+ ! decNumberIsZero(op1)) ? 015 : 013);
+ else
+ // default +
+ EISwrite49 (& e->ADDR2, & pos, (int) dstTN,
+ (decNumberIsNegative (op1) &&
+ ! decNumberIsZero (op1)) ? 015 : 014);
+ break;
+ case CTN9:
+ EISwrite49 (& e->ADDR2, & pos, (int) dstTN,
+ (decNumberIsNegative (op1) &&
+ ! decNumberIsZero (op1)) ? '-' : '+');
+ break;
+ }
+ break;
+
+ case CSTS: // nuttin' to do here .....
+ case CSNS:
+ break; // no sign wysiwyg
+ }
+
+ // 2nd, write the digits .....
+ for (int i = 0 ; i < n2 ; i ++)
+ switch (dstTN)
+ {
+ case CTN4:
+ EISwrite49 (& e->ADDR2, & pos, (int) dstTN,
+ (word9) (res[i] - '0'));
+ break;
+ case CTN9:
+ EISwrite49 (& e->ADDR2, & pos, (int) dstTN, (word9) res[i]);
+ break;
+ }
+
+ // 3rd, take care of any trailing sign or exponent ...
+ switch(e->S2)
+ {
+ case CSTS: // write trailing sign ....
+ switch(dstTN)
+ {
+ case CTN4:
+// If TN2 and S2 specify a 4-bit signed number and P = 1, then the 13(8) plus
+// sign character is placed appropriately if the result of the operation is
+// positive.
+ if (e->P)
+ // special +
+ EISwrite49 (& e->ADDR2, & pos, (int) dstTN,
+ (decNumberIsNegative (op1) &&
+ ! decNumberIsZero(op1)) ? 015 : 013);
+ else
+ // default +
+ EISwrite49 (& e->ADDR2, & pos, (int) dstTN,
+ (decNumberIsNegative (op1) &&
+ ! decNumberIsZero (op1)) ? 015 : 014);
+ break;
+
+ case CTN9:
+ EISwrite49 (& e->ADDR2, & pos, (int) dstTN,
+ (decNumberIsNegative (op1) &&
+ ! decNumberIsZero(op1)) ? '-' : '+');
+ break;
+ }
+ break;
+
+ case CSFL: // floating-point, leading sign.
+ // write the exponent
+ switch(dstTN)
+ {
+ case CTN4:
+ EISwrite49 (& e->ADDR2, & pos, (int) dstTN,
+ (op1->exponent >> 4) & 0xf); // upper 4-bits
+ EISwrite49 (& e->ADDR2, & pos, (int) dstTN,
+ op1->exponent & 0xf); // lower 4-bits
+ break;
+ case CTN9:
+ EISwrite49 (& e->ADDR2, & pos, (int) dstTN,
+ op1->exponent & 0xff); // write 8-bit exponent
+ break;
+ }
+ break;
+
+ case CSLS: // fixed point, leading sign - already done
+ case CSNS: // fixed point, unsigned - nuttin' needed to do
+ break;
+ }
+
+ // set flags, etc ...
+ if (e->S2 == CSFL)
+ {
+ if (op1->exponent > 127)
+ {
+ SET_I_EOFL;
+ EOvr = true;
+ }
+ if (op1->exponent < -128)
+ {
+ SET_I_EUFL;
+ EOvr = true;
+ }
+ }
+
+sim_debug (DBG_CAC, & cpu_dev, "is neg %o\n", decNumberIsNegative(op1));
+sim_debug (DBG_CAC, & cpu_dev, "is zero %o\n", decNumberIsZero(op1));
+sim_debug (DBG_CAC, & cpu_dev, "R %o\n", R);
+sim_debug (DBG_CAC, & cpu_dev, "Trunc %o\n", Trunc);
+sim_debug (DBG_CAC, & cpu_dev, "TRUNC %o\n", TST_I_TRUNC);
+sim_debug (DBG_CAC, & cpu_dev, "OMASK %o\n", TST_I_OMASK);
+sim_debug (DBG_CAC, & cpu_dev, "tstOVFfault %o\n", tstOVFfault ());
+sim_debug (DBG_CAC, & cpu_dev, "T %o\n", T);
+sim_debug (DBG_CAC, & cpu_dev, "EOvr %o\n", EOvr);
+sim_debug (DBG_CAC, & cpu_dev, "Ovr %o\n", Ovr);
+ // set negative indicator if op3 < 0
+ SC_I_NEG (decNumberIsNegative(op1) && !decNumberIsZero(op1));
+
+ // set zero indicator if op3 == 0
+ SC_I_ZERO (decNumberIsZero(op1));
+
+ // If the truncation condition exists without rounding, then ON;
+ // otherwise OFF
+ SC_I_TRUNC (!R && Trunc);
+
+ cleanupOperandDescriptor (1);
+ cleanupOperandDescriptor (2);
+
+ if (TST_I_TRUNC && T && tstOVFfault ())
+ doFault (FAULT_OFL, fst_zero, "mvn truncation(overflow) fault");
+ if (EOvr && tstOVFfault ())
+ doFault (FAULT_OFL, fst_zero, "mvn over/underflow fault");
+ if (Ovr)
+ {
+ SET_I_OFLOW;
+ if (tstOVFfault ())
+ doFault (FAULT_OFL, fst_zero, "mvn overflow fault");
+ }
+}
+
+
+void csl (void)
+ {
+ EISstruct * e = & cpu.currentEISinstruction;
+
+ // For i = bits 1, 2, ..., minimum (N1,N2)
+ // m = C(Y-bit1)i-1 || C(Y-bit2)i-1 (a 2-bit number)
+ // C(BOLR)m → C(Y-bit2)i-1
+ // If N1 < N2, then for i = N1+l, N1+2, ..., N2
+ // m = C(F) || C(Y-bit2)i-1 (a 2-bit number)
+ // C(BOLR)m → C(Y-bit2)i-1
+ //
+ // INDICATORS: (Indicators not listed are not affected)
+ // Zero If C(Y-bit2) = 00...0, then ON; otherwise OFF
+ // Truncation If N1 > N2, then ON; otherwise OFF
+ //
+ // NOTES: If N1 > N2, the low order (N1-N2) bits of C(Y-bit1) are not
+ // processed and the truncation indicator is set ON.
+ //
+ // If T = 1 and the truncation indicator is set ON by execution of the
+ // instruction, then a truncation (overflow) fault occurs.
+ //
+ // BOLR
+ // If first operand and second operand then result
+ // bit is: bit is: is from bit:
+ // 0 0 5
+ // 0 1 6
+ // 1 0 7
+ // 1 1 8
+ //
+ // The Boolean operations most commonly used are
+ // BOLR Field Bits
+ // Operation 5 6 7 8
+ //
+ // MOVE 0 0 1 1
+ // AND 0 0 0 1
+ // OR 0 1 1 1
+ // NAND 1 1 1 0
+ // EXCLUSIVE OR 0 1 1 0
+ // Clear 0 0 0 0
+ // Invert 1 1 0 0
+ //
+
+// 0 0 0 0 Clear
+// 0 0 0 1 a AND b
+// 0 0 1 0 a AND !b
+// 0 0 1 1 a
+// 0 1 0 0 !a AND b
+// 0 1 0 1 b
+// 0 1 1 0 a XOR b
+// 0 1 1 1 a OR b
+// 1 0 0 0 !a AND !b !(a OR b)
+// 1 0 0 1 a == b !(a XOR b)
+// 1 0 1 0 !b
+// 1 0 1 1 !b OR A
+// 1 1 0 0 !a
+// 1 1 0 1 !b AND a
+// 1 1 1 0 a NAND b
+// 1 1 1 1 Set
+
+ fault_ipr_subtype_ mod_fault = 0;
+
+#ifndef EIS_SETUP
+ setupOperandDescriptor (1, & mod_fault);
+ setupOperandDescriptor (2, & mod_fault);
+#endif
+
+ parseBitstringOperandDescriptor (1, & mod_fault);
+ parseBitstringOperandDescriptor (2, & mod_fault);
+
+#ifdef L68
+ // L68 raises it immediately
+ if (mod_fault)
+ doFault (FAULT_IPR,
+ (_fault_subtype) {.fault_ipr_subtype=mod_fault},
+ "Illegal modifier");
+#endif
+
+ // Bits 1-4 and 10 MBZ
+ if (IWB_IRODD & 0360200000000)
+ doFault (FAULT_IPR, (_fault_subtype) {.fault_ipr_subtype=FR_ILL_OP|mod_fault}, "csl 1-4,10 MBZ");
+
+#ifdef DPS8M
+ // DPS8M raises it delayed
+ if (mod_fault)
+ doFault (FAULT_IPR,
+ (_fault_subtype) {.fault_ipr_subtype=mod_fault},
+ "Illegal modifier");
+#endif
+
+ e->ADDR1.cPos = (int) e->C1;
+ e->ADDR2.cPos = (int) e->C2;
+
+ e->ADDR1.bPos = (int) e->B1;
+ e->ADDR2.bPos = (int) e->B2;
+
+ bool F = getbits36_1 (cpu.cu.IWB, 0) != 0; // fill bit
+ bool T = getbits36_1 (cpu.cu.IWB, 9) != 0; // T (enablefault) bit
+
+ uint BOLR = getbits36_4 (cpu.cu.IWB, 5); // T (enablefault) bit
+ bool B5 = !! (BOLR & 8);
+ bool B6 = !! (BOLR & 4);
+ bool B7 = !! (BOLR & 2);
+ bool B8 = !! (BOLR & 1);
+
+ e->ADDR1.mode = eRWreadBit;
+
+#ifndef EIS_PTR
+ sim_debug (DBG_TRACEEXT, & cpu_dev,
+ "CSL N1 %d N2 %d\n"
+ "CSL C1 %d C2 %d B1 %d B2 %d F %o T %d\n"
+ "CSL BOLR %u%u%u%u\n"
+ "CSL op1 SNR %06o WORDNO %06o CHAR %d BITNO %d\n"
+ "CSL op2 SNR %06o WORDNO %06o CHAR %d BITNO %d\n",
+ e -> N1, e -> N2,
+ e -> C1, e -> C2, e -> B1, e -> B2, F, T,
+ B5, B6, B7, B8,
+ e -> addr [0].SNR, e -> addr [0].address,
+ e -> addr [0].cPos, e -> addr [0].bPos,
+ e -> addr [1].SNR, e -> addr [1].address,
+ e -> addr [1].cPos, e -> addr [1].bPos);
+#endif
+
+ bool bR = false; // result bit
+
+ PNL (L68_ (if (max (e->N1, e->N2) < 128)
+ DU_CYCLE_FLEN_128;))
+
+ for( ; cpu.du.CHTALLY < min(e->N1, e->N2); cpu.du.CHTALLY += 1)
+ {
+ bool b1 = EISgetBitRWN(&e->ADDR1, true);
+ e->ADDR2.mode = eRWreadBit;
+ bool b2 = EISgetBitRWN(&e->ADDR2, true);
+
+ if (b1) if (b2) bR = B8; else bR = B7; else if (b2) bR = B6; else bR = B5;
+
+ if (bR)
+ {
+ //CLR_I_ZERO);
+ cpu.du.Z = 0;
+ }
+
+ // write out modified bit
+ e->ADDR2.bit = bR ? 1 : 0; // set bit contents to write
+ e->ADDR2.mode = eRWwriteBit; // we want to write the bit
+ // if ADDR1 is on a word boundary, it might fault on the next loop,
+ // so we flush the write in case.
+ EISgetBitRWN(&e->ADDR2, e->ADDR1.last_bit_posn == 35); // write bit w/ addr increment to memory
+ }
+
+ if (e->N1 < e->N2)
+ {
+ for(; cpu.du.CHTALLY < e->N2; cpu.du.CHTALLY += 1)
+ {
+ bool b1 = F;
+
+ e->ADDR2.mode = eRWreadBit;
+ bool b2 = EISgetBitRWN(&e->ADDR2, true);
+
+ if (b1) if (b2) bR = B8; else bR = B7; else if (b2) bR = B6; else bR = B5;
+
+ if (bR)
+ {
+ //CLR_I_ZERO;
+ cpu.du.Z = 0;
+ }
+
+ // write out modified bit
+ e->ADDR2.bit = bR ? 1 : 0;
+ e->ADDR2.mode = eRWwriteBit;
+ // if ADDR1 is on a word boundary, it might fault on the next loop,
+ // so we flush the write in case.
+ EISgetBitRWN(&e->ADDR2, e->ADDR1.last_bit_posn == 35); // write bit w/ addr increment to memory
+ }
+ }
+
+ EISWriteCache (&e->ADDR2);
+
+ cleanupOperandDescriptor (1);
+ cleanupOperandDescriptor (2);
+
+ SC_I_ZERO (cpu.du.Z);
+ if (e->N1 > e->N2)
+ {
+ // NOTES: If N1 > N2, the low order (N1-N2) bits of C(Y-bit1) are not
+ // processed and the truncation indicator is set ON.
+ //
+ // If T = 1 and the truncation indicator is set ON by execution of the
+ // instruction, then a truncation (overflow) fault occurs.
+
+ SET_I_TRUNC;
+ if (T && tstOVFfault ())
+ doFault(FAULT_OFL, fst_zero, "csl truncation fault");
+ }
+ else
+ {
+ CLR_I_TRUNC;
+ }
+ }
+
+/*
+ * return B (bit position), C (char position) and word offset given:
+ * 'length' # of bits, etc ....
+ * 'initC' initial char position (C)
+ * 'initB' initial bit position
+ */
+
+static void getBitOffsets(int length, int initC, int initB, int *nWords, int *newC, int *newB)
+{
+ if (length == 0)
+ return;
+
+ int endBit = (length + 9 * initC + initB - 1) % 36;
+
+ //int numWords = (length + 35) / 36; // how many additional words will the bits take up?
+ int numWords = (length + 9 * initC + initB + 35) / 36; // how many additional words will the bits take up?
+ int lastWordOffset = numWords - 1;
+
+ if (lastWordOffset > 0) // more that the 1 word needed?
+ *nWords = lastWordOffset; // # of additional words
+ else
+ *nWords = 0; // no additional words needed
+
+ *newC = endBit / 9; // last character number
+ *newB = endBit % 9; // last bit number
+}
+
+static bool EISgetBitRWNR (EISaddr * p, bool flush)
+ {
+ int baseCharPosn = (p -> cPos * 9); // 9-bit char bit position
+ int baseBitPosn = baseCharPosn + p -> bPos;
+ baseBitPosn -= (int) cpu.du.CHTALLY;
+
+ int bitPosn = baseBitPosn % 36;
+ int woff = baseBitPosn / 36;
+ while (bitPosn < 0)
+ {
+ bitPosn += 36;
+ woff -= 1;
+ }
+if (bitPosn < 0) {
+sim_printf ("cPos %d bPos %d\n", p->cPos, p->bPos);
+sim_printf ("baseCharPosn %d baseBitPosn %d\n", baseCharPosn, baseBitPosn);
+sim_printf ("CHTALLY %d baseBitPosn %d\n", cpu.du.CHTALLY, baseBitPosn);
+sim_printf ("bitPosn %d woff %d\n", bitPosn, woff);
+sim_warn ("EISgetBitRWNR oops\n");
+return false;
+}
+
+#ifdef EIS_PTR
+ long eisaddr_idx = EISADDR_IDX (p);
+if (eisaddr_idx < 0 || eisaddr_idx > 2) { sim_warn ("IDX1"); return }
+ word18 saveAddr = cpu.du.Dk_PTR_W[eisaddr_idx];
+ cpu.du.Dk_PTR_W[eisaddr_idx] += (word18) woff;
+ cpu.du.Dk_PTR_W[eisaddr_idx] &= AMASK;
+#else
+ word18 saveAddr = p -> address;
+ p -> address += (word18) woff;
+#endif
+
+ p -> data = EISRead (p); // read data word from memory
+
+ if (p -> mode == eRWreadBit)
+ {
+ p -> bit = getbits36_1 (p -> data, (uint) bitPosn);
+ }
+ else if (p -> mode == eRWwriteBit)
+ {
+ //p -> data = bitfieldInsert36 (p -> data, p -> bit, bitPosn, 1);
+ p -> data = setbits36_1 (p -> data, (uint) bitPosn, p -> bit);
+
+ EISWriteIdx (p, 0, p -> data, flush); // write data word to memory
+ }
+
+ p->last_bit_posn = bitPosn;
+
+#ifdef EIS_PTR
+ cpu.du.Dk_PTR_W[eisaddr_idx] = saveAddr;
+#else
+ p -> address = saveAddr;
+#endif
+ return p -> bit;
+ }
+
+void csr (void)
+ {
+ EISstruct * e = & cpu.currentEISinstruction;
+
+ // For i = bits 1, 2, ..., minimum (N1,N2)
+ // m = C(Y-bit1)N1-i || C(Y-bit2)N2-i (a 2-bit number)
+ // C(BOLR)m → C( Y-bit2)N2-i
+ // If N1 < N2, then for i = N1+i, N1+2, ..., N2
+ // m = C(F) || C(Y-bit2)N2-i (a 2-bit number)
+ // C(BOLR)m → C( Y-bit2)N2-i
+ // INDICATORS: (Indicators not listed are not affected)
+ // Zero If C(Y-bit2) = 00...0, then ON; otherwise OFF
+ // Truncation If N1 > N2, then ON; otherwise OFF
+ //
+ // NOTES: If N1 > N2, the low order (N1-N2) bits of C(Y-bit1) are not
+ // processed and the truncation indicator is set ON.
+ //
+ // If T = 1 and the truncation indicator is set ON by execution of the
+ // instruction, then a truncation (overflow) fault occurs.
+ //
+ // BOLR
+ // If first operand and second operand then result
+ // bit is: bit is: is from bit:
+ // 0 0 5
+ // 0 1 6
+ // 1 0 7
+ // 1 1 8
+ //
+ // The Boolean operations most commonly used are
+ // BOLR Field Bits
+ // Operation 5 6 7 8
+ //
+ // MOVE 0 0 1 1
+ // AND 0 0 0 1
+ // OR 0 1 1 1
+ // NAND 1 1 1 0
+ // EXCLUSIVE OR 0 1 1 0
+ // Clear 0 0 0 0
+ // Invert 1 1 0 0
+ //
+
+ fault_ipr_subtype_ mod_fault = 0;
+
+#ifndef EIS_SETUP
+ setupOperandDescriptor(1, &mod_fault);
+ setupOperandDescriptor(2, &mod_fault);
+#endif
+
+ parseBitstringOperandDescriptor(1, &mod_fault);
+ parseBitstringOperandDescriptor(2, &mod_fault);
+
+#ifdef L68
+ // L68 raises it immediately
+ if (mod_fault)
+ doFault (FAULT_IPR,
+ (_fault_subtype) {.fault_ipr_subtype=mod_fault},
+ "Illegal modifier");
+#endif
+
+ // Bits 1-4 and 10 MBZ
+ if (IWB_IRODD & 0360200000000)
+ doFault (FAULT_IPR, (_fault_subtype) {.fault_ipr_subtype=FR_ILL_OP|mod_fault}, "csr 1-4,10 MBZ");
+
+#ifdef DPS8M
+ // DPS8M raises it delayed
+ if (mod_fault)
+ doFault (FAULT_IPR,
+ (_fault_subtype) {.fault_ipr_subtype=mod_fault},
+ "Illegal modifier");
+#endif
+
+ e->ADDR1.cPos = (int) e->C1;
+ e->ADDR2.cPos = (int) e->C2;
+
+ e->ADDR1.bPos = (int) e->B1;
+ e->ADDR2.bPos = (int) e->B2;
+
+ // get new char/bit offsets
+ int numWords1=0, numWords2=0;
+
+ getBitOffsets((int) e->N1, (int) e->C1, (int) e->B1, &numWords1, &e->ADDR1.cPos, &e->ADDR1.bPos);
+ PNL (cpu.du.D1_PTR_W += (word18) numWords1);
+ PNL (cpu.du.D1_PTR_W &= AMASK);
+#ifdef EIS_PTR
+ cpu.du.D1_PTR_W += (word18) numWords1;
+ cpu.du.D1_PTR_W &= AMASK;
+#else
+ e->ADDR1.address += (word18) numWords1;
+#endif
+
+ sim_debug (DBG_TRACEEXT, & cpu_dev,
+ "CSR N1 %d C1 %d B1 %d numWords1 %d cPos %d bPos %d\n",
+ e->N1, e->C1, e->B1, numWords1, e->ADDR1.cPos, e->ADDR1.bPos);
+ getBitOffsets((int) e->N2, (int) e->C2, (int) e->B2, &numWords2, &e->ADDR2.cPos, &e->ADDR2.bPos);
+ sim_debug (DBG_TRACEEXT, & cpu_dev,
+ "CSR N2 %d C2 %d B2 %d numWords2 %d cPos %d bPos %d\n",
+ e->N2, e->C2, e->B2, numWords2, e->ADDR2.cPos, e->ADDR2.bPos);
+ PNL (cpu.du.D2_PTR_W += (word18) numWords1);
+ PNL (cpu.du.D2_PTR_W &= AMASK);
+#ifdef EIS_PTR
+ cpu.du.D2_PTR_W += (word18) numWords1;
+ cpu.du.D2_PTR_W &= AMASK;
+#else
+ e->ADDR2.address += (word18) numWords2;
+#endif
+
+ bool F = getbits36_1 (cpu.cu.IWB, 0) != 0; // fill bit
+ bool T = getbits36_1 (cpu.cu.IWB, 9) != 0; // T (enablefault) bit
+
+ uint BOLR = getbits36_4 (cpu.cu.IWB, 5); // T (enablefault) bit
+ bool B5 = !! (BOLR & 8);
+ bool B6 = !! (BOLR & 4);
+ bool B7 = !! (BOLR & 2);
+ bool B8 = !! (BOLR & 1);
+
+ e->ADDR1.mode = eRWreadBit;
+
+ CLR_I_TRUNC; // assume N1 <= N2
+
+ bool bR = false; // result bit
+
+ PNL (L68_ (if (max (e->N1, e->N2) < 128)
+ DU_CYCLE_FLEN_128;))
+
+ for( ; cpu.du.CHTALLY < min(e->N1, e->N2); cpu.du.CHTALLY += 1)
+ {
+ bool b1 = EISgetBitRWNR(&e->ADDR1, true);
+
+ e->ADDR2.mode = eRWreadBit;
+ bool b2 = EISgetBitRWNR(&e->ADDR2, true);
+
+ if (b1) if (b2) bR = B8; else bR = B7; else if (b2) bR = B6; else bR = B5;
+
+ if (bR)
+ cpu.du.Z = 0;
+
+ // write out modified bit
+ e->ADDR2.bit = bR ? 1 : 0; // set bit contents to write
+ e->ADDR2.mode = eRWwriteBit; // we want to write the bit
+ // if ADDR1 is on a word boundary, it might fault on the next loop,
+ // so we flush the write in case.
+ EISgetBitRWNR(&e->ADDR2, e->ADDR1.last_bit_posn == 0);
+ }
+
+ if (e->N1 < e->N2)
+ {
+ for(; cpu.du.CHTALLY < e->N2; cpu.du.CHTALLY += 1)
+ {
+ bool b1 = F;
+
+ e->ADDR2.mode = eRWreadBit;
+ bool b2 = EISgetBitRWNR(&e->ADDR2, true);
+
+ if (b1) if (b2) bR = B8; else bR = B7; else if (b2) bR = B6; else bR = B5;
+
+ if (bR)
+ {
+ //CLR_I_ZERO;
+ cpu.du.Z = 0;
+ }
+
+ // write out modified bit
+ e->ADDR2.bit = bR ? 1 : 0;
+ e->ADDR2.mode = eRWwriteBit;
+ // if ADDR1 is on a word boundary, it might fault on the next loop,
+ // so we flush the write in case.
+ EISgetBitRWNR(&e->ADDR2, e->ADDR1.last_bit_posn == 0);
+ }
+ }
+
+ EISWriteCache (&e->ADDR2);
+
+ cleanupOperandDescriptor (1);
+ cleanupOperandDescriptor (2);
+
+ SC_I_ZERO (cpu.du.Z);
+ if (e->N1 > e->N2)
+ {
+ // NOTES: If N1 > N2, the low order (N1-N2) bits of C(Y-bit1) are not
+ // processed and the truncation indicator is set ON.
+ //
+ // If T = 1 and the truncation indicator is set ON by execution of the
+ // instruction, then a truncation (overflow) fault occurs.
+
+ SET_I_TRUNC;
+ if (T && tstOVFfault ())
+ doFault(FAULT_OFL, fst_zero, "csr truncation fault");
+ }
+ else
+ {
+ CLR_I_TRUNC;
+ }
+ }
+
+void sztl (void)
+ {
+
+ // The execution of this instruction is identical to the Combine
+ // Bit Strings Left (csl) instruction except that C(BOLR)m is
+ // not placed into C(Y-bit2)i-1.
+
+ EISstruct * e = & cpu.currentEISinstruction;
+
+ // For i = bits 1, 2, ..., minimum (N1,N2)
+ // m = C(Y-bit1)i-1 || C(Y-bit2)i-1 (a 2-bit number)
+ // C(BOLR)m → C(Y-bit2)i-1
+ // If N1 < N2, then for i = N1+l, N1+2, ..., N2
+ // m = C(F) || C(Y-bit2)i-1 (a 2-bit number)
+ // C(BOLR)m → C(Y-bit2)i-1
+ //
+ // INDICATORS: (Indicators not listed are not affected)
+ // Zero If C(Y-bit2) = 00...0, then ON; otherwise OFF
+ // Truncation If N1 > N2, then ON; otherwise OFF
+ //
+ // NOTES: If N1 > N2, the low order (N1-N2) bits of C(Y-bit1) are not
+ // processed and the truncation indicator is set ON.
+ //
+ // If T = 1 and the truncation indicator is set ON by execution of the
+ // instruction, then a truncation (overflow) fault occurs.
+ //
+ // BOLR
+ // If first operand and second operand then result
+ // bit is: bit is: is from bit:
+ // 0 0 5
+ // 0 1 6
+ // 1 0 7
+ // 1 1 8
+ //
+ // The Boolean operations most commonly used are
+ // BOLR Field Bits
+ // Operation 5 6 7 8
+ //
+ // MOVE 0 0 1 1
+ // AND 0 0 0 1
+ // OR 0 1 1 1
+ // NAND 1 1 1 0
+ // EXCLUSIVE OR 0 1 1 0
+ // Clear 0 0 0 0
+ // Invert 1 1 0 0
+ //
+
+// 0 0 0 0 Clear
+// 0 0 0 1 a AND b
+// 0 0 1 0 a AND !b
+// 0 0 1 1 a
+// 0 1 0 0 !a AND b
+// 0 1 0 1 b
+// 0 1 1 0 a XOR b
+// 0 1 1 1 a OR b
+// 1 0 0 0 !a AND !b !(a OR b)
+// 1 0 0 1 a == b !(a XOR b)
+// 1 0 1 0 !b
+// 1 0 1 1 !b OR A
+// 1 1 0 0 !a
+// 1 1 0 1 !b AND a
+// 1 1 1 0 a NAND b
+// 1 1 1 1 Set
+
+ fault_ipr_subtype_ mod_fault = 0;
+
+#ifndef EIS_SETUP
+ setupOperandDescriptor (1, &mod_fault);
+ setupOperandDescriptor (2, &mod_fault);
+#endif
+
+ parseBitstringOperandDescriptor (1, &mod_fault);
+ parseBitstringOperandDescriptor (2, &mod_fault);
+
+#ifdef L68
+ // L68 raises it immediately
+ if (mod_fault)
+ doFault (FAULT_IPR,
+ (_fault_subtype) {.fault_ipr_subtype=mod_fault},
+ "Illegal modifier");
+#endif
+
+ // Bits 1-4 and 10 MBZ
+ if (IWB_IRODD & 0360200000000)
+ doFault (FAULT_IPR, (_fault_subtype) {.fault_ipr_subtype=FR_ILL_OP|mod_fault}, "csl 1-4,10 MBZ");
+
+#ifdef DPS8M
+ // DPS8M raises it delayed
+ if (mod_fault)
+ doFault (FAULT_IPR,
+ (_fault_subtype) {.fault_ipr_subtype=mod_fault},
+ "Illegal modifier");
+#endif
+
+ e->ADDR1.cPos = (int) e->C1;
+ e->ADDR2.cPos = (int) e->C2;
+
+ e->ADDR1.bPos = (int) e->B1;
+ e->ADDR2.bPos = (int) e->B2;
+
+ bool F = getbits36_1 (cpu.cu.IWB, 0) != 0; // fill bit
+ bool T = getbits36_1 (cpu.cu.IWB, 9) != 0; // T (enablefault) bit
+
+ uint BOLR = getbits36_4 (cpu.cu.IWB, 5); // T (enablefault) bit
+ bool B5 = !! (BOLR & 8);
+ bool B6 = !! (BOLR & 4);
+ bool B7 = !! (BOLR & 2);
+ bool B8 = !! (BOLR & 1);
+
+ e->ADDR1.mode = eRWreadBit;
+ e->ADDR2.mode = eRWreadBit;
+
+#ifndef EIS_PTR
+ sim_debug (DBG_TRACEEXT, & cpu_dev,
+ "SZTL N1 %d N2 %d\n"
+ "SZTL C1 %d C2 %d B1 %d B2 %d F %o T %d\n"
+ "SZTL BOLR %u%u%u%u\n"
+ "SZTL op1 SNR %06o WORDNO %06o CHAR %d BITNO %d\n"
+ "SZTL op2 SNR %06o WORDNO %06o CHAR %d BITNO %d\n",
+ e -> N1, e -> N2,
+ e -> C1, e -> C2, e -> B1, e -> B2, F, T,
+ B5, B6, B7, B8,
+ e -> addr [0].SNR, e -> addr [0].address,
+ e -> addr [0].cPos, e -> addr [0].bPos,
+ e -> addr [1].SNR, e -> addr [1].address,
+ e -> addr [1].cPos, e -> addr [1].bPos);
+#endif
+
+ bool bR = false; // result bit
+
+ PNL (L68_ (if (max (e->N1, e->N2) < 128)
+ DU_CYCLE_FLEN_128;))
+
+ for( ; cpu.du.CHTALLY < min (e->N1, e->N2); cpu.du.CHTALLY += 1)
+ {
+ bool b1 = EISgetBitRWN (& e->ADDR1, true);
+ bool b2 = EISgetBitRWN (& e->ADDR2, true);
+
+ if (b1) if (b2) bR = B8; else bR = B7; else if (b2) bR = B6; else bR = B5;
+
+ if (bR)
+ {
+ //CLR_I_ZERO);
+ cpu.du.Z = 0;
+ break;
+ }
+ }
+
+ if (e->N1 < e->N2)
+ {
+ for (; cpu.du.CHTALLY < e->N2; cpu.du.CHTALLY += 1)
+ {
+ bool b1 = F;
+ bool b2 = EISgetBitRWN (& e->ADDR2, true);
+
+ if (b1) if (b2) bR = B8; else bR = B7; else if (b2) bR = B6; else bR = B5;
+
+ if (bR)
+ {
+ //CLR_I_ZERO;
+ cpu.du.Z = 0;
+ break;
+ }
+ }
+ }
+
+ cleanupOperandDescriptor (1);
+ cleanupOperandDescriptor (2);
+
+ SC_I_ZERO (cpu.du.Z);
+ if (e->N1 > e->N2)
+ {
+ // NOTES: If N1 > N2, the low order (N1-N2) bits of C(Y-bit1) are not
+ // processed and the truncation indicator is set ON.
+ //
+ // If T = 1 and the truncation indicator is set ON by execution of the
+ // instruction, then a truncation (overflow) fault occurs.
+
+ SET_I_TRUNC;
+ if (T && tstOVFfault ())
+ doFault(FAULT_OFL, fst_zero, "csl truncation fault");
+ }
+ else
+ {
+ CLR_I_TRUNC;
+ }
+ }
+
+void sztr (void)
+ {
+
+ // The execution of this instruction is identical to the Combine
+ // Bit Strings Left (csl) instruction except that C(BOLR)m is
+ // not placed into C(Y-bit2)i-1.
+
+ EISstruct * e = & cpu.currentEISinstruction;
+
+ // For i = bits 1, 2, ..., minimum (N1,N2)
+ // m = C(Y-bit1)N1-i || C(Y-bit2)N2-i (a 2-bit number)
+ // C(BOLR)m → C( Y-bit2)N2-i
+ // If N1 < N2, then for i = N1+i, N1+2, ..., N2
+ // m = C(F) || C(Y-bit2)N2-i (a 2-bit number)
+ // C(BOLR)m → C( Y-bit2)N2-i
+ // INDICATORS: (Indicators not listed are not affected)
+ // Zero If C(Y-bit2) = 00...0, then ON; otherwise OFF
+ // Truncation If N1 > N2, then ON; otherwise OFF
+ //
+ // NOTES: If N1 > N2, the low order (N1-N2) bits of C(Y-bit1) are not
+ // processed and the truncation indicator is set ON.
+ //
+ // If T = 1 and the truncation indicator is set ON by execution of the
+ // instruction, then a truncation (overflow) fault occurs.
+ //
+ // BOLR
+ // If first operand and second operand then result
+ // bit is: bit is: is from bit:
+ // 0 0 5
+ // 0 1 6
+ // 1 0 7
+ // 1 1 8
+ //
+ // The Boolean operations most commonly used are
+ // BOLR Field Bits
+ // Operation 5 6 7 8
+ //
+ // MOVE 0 0 1 1
+ // AND 0 0 0 1
+ // OR 0 1 1 1
+ // NAND 1 1 1 0
+ // EXCLUSIVE OR 0 1 1 0
+ // Clear 0 0 0 0
+ // Invert 1 1 0 0
+ //
+
+ fault_ipr_subtype_ mod_fault = 0;
+
+#ifndef EIS_SETUP
+ setupOperandDescriptor(1, &mod_fault);
+ setupOperandDescriptor(2, &mod_fault);
+#endif
+
+ parseBitstringOperandDescriptor(1, &mod_fault);
+ parseBitstringOperandDescriptor(2, &mod_fault);
+
+#ifdef L68
+ // L68 raises it immediately
+ if (mod_fault)
+ doFault (FAULT_IPR,
+ (_fault_subtype) {.fault_ipr_subtype=mod_fault},
+ "Illegal modifier");
+#endif
+
+ // Bits 1-4 and 10 MBZ
+ if (IWB_IRODD & 0360200000000)
+ doFault (FAULT_IPR, (_fault_subtype) {.fault_ipr_subtype=FR_ILL_OP|mod_fault}, "csr 1-4,10 MBZ");
+
+#ifdef DPS8M
+ // DPS8M raises it delayed
+ if (mod_fault)
+ doFault (FAULT_IPR,
+ (_fault_subtype) {.fault_ipr_subtype=mod_fault},
+ "Illegal modifier");
+#endif
+
+ e->ADDR1.cPos = (int) e->C1;
+ e->ADDR2.cPos = (int) e->C2;
+
+ e->ADDR1.bPos = (int) e->B1;
+ e->ADDR2.bPos = (int) e->B2;
+
+ // get new char/bit offsets
+ int numWords1=0, numWords2=0;
+
+ getBitOffsets((int) e->N1, (int) e->C1, (int) e->B1, &numWords1, &e->ADDR1.cPos, &e->ADDR1.bPos);
+ PNL (cpu.du.D1_PTR_W += (word18) numWords1);
+ PNL (cpu.du.D1_PTR_W &= AMASK);
+#ifdef EIS_PTR
+ cpu.du.D1_PTR_W += (word18) numWords1;
+ cpu.du.D1_PTR_W &= AMASK;
+#else
+ e->ADDR1.address += (word18) numWords1;
+#endif
+
+ sim_debug (DBG_TRACEEXT, & cpu_dev,
+ "CSR N1 %d C1 %d B1 %d numWords1 %d cPos %d bPos %d\n",
+ e->N1, e->C1, e->B1, numWords1, e->ADDR1.cPos, e->ADDR1.bPos);
+ getBitOffsets((int) e->N2, (int) e->C2, (int) e->B2, &numWords2, &e->ADDR2.cPos, &e->ADDR2.bPos);
+ sim_debug (DBG_TRACEEXT, & cpu_dev,
+ "CSR N2 %d C2 %d B2 %d numWords2 %d cPos %d bPos %d\n",
+ e->N2, e->C2, e->B2, numWords2, e->ADDR2.cPos, e->ADDR2.bPos);
+ PNL (cpu.du.D2_PTR_W += (word18) numWords1);
+ PNL (cpu.du.D2_PTR_W &= AMASK);
+#ifdef EIS_PTR
+ cpu.du.D2_PTR_W += (word18) numWords1;
+ cpu.du.D2_PTR_W &= AMASK;
+#else
+ e->ADDR2.address += (word18) numWords2;
+#endif
+
+ bool F = getbits36_1 (cpu.cu.IWB, 0) != 0; // fill bit
+ bool T = getbits36_1 (cpu.cu.IWB, 9) != 0; // T (enablefault) bit
+
+ uint BOLR = getbits36_4 (cpu.cu.IWB, 5); // T (enablefault) bit
+ bool B5 = !! (BOLR & 8);
+ bool B6 = !! (BOLR & 4);
+ bool B7 = !! (BOLR & 2);
+ bool B8 = !! (BOLR & 1);
+
+ e->ADDR1.mode = eRWreadBit;
+
+ CLR_I_TRUNC; // assume N1 <= N2
+
+ bool bR = false; // result bit
+
+ PNL (L68_ (if (max (e->N1, e->N2) < 128)
+ DU_CYCLE_FLEN_128;))
+
+ for( ; cpu.du.CHTALLY < min(e->N1, e->N2); cpu.du.CHTALLY += 1)
+ {
+ bool b1 = EISgetBitRWNR(&e->ADDR1, true);
+
+ e->ADDR2.mode = eRWreadBit;
+ bool b2 = EISgetBitRWNR(&e->ADDR2, true);
+
+ if (b1) if (b2) bR = B8; else bR = B7; else if (b2) bR = B6; else bR = B5;
+
+ if (bR)
+ {
+ cpu.du.Z = 0;
+ break;
+ }
+
+ }
+
+ if (e->N1 < e->N2)
+ {
+ for(; cpu.du.CHTALLY < e->N2; cpu.du.CHTALLY += 1)
+ {
+ bool b1 = F;
+
+ e->ADDR2.mode = eRWreadBit;
+ bool b2 = EISgetBitRWNR(&e->ADDR2, true);
+
+ if (b1) if (b2) bR = B8; else bR = B7; else if (b2) bR = B6; else bR = B5;
+
+ if (bR)
+ {
+ //CLR_I_ZERO;
+ cpu.du.Z = 0;
+ break;
+ }
+
+ }
+ }
+
+ cleanupOperandDescriptor (1);
+ cleanupOperandDescriptor (2);
+
+ SC_I_ZERO (cpu.du.Z);
+ if (e->N1 > e->N2)
+ {
+ // NOTES: If N1 > N2, the low order (N1-N2) bits of C(Y-bit1) are not
+ // processed and the truncation indicator is set ON.
+ //
+ // If T = 1 and the truncation indicator is set ON by execution of the
+ // instruction, then a truncation (overflow) fault occurs.
+
+ SET_I_TRUNC;
+ if (T && tstOVFfault ())
+ doFault(FAULT_OFL, fst_zero, "csr truncation fault");
+ }
+ else
+ {
+ CLR_I_TRUNC;
+ }
+ }
+
+
+/*
+ * CMPB - Compare Bit Strings
+ */
+
+/*
+ * get a bit from memory ....
+ */
+// XXX this is terribly inefficient, but it'll do for now ......
+
+static bool EISgetBit(EISaddr *p, int *cpos, int *bpos)
+{
+#ifdef EIS_PTR
+ long eisaddr_idx = EISADDR_IDX (p);
+if (eisaddr_idx < 0 || eisaddr_idx > 2) { sim_warn ("IDX1"); return }
+#endif
+
+ if (!p)
+ {
+ //lastAddress = -1;
+ return 0;
+ }
+
+ if (*bpos > 8) // bits 0-8
+ {
+ *bpos = 0;
+ *cpos += 1;
+ if (*cpos > 3) // chars 0-3
+ {
+ *cpos = 0;
+#ifdef EIS_PTR
+ cpu.du.Dk_PTR_W[eisaddr_idx] += 1;
+ cpu.du.Dk_PTR_W[eisaddr_idx] &= AMASK;
+#else
+ p->address += 1;
+ p->address &= AMASK;
+#endif
+ }
+ }
+
+ p->data = EISRead(p); // read data word from memory
+
+ int charPosn = *cpos * 9;
+ int bitPosn = charPosn + *bpos;
+ bool b = getbits36_1 (p->data, (uint) bitPosn) != 0;
+
+ *bpos += 1;
+
+ return b;
+}
+
+void cmpb (void)
+{
+ EISstruct * e = & cpu.currentEISinstruction;
+
+
+ // For i = 1, 2, ..., minimum (N1,N2)
+ // C(Y-bit1)i-1 :: C(Y-bit2)i-1
+ // If N1 < N2, then for i = N1+1, N1+2, ..., N2
+ // C(FILL) :: C(Y-bit2)i-1
+ // If N1 > N2, then for i = N2+l, N2+2, ..., N1
+ // C(Y-bit1)i-1 :: C(FILL)
+ //
+ // Indicators:
+ // Zero: If C(Y-bit1)i = C(Y-bit2)i for all i, then ON; otherwise, OFF
+ // Carry: If C(Y-bit1)i < C(Y-bit2)i for any i, then OFF; otherwise ON
+
+ fault_ipr_subtype_ mod_fault = 0;
+
+#ifndef EIS_SETUP
+ setupOperandDescriptor(1, &mod_fault);
+ setupOperandDescriptor(2, &mod_fault);
+#endif
+
+ parseBitstringOperandDescriptor(1, &mod_fault);
+ parseBitstringOperandDescriptor(2, &mod_fault);
+
+#ifdef L68
+ // L68 raises it immediately
+ if (mod_fault)
+ {
+ doFault (FAULT_IPR,
+ (_fault_subtype) {.fault_ipr_subtype=mod_fault},
+ "Illegal modifier");
+ }
+#endif
+
+ // Bits 1-8 and 10 MBZ
+ if (IWB_IRODD & 0377200000000)
+ doFault (FAULT_IPR, (_fault_subtype) {.fault_ipr_subtype=FR_ILL_OP|mod_fault}, "cmpb 1-8,10 MBZ");
+
+#ifdef DPS8M
+ // DPS8M raises it delayed
+ if (mod_fault)
+ {
+ doFault (FAULT_IPR,
+ (_fault_subtype) {.fault_ipr_subtype=mod_fault},
+ "Illegal modifier");
+ }
+#endif
+
+ int charPosn1 = (int) e->C1;
+ int charPosn2 = (int) e->C2;
+
+ int bitPosn1 = (int) e->B1;
+ int bitPosn2 = (int) e->B2;
+
+ bool F = getbits36_1 (cpu.cu.IWB, 0) != 0; // fill bit
+
+ SET_I_ZERO; // assume all =
+ SET_I_CARRY; // assume all >=
+
+sim_debug (DBG_TRACEEXT, & cpu_dev, "cmpb N1 %d N2 %d\n", e -> N1, e -> N2);
+
+#if 0
+// RJ78: Notes 1: If L1 or L2 = 0, both the Zero and Carry indicators are
+// turned ON, but no Illegal Procedure fault occurs.
+
+// CAC: This makes sense if you s/or/and/; the behavior for the 'or'
+// condition is well-defined by the text, but the case of 'and' is
+// not covered. However, this test is just an optimization -- the
+// code behaves this way for the 'and' case.
+
+ //if (e -> N1 == 0 || e -> N2 == 0)
+ if (e -> N1 == 0 && e -> N2 == 0)
+ {
+ return;
+ }
+#endif
+
+ PNL (L68_ (if (max (e->N1, e->N2) < 128)
+ DU_CYCLE_FLEN_128;))
+
+ uint i;
+ for(i = 0 ; i < min(e->N1, e->N2) ; i += 1)
+ {
+ bool b1 = EISgetBit (&e->ADDR1, &charPosn1, &bitPosn1);
+ bool b2 = EISgetBit (&e->ADDR2, &charPosn2, &bitPosn2);
+
+sim_debug (DBG_TRACEEXT, & cpu_dev, "cmpb(min(e->N1, e->N2)) i %d b1 %d b2 %d\n", i, b1, b2);
+ if (b1 != b2)
+ {
+ CLR_I_ZERO;
+ if (!b1 && b2) // 0 < 1
+ CLR_I_CARRY;
+
+ cleanupOperandDescriptor (1);
+ cleanupOperandDescriptor (2);
+
+ return;
+ }
+
+ }
+ if (e->N1 < e->N2)
+ {
+ for(; i < e->N2 ; i += 1)
+ {
+ bool b1 = F;
+ bool b2 = EISgetBit(&e->ADDR2, &charPosn2, &bitPosn2);
+sim_debug (DBG_TRACEEXT, & cpu_dev, "cmpb(e->N1 < e->N2) i %d b1fill %d b2 %d\n", i, b1, b2);
+
+ if (b1 != b2)
+ {
+ CLR_I_ZERO;
+ if (!b1 && b2) // 0 < 1
+ CLR_I_CARRY;
+
+ cleanupOperandDescriptor (1);
+ cleanupOperandDescriptor (2);
+
+ return;
+ }
+ }
+ } else if (e->N1 > e->N2)
+ {
+ for(; i < e->N1 ; i += 1)
+ {
+ bool b1 = EISgetBit(&e->ADDR1, &charPosn1, &bitPosn1);
+ bool b2 = F;
+sim_debug (DBG_TRACEEXT, & cpu_dev, "cmpb(e->N1 > e->N2) i %d b1 %d b2fill %d\n", i, b1, b2);
+
+ if (b1 != b2)
+ {
+ CLR_I_ZERO;
+ if (!b1 && b2) // 0 < 1
+ CLR_I_CARRY;
+
+ cleanupOperandDescriptor (1);
+ cleanupOperandDescriptor (2);
+
+ return;
+ }
+ }
+ }
+ cleanupOperandDescriptor (1);
+ cleanupOperandDescriptor (2);
+}
+
+#if 0
+/*
+ * write 4-bit digits to memory @ pos (in reverse) ...
+ */
+
+static void EISwrite4r(EISaddr *p, int *pos, word4 char4)
+{
+#ifdef EIS_PTR
+ long eisaddr_idx = EISADDR_IDX (p);
+if (eisaddr_idx < 0 || eisaddr_idx > 2) { sim_warn ("IDX1"); return }
+#endif
+ word36 w;
+
+ if (*pos < 0) // out-of-range?
+ {
+ *pos = 7; // reset to 1st byte
+#ifdef EIS_PTR
+ cpu.du.Dk_PTR_W[eisaddr_idx] = (cpu.du.Dk_PTR_W[eisaddr_idx] - 1) & AMASK; // goto prev dstAddr in memory
+#else
+ p->address = (p->address - 1) & AMASK; // goto prev dstAddr in memory
+#endif
+ }
+ w = EISRead(p);
+
+// AL39, Figure 2-3
+ switch (*pos)
+ {
+ case 0:
+ //w = bitfieldInsert36(w, char4, 31, 5);
+ w = setbits36_4 (w, 1, char4);
+ break;
+ case 1:
+ //w = bitfieldInsert36(w, char4, 27, 4);
+ w = setbits36_4 (w, 5, char4);
+ break;
+ case 2:
+ //w = bitfieldInsert36(w, char4, 22, 5);
+ w = setbits36_4 (w, 10, char4);
+ break;
+ case 3:
+ //w = bitfieldInsert36(w, char4, 18, 4);
+ w = setbits36_4 (w, 14, char4);
+ break;
+ case 4:
+ //w = bitfieldInsert36(w, char4, 13, 5);
+ w = setbits36_4 (w, 19, char4);
+ break;
+ case 5:
+ //w = bitfieldInsert36(w, char4, 9, 4);
+ w = setbits36_4 (w, 23, char4);
+ break;
+ case 6:
+ //w = bitfieldInsert36(w, char4, 4, 5);
+ w = setbits36_4 (w, 28, char4);
+ break;
+ case 7:
+ //w = bitfieldInsert36(w, char4, 0, 4);
+ w = setbits36_4 (w, 32, char4);
+ break;
+ }
+
+ //Write (*dstAddr, w, OperandWrite, 0); // XXX this is the inefficient part!
+ EISWriteIdx(p, 0, w, true); // XXX this is the inefficient part!
+
+ *pos -= 1; // to prev byte.
+}
+
+/*
+ * write 9-bit bytes to memory @ pos (in reverse)...
+ */
+
+static void EISwrite9r(EISaddr *p, int *pos, word9 char9)
+{
+#ifdef EIS_PTR
+ long eisaddr_idx = EISADDR_IDX (p);
+if (eisaddr_idx < 0 || eisaddr_idx > 2) { sim_warn ("IDX1"); return }
+#endif
+ word36 w;
+ if (*pos < 0) // out-of-range?
+ {
+ *pos = 3; // reset to 1st byte
+#ifdef EIS_PTR
+ cpu.du.Dk_PTR_W[eisaddr_idx] = (cpu.du.Dk_PTR_W[eisaddr_idx] - 1) & AMASK; // goto prev dstAddr in memory
+#else
+ p->address = (p->address - 1) & AMASK; // goto next dstAddr in memory
+#endif
+ }
+
+ w = EISRead(p); // read dst memory into w
+
+// AL39, Figure 2-5
+ switch (*pos)
+ {
+ case 0:
+ //w = bitfieldInsert36(w, char9, 27, 9);
+ w = setbits36_9 (w, 0, char9);
+ break;
+ case 1:
+ //w = bitfieldInsert36(w, char9, 18, 9);
+ w = setbits36_9 (w, 9, char9);
+ break;
+ case 2:
+ //w = bitfieldInsert36(w, char9, 9, 9);
+ w = setbits36_9 (w, 18, char9);
+ break;
+ case 3:
+ //w = bitfieldInsert36(w, char9, 0, 9);
+ w = setbits36_9 (w, 27, char9);
+ break;
+ }
+
+ //Write (*dstAddr, w, OperandWrite, 0); // XXX this is the inefficient part!
+ EISWriteIdx(p, 0, w, true); // XXX this is the inefficient part!
+
+ *pos -= 1; // to prev byte.
+}
+
+/*
+ * write char to output string in Reverse. Right Justified and taking into account string length of destination
+ */
+
+static void EISwriteToOutputStringReverse (int k, word9 charToWrite, bool * ovf)
+{
+ EISstruct * e = & cpu.currentEISinstruction;
+ // first thing we need to do is to find out the last position is the buffer we want to start writing to.
+
+ static int N = 0; // length of output buffer in native chars (4, 6 or 9-bit chunks)
+ static int CN = 0; // character number 0-7 (4), 0-5 (6), 0-3 (9)
+ static int TN = 0; // type code
+ static int pos = 0; // current character position
+ //static int size = 0; // size of char
+ static int _k = -1; // k of MFk
+
+ if (k)
+ {
+ _k = k;
+
+ N = (int) e->N[k-1]; // length of output buffer in native chars (4, 9-bit chunks)
+ CN = (int) e->CN[k-1]; // character number (position) 0-7 (4), 0-5 (6), 0-3 (9)
+ TN = (int) e->TN[k-1]; // type code
+
+ //int chunk = 0;
+ int maxPos = 4;
+ switch (TN)
+ {
+ case CTN4:
+ //address = e->addr[k-1].address;
+ //size = 4;
+ //chunk = 32;
+ maxPos = 8;
+ break;
+ case CTN9:
+ //address = e->addr[k-1].address;
+ //size = 9;
+ //chunk = 36;
+ maxPos = 4;
+ break;
+ }
+
+ // since we want to write the data in reverse (since it's right
+ // justified) we need to determine the final address/CN for the
+ // type and go backwards from there
+
+ //int numBits = size * N; // 8 4-bit digits, 4 9-bit bytes / word
+ // (remember there are 4 slop bits in a 36-bit word when dealing with
+ // BCD)
+
+ // how many additional words will the N chars take up?
+ //int numWords = numBits / ((TN == CTN4) ? 32 : 36);
+
+// CN+N numWords (CN+N+3)/4 lastChar
+// 1 1 0
+// 2 1 1
+// 3 1 2
+// 4 1 3
+// 5 2 0
+
+ int numWords = (CN + N + (maxPos - 1)) / maxPos;
+ int lastWordOffset = numWords - 1;
+ int lastChar = (CN + N - 1) % maxPos; // last character number
+
+ if (lastWordOffset > 0) // more that the 1 word needed?
+ {
+ //address += lastWordOffset; // highest memory address
+ PNL (cpu.du.Dk_PTR_W[k-1] += (word18) lastWordOffset);
+ PNL (cpu.du.Dk_PTR_W[k-1] &= AMASK);
+#ifdef EIS_PTR
+ cpu.du.Dk_PTR_W[k-1] += (word18) lastWordOffset;
+ cpu.du.Dk_PTR_W[k-1] &= AMASK;
+#else
+ e->addr[k-1].address += (word18) lastWordOffset;
+ e->addr[k-1].address &= MASK18;
+#endif
+ }
+
+ pos = lastChar; // last character number
+ return;
+ }
+
+ // any room left in output string?
+ if (N <= 0)
+ {
+ * ovf = true;
+ return;
+ }
+
+ // we should write character to word/pos in memory .....
+ switch(TN)
+ {
+ case CTN4:
+ EISwrite4r(&e->addr[_k-1], &pos, (word4) charToWrite);
+ break;
+ case CTN9:
+ EISwrite9r(&e->addr[_k-1], &pos, charToWrite);
+ break;
+ }
+
+ N -= 1;
+}
+#endif
+
+/*
+ * determine sign of N*9-bit length word
+ */
+static bool sign9n(word72 n128, int N)
+{
+
+ // sign bit of 9-bit is bit 8 (1 << 8)
+ // sign bit of 18-bit is bit 17 (1 << 17)
+ // .
+ // .
+ // .
+ // sign bit of 72-bit is bit 71 (1 << 71)
+
+ if (N < 1 || N > 8) // XXX largest int we'll play with is 72-bits? Makes sense
+ return false;
+
+#ifdef NEED_128
+ word72 sgnmask = lshift_128 (construct_128 (0, 1), (uint) (N * 9 - 1));
+ return isnonzero_128 (and_128 (sgnmask, n128));
+#else
+ word72 sgnmask = (word72)1 << ((N * 9) - 1);
+
+ return (bool)(sgnmask & n128);
+#endif
+}
+
+/*
+ * sign extend a N*9 length word to a (word72) 128-bit word
+ */
+static word72s signExt9(word72 n128, int N)
+{
+ // ext mask for 9-bit = 037777777777777777777777777777777777777400 8 0's
+ // ext mask for 18-bit = 037777777777777777777777777777777777400000 17 0's
+ // ext mask for 36-bit = 037777777777777777777777777777400000000000 35 0's
+ // etc...
+
+ int bits = (N * 9) - 1;
+ if (sign9n(n128, N))
+ {
+#ifdef NEED_128
+ uint128 extBits = lshift_128 (construct_128 (MASK64, MASK64), (uint) bits);
+ uint128 or = or_128 (n128, extBits);
+ return cast_s128 (or);
+#else
+ uint128 extBits = ((uint128)-1 << bits);
+ return (word72s) (n128 | extBits);
+#endif
+ }
+#ifdef NEED_128
+ uint128 zeroBits = complement_128 (lshift_128 (construct_128 (MASK64, MASK64), (uint) bits));
+ uint128 and = and_128 (n128, zeroBits);
+ return cast_s128 (and);
+#else
+ uint128 zeroBits = ~((uint128)-1 << bits);
+ return (word72s) (n128 & zeroBits);
+#endif
+}
+
+/*
+ * load a 9*n bit integer into e->x ...
+ */
+
+static void load9x(int n, EISaddr *addr, int pos)
+{
+ EISstruct * e = & cpu.currentEISinstruction;
+#ifdef NEED_128
+ word72 x = construct_128 (0, 0);
+#else
+ word72 x = 0;
+#endif
+#ifdef EIS_PTR
+ long eisaddr_idx = EISADDR_IDX (addr);
+if (eisaddr_idx < 0 || eisaddr_idx > 2) { sim_warn ("IDX1"); return }
+#endif
+
+ word36 data = EISRead(addr);
+
+ int m = n;
+ while (m)
+ {
+#ifdef NEED_128
+ x = lshift_128 (x, 9);
+#else
+ x <<= 9; // make room for next 9-bit byte
+#endif
+
+ if (pos > 3) // overflows to next word?
+ { // yep....
+ pos = 0; // reset to 1st byte
+#if EIS_PTR
+ cpu.du.Dk_PTR_W[eisaddr_idx] = (cpu.du.Dk_PTR_W[eisaddr_idx] + 1) & AMASK; // bump source to next address
+#else
+ addr->address = (addr->address + 1) & AMASK; // bump source to next address
+#endif
+ data = EISRead(addr); // read it from memory
+ }
+
+#ifdef NEED_128
+ x = or_128 (x, construct_128 (0, GETBYTE (data, pos)));
+#else
+ x |= GETBYTE(data, pos); // fetch byte at position pos and 'or' it in
+#endif
+
+ pos += 1; // onto next posotion
+
+ m -= 1; // decrement byte counter
+ }
+ e->x = signExt9(x, n); // form proper 2's-complement integer
+}
+
+#if 0
+/*
+ * get sign to buffer position p
+ */
+
+static word9 getSign (word72s n128)
+{
+ EISstruct * e = & cpu.currentEISinstruction;
+ // 4- or 9-bit?
+ if (e->TN2 == CTN4) // 4-bit
+ {
+ // If P=1, positive signed 4-bit results are stored using octal 13 as the plus sign.
+ // If P=0, positive signed 4-bit results are stored with octal 14 as the plus sign.
+ if (n128 >= 0)
+ {
+ if (e->P)
+ return 013; // alternate + sign
+ else
+ return 014; // default + sign
+ }
+ else
+ {
+ SETF(e->_flags, I_NEG);
+ return 015; // - sign
+ }
+ }
+ else
+ { // 9-bit
+ if (n128 >= 0)
+ return 053; // default 9-bit +
+ else
+ {
+ SETF(e->_flags, I_NEG);
+ return 055; // default 9-bit -
+ }
+ }
+}
+
+
+// perform a binary to decimal conversion ...
+
+// Since (according to DH02) we want to "right-justify" the output string it
+// might be better to presere the reverse writing and start writing
+// characters directly into the output string taking into account the output
+// string length.....
+
+static void _btd (bool * ovfp)
+{
+ EISwriteToOutputStringReverse(2, 0, ovfp); // initialize output writer .....
+
+ PNL (L68_ (DU_CYCLE_DGBD;))
+
+ EISstruct * e = & cpu.currentEISinstruction;
+ * ovfp = false;
+
+ word72s n128 = e->x; // signExt9(e->x, e->N1); // adjust for +/-
+
+ SC_I_ZERO (n128 == 0);
+ SC_I_NEG (n128 == 0);
+
+ int sgn = (n128 < 0) ? -1 : 1; // sgn(x)
+ if (n128 < 0)
+ n128 = -n128;
+
+ int N = (int) e->N2; // number of chars to write ....
+
+ // handle any trailing sign stuff ...
+ if (e->S2 == CSTS) // a trailing sign
+ {
+ EISwriteToOutputStringReverse(0, getSign(sgn), ovfp);
+ if (! * ovfp)
+ N -= 1;
+ }
+ if (! * ovfp)
+ {
+ do
+ {
+ int n = n128 % 10;
+
+ EISwriteToOutputStringReverse(0, (word9) ((e->TN2 == CTN4) ? n : (n + '0')), ovfp);
+
+ if (* ovfp) // Overflow! Too many chars, not enough room!
+ break;
+
+ N -= 1;
+
+ n128 /= 10;
+ } while (n128);
+ }
+
+ // at this point we've exhausted our digits, but may still have spaces left.
+
+ // handle any leading sign stuff ...
+ if (! * ovfp)
+ {
+ if (e->S2 == CSLS) // a leading sign
+ {
+ while (N > 1)
+ {
+ EISwriteToOutputStringReverse(0, (e->TN2 == CTN4) ? 0 : '0', ovfp);
+ if (* ovfp)
+ break;
+ N -= 1;
+ }
+ if (! * ovfp)
+ EISwriteToOutputStringReverse(0, getSign(sgn), ovfp);
+ }
+ else
+ {
+ while (N > 0)
+ {
+ EISwriteToOutputStringReverse(0, (e->TN2 == CTN4) ? 0 : '0', ovfp);
+ if (* ovfp)
+ break;
+ N -= 1;
+ }
+ }
+ }
+}
+#endif
+
+void btd (void)
+{
+ EISstruct * e = & cpu.currentEISinstruction;
+
+
+ // C(Y-char91) converted to decimal → C(Y-charn2)
+ /*!
+ * C(Y-char91) contains a twos complement binary integer aligned on 9-bit
+ * character boundaries with length 0 < N1 <= 8.
+ *
+ * If TN2 and S2 specify a 4-bit signed number and P = 1, then if
+ * C(Y-char91) is positive (bit 0 of C(Y-char91)0 = 0), then the 13(8) plus
+ * sign character is moved to C(Y-charn2) as appropriate.
+ *
+ * The scaling factor of C(Y-charn2), SF2, must be 0.
+ *
+ * If N2 is not large enough to hold the digits generated by conversion
+ * of C(Y-char91) an overflow condition exists; the overflow indicator is
+ * set ON and an overflow fault occurs. This implies that an unsigned
+ * fixed-point receiving field has a minimum length of 1 character and a
+ * signed fixed- point field, 2 characters.
+ *
+ * If MFk.RL = 1, then Nk does not contain the operand length; instead; it
+ * contains a register code for a register holding the operand length.
+ *
+ * If MFk.ID = 1, then the kth word following the instruction word does not
+ * contain an operand descriptor; instead, it contains an indirect pointer
+ * to the operand descriptor.
+ *
+ * C(Y-char91) and C(Y-charn2) may be overlapping strings; no check is made.
+ *
+ * Attempted conversion to a floating-point number (S2 = 0) or attempted
+ * use of a scaling factor (SF2 ≠ 0) causes an illegal procedure fault.
+ *
+ * If N1 = 0 or N1 > 8 an illegal procedure fault occurs.
+ *
+ * Attempted execution with the xed instruction causes an illegal procedure fault.
+ *
+ * Attempted repetition with the rpt, rpd, or rpl instructions causes an illegal procedure fault.
+ *
+ */
+
+ // C(string 1) -> C(string 2) (converted)
+
+ // The two's complement binary integer starting at location YC1 is
+ // converted into a signed string of decimal characters of data type TN2,
+ // sign and decimal type S2 (S2 = 00 is illegal) and scale factor 0; and is
+ // stored, right-justified, as a string of length L2 starting at location
+ // YC2. If the string generated is longer than L2, the high-order excess is
+ // truncated and the overflow indicator is set. If strings 1 and 2 are not
+ // overlapped, the contents of string 1 remain unchanged. The length of
+ // string 1 (L1) is given as the number of 9-bit segments that make up the
+ // string. L1 is equal to or is less than 8. Thus, the binary string to be
+ // converted can be 9, 18, 27, 36, 45, 54, 63, or 72 bits long. CN1
+ // designates a 9-bit character boundary. If P=1, positive signed 4-bit
+ // results are stored using octal 13 as the plus sign. If P=0, positive
+ // signed 4-bit results are stored with octal 14 as the plus sign.
+
+ fault_ipr_subtype_ mod_fault = 0;
+
+#ifndef EIS_SETUP
+ setupOperandDescriptor(1, &mod_fault);
+ setupOperandDescriptor(2, &mod_fault);
+#endif
+
+ parseNumericOperandDescriptor(1, &mod_fault);
+ parseNumericOperandDescriptor(2, &mod_fault);
+
+#ifdef L68
+ // L68 raises it immediately
+ if (mod_fault)
+ {
+ doFault (FAULT_IPR,
+ (_fault_subtype) {.fault_ipr_subtype=mod_fault},
+ "Illegal modifier");
+ }
+#endif
+
+ // Bits 1-10 MBZ
+ if (IWB_IRODD & 0377600000000)
+ {
+ //sim_printf ("sb2d %012"PRIo64"\n", IWB_IRODD);
+ doFault (FAULT_IPR, (_fault_subtype) {.fault_ipr_subtype=FR_ILL_OP|mod_fault}, "btd 0-8 MBZ");
+ }
+
+ // Bits 21-29 of OP1 MBZ
+ if (!(e->MF[0] & MFkID) && e -> op [0] & 0000000077700)
+ doFault (FAULT_IPR, (_fault_subtype) {.fault_ipr_subtype=FR_ILL_PROC|mod_fault}, "btd op1 21-29 MBZ");
+
+ // Bits 24-29 of OP2 MBZ
+ if (!(e->MF[1] & MFkID) && e -> op [1] & 0000000007700)
+ doFault (FAULT_IPR, (_fault_subtype) {.fault_ipr_subtype=FR_ILL_PROC|mod_fault}, "btd op2 24-29 MBZ");
+
+ if (e->S[1] == 0)
+ doFault (FAULT_IPR, (_fault_subtype) {.fault_ipr_subtype=FR_ILL_PROC|mod_fault}, "btd op2 S=0");
+
+#ifdef DPS8M
+ // DPS8 raises it delayed
+ if (mod_fault)
+ {
+ doFault (FAULT_IPR,
+ (_fault_subtype) {.fault_ipr_subtype=mod_fault},
+ "Illegal modifier");
+ }
+#endif
+
+ e->P = getbits36_1 (cpu.cu.IWB, 0) != 0; // 4-bit data sign character control
+
+ if (e->N1 == 0 || e->N1 > 8)
+ doFault(FAULT_IPR, fst_ill_proc, "btd(1): N1 == 0 || N1 > 8");
+
+ uint dstTN = e->TN2; // type of chars in dst
+ uint dstCN = e->CN2; // starting at char pos CN
+
+ int n2 = 0;
+
+ switch(e->S2)
+ {
+ case CSLS:
+ case CSTS:
+ n2 = (int) e->N2 - 1; // 1 sign
+ break;
+
+ case CSNS:
+ n2 = (int) e->N2; // no sign
+ break; // no sign wysiwyg
+ }
+
+ sim_debug (DBG_CAC, & cpu_dev,
+ "n2 %d\n",
+ n2);
+
+ if (n2 < 1)
+ doFault (FAULT_IPR, fst_ill_proc, "btd adjusted n2<1");
+
+
+ decContext set;
+ decContextDefaultDPS8(&set);
+ set.traps=0;
+
+ load9x((int) e->N1, &e->ADDR1, (int) e->CN1);
+
+ // handle sign
+ e->sign = 1;
+#ifdef NEED_128
+ word72 x = cast_128 (e->x);
+ if (islt_s128 (e->x, construct_s128 (0, 0)))
+ {
+ e->sign = -1;
+ x = and_128 (negate_128 (x), MASK72);
+
+ }
+
+ // convert to decimal string, workaround missing sprintf uint128
+ char tmp[32];
+ tmp[31] = 0;
+ int i;
+ for (i=30;i>=0;i--) {
+ //tmp[i] = x%10 + '0';
+ //x /= 10;
+ uint16_t r;
+ x = divide_128_16 (x, 10, & r);
+ tmp[i] = (char) r + '0';
+ if (iszero_128 (x))
+ break;
+ }
+#else
+ word72 x = (word72)e->x;
+ if (e->x < 0) {
+ e->sign = -1;
+ x = (-x) & MASK72;
+ }
+
+ // convert to decimal string, workaround missing sprintf uint128
+ char tmp[32];
+ tmp[31] = 0;
+ int i;
+ for (i=30;i>=0;i--) {
+ tmp[i] = x%10 + '0';
+ x /= 10;
+ if (x == 0)
+ break;
+ }
+#endif
+
+ decNumber _1;
+ decNumber *op1 = decNumberFromString(&_1, tmp+i, &set);
+ if (e->sign == -1)
+ op1->bits |= DECNEG;
+
+ bool Ovr = false, Trunc = false;
+
+ uint8_t out [256];
+ char * res = formatDecimal (out, &set, op1, n2, (int) e->S2, e->SF2, 0, &Ovr, &Trunc);
+
+ // now write to memory in proper format.....
+
+ //word18 dstAddr = e->dstAddr;
+ int pos = (int) dstCN;
+
+ // 1st, take care of any leading sign .......
+ switch(e->S2)
+ {
+ case CSLS: // fixed-point, leading sign
+ switch(dstTN)
+ {
+ case CTN4:
+ if (e->P) //If TN2 and S2 specify a 4-bit signed number and P = 1, then the 13(8) plus sign character is placed appropriately if the result of the operation is positive.
+ EISwrite49(&e->ADDR2, &pos, (int) dstTN, (decNumberIsNegative(op1) && !decNumberIsZero(op1)) ? 015 : 013); // special +
+ else
+ EISwrite49(&e->ADDR2, &pos, (int) dstTN, (decNumberIsNegative(op1) && !decNumberIsZero(op1)) ? 015 : 014); // default +
+ break;
+ case CTN9:
+ EISwrite49(&e->ADDR2, &pos, (int) dstTN, (decNumberIsNegative(op1) && !decNumberIsZero(op1)) ? '-' : '+');
+ break;
+ }
+ break;
+
+ case CSTS: // nuttin' to do here .....
+ case CSNS:
+ break; // no sign wysiwyg
+ }
+
+ // 2nd, write the digits .....
+ for(int i = 0 ; i < n2 ; i++)
+ switch(dstTN)
+ {
+ case CTN4:
+ EISwrite49(&e->ADDR2, &pos, (int) dstTN, (word9) (res[i] - '0'));
+ break;
+ case CTN9:
+ EISwrite49(&e->ADDR2, &pos, (int) dstTN, (word9) res[i]);
+ break;
+ }
+
+ // 3rd, take care of any trailing sign or exponent ...
+ switch(e->S2)
+ {
+ case CSTS: // write trailing sign ....
+ switch(dstTN)
+ {
+ case CTN4:
+ if (e->P) //If TN2 and S2 specify a 4-bit signed number and P = 1, then the 13(8) plus sign character is placed appropriately if the result of the operation is positive.
+ EISwrite49(&e->ADDR2, &pos, (int) dstTN, (decNumberIsNegative(op1) && !decNumberIsZero(op1)) ? 015 : 013); // special +
+ else
+ EISwrite49(&e->ADDR2, &pos, (int) dstTN, (decNumberIsNegative(op1) && !decNumberIsZero(op1)) ? 015 : 014); // default +
+ break;
+
+ case CTN9:
+ EISwrite49(&e->ADDR2, &pos, (int) dstTN, (decNumberIsNegative(op1) && !decNumberIsZero(op1)) ? '-' : '+');
+ break;
+ }
+ break;
+
+ case CSLS: // fixed point, leading sign - already done
+ case CSNS: // fixed point, unsigned - nuttin' needed to do
+ break;
+ }
+
+ SC_I_NEG (decNumberIsNegative(op1) && !decNumberIsZero(op1)); // set negative indicator if op3 < 0
+ SC_I_ZERO (decNumberIsZero(op1)); // set zero indicator if op3 == 0
+
+ cleanupOperandDescriptor (1);
+ cleanupOperandDescriptor (2);
+
+ if (Ovr)
+ {
+ SET_I_OFLOW;
+ if (tstOVFfault ())
+ doFault(FAULT_OFL, fst_zero, "btd overflow fault");
+ }
+}
+
+#if 0
+/*
+ * load a decimal number into e->x ...
+ */
+
+static int loadDec (EISaddr *p, int pos)
+{
+ EISstruct * e = & cpu.currentEISinstruction;
+ int128 x = 0;
+#ifdef EIS_PTR
+ long eisaddr_idx = EISADDR_IDX (p);
+if (eisaddr_idx < 0 || eisaddr_idx > 2) { sim_warn ("IDX1"); return }
+#endif
+
+ p->data = EISRead(p); // read data word from memory
+
+ int maxPos = e->TN1 == CTN4 ? 7 : 3;
+
+ int sgn = 1;
+
+ sim_debug (DBG_TRACEEXT, & cpu_dev,
+ "loadDec: maxPos %d N1 %d\n", maxPos, e->N1);
+ for(uint n = 0 ; n < e->N1 ; n += 1)
+ {
+ if (pos > maxPos) // overflows to next word?
+ { // yep....
+ pos = 0; // reset to 1st byte
+#if EIS_PTR
+ cpu.du.Dk_PTR_W[eisaddr_idx] = (cpu.du.Dk_PTR_W[eisaddr_idx] + 1) & AMASK; // bump source to next address
+#else
+ p->address = (p->address + 1) & AMASK; // bump source to next address
+#endif
+ p->data = EISRead(p); // read it from memory
+ }
+
+ int c = 0;
+ switch(e->TN1)
+ {
+ case CTN4:
+ c = (word4)get4(p->data, pos);
+ break;
+ case CTN9:
+ c = GETBYTE(p->data, pos);
+ break;
+ }
+ sim_debug (DBG_TRACEEXT, & cpu_dev,
+ "loadDec: n %d c %d(%o)\n", n, c, c);
+
+ // per CAC suggestion
+ if (n == 0 && c == 0) // treat as +0
+ {
+ return -1;
+ }
+
+ if (n == 0 && e->TN1 == CTN4 && e->S1 == CSLS)
+ {
+ sim_debug (DBG_TRACEEXT, & cpu_dev,
+ "loadDec: n 0, TN1 CTN4, S1 CSLS\n");
+ switch (c)
+ {
+ case 015: // 6-bit - sign
+ SETF(e->_flags, I_NEG);
+
+ sgn = -1;
+ break;
+ case 013: // alternate 4-bit + sign
+ case 014: // default 4-bit + sign
+ break;
+ default:
+ // not a leading sign
+ doFault(FAULT_IPR, fst_ill_proc, "loadDec(): no leading sign (1)");
+ }
+ pos += 1; // onto next posotion
+ continue;
+ }
+
+ if (n == 0 && e->TN1 == CTN9 && e->S1 == CSLS)
+ {
+ sim_debug (DBG_TRACEEXT, & cpu_dev,
+ "loadDec: n 0, TN1 CTN9, S1 CSLS\n");
+ switch (c)
+ {
+ case '-':
+ SETF(e->_flags, I_NEG);
+
+ sgn = -1;
+ break;
+ case '+':
+ break;
+ default:
+ // not a leading sign
+ doFault(FAULT_IPR, fst_ill_proc, "loadDec(): no leading sign (2)");
+ }
+ pos += 1; // onto next posotion
+ continue;
+ }
+
+ if (n == e->N1-1 && e->TN1 == CTN4 && e->S1 == CSTS)
+ {
+ sim_debug (DBG_TRACEEXT, & cpu_dev,
+ "loadDec: n N1-1, TN1 CTN4, S1 CSTS\n");
+ switch (c)
+ {
+ case 015: // 4-bit - sign
+ SETF(e->_flags, I_NEG);
+
+ sgn = -1;
+ break;
+ case 013: // alternate 4-bit + sign
+ case 014: // default 4-bit + sign
+ break;
+ default:
+ // not a trailing sign
+ doFault(FAULT_IPR, fst_ill_proc, "loadDec(): no trailing sign (1)");
+ }
+ break;
+ }
+
+ if (n == e->N1-1 && e->TN1 == CTN9 && e->S1 == CSTS)
+ {
+ sim_debug (DBG_TRACEEXT, & cpu_dev,
+ "loadDec: n N1-1, TN1 CTN9, S1 CSTS\n");
+ switch (c)
+ {
+ case '-':
+ SETF(e->_flags, I_NEG);
+
+ sgn = -1;
+ break;
+ case '+':
+ break;
+ default:
+ // not a trailing sign
+ doFault(FAULT_IPR, fst_ill_proc, "loadDec(): no trailing sign (2)");
+ }
+ break;
+ }
+
+ x *= 10;
+ x += c & 0xf;
+ sim_debug (DBG_TRACEEXT, & cpu_dev,
+ "loadDec: x %"PRId64"\n", (int64) x);
+
+ pos += 1; // onto next posotion
+ }
+
+ e->x = sgn * x;
+ sim_debug (DBG_TRACEEXT, & cpu_dev,
+ "loadDec: final x %"PRId64"\n", (int64) x);
+
+ return 0;
+}
+
+static void EISwriteToBinaryStringReverse(EISaddr *p, int k)
+{
+ EISstruct * e = & cpu.currentEISinstruction;
+#ifdef EIS_PTR
+ long eisaddr_idx = EISADDR_IDX (p);
+if (eisaddr_idx < 0 || eisaddr_idx > 2) { sim_warn ("IDX1"); return }
+#endif
+ /// first thing we need to do is to find out the last position is the buffer we want to start writing to.
+
+ int N = (int) e->N[k-1]; // length of output buffer in native chars (4, 6 or 9-bit chunks)
+ int CN = (int) e->CN[k-1]; // character number 0-3 (9)
+ //word18 address = e->YChar9[k-1]; // current write address
+
+ /// since we want to write the data in reverse (since it's right justified) we need to determine
+ /// the final address/CN for the type and go backwards from there
+
+ int numBits = 9 * N; // 4 9-bit bytes / word
+ //int numWords = numBits / 36; // how many additional words will the N chars take up?
+ //int numWords = (numBits + CN * 9) / 36; // how many additional words will the N chars take up?
+ int numWords = (numBits + CN * 9 + 35) / 36; // how many additional words will the N chars take up?
+ // convert from count to offset
+ int lastWordOffset = numWords - 1;
+ int lastChar = (CN + N - 1) % 4; // last character number
+
+ if (lastWordOffset > 0) // more that the 1 word needed?
+ {
+#ifdef EIS_PTR
+ cpu.du.Dk_PTR_W[eisaddr_idx] += (word18) lastWordOffset; // highest memory address
+ cpu.du.Dk_PTR_W[eisaddr_idx] &= AMASK;
+#else
+ p->address += (word18) lastWordOffset; // highest memory address
+#endif
+ }
+ int pos = lastChar; // last character number
+
+ int128 x = e->x;
+
+ for(int n = 0 ; n < N ; n += 1)
+ {
+ word9 charToWrite = x & 0777; // get 9-bits of data
+ x >>=9;
+
+ // we should write character to word/pos in memory .....
+ //write9r(e, &address, &pos, charToWrite);
+ EISwrite9r(p, &pos, charToWrite);
+ }
+
+ // anything left in x?. If it's not all 1's we have an overflow!
+ if (~x && x != 0) // if it's all 1's this will be 0
+ SETF(e->_flags, I_OFLOW);
+}
+#endif
+
+void dtb (void)
+{
+ EISstruct * e = & cpu.currentEISinstruction;
+
+ fault_ipr_subtype_ mod_fault = 0;
+
+#ifndef EIS_SETUP
+ setupOperandDescriptor(1, &mod_fault);
+ setupOperandDescriptor(2, &mod_fault);
+#endif
+
+ PNL (L68_ (DU_CYCLE_DGDB;))
+
+ parseNumericOperandDescriptor(1, &mod_fault);
+ parseNumericOperandDescriptor(2, &mod_fault);
+
+#ifdef L68
+ // L68 raises it immediately
+ if (mod_fault)
+ {
+ doFault (FAULT_IPR,
+ (_fault_subtype) {.fault_ipr_subtype=mod_fault},
+ "Illegal modifier");
+ }
+#endif
+
+ // Bits 0 to 10 of the instruction Must Be Zero. So Say We ISOLTS.
+ uint mbz = (uint) getbits36 (IWB_IRODD, 0, 11);
+ if (mbz)
+ {
+ doFault (FAULT_IPR, (_fault_subtype) {.fault_ipr_subtype=FR_ILL_OP|mod_fault}, "dtb(): 0-10 MBZ");
+ }
+
+ // Bits 24-29 of OP1 MBZ
+ if (!(e->MF[0] & MFkID) && e -> op [0] & 0000000007700)
+ {
+ doFault (FAULT_IPR, (_fault_subtype) {.fault_ipr_subtype=FR_ILL_PROC|mod_fault}, "dtb op1 24-29 MBZ");
+ }
+
+ // Bits 21-29 of OP2 MBZ
+ if (!(e->MF[1] & MFkID) && e -> op [1] & 0000000077700)
+ {
+ doFault (FAULT_IPR, (_fault_subtype) {.fault_ipr_subtype=FR_ILL_PROC|mod_fault}, "dtb op2 21-29 MBZ");
+ }
+
+ // Attempted conversion of a floating-point number (S1 = 0) or attempted
+ // use of a scaling factor (SF1 ≠ 0) causes an illegal procedure fault.
+ if (e->S1 == 0 || e->SF1 != 0)
+ {
+ doFault(FAULT_IPR, (_fault_subtype) {.fault_ipr_subtype=FR_ILL_PROC|mod_fault}, "dtb(): S1=0 or SF1!=0");
+ }
+
+#ifdef DPS8M
+ // DPS8M raises it delayed
+ if (mod_fault)
+ {
+ doFault (FAULT_IPR,
+ (_fault_subtype) {.fault_ipr_subtype=mod_fault},
+ "Illegal modifier");
+ }
+#endif
+
+ // If N2 = 0 or N2 > 8 an illegal procedure fault occurs.
+ if (e->N2 == 0 || e->N2 > 8)
+ {
+ doFault(FAULT_IPR, fst_ill_proc, "dtb(): N2 = 0 or N2 > 8 etc.");
+ }
+
+
+ int n1 = 0;
+
+ /*
+ * Here we need to distinguish between 4 type of numbers.
+ *
+ * CSFL - Floating-point, leading sign
+ * CSLS - Scaled fixed-point, leading sign
+ * CSTS - Scaled fixed-point, trailing sign
+ * CSNS - Scaled fixed-point, unsigned
+ */
+
+ // determine precision
+ switch(e->S1)
+ {
+ case CSLS:
+ case CSTS:
+ n1 = (int) e->N1 - 1; // only 1 sign
+ break;
+
+ case CSNS:
+ n1 = (int) e->N1; // no sign
+ break; // no sign wysiwyg
+ }
+ // RJ78: An Illegal Procedure fault occurs if:
+ // N1 is not large enough to specify the number of characters required for the
+ // specified sign and/or exponent, plus at least one digit.
+
+ if (n1 < 1)
+ doFault (FAULT_IPR, fst_ill_proc, "dtb adjusted n1<1");
+
+
+ EISloadInputBufferNumeric (1); // according to MF1
+
+ // prepare output mask
+#ifdef NEED_128
+ word72 msk = subtract_128 (lshift_128 (construct_128 (0, 1), (9*e->N2-1)),construct_128 (0, 1));
+#else
+ word72 msk = ((word72)1<<(9*e->N2-1))-1; // excluding sign
+#endif
+
+#if 0
+ decNumber _1;
+ decNumber *op1 = decBCD9ToNumber(e->inBuffer, n1, 0, &_1);
+ if (e->sign == -1)
+ op1->bits |= DECNEG;
+ if (decNumberIsZero(op1))
+ op1->exponent = 127;
+
+sim_printf("dtb: N1 %d N2 %d nin %d CN1 %d CN2 %d msk %012"PRIo64" %012"PRIo64"\n",e->N1,e->N2,n1,e->CN1,e->CN2,(word36)((msk >> 36) & DMASK), (word36)(msk & DMASK));
+ PRINTDEC("dtb input (op1)", op1);
+#endif
+
+ // input is unscaled fixed point, so just get the digits
+ bool Ovr = false;
+#ifdef NEED_128
+ word72 x = construct_128 (0, 0);
+ for (int i = 0; i < n1; i++) {
+ //x *= 10;
+ x = multiply_128 (x, construct_128 (0, 10));
+ //x += e->inBuffer[i];
+ x = add_128 (x, construct_128 (0, (uint) e->inBuffer[i]));
+ //Ovr |= x>msk?1:0;
+ Ovr |= isgt_128 (x, msk) ? 1 : 0;
+ //x &= msk; // multiplication and addition mod msk+1
+ x = and_128 (x, msk); // multiplication and addition mod msk+1
+ }
+ if (e->sign == -1)
+ //x = -x; // no need to mask it
+ x = negate_128 (x); // no need to mask it
+
+#else
+ word72 x = 0;
+ for (int i = 0; i < n1; i++) {
+ x *= 10;
+ x += e->inBuffer[i];
+ //sim_printf("%d %012"PRIo64" %012"PRIo64"\n",e->inBuffer[i],(word36)((x >> 36) & DMASK), (word36)(x & DMASK));
+ Ovr |= x>msk?1:0;
+ x &= msk; // multiplication and addition mod msk+1
+ }
+ if (e->sign == -1)
+ x = -x; // no need to mask it
+
+ //sim_printf ("dtb out %012"PRIo64" %012"PRIo64"\n", (word36)((x >> 36) & DMASK), (word36)(x & DMASK));
+#endif
+ int pos = (int)e->CN2;
+
+ // now write to memory in proper format.....
+
+ int shift = 9*((int)e->N2-1);
+ for(int i = 0; i < (int)e->N2; i++) {
+#ifdef NEED_128
+ EISwrite9(&e->ADDR2, &pos, (word9) rshift_128 (x, (uint) shift).l & 0777);
+#else
+ EISwrite9(&e->ADDR2, &pos, (word9) (x >> shift )& 0777);
+#endif
+ shift -= 9;
+ }
+
+ SC_I_NEG (e->sign == -1); // set negative indicator
+#ifdef NEED_128
+ SC_I_ZERO (iszero_128 (x)); // set zero indicator
+#else
+ SC_I_ZERO (x==0); // set zero indicator
+#endif
+
+ cleanupOperandDescriptor (1);
+ cleanupOperandDescriptor (2);
+
+ if (Ovr)
+ {
+ SET_I_OFLOW;
+ if (tstOVFfault ())
+ doFault(FAULT_OFL, fst_zero, "dtb overflow fault");
+ }
+}
+
+/*
+ * decimal EIS instructions ...
+ */
+
+
+#define ASC(x) ((x) + '0')
+
+/*
+ * ad2d - Add Using Two Decimal Operands
+ */
+
+void ad2d (void)
+{
+ EISstruct * e = & cpu.currentEISinstruction;
+
+ fault_ipr_subtype_ mod_fault = 0;
+
+#ifndef EIS_SETUP
+ setupOperandDescriptor(1, &mod_fault);
+ setupOperandDescriptor(2, &mod_fault);
+ setupOperandDescriptorCache(3);
+#endif
+
+ parseNumericOperandDescriptor(1, &mod_fault);
+ parseNumericOperandDescriptor(2, &mod_fault);
+
+#ifdef L68
+ // L68 raises it immediately
+ if (mod_fault)
+ {
+ doFault (FAULT_IPR,
+ (_fault_subtype) {.fault_ipr_subtype=mod_fault},
+ "Illegal modifier");
+ }
+#endif
+
+ // Bits 1-8 MBZ
+ if (IWB_IRODD & 0377000000000)
+ doFault (FAULT_IPR, fst_ill_op, "ad2d 1-8 MBZ");
+
+#ifdef DPS8M
+ // DPS8M raises it delayed
+ if (mod_fault)
+ {
+ doFault (FAULT_IPR,
+ (_fault_subtype) {.fault_ipr_subtype=mod_fault},
+ "Illegal modifier");
+ }
+#endif
+
+ e->P = getbits36_1 (cpu.cu.IWB, 0) != 0; // 4-bit data sign character control
+ bool T = getbits36_1 (cpu.cu.IWB, 9) != 0; // truncation bit
+ bool R = getbits36_1 (cpu.cu.IWB, 10) != 0; // rounding bit
+
+ PNL (L68_ (if (R)
+ DU_CYCLE_FRND;))
+
+ uint srcTN = e->TN1; // type of chars in src
+
+ uint dstTN = e->TN2; // type of chars in dst
+ uint dstCN = e->CN2; // starting at char pos CN
+
+ e->ADDR3 = e->ADDR2;
+
+ int n1 = 0, n2 = 0, sc1 = 0, sc2 = 0;
+
+ /*
+ * Here we need to distinguish between 4 type of numbers.
+ *
+ * CSFL - Floating-point, leading sign
+ * CSLS - Scaled fixed-point, leading sign
+ * CSTS - Scaled fixed-point, trailing sign
+ * CSNS - Scaled fixed-point, unsigned
+ */
+
+ // determine precision
+ switch(e->S1)
+ {
+ case CSFL:
+ n1 = (int) e->N1 - 1; // need to account for the - sign
+ if (srcTN == CTN4)
+ n1 -= 2; // 2 4-bit digits exponent
+ else
+ n1 -= 1; // 1 9-bit digit exponent
+ sc1 = 0; // no scaling factor
+ break;
+
+ case CSLS:
+ case CSTS:
+ n1 = (int) e->N1 - 1; // only 1 sign
+ sc1 = -e->SF1;
+ break;
+
+ case CSNS:
+ n1 = (int) e->N1; // no sign
+ sc1 = -e->SF1;
+ break; // no sign wysiwyg
+ }
+
+ if (n1 < 1)
+ doFault (FAULT_IPR, fst_ill_proc, "ad2d adjusted n1<1");
+
+ switch(e->S2)
+ {
+ case CSFL:
+ n2 = (int) e->N2 - 1; // need to account for the sign
+ if (e->TN2 == CTN4)
+ n2 -= 2; // 2 4-bit digit exponent
+ else
+ n2 -= 1; // 1 9-bit digit exponent
+ sc2 = 0; // no scaling factor
+ break;
+
+ case CSLS:
+ case CSTS:
+ n2 = (int) e->N2 - 1; // 1 sign
+ sc2 = -e->SF2;
+ break;
+
+ case CSNS:
+ n2 = (int) e->N2; // no sign
+ sc2 = -e->SF2;
+ break; // no sign wysiwyg
+ }
+
+ if (n2 < 1)
+ doFault (FAULT_IPR, fst_ill_proc, "ad2d adjusted n2<1");
+
+
+ decContext set;
+ //decContextDefault(&set, DEC_INIT_BASE); // initialize
+ decContextDefaultDPS8(&set);
+
+ set.traps=0;
+
+ decNumber _1, _2, _3;
+
+ EISloadInputBufferNumeric (1); // according to MF1
+
+ decNumber *op1 = decBCD9ToNumber(e->inBuffer, n1, sc1, &_1);
+ if (e->sign == -1)
+ op1->bits |= DECNEG;
+ if (e->S1 == CSFL)
+ op1->exponent = e->exponent;
+
+ EISloadInputBufferNumeric (2); // according to MF2
+
+ decNumber *op2 = decBCD9ToNumber(e->inBuffer, n2, sc2, &_2);
+ if (e->sign == -1)
+ op2->bits |= DECNEG;
+ if (e->S2 == CSFL)
+ op2->exponent = e->exponent;
+
+ decNumber *op3 = decNumberAdd(&_3, op1, op2, &set);
+
+ // ISOLTS 846 07c, 10a, 11b internal register overflow - see ad3d
+ bool iOvr = 0;
+ if (op3->digits > 63) {
+ uint8_t pr[256];
+ // if sf<=0, trailing zeroes can't be shifted out
+ // if sf> 0, (some of) trailing zeroes can be shifted out
+ int sf = e->S3==CSFL?op3->exponent:e->SF3;
+
+ int ctz = 0;
+ if (sf>0) { // optimize: we don't care when sf>0
+ decNumberGetBCD(op3,pr);
+ for (int i=op3->digits-1;i>=0 && pr[i]==0;i--)
+ ctz ++;
+ }
+
+ if (op3->digits - min(max(sf,0),ctz) > 63) {
+
+ enum rounding safeR = decContextGetRounding(&set); // save rounding mode
+ int safe = set.digits;
+ decNumber tmp;
+
+ // discard MS digits
+ decContextSetRounding(&set, DEC_ROUND_DOWN); // Round towards 0 (truncation).
+ set.digits = op3->digits - min(max(sf,0),ctz) - 63;
+ decNumberPlus(&tmp, op3, &set);
+ set.digits = safe;
+
+ decNumberSubtract(op3, op3, &tmp, &set);
+
+ //decNumberToString(op3,(char*)pr); sim_printf("discarded: %s\n",pr);
+
+ decContextSetRounding(&set, safeR);
+ iOvr = 1;
+ }
+ }
+
+ bool Ovr = false, EOvr = false, Trunc = false;
+
+ uint8_t out [256];
+ char *res = formatDecimal(out, &set, op3, n2, (int) e->S2, e->SF2, R, &Ovr, &Trunc);
+
+ Ovr |= iOvr;
+
+ if (decNumberIsZero(op3))
+ op3->exponent = 127;
+
+ //printf("%s\r\n", res);
+
+ // now write to memory in proper format.....
+
+ //word18 dstAddr = e->dstAddr;
+ int pos = (int) dstCN;
+
+ // 1st, take care of any leading sign .......
+ switch(e->S2)
+ {
+ case CSFL: // floating-point, leading sign.
+ case CSLS: // fixed-point, leading sign
+ switch(dstTN)
+ {
+ case CTN4:
+ if (e->P) //If TN2 and S2 specify a 4-bit signed number and P = 1, then the 13(8) plus sign character is placed appropriately if the result of the operation is positive.
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, (decNumberIsNegative(op3) && !decNumberIsZero(op3)) ? 015 : 013); // special +
+ else
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, (decNumberIsNegative(op3) && !decNumberIsZero(op3)) ? 015 : 014); // default +
+ break;
+ case CTN9:
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, (decNumberIsNegative(op3) && !decNumberIsZero(op3)) ? '-' : '+');
+ break;
+ }
+ break;
+
+ case CSTS: // nuttin' to do here .....
+ case CSNS:
+ break; // no sign wysiwyg
+ }
+
+ // 2nd, write the digits .....
+ for(int j = 0 ; j < n2 ; j++)
+ switch(dstTN)
+ {
+ case CTN4:
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, (word9) (res[j] - '0'));
+ break;
+ case CTN9:
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, (word9) res[j]);
+ break;
+ }
+
+ // 3rd, take care of any trailing sign or exponent ...
+ switch(e->S2)
+ {
+ case CSTS: // write trailing sign ....
+ switch(dstTN)
+ {
+ case CTN4:
+ if (e->P) //If TN2 and S2 specify a 4-bit signed number and P = 1, then the 13(8) plus sign character is placed appropriately if the result of the operation is positive.
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, (decNumberIsNegative(op3) && !decNumberIsZero(op3)) ? 015 : 013); // special +
+ else
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, (decNumberIsNegative(op3) && !decNumberIsZero(op3)) ? 015 : 014); // default +
+ break;
+ case CTN9:
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, (decNumberIsNegative(op3) && !decNumberIsZero(op3)) ? '-' : '+');
+ break;
+ }
+ break;
+
+ case CSFL: // floating-point, leading sign.
+ // write the exponent
+ switch(dstTN)
+ {
+ case CTN4:
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, (op3->exponent >> 4) & 0xf); // upper 4-bits
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, op3->exponent & 0xf); // lower 4-bits
+ break;
+ case CTN9:
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, op3->exponent & 0xff); // write 8-bit exponent
+ break;
+ }
+ break;
+
+ case CSLS: // fixed point, leading sign - already done
+ case CSNS: // fixed point, unsigned - nuttin' needed to do
+ break;
+ }
+
+ // set flags, etc ...
+ if (e->S2 == CSFL)
+ {
+ if (op3->exponent > 127)
+ {
+ SET_I_EOFL;
+ EOvr = true;
+ }
+ if (op3->exponent < -128)
+ {
+ SET_I_EUFL;
+ EOvr = true;
+ }
+ }
+
+ SC_I_NEG (decNumberIsNegative(op3) && !decNumberIsZero(op3)); // set negative indicator if op3 < 0
+ SC_I_ZERO (decNumberIsZero(op3)); // set zero indicator if op3 == 0
+
+ SC_I_TRUNC (!R && Trunc); // If the truncation condition exists without rounding, then ON; otherwise OFF
+
+ cleanupOperandDescriptor (1);
+ cleanupOperandDescriptor (2);
+ cleanupOperandDescriptor (3);
+
+ if (TST_I_TRUNC && T && tstOVFfault ())
+ doFault(FAULT_OFL, fst_zero, "ad2d truncation(overflow) fault");
+ if (EOvr && tstOVFfault ())
+ doFault(FAULT_OFL, fst_zero, "ad2d over/underflow fault");
+ if (Ovr)
+ {
+ SET_I_OFLOW;
+ if (tstOVFfault ())
+ doFault(FAULT_OFL, fst_zero, "ad2d overflow fault");
+ }
+}
+
+#if 0
+static const char *CS[] = {"CSFL", "CSLS", "CSTS", "CSNS"};
+static const char *CTN[] = {"CTN9", "CTN4"};
+
+static int calcSF(int sf1, int sf2, int sf3)
+{
+ //If the result is given by a fixed-point, operations are performed by justifying the scaling factors (SF1, SF2, and SF3) of the operands 1, 2, and 3:
+ //If SF1 > SF2
+ // SF1 > SF2 >= SF3 —> Justify to SF2
+ // SF3 > SF1 > SF2 —> Justify to SF1
+ // SF1 >= SF3 > SF1 —>Justify to SF3-1
+ //If SF2 > SF1
+ // SF2 > SF1 >= SF3 —> Justify to SF1
+ // SF3 > SF2 > SF1 —> Justify to SF2
+ // SF2 >= SF3 > SF1 —> Justify to SF3-1
+ //
+ if (sf1 > sf2)
+ {
+ if (sf1 > sf2 && sf2 >= sf3)
+ return sf2;
+
+ if (sf3 > sf1 && sf1 > sf2)
+ return sf1;
+
+ if (sf1 >= sf3 && sf3 > sf1)
+ return sf3-1;
+ }
+ if (sf2 > sf1)
+ {
+ if (sf2 > sf1 && sf1 >= sf3)
+ return sf1;
+ if (sf3 > sf2 && sf2 > sf1)
+ return sf2;
+ if (sf2 >= sf3 && sf3 > sf1)
+ return sf3-1;
+ }
+ return sf3;
+}
+#endif
+
+/*
+ * ad3d - Add Using Three Decimal Operands
+ */
+
+void ad3d (void)
+{
+ EISstruct * e = & cpu.currentEISinstruction;
+
+ fault_ipr_subtype_ mod_fault = 0;
+
+#ifndef EIS_SETUP
+ setupOperandDescriptor(1, &mod_fault);
+ setupOperandDescriptor(2, &mod_fault);
+ setupOperandDescriptor(3, &mod_fault);
+#endif
+
+ parseNumericOperandDescriptor(1, &mod_fault);
+ parseNumericOperandDescriptor(2, &mod_fault);
+ parseNumericOperandDescriptor(3, &mod_fault);
+
+#ifdef L68
+ // L68 raises it immediately
+ if (mod_fault)
+ {
+ doFault (FAULT_IPR,
+ (_fault_subtype) {.fault_ipr_subtype=mod_fault},
+ "Illegal modifier");
+ }
+#endif
+
+ // Bit 1 MBZ
+ if (IWB_IRODD & 0200000000000)
+ doFault (FAULT_IPR, (_fault_subtype) {.fault_ipr_subtype=FR_ILL_OP|mod_fault}, "ad3d(): 1 MBZ");
+
+#ifdef DPS8M
+ // DPS8M raises it delayed
+ if (mod_fault)
+ {
+ doFault (FAULT_IPR,
+ (_fault_subtype) {.fault_ipr_subtype=mod_fault},
+ "Illegal modifier");
+ }
+#endif
+
+ // initialize mop flags. Probably best done elsewhere.
+ e->P = getbits36_1 (cpu.cu.IWB, 0) != 0; // 4-bit data sign character control
+ bool T = getbits36_1 (cpu.cu.IWB, 9) != 0; // truncation bit
+ bool R = getbits36_1 (cpu.cu.IWB, 10) != 0; // rounding bit
+
+ PNL (L68_ (if (R)
+ DU_CYCLE_FRND;))
+
+ uint srcTN = e->TN1; // type of chars in src
+
+ uint dstTN = e->TN3; // type of chars in dst
+ uint dstCN = e->CN3; // starting at char pos CN
+
+ int n1 = 0, n2 = 0, n3 = 0, sc1 = 0, sc2 = 0;
+
+ /*
+ * Here we need to distinguish between 4 type of numbers.
+ *
+ * CSFL - Floating-point, leading sign
+ * CSLS - Scaled fixed-point, leading sign
+ * CSTS - Scaled fixed-point, trailing sign
+ * CSNS - Scaled fixed-point, unsigned
+ */
+
+ // determine precision
+ switch(e->S1)
+ {
+ case CSFL:
+ n1 = (int) e->N1 - 1; // need to account for the - sign
+ if (srcTN == CTN4)
+ n1 -= 2; // 2 4-bit digits exponent
+ else
+ n1 -= 1; // 1 9-bit digit exponent
+ sc1 = 0; // no scaling factor
+ break;
+
+ case CSLS:
+ case CSTS:
+ n1 = (int) e->N1 - 1; // only 1 sign
+ sc1 = -e->SF1;
+ break;
+
+ case CSNS:
+ n1 = (int) e->N1; // no sign
+ sc1 = -e->SF1;
+ break; // no sign wysiwyg
+ }
+
+ if (n1 < 1)
+ doFault (FAULT_IPR, fst_ill_proc, "ad3d adjusted n1<1");
+
+ switch(e->S2)
+ {
+ case CSFL:
+ n2 = (int) e->N2 - 1; // need to account for the sign
+ if (e->TN2 == CTN4)
+ n2 -= 2; // 2 4-bit digit exponent
+ else
+ n2 -= 1; // 1 9-bit digit exponent
+ sc2 = 0; // no scaling factor
+ break;
+
+ case CSLS:
+ case CSTS:
+ n2 = (int) e->N2 - 1; // 1 sign
+ sc2 = -e->SF2;
+ break;
+
+ case CSNS:
+ n2 = (int) e->N2; // no sign
+ sc2 = -e->SF2;
+ break; // no sign wysiwyg
+ }
+
+ if (n2 < 1)
+ doFault (FAULT_IPR, fst_ill_proc, "ad3d adjusted n2<1");
+
+ switch(e->S3)
+ {
+ case CSFL:
+ n3 = (int) e->N3 - 1; // need to account for the sign
+ if (dstTN == CTN4)
+ n3 -= 2; // 2 4-bit digit exponent
+ else
+ n3 -= 1; // 1 9-bit digit exponent
+ break;
+
+ case CSLS:
+ case CSTS:
+ n3 = (int) e->N3 - 1; // 1 sign
+ break;
+
+ case CSNS:
+ n3 = (int) e->N3; // no sign
+ break; // no sign wysiwyg
+ }
+
+ if (n3 < 1)
+ doFault (FAULT_IPR, fst_ill_proc, "ad3d adjusted n3<1");
+
+
+ decContext set;
+ //decContextDefault(&set, DEC_INIT_BASE); // initialize
+ decContextDefaultDPS8(&set);
+ set.traps=0;
+
+ decNumber _1, _2, _3;
+
+ EISloadInputBufferNumeric (1); // according to MF1
+
+ decNumber *op1 = decBCD9ToNumber(e->inBuffer, n1, sc1, &_1);
+ if (e->sign == -1)
+ op1->bits |= DECNEG;
+ if (e->S1 == CSFL)
+ op1->exponent = e->exponent;
+
+ EISloadInputBufferNumeric (2); // according to MF2
+
+ decNumber *op2 = decBCD9ToNumber(e->inBuffer, n2, sc2, &_2);
+ if (e->sign == -1)
+ op2->bits |= DECNEG;
+ if (e->S2 == CSFL)
+ op2->exponent = e->exponent;
+
+ decNumber *op3 = decNumberAdd(&_3, op1, op2, &set);
+
+ // RJ78: significant digits in the result may be lost if:
+ // The difference between the scaling factors (exponents) of the source
+ // operands is large enough to cause the expected length of the intermediate
+ // result to exceed 63 digits after decimal point alignment of source operands, followed by addition.
+ // ISOLTS 846 07c, 10a, 11b internal register overflow
+ // trailing zeros are not counted towards the limit
+ // XXX it is not clear which digits are lost, but I suppose it should be the most significant. ISOLTS doesn't check for this
+ // XXX the algorithm should be similar/the same as dv3d NQ? It is to some extent already...
+ bool iOvr = 0;
+ if (op3->digits > 63) {
+ uint8_t pr[256];
+ // if sf<=0, trailing zeroes can't be shifted out
+ // if sf> 0, (some of) trailing zeroes can be shifted out
+ int sf = e->S3==CSFL?op3->exponent:e->SF3;
+
+ int ctz = 0;
+ if (sf>0) { // optimize: we don't care when sf>0
+ decNumberGetBCD(op3,pr);
+ for (int i=op3->digits-1;i>=0 && pr[i]==0;i--)
+ ctz ++;
+ }
+
+ if (op3->digits - min(max(sf,0),ctz) > 63) {
+
+ enum rounding safeR = decContextGetRounding(&set); // save rounding mode
+ int safe = set.digits;
+ decNumber tmp;
+
+ // discard MS digits
+ decContextSetRounding(&set, DEC_ROUND_DOWN); // Round towards 0 (truncation).
+ set.digits = op3->digits - min(max(sf,0),ctz) - 63;
+ decNumberPlus(&tmp, op3, &set);
+ set.digits = safe;
+
+ decNumberSubtract(op3, op3, &tmp, &set);
+
+ //decNumberToString(op3,(char*)pr); sim_printf("discarded: %s\n",pr);
+
+ decContextSetRounding(&set, safeR);
+ iOvr = 1;
+ }
+ }
+
+ bool Ovr = false, EOvr = false, Trunc = false;
+
+ uint8_t out [256];
+ char *res = formatDecimal(out, &set, op3, n3, (int) e->S3, e->SF3, R, &Ovr, &Trunc);
+
+ Ovr |= iOvr;
+
+ if (decNumberIsZero(op3))
+ op3->exponent = 127;
+
+ //printf("%s\r\n", res);
+
+ // now write to memory in proper format.....
+
+ //word18 dstAddr = e->dstAddr;
+ int pos = (int) dstCN;
+
+ // 1st, take care of any leading sign .......
+ switch(e->S3)
+ {
+ case CSFL: // floating-point, leading sign.
+ case CSLS: // fixed-point, leading sign
+ switch(dstTN)
+ {
+ case CTN4:
+ if (e->P) //If TN2 and S2 specify a 4-bit signed number and P = 1, then the 13(8) plus sign character is placed appropriately if the result of the operation is positive.
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, (decNumberIsNegative(op3) && !decNumberIsZero(op3)) ? 015 : 013); // special +
+ else
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, (decNumberIsNegative(op3) && !decNumberIsZero(op3)) ? 015 : 014); // default +
+ break;
+ case CTN9:
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, (decNumberIsNegative(op3) && !decNumberIsZero(op3)) ? '-' : '+');
+ break;
+ }
+ break;
+
+ case CSTS: // nuttin' to do here .....
+ case CSNS:
+ break; // no sign wysiwyg
+ }
+
+ // 2nd, write the digits .....
+ for(int i = 0 ; i < n3 ; i++)
+ switch(dstTN)
+ {
+ case CTN4:
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, (word9) (res[i] - '0'));
+ break;
+ case CTN9:
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, (word9) res[i]);
+ break;
+ }
+
+ // 3rd, take care of any trailing sign or exponent ...
+ switch(e->S3)
+ {
+ case CSTS: // write trailing sign ....
+ switch(dstTN)
+ {
+ case CTN4:
+ if (e->P) //If TN2 and S2 specify a 4-bit signed number and P = 1, then the 13(8) plus sign character is placed appropriately if the result of the operation is positive.
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, (decNumberIsNegative(op3) && !decNumberIsZero(op3)) ? 015 : 013); // special +
+ else
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, (decNumberIsNegative(op3) && !decNumberIsZero(op3)) ? 015 : 014); // default +
+ break;
+ case CTN9:
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, (decNumberIsNegative(op3) && !decNumberIsZero(op3)) ? '-' : '+');
+ break;
+ }
+ break;
+
+ case CSFL: // floating-point, leading sign.
+ // write the exponent
+ switch(dstTN)
+ {
+ case CTN4:
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, (op3->exponent >> 4) & 0xf); // upper 4-bits
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, op3->exponent & 0xf); // lower 4-bits
+
+ break;
+ case CTN9:
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, op3->exponent & 0xff); // write 8-bit exponent
+ break;
+ }
+ break;
+
+ case CSLS: // fixed point, leading sign - already done
+ case CSNS: // fixed point, unsigned - nuttin' needed to do
+ break;
+ }
+
+ // set flags, etc ...
+ if (e->S3 == CSFL)
+ {
+ if (op3->exponent > 127)
+ {
+ SET_I_EOFL;
+ EOvr = true;
+ }
+ if (op3->exponent < -128)
+ {
+ SET_I_EUFL;
+ EOvr = true;
+ }
+ }
+
+ SC_I_NEG (decNumberIsNegative(op3) && !decNumberIsZero(op3)); // set negative indicator if op3 < 0
+ SC_I_ZERO (decNumberIsZero(op3)); // set zero indicator if op3 == 0
+
+ SC_I_TRUNC (!R && Trunc); // If the truncation condition exists without rounding, then ON; otherwise OFF
+
+ cleanupOperandDescriptor (1);
+ cleanupOperandDescriptor (2);
+ cleanupOperandDescriptor (3);
+
+ if (TST_I_TRUNC && T && tstOVFfault ())
+ doFault(FAULT_OFL, fst_zero, "ad3d truncation(overflow) fault");
+ if (EOvr && tstOVFfault ())
+ doFault(FAULT_OFL, fst_zero, "ad3d over/underflow fault");
+ if (Ovr)
+ {
+ SET_I_OFLOW;
+ if (tstOVFfault ())
+ doFault(FAULT_OFL, fst_zero, "ad3d overflow fault");
+ }
+}
+
+/*
+ * sb2d - Subtract Using Two Decimal Operands
+ */
+
+void sb2d (void)
+{
+ EISstruct * e = & cpu.currentEISinstruction;
+
+ fault_ipr_subtype_ mod_fault = 0;
+
+#ifndef EIS_SETUP
+ setupOperandDescriptor(1, &mod_fault);
+ setupOperandDescriptor(2, &mod_fault);
+ setupOperandDescriptorCache(3);
+#endif
+
+ parseNumericOperandDescriptor(1, &mod_fault);
+ parseNumericOperandDescriptor(2, &mod_fault);
+
+#ifdef L68
+ // L68 raises it immediately
+ if (mod_fault)
+ {
+ doFault (FAULT_IPR,
+ (_fault_subtype) {.fault_ipr_subtype=mod_fault},
+ "Illegal modifier");
+ }
+#endif
+
+ // Bits 1-8 MBZ
+ if (IWB_IRODD & 0377000000000)
+ {
+ //sim_printf ("sb2d %012"PRIo64"\n", IWB_IRODD);
+ doFault (FAULT_IPR, (_fault_subtype) {.fault_ipr_subtype=FR_ILL_OP|mod_fault}, "sb2d 0-8 MBZ");
+ }
+
+#ifdef DPS8M
+ // DPS8M raises it delayed
+ if (mod_fault)
+ {
+ doFault (FAULT_IPR,
+ (_fault_subtype) {.fault_ipr_subtype=mod_fault},
+ "Illegal modifier");
+ }
+#endif
+
+ e->P = getbits36_1 (cpu.cu.IWB, 0) != 0; // 4-bit data sign character control
+ bool T = getbits36_1 (cpu.cu.IWB, 9) != 0; // truncation bit
+ bool R = getbits36_1 (cpu.cu.IWB, 10) != 0; // rounding bit
+
+ PNL (L68_ (if (R)
+ DU_CYCLE_FRND;))
+
+
+ uint srcTN = e->TN1; // type of chars in src
+
+ uint dstTN = e->TN2; // type of chars in dst
+ uint dstCN = e->CN2; // starting at char pos CN
+
+ e->ADDR3 = e->ADDR2;
+
+ int n1 = 0, n2 = 0, sc1 = 0, sc2 = 0;
+
+ /*
+ * Here we need to distinguish between 4 type of numbers.
+ *
+ * CSFL - Floating-point, leading sign
+ * CSLS - Scaled fixed-point, leading sign
+ * CSTS - Scaled fixed-point, trailing sign
+ * CSNS - Scaled fixed-point, unsigned
+ */
+
+ // determine precision
+ switch(e->S1)
+ {
+ case CSFL:
+ n1 = (int) e->N1 - 1; // need to account for the - sign
+ if (srcTN == CTN4)
+ n1 -= 2; // 2 4-bit digits exponent
+ else
+ n1 -= 1; // 1 9-bit digit exponent
+ sc1 = 0; // no scaling factor
+ break;
+
+ case CSLS:
+ case CSTS:
+ n1 = (int) e->N1 - 1; // only 1 sign
+ sc1 = -e->SF1;
+ break;
+
+ case CSNS:
+ n1 = (int) e->N1; // no sign
+ sc1 = -e->SF1;
+ break; // no sign wysiwyg
+ }
+
+ if (n1 < 1)
+ doFault (FAULT_IPR, fst_ill_proc, "sb2d adjusted n1<1");
+
+ switch(e->S2)
+ {
+ case CSFL:
+ n2 = (int) e->N2 - 1; // need to account for the sign
+ if (e->TN2 == CTN4)
+ n2 -= 2; // 2 4-bit digit exponent
+ else
+ n2 -= 1; // 1 9-bit digit exponent
+ sc2 = 0; // no scaling factor
+ break;
+
+ case CSLS:
+ case CSTS:
+ n2 = (int) e->N2 - 1; // 1 sign
+ sc2 = -e->SF2;
+ break;
+
+ case CSNS:
+ n2 = (int) e->N2; // no sign
+ sc2 = -e->SF2;
+ break; // no sign wysiwyg
+ }
+
+ if (n2 < 1)
+ doFault (FAULT_IPR, fst_ill_proc, "sb2d adjusted n2<1");
+
+
+ decContext set;
+ //decContextDefault(&set, DEC_INIT_BASE); // initialize
+ decContextDefaultDPS8(&set);
+ set.traps=0;
+
+ decNumber _1, _2, _3;
+
+ EISloadInputBufferNumeric (1); // according to MF1
+
+ decNumber *op1 = decBCD9ToNumber(e->inBuffer, n1, sc1, &_1);
+ if (e->sign == -1)
+ op1->bits |= DECNEG;
+ if (e->S1 == CSFL)
+ op1->exponent = e->exponent;
+
+ EISloadInputBufferNumeric (2); // according to MF2
+
+ decNumber *op2 = decBCD9ToNumber(e->inBuffer, n2, sc2, &_2);
+ if (e->sign == -1)
+ op2->bits |= DECNEG;
+ if (e->S2 == CSFL)
+ op2->exponent = e->exponent;
+
+ decNumber *op3 = decNumberSubtract(&_3, op2, op1, &set);
+
+ // ISOLTS 846 07c, 10a, 11b internal register overflow - see ad3d
+ bool iOvr = 0;
+ if (op3->digits > 63) {
+ uint8_t pr[256];
+ // if sf<=0, trailing zeroes can't be shifted out
+ // if sf> 0, (some of) trailing zeroes can be shifted out
+ int sf = e->S3==CSFL?op3->exponent:e->SF3;
+
+ int ctz = 0;
+ if (sf>0) { // optimize: we don't care when sf>0
+ decNumberGetBCD(op3,pr);
+ for (int i=op3->digits-1;i>=0 && pr[i]==0;i--)
+ ctz ++;
+ }
+
+ if (op3->digits - min(max(sf,0),ctz) > 63) {
+
+ enum rounding safeR = decContextGetRounding(&set); // save rounding mode
+ int safe = set.digits;
+ decNumber tmp;
+
+ // discard MS digits
+ decContextSetRounding(&set, DEC_ROUND_DOWN); // Round towards 0 (truncation).
+ set.digits = op3->digits - min(max(sf,0),ctz) - 63;
+ decNumberPlus(&tmp, op3, &set);
+ set.digits = safe;
+
+ decNumberSubtract(op3, op3, &tmp, &set);
+
+ //decNumberToString(op3,(char*)pr); sim_printf("discarded: %s\n",pr);
+
+ decContextSetRounding(&set, safeR);
+ iOvr = 1;
+ }
+ }
+
+ bool Ovr = false, EOvr = false, Trunc = false;
+
+ uint8_t out [256];
+ char *res = formatDecimal(out, &set, op3, n2, (int) e->S2, e->SF2, R, &Ovr, &Trunc);
+
+ Ovr |= iOvr;
+
+ if (decNumberIsZero(op3))
+ op3->exponent = 127;
+
+ //printf("%s\r\n", res);
+
+ // now write to memory in proper format.....
+
+ //word18 dstAddr = e->dstAddr;
+ int pos = (int) dstCN;
+
+ // 1st, take care of any leading sign .......
+ switch(e->S2)
+ {
+ case CSFL: // floating-point, leading sign.
+ case CSLS: // fixed-point, leading sign
+ switch(dstTN)
+ {
+ case CTN4:
+ if (e->P) //If TN2 and S2 specify a 4-bit signed number and P = 1, then the 13(8) plus sign character is placed appropriately if the result of the operation is positive.
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, (decNumberIsNegative(op3) && !decNumberIsZero(op3)) ? 015 : 013); // special +
+ else
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, (decNumberIsNegative(op3) && !decNumberIsZero(op3)) ? 015 : 014); // default +
+ break;
+ case CTN9:
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, (decNumberIsNegative(op3) && !decNumberIsZero(op3)) ? '-' : '+');
+ break;
+ }
+ break;
+
+ case CSTS: // nuttin' to do here .....
+ case CSNS:
+ break; // no sign wysiwyg
+ }
+
+ // 2nd, write the digits .....
+ for(int i = 0 ; i < n2 ; i++)
+ switch(dstTN)
+ {
+ case CTN4:
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, (word9) (res[i] - '0'));
+ break;
+ case CTN9:
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, (word9) res[i]);
+ break;
+ }
+
+ // 3rd, take care of any trailing sign or exponent ...
+ switch(e->S2)
+ {
+ case CSTS: // write trailing sign ....
+ switch(dstTN)
+ {
+ case CTN4:
+ if (e->P) //If TN2 and S2 specify a 4-bit signed number and P = 1, then the 13(8) plus sign character is placed appropriately if the result of the operation is positive.
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, (decNumberIsNegative(op3) && !decNumberIsZero(op3)) ? 015 : 013); // special +
+ else
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, (decNumberIsNegative(op3) && !decNumberIsZero(op3)) ? 015 : 014); // default +
+ break;
+ case CTN9:
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, (decNumberIsNegative(op3) && !decNumberIsZero(op3)) ? '-' : '+');
+ break;
+ }
+ break;
+
+ case CSFL: // floating-point, leading sign.
+ // write the exponent
+ switch(dstTN)
+ {
+ case CTN4:
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, (op3->exponent >> 4) & 0xf); // upper 4-bits
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, op3->exponent & 0xf); // lower 4-bits
+
+ break;
+ case CTN9:
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, op3->exponent & 0xff); // write 8-bit exponent
+ break;
+ }
+ break;
+
+ case CSLS: // fixed point, leading sign - already done
+ case CSNS: // fixed point, unsigned - nuttin' needed to do
+ break;
+ }
+
+ // set flags, etc ...
+ if (e->S2 == CSFL)
+ {
+ if (op3->exponent > 127)
+ {
+ SET_I_EOFL;
+ EOvr = true;
+ }
+ if (op3->exponent < -128)
+ {
+ SET_I_EUFL;
+ EOvr = true;
+ }
+ }
+
+ SC_I_NEG (decNumberIsNegative(op3) && !decNumberIsZero(op3)); // set negative indicator if op3 < 0
+ SC_I_ZERO (decNumberIsZero(op3)); // set zero indicator if op3 == 0
+
+ SC_I_TRUNC (!R && Trunc); // If the truncation condition exists without rounding, then ON; otherwise OFF
+
+ cleanupOperandDescriptor (1);
+ cleanupOperandDescriptor (2);
+ cleanupOperandDescriptor (3);
+
+ if (TST_I_TRUNC && T && tstOVFfault ())
+ doFault(FAULT_OFL, fst_zero, "sb2d truncation(overflow) fault");
+ if (EOvr && tstOVFfault ())
+ doFault(FAULT_OFL, fst_zero, "sb2d over/underflow fault");
+ if (Ovr)
+ {
+ SET_I_OFLOW;
+ if (tstOVFfault ())
+ doFault(FAULT_OFL, fst_zero, "sb2d overflow fault");
+ }
+}
+
+/*
+ * sb3d - Subtract Using Three Decimal Operands
+ */
+
+void sb3d (void)
+{
+ EISstruct * e = & cpu.currentEISinstruction;
+
+ fault_ipr_subtype_ mod_fault = 0;
+
+#ifndef EIS_SETUP
+ setupOperandDescriptor(1, &mod_fault);
+ setupOperandDescriptor(2, &mod_fault);
+ setupOperandDescriptor(3, &mod_fault);
+#endif
+
+ parseNumericOperandDescriptor(1, &mod_fault);
+ parseNumericOperandDescriptor(2, &mod_fault);
+ parseNumericOperandDescriptor(3, &mod_fault);
+
+#ifdef L68
+ // L68 raises it immediately
+ if (mod_fault)
+ {
+ doFault (FAULT_IPR,
+ (_fault_subtype) {.fault_ipr_subtype=mod_fault},
+ "Illegal modifier");
+ }
+#endif
+
+ // Bit 1 MBZ
+ if (IWB_IRODD & 0200000000000)
+ doFault (FAULT_IPR, (_fault_subtype) {.fault_ipr_subtype=FR_ILL_OP|mod_fault}, "sb3d(): 1 MBZ");
+
+#ifdef DPS8M
+ // DPS8M raises it delayed
+ if (mod_fault)
+ {
+ doFault (FAULT_IPR,
+ (_fault_subtype) {.fault_ipr_subtype=mod_fault},
+ "Illegal modifier");
+ }
+#endif
+
+ e->P = getbits36_1 (cpu.cu.IWB, 0) != 0; // 4-bit data sign character control
+ bool T = getbits36_1 (cpu.cu.IWB, 9) != 0; // truncation bit
+ bool R = getbits36_1 (cpu.cu.IWB, 10) != 0; // rounding bit
+
+ PNL (L68_ (if (R)
+ DU_CYCLE_FRND;))
+
+ uint srcTN = e->TN1; // type of chars in src
+
+ uint dstTN = e->TN3; // type of chars in dst
+ uint dstCN = e->CN3; // starting at char pos CN
+
+ int n1 = 0, n2 = 0, n3 = 0, sc1 = 0, sc2 = 0;
+
+ /*
+ * Here we need to distinguish between 4 type of numbers.
+ *
+ * CSFL - Floating-point, leading sign
+ * CSLS - Scaled fixed-point, leading sign
+ * CSTS - Scaled fixed-point, trailing sign
+ * CSNS - Scaled fixed-point, unsigned
+ */
+
+ // determine precision
+ switch(e->S1)
+ {
+ case CSFL:
+ n1 = (int) e->N1 - 1; // need to account for the - sign
+ if (srcTN == CTN4)
+ n1 -= 2; // 2 4-bit digits exponent
+ else
+ n1 -= 1; // 1 9-bit digit exponent
+ sc1 = 0; // no scaling factor
+ break;
+
+ case CSLS:
+ case CSTS:
+ n1 = (int) e->N1 - 1; // only 1 sign
+ sc1 = -e->SF1;
+ break;
+
+ case CSNS:
+ n1 = (int) e->N1; // no sign
+ sc1 = -e->SF1;
+ break; // no sign wysiwyg
+ }
+
+ if (n1 < 1)
+ doFault (FAULT_IPR, fst_ill_proc, "sb3d adjusted n1<1");
+
+ switch(e->S2)
+ {
+ case CSFL:
+ n2 = (int) e->N2 - 1; // need to account for the sign
+ if (e->TN2 == CTN4)
+ n2 -= 2; // 2 4-bit digit exponent
+ else
+ n2 -= 1; // 1 9-bit digit exponent
+ sc2 = 0; // no scaling factor
+ break;
+
+ case CSLS:
+ case CSTS:
+ n2 = (int) e->N2 - 1; // 1 sign
+ sc2 = -e->SF2;
+ break;
+
+ case CSNS:
+ n2 = (int) e->N2; // no sign
+ sc2 = -e->SF2;
+ break; // no sign wysiwyg
+ }
+
+ if (n2 < 1)
+ doFault (FAULT_IPR, fst_ill_proc, "sb3d adjusted n2<1");
+
+ switch(e->S3)
+ {
+ case CSFL:
+ n3 = (int) e->N3 - 1; // need to account for the sign
+ if (dstTN == CTN4)
+ n3 -= 2; // 2 4-bit digit exponent
+ else
+ n3 -= 1; // 1 9-bit digit exponent
+ break;
+
+ case CSLS:
+ case CSTS:
+ n3 = (int) e->N3 - 1; // 1 sign
+ break;
+
+ case CSNS:
+ n3 = (int) e->N3; // no sign
+ break; // no sign wysiwyg
+ }
+
+ if (n3 < 1)
+ doFault (FAULT_IPR, fst_ill_proc, "sb3d adjusted n3<1");
+
+
+ decContext set;
+ //decContextDefault(&set, DEC_INIT_BASE); // initialize
+ decContextDefaultDPS8(&set);
+
+ set.traps=0;
+
+ decNumber _1, _2, _3;
+
+ EISloadInputBufferNumeric (1); // according to MF1
+
+ decNumber *op1 = decBCD9ToNumber(e->inBuffer, n1, sc1, &_1);
+ if (e->sign == -1)
+ op1->bits |= DECNEG;
+ if (e->S1 == CSFL)
+ op1->exponent = e->exponent;
+
+ EISloadInputBufferNumeric (2); // according to MF2
+
+ decNumber *op2 = decBCD9ToNumber(e->inBuffer, n2, sc2, &_2);
+ if (e->sign == -1)
+ op2->bits |= DECNEG;
+ if (e->S2 == CSFL)
+ op2->exponent = e->exponent;
+
+ decNumber *op3 = decNumberSubtract(&_3, op2, op1, &set);
+
+ // ISOLTS 846 07c, 10a, 11b internal register overflow - see ad3d
+ bool iOvr = 0;
+ if (op3->digits > 63) {
+ uint8_t pr[256];
+ // if sf<=0, trailing zeroes can't be shifted out
+ // if sf> 0, (some of) trailing zeroes can be shifted out
+ int sf = e->S3==CSFL?op3->exponent:e->SF3;
+
+ int ctz = 0;
+ if (sf>0) { // optimize: we don't care when sf>0
+ decNumberGetBCD(op3,pr);
+ for (int i=op3->digits-1;i>=0 && pr[i]==0;i--)
+ ctz ++;
+ }
+
+ if (op3->digits - min(max(sf,0),ctz) > 63) {
+
+ enum rounding safeR = decContextGetRounding(&set); // save rounding mode
+ int safe = set.digits;
+ decNumber tmp;
+
+ // discard MS digits
+ decContextSetRounding(&set, DEC_ROUND_DOWN); // Round towards 0 (truncation).
+ set.digits = op3->digits - min(max(sf,0),ctz) - 63;
+ decNumberPlus(&tmp, op3, &set);
+ set.digits = safe;
+
+ decNumberSubtract(op3, op3, &tmp, &set);
+
+ //decNumberToString(op3,(char*)pr); sim_printf("discarded: %s\n",pr);
+
+ decContextSetRounding(&set, safeR);
+ iOvr = 1;
+ }
+ }
+
+ bool Ovr = false, EOvr = false, Trunc = false;
+
+ uint8_t out [256];
+ char *res = formatDecimal(out, &set, op3, n3, (int) e->S3, e->SF3, R, &Ovr, &Trunc);
+
+ Ovr |= iOvr;
+
+ if (decNumberIsZero(op3))
+ op3->exponent = 127;
+
+ // now write to memory in proper format.....
+
+ //word18 dstAddr = e->dstAddr;
+ int pos = (int) dstCN;
+
+ // 1st, take care of any leading sign .......
+ switch(e->S3)
+ {
+ case CSFL: // floating-point, leading sign.
+ case CSLS: // fixed-point, leading sign
+ switch(dstTN)
+ {
+ case CTN4:
+ if (e->P) //If TN2 and S2 specify a 4-bit signed number and P = 1, then the 13(8) plus sign character is placed appropriately if the result of the operation is positive.
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, (decNumberIsNegative(op3) && !decNumberIsZero(op3)) ? 015 : 013); // special +
+ else
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, (decNumberIsNegative(op3) && !decNumberIsZero(op3)) ? 015 : 014); // default +
+ break;
+ case CTN9:
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, (decNumberIsNegative(op3) && !decNumberIsZero(op3)) ? '-' : '+');
+ break;
+ }
+ break;
+
+ case CSTS: // nuttin' to do here .....
+ case CSNS:
+ break; // no sign wysiwyg
+ }
+
+ // 2nd, write the digits .....
+ for(int i = 0 ; i < n3 ; i++)
+ switch(dstTN)
+ {
+ case CTN4:
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, (word9) (res[i] - '0'));
+ break;
+ case CTN9:
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, (word9) res[i]);
+ break;
+ }
+
+ // 3rd, take care of any trailing sign or exponent ...
+ switch(e->S3)
+ {
+ case CSTS: // write trailing sign ....
+ switch(dstTN)
+ {
+ case CTN4:
+ if (e->P) //If TN2 and S2 specify a 4-bit signed number and P = 1, then the 13(8) plus sign character is placed appropriately if the result of the operation is positive.
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, (decNumberIsNegative(op3) && !decNumberIsZero(op3)) ? 015 : 013); // special +
+ else
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, (decNumberIsNegative(op3) && !decNumberIsZero(op3)) ? 015 : 014); // default +
+ break;
+ case CTN9:
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, (decNumberIsNegative(op3) && !decNumberIsZero(op3)) ? '-' : '+');
+ break;
+ }
+ break;
+
+ case CSFL: // floating-point, leading sign.
+ // write the exponent
+ switch(dstTN)
+ {
+ case CTN4:
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, (op3->exponent >> 4) & 0xf); // upper 4-bits
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, op3->exponent & 0xf); // lower 4-bits
+
+ break;
+ case CTN9:
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, op3->exponent & 0xff); // write 8-bit exponent
+ break;
+ }
+ break;
+
+ case CSLS: // fixed point, leading sign - already done
+ case CSNS: // fixed point, unsigned - nuttin' needed to do
+ break;
+ }
+
+ // set flags, etc ...
+ if (e->S3 == CSFL)
+ {
+ if (op3->exponent > 127)
+ {
+ SET_I_EOFL;
+ EOvr = true;
+ }
+ if (op3->exponent < -128)
+ {
+ SET_I_EUFL;
+ EOvr = true;
+ }
+ }
+
+ SC_I_NEG (decNumberIsNegative(op3) && !decNumberIsZero(op3)); // set negative indicator if op3 < 0
+ SC_I_ZERO (decNumberIsZero(op3)); // set zero indicator if op3 == 0
+
+ SC_I_TRUNC (!R && Trunc); // If the truncation condition exists without rounding, then ON; otherwise OFF
+
+ cleanupOperandDescriptor (1);
+ cleanupOperandDescriptor (2);
+ cleanupOperandDescriptor (3);
+
+ if (TST_I_TRUNC && T && tstOVFfault ())
+ doFault(FAULT_OFL, fst_zero, "sb3d truncation(overflow) fault");
+ if (EOvr && tstOVFfault ())
+ doFault(FAULT_OFL, fst_zero, "sb3d over/underflow fault");
+ if (Ovr)
+ {
+ SET_I_OFLOW;
+ if (tstOVFfault ())
+ doFault(FAULT_OFL, fst_zero, "sb3d overflow fault");
+ }
+}
+
+/*
+ * mp2d - Multiply Using Two Decimal Operands
+ */
+
+void mp2d (void)
+{
+ EISstruct * e = & cpu.currentEISinstruction;
+
+ fault_ipr_subtype_ mod_fault = 0;
+
+#ifndef EIS_SETUP
+ setupOperandDescriptor(1, &mod_fault);
+ setupOperandDescriptor(2, &mod_fault);
+ setupOperandDescriptorCache(3);
+#endif
+
+ parseNumericOperandDescriptor(1, &mod_fault);
+ parseNumericOperandDescriptor(2, &mod_fault);
+
+#ifdef L68
+ // L68 raises it immediately
+ if (mod_fault)
+ {
+ doFault (FAULT_IPR,
+ (_fault_subtype) {.fault_ipr_subtype=mod_fault},
+ "Illegal modifier");
+ }
+#endif
+
+ // Bits 1-8 MBZ
+ if (IWB_IRODD & 0377000000000)
+ doFault (FAULT_IPR, (_fault_subtype) {.fault_ipr_subtype=FR_ILL_OP|mod_fault}, "mp2d 1-8 MBZ");
+
+#ifdef DPS8M
+ // DPS8M raises it delayed
+ if (mod_fault)
+ {
+ doFault (FAULT_IPR,
+ (_fault_subtype) {.fault_ipr_subtype=mod_fault},
+ "Illegal modifier");
+ }
+#endif
+
+ e->P = getbits36_1 (cpu.cu.IWB, 0) != 0; // 4-bit data sign character control
+ bool T = getbits36_1 (cpu.cu.IWB, 9) != 0; // truncation bit
+ bool R = getbits36_1 (cpu.cu.IWB, 10) != 0; // rounding bit
+
+ PNL (L68_ (if (R)
+ DU_CYCLE_FRND;))
+
+ uint srcTN = e->TN1; // type of chars in src
+
+ uint dstTN = e->TN2; // type of chars in dst
+ uint dstCN = e->CN2; // starting at char pos CN
+
+ e->ADDR3 = e->ADDR2;
+
+ int n1 = 0, n2 = 0, sc1 = 0, sc2 = 0;
+
+ /*
+ * Here we need to distinguish between 4 type of numbers.
+ *
+ * CSFL - Floating-point, leading sign
+ * CSLS - Scaled fixed-point, leading sign
+ * CSTS - Scaled fixed-point, trailing sign
+ * CSNS - Scaled fixed-point, unsigned
+ */
+
+ // determine precision
+ switch(e->S1)
+ {
+ case CSFL:
+ n1 = (int) e->N1 - 1; // need to account for the - sign
+ if (srcTN == CTN4)
+ n1 -= 2; // 2 4-bit digits exponent
+ else
+ n1 -= 1; // 1 9-bit digit exponent
+ sc1 = 0; // no scaling factor
+ break;
+
+ case CSLS:
+ case CSTS:
+ n1 = (int) e->N1 - 1; // only 1 sign
+ sc1 = -e->SF1;
+ break;
+
+ case CSNS:
+ n1 = (int) e->N1; // no sign
+ sc1 = -e->SF1;
+ break; // no sign wysiwyg
+ }
+
+ if (n1 < 1)
+ doFault (FAULT_IPR, fst_ill_proc, "mp2d adjusted n1<1");
+
+ switch(e->S2)
+ {
+ case CSFL:
+ n2 = (int) e->N2 - 1; // need to account for the sign
+ if (e->TN2 == CTN4)
+ n2 -= 2; // 2 4-bit digit exponent
+ else
+ n2 -= 1; // 1 9-bit digit exponent
+ sc2 = 0; // no scaling factor
+ break;
+
+ case CSLS:
+ case CSTS:
+ n2 = (int) e->N2 - 1; // 1 sign
+ sc2 = -e->SF2;
+ break;
+
+ case CSNS:
+ n2 = (int) e->N2; // no sign
+ sc2 = -e->SF2;
+ break; // no sign wysiwyg
+ }
+
+ if (n2 < 1)
+ doFault (FAULT_IPR, fst_ill_proc, "mp2d adjusted n2<1");
+
+
+ decContext set;
+ decContextDefaultDPS8Mul(&set); // 126 digits for multiply
+
+ set.traps=0;
+
+ decNumber _1, _2, _3;
+
+ EISloadInputBufferNumeric (1); // according to MF1
+
+ decNumber *op1 = decBCD9ToNumber(e->inBuffer, n1, sc1, &_1);
+ if (e->sign == -1)
+ op1->bits |= DECNEG;
+ if (e->S1 == CSFL)
+ op1->exponent = e->exponent;
+
+ EISloadInputBufferNumeric (2); // according to MF2
+
+ decNumber *op2 = decBCD9ToNumber(e->inBuffer, n2, sc2, &_2);
+ if (e->sign == -1)
+ op2->bits |= DECNEG;
+ if (e->S2 == CSFL)
+ op2->exponent = e->exponent;
+
+ decNumber *op3 = decNumberMultiply(&_3, op1, op2, &set);
+
+ bool Ovr = false, EOvr = false, Trunc = false;
+
+ uint8_t out [256];
+ char *res = formatDecimal(out, &set, op3, n2, (int) e->S2, e->SF2, R, &Ovr, &Trunc);
+
+ if (decNumberIsZero(op3))
+ op3->exponent = 127;
+
+ // now write to memory in proper format.....
+
+ //word18 dstAddr = e->dstAddr;
+ int pos = (int) dstCN;
+
+ // 1st, take care of any leading sign .......
+ switch(e->S2)
+ {
+ case CSFL: // floating-point, leading sign.
+ case CSLS: // fixed-point, leading sign
+ switch(dstTN)
+ {
+ case CTN4:
+ if (e->P) //If TN2 and S2 specify a 4-bit signed number and P = 1, then the 13(8) plus sign character is placed appropriately if the result of the operation is positive.
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, (decNumberIsNegative(op3) && !decNumberIsZero(op3)) ? 015 : 013); // special +
+ else
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, (decNumberIsNegative(op3) && !decNumberIsZero(op3)) ? 015 : 014); // default +
+ break;
+ case CTN9:
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, (decNumberIsNegative(op3) && !decNumberIsZero(op3)) ? '-' : '+');
+ break;
+ }
+ break;
+
+ case CSTS: // nuttin' to do here .....
+ case CSNS:
+ break; // no sign wysiwyg
+ }
+
+ // 2nd, write the digits .....
+ for(int i = 0 ; i < n2 ; i++)
+ switch(dstTN)
+ {
+ case CTN4:
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, (word9) (res[i] - '0'));
+ break;
+ case CTN9:
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, (word9) res[i]);
+ break;
+ }
+
+ // 3rd, take care of any trailing sign or exponent ...
+ switch(e->S2)
+ {
+ case CSTS: // write trailing sign ....
+ switch(dstTN)
+ {
+ case CTN4:
+ if (e->P) //If TN2 and S2 specify a 4-bit signed number and P = 1, then the 13(8) plus sign character is placed appropriately if the result of the operation is positive.
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, (decNumberIsNegative(op3) && !decNumberIsZero(op3)) ? 015 : 013); // special +
+ else
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, (decNumberIsNegative(op3) && !decNumberIsZero(op3)) ? 015 : 014); // default +
+ break;
+ case CTN9:
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, (decNumberIsNegative(op3) && !decNumberIsZero(op3)) ? '-' : '+');
+ break;
+ }
+ break;
+
+ case CSFL: // floating-point, leading sign.
+ // write the exponent
+ switch(dstTN)
+ {
+ case CTN4:
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, (op3->exponent >> 4) & 0xf); // upper 4-bits
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, op3->exponent & 0xf); // lower 4-bits
+
+ break;
+ case CTN9:
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, op3->exponent & 0xff); // write 8-bit exponent
+ break;
+ }
+ break;
+
+ case CSLS: // fixed point, leading sign - already done
+ case CSNS: // fixed point, unsigned - nuttin' needed to do
+ break;
+ }
+
+ // set flags, etc ...
+ if (e->S2 == CSFL)
+ {
+ if (op3->exponent > 127)
+ {
+ SET_I_EOFL;
+ EOvr = true;
+ }
+ if (op3->exponent < -128)
+ {
+ SET_I_EUFL;
+ EOvr = true;
+ }
+ }
+
+ SC_I_NEG (decNumberIsNegative(op3) && !decNumberIsZero(op3)); // set negative indicator if op3 < 0
+ SC_I_ZERO (decNumberIsZero(op3)); // set zero indicator if op3 == 0
+
+ SC_I_TRUNC (!R && Trunc); // If the truncation condition exists without rounding, then ON; otherwise OFF
+
+ cleanupOperandDescriptor (1);
+ cleanupOperandDescriptor (2);
+ cleanupOperandDescriptor (3);
+
+ if (TST_I_TRUNC && T && tstOVFfault ())
+ doFault(FAULT_OFL, fst_zero, "mp2d truncation(overflow) fault");
+ if (EOvr && tstOVFfault ())
+ doFault(FAULT_OFL, fst_zero, "mp2d over/underflow fault");
+ if (Ovr)
+ {
+ SET_I_OFLOW;
+ if (tstOVFfault ())
+ doFault(FAULT_OFL, fst_zero, "mp2d overflow fault");
+ }
+}
+
+/*
+ * mp3d - Multiply Using Three Decimal Operands
+ */
+
+void mp3d (void)
+{
+ EISstruct * e = & cpu.currentEISinstruction;
+
+ fault_ipr_subtype_ mod_fault = 0;
+
+#ifndef EIS_SETUP
+ setupOperandDescriptor(1, &mod_fault);
+ setupOperandDescriptor(2, &mod_fault);
+ setupOperandDescriptor(3, &mod_fault);
+#endif
+
+ parseNumericOperandDescriptor(1, &mod_fault);
+ parseNumericOperandDescriptor(2, &mod_fault);
+ parseNumericOperandDescriptor(3, &mod_fault);
+
+#ifdef L68
+ // L68 raises it immediately
+ if (mod_fault)
+ {
+ doFault (FAULT_IPR,
+ (_fault_subtype) {.fault_ipr_subtype=mod_fault},
+ "Illegal modifier");
+ }
+#endif
+
+ // Bit 1 MBZ
+ if (IWB_IRODD & 0200000000000)
+ doFault (FAULT_IPR, (_fault_subtype) {.fault_ipr_subtype=FR_ILL_OP|mod_fault}, "mp3d(): 1 MBZ");
+
+#ifdef DPS8M
+ // DPS8M raises it delayed
+ if (mod_fault)
+ {
+ doFault (FAULT_IPR,
+ (_fault_subtype) {.fault_ipr_subtype=mod_fault},
+ "Illegal modifier");
+ }
+#endif
+
+ e->P = getbits36_1 (cpu.cu.IWB, 0) != 0; // 4-bit data sign character control
+ bool T = getbits36_1 (cpu.cu.IWB, 9) != 0; // truncation bit
+ bool R = getbits36_1 (cpu.cu.IWB, 10) != 0; // rounding bit
+
+ PNL (L68_ (if (R)
+ DU_CYCLE_FRND;))
+
+ uint srcTN = e->TN1; // type of chars in src
+
+ uint dstTN = e->TN3; // type of chars in dst
+ uint dstCN = e->CN3; // starting at char pos CN
+
+ int n1 = 0, n2 = 0, n3 = 0, sc1 = 0, sc2 = 0;
+
+ /*
+ * Here we need to distinguish between 4 type of numbers.
+ *
+ * CSFL - Floating-point, leading sign
+ * CSLS - Scaled fixed-point, leading sign
+ * CSTS - Scaled fixed-point, trailing sign
+ * CSNS - Scaled fixed-point, unsigned
+ */
+
+ // determine precision
+ switch(e->S1)
+ {
+ case CSFL:
+ n1 = (int) e->N1 - 1; // need to account for the - sign
+ if (srcTN == CTN4)
+ n1 -= 2; // 2 4-bit digits exponent
+ else
+ n1 -= 1; // 1 9-bit digit exponent
+ sc1 = 0; // no scaling factor
+ break;
+
+ case CSLS:
+ case CSTS:
+ n1 = (int) e->N1 - 1; // only 1 sign
+ sc1 = -e->SF1;
+ break;
+
+ case CSNS:
+ n1 = (int) e->N1; // no sign
+ sc1 = -e->SF1;
+ break; // no sign wysiwyg
+ }
+
+ if (n1 < 1)
+ doFault (FAULT_IPR, fst_ill_proc, "mp3d adjusted n1<1");
+
+ switch(e->S2)
+ {
+ case CSFL:
+ n2 = (int) e->N2 - 1; // need to account for the sign
+ if (e->TN2 == CTN4)
+ n2 -= 2; // 2 4-bit digit exponent
+ else
+ n2 -= 1; // 1 9-bit digit exponent
+ sc2 = 0; // no scaling factor
+ break;
+
+ case CSLS:
+ case CSTS:
+ n2 = (int) e->N2 - 1; // 1 sign
+ sc2 = -e->SF2;
+ break;
+
+ case CSNS:
+ n2 = (int) e->N2; // no sign
+ sc2 = -e->SF2;
+ break; // no sign wysiwyg
+ }
+
+ if (n2 < 1)
+ doFault (FAULT_IPR, fst_ill_proc, "mp3d adjusted n2<1");
+
+ switch(e->S3)
+ {
+ case CSFL:
+ n3 = (int) e->N3 - 1; // need to account for the sign
+ if (dstTN == CTN4)
+ n3 -= 2; // 2 4-bit digit exponent
+ else
+ n3 -= 1; // 1 9-bit digit exponent
+ break;
+
+ case CSLS:
+ case CSTS:
+ n3 = (int) e->N3 - 1; // 1 sign
+ break;
+
+ case CSNS:
+ n3 = (int) e->N3; // no sign
+ break; // no sign wysiwyg
+ }
+
+ if (n3 < 1)
+ doFault (FAULT_IPR, fst_ill_proc, "mp3d adjusted n3<1");
+
+
+ decContext set;
+ //decContextDefault(&set, DEC_INIT_BASE); // initialize
+ decContextDefaultDPS8Mul(&set); // 126 digits for multiply
+
+ set.traps=0;
+
+ decNumber _1, _2, _3;
+
+ EISloadInputBufferNumeric (1); // according to MF1
+
+ decNumber *op1 = decBCD9ToNumber(e->inBuffer, n1, sc1, &_1);
+ if (e->sign == -1)
+ op1->bits |= DECNEG;
+ if (e->S1 == CSFL)
+ op1->exponent = e->exponent;
+
+ EISloadInputBufferNumeric (2); // according to MF2
+
+ decNumber *op2 = decBCD9ToNumber(e->inBuffer, n2, sc2, &_2);
+ if (e->sign == -1)
+ op2->bits |= DECNEG;
+ if (e->S2 == CSFL)
+ op2->exponent = e->exponent;
+
+ decNumber *op3 = decNumberMultiply(&_3, op1, op2, &set);
+
+// char c1[1024];
+// char c2[1024];
+// char c3[1024];
+//
+// decNumberToString(op1, c1);
+// sim_printf("c1:%s\n", c1);
+// decNumberToString(op2, c2);
+// sim_printf("c2:%s\n", c2);
+// decNumberToString(op3, c3);
+// sim_printf("c3:%s\n", c3);
+
+ bool Ovr = false, EOvr = false, Trunc = false;
+
+ uint8_t out [256];
+ char *res = formatDecimal(out, &set, op3, n3, (int) e->S3, e->SF3, R, &Ovr, &Trunc);
+
+ if (decNumberIsZero(op3))
+ op3->exponent = 127;
+
+ // now write to memory in proper format.....
+
+ //word18 dstAddr = e->dstAddr;
+ int pos = (int) dstCN;
+
+ // 1st, take care of any leading sign .......
+ switch(e->S3)
+ {
+ case CSFL: // floating-point, leading sign.
+ case CSLS: // fixed-point, leading sign
+ switch(dstTN)
+ {
+ case CTN4:
+ if (e->P) // If TN2 and S2 specify a 4-bit signed number and P
+ // = 1, then the 13(8) plus sign character is placed
+ // appropriately if the result of the operation is
+ // positive.
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, (decNumberIsNegative(op3) && !decNumberIsZero(op3)) ? 015 : 013); // special +
+ else
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, (decNumberIsNegative(op3) && !decNumberIsZero(op3)) ? 015 : 014); // default +
+ break;
+ case CTN9:
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, (decNumberIsNegative(op3) && !decNumberIsZero(op3)) ? '-' : '+');
+ break;
+ }
+ break;
+
+ case CSTS: // nuttin' to do here .....
+ case CSNS:
+ break; // no sign wysiwyg
+ }
+
+ // 2nd, write the digits .....
+ for(int i = 0 ; i < n3 ; i++)
+ switch(dstTN)
+ {
+ case CTN4:
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, (word9) (res[i] - '0'));
+ break;
+ case CTN9:
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, (word9) res[i]);
+ break;
+ }
+
+ // 3rd, take care of any trailing sign or exponent ...
+ switch(e->S3)
+ {
+ case CSTS: // write trailing sign ....
+ switch(dstTN)
+ {
+ case CTN4:
+ if (e->P) //If TN2 and S2 specify a 4-bit signed number and P = 1, then the 13(8) plus sign character is placed appropriately if the result of the operation is positive.
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, (decNumberIsNegative(op3) && !decNumberIsZero(op3)) ? 015 : 013); // special +
+ else
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, (decNumberIsNegative(op3) && !decNumberIsZero(op3)) ? 015 : 014); // default +
+ break;
+ case CTN9:
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, (decNumberIsNegative(op3) && !decNumberIsZero(op3)) ? '-' : '+');
+ break;
+ }
+ break;
+
+ case CSFL: // floating-point, leading sign.
+ // write the exponent
+ switch(dstTN)
+ {
+ case CTN4:
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, (op3->exponent >> 4) & 0xf); // upper 4-bits
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, op3->exponent & 0xf); // lower 4-bits
+ break;
+ case CTN9:
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, op3->exponent & 0xff); // write 8-bit exponent
+ break;
+ }
+ break;
+
+ case CSLS: // fixed point, leading sign - already done
+ case CSNS: // fixed point, unsigned - nuttin' needed to do
+ break;
+ }
+
+ // set flags, etc ...
+ if (e->S3 == CSFL)
+ {
+ if (op3->exponent > 127)
+ {
+ SET_I_EOFL;
+ EOvr = true;
+ }
+ if (op3->exponent < -128)
+ {
+ SET_I_EUFL;
+ EOvr = true;
+ }
+ }
+
+ SC_I_NEG (decNumberIsNegative(op3) && !decNumberIsZero(op3)); // set negative indicator if op3 < 0
+ SC_I_ZERO (decNumberIsZero(op3)); // set zero indicator if op3 == 0
+
+ SC_I_TRUNC (!R && Trunc); // If the truncation condition exists without rounding, then ON; otherwise OFF
+
+ cleanupOperandDescriptor (1);
+ cleanupOperandDescriptor (2);
+ cleanupOperandDescriptor (3);
+
+ if (TST_I_TRUNC && T && tstOVFfault ())
+ doFault(FAULT_OFL, fst_zero, "mp3d truncation(overflow) fault");
+ if (EOvr && tstOVFfault ())
+ doFault(FAULT_OFL, fst_zero, "mp3d over/underflow fault");
+ if (Ovr)
+ {
+ SET_I_OFLOW;
+ if (tstOVFfault ())
+ doFault(FAULT_OFL, fst_zero, "mp3d overflow fault");
+ }
+}
+
+#if 0
+/* ------------------------------------------------------------------ */
+/* HWR 2/07 15:49 derived from ...... */
+/* */
+/* decPackedFromNumber -- convert decNumber to BCD Packed Decimal */
+/* */
+/* bcd is the BCD bytes */
+/* length is the length of the BCD array */
+/* scale is the scale result */
+/* dn is the decNumber */
+/* returns bcd, or NULL if error */
+/* */
+/* The number is converted to a BCD decimal byte array, */
+/* right aligned in the bcd array, whose length is indicated by the */
+/* second parameter. */
+/* scale is set to the scale of the number (this is the exponent, */
+/* negated). To force the number to a specified scale, first use the */
+/* decNumberRescale routine, which will round and change the exponent */
+/* as necessary. */
+/* */
+/* If there is an error (that is, the decNumber has too many digits */
+/* to fit in length bytes, or it is a NaN or Infinity), NULL is */
+/* returned and the bcd and scale results are unchanged. Otherwise */
+/* bcd is returned. */
+/* ------------------------------------------------------------------ */
+static uint8_t * decBCDFromNumber(uint8_t *bcd, int length, int *scale, const decNumber *dn) {
+
+ //PRINTDEC("decBCDFromNumber()", dn);
+
+ const Unit *up=dn->lsu; // Unit array pointer
+ uByte obyte=0, *out; // current output byte, and where it goes
+ Int indigs=dn->digits; // digits processed
+ uInt cut=DECDPUN; // downcounter per Unit
+ uInt u=*up; // work
+ uInt nib; // ..
+#if DECDPUN<=4
+ uInt temp; // ..
+#endif
+
+ if (dn->digits>length // too long ..
+ ||(dn->bits & DECSPECIAL)) return NULL; // .. or special -- hopeless
+
+ //if (dn->bits&DECNEG) obyte=DECPMINUS; // set the sign ..
+ //else obyte=DECPPLUS;
+ *scale=-dn->exponent; // .. and scale
+
+ // loop from lowest (rightmost) byte
+ out=bcd+length-1; // -> final byte
+ for (; out>=bcd; out--) {
+ if (indigs>0) {
+ if (cut==0) {
+ up++;
+ u=*up;
+ cut=DECDPUN;
+ }
+#if DECDPUN<=4
+ temp=(u*6554)>>16; // fast /10
+ nib=u-X10(temp);
+ u=temp;
+#else
+ nib=u%10; // cannot use *6554 trick :-(
+ u=u/10;
+#endif
+ //obyte|=(nib<<4);
+ obyte=nib & 255U;
+ indigs--;
+ cut--;
+ }
+ *out=obyte;
+ obyte=0; // assume 0
+ // if (indigs>0) {
+ // if (cut==0) {
+ // up++;
+ // u=*up;
+ // cut=DECDPUN;
+ // }
+ //#if DECDPUN<=4
+ // temp=(u*6554)>>16; // as above
+ // obyte=(uByte)(u-X10(temp));
+ // u=temp;
+ //#else
+ // obyte=(uByte)(u%10);
+ // u=u/10;
+ //#endif
+ // indigs--;
+ // cut--;
+ // }
+ } // loop
+
+ return bcd;
+} // decBCDFromNumber
+
+
+static unsigned char *getBCD(decNumber *a)
+{
+ static uint8_t bcd[256];
+ memset(bcd, 0, sizeof(bcd));
+ int scale;
+
+ decBCDFromNumber(bcd, a->digits, &scale, a);
+ for(int i = 0 ; i < a->digits ; i += 1 )
+ bcd[i] += '0';
+
+ return (unsigned char *) bcd;
+}
+
+
+static char *getBCDn(decNumber *a, int digits)
+{
+ static uint8_t bcd[256];
+ memset(bcd, 0, sizeof(bcd));
+ int scale;
+
+ decBCDFromNumber(bcd, digits, &scale, a);
+ for(int i = 0 ; i < digits ; i += 1 )
+ bcd[i] += '0';
+
+ return (char *) bcd;
+}
+
+
+static int findFirstDigit(unsigned char *bcd)
+{
+ int i = 0;
+ while (bcd[i] == '0' && bcd[i])
+ i += 1;
+
+ return i;
+}
+
+/* ------------------------------------------------------------------ */
+/* HWR 2/07 15:49 derived from ...... */
+/* */
+/* decPackedToNumber -- convert BCD Packed Decimal to a decNumber */
+/* */
+/* bcd is the BCD bytes */
+/* length is the length of the BCD array */
+/* scale is the scale associated with the BCD integer */
+/* dn is the decNumber [with space for length*2 digits] */
+/* returns dn, or NULL if error */
+/* */
+/* The BCD decimal byte array, together with an associated scale, */
+/* is converted to a decNumber. The BCD array is assumed full */
+/* of digits. */
+/* The scale is used (negated) as the exponent of the decNumber. */
+/* Note that zeros may have a scale. */
+/* */
+/* The decNumber structure is assumed to have sufficient space to */
+/* hold the converted number (that is, up to length-1 digits), so */
+/* no error is possible unless the adjusted exponent is out of range. */
+/* In these error cases, NULL is returned and the decNumber will be 0.*/
+/* ------------------------------------------------------------------ */
+
+static decNumber * decBCDToNumber(const uByte *bcd, Int length, const Int scale, decNumber *dn)
+{
+ const uByte *last=bcd+length-1; // -> last byte
+ const uByte *first; // -> first non-zero byte
+ uInt nib; // work nibble
+ Unit *up=dn->lsu; // output pointer
+ Int digits; // digits count
+ Int cut=0; // phase of output
+
+ decNumberZero(dn); // default result
+ last = &bcd[length-1];
+ //nib = *last & 0x0f; // get the sign
+ //if (nib==DECPMINUS || nib==DECPMINUSALT) dn->bits=DECNEG;
+ //else if (nib<=9) return NULL; // not a sign nibble
+
+ // skip leading zero bytes [final byte is always non-zero, due to sign]
+ for (first=bcd; *first==0 && first <= last;) first++;
+ digits= (int32_t) (last-first)+1; // calculate digits ..
+ //if ((*first & 0xf0)==0) digits--; // adjust for leading zero nibble
+ if (digits!=0) dn->digits=digits; // count of actual digits [if 0,
+ // leave as 1]
+
+ // check the adjusted exponent; note that scale could be unbounded
+ dn->exponent=-scale; // set the exponent
+ if (scale>=0) { // usual case
+ if ((dn->digits-scale-1)<-DECNUMMAXE) { // underflow
+ decNumberZero(dn);
+ return NULL;}
+ }
+ else { // -ve scale; +ve exponent
+ // need to be careful to avoid wrap, here, also BADINT case
+ if ((scale<-DECNUMMAXE) // overflow even without digits
+ || ((dn->digits-scale-1)>DECNUMMAXE)) { // overflow
+ decNumberZero(dn);
+ return NULL;}
+ }
+ if (digits==0) return dn; // result was zero
+
+ // copy the digits to the number's units, starting at the lsu
+ // [unrolled]
+ for (;last >= bcd;) { // forever
+ nib=(unsigned)(*last & 0x0f);
+ // got a digit, in nib
+ if (nib>9) {decNumberZero(dn); return NULL;} // bad digit
+
+ if (cut==0) *up=(Unit)nib;
+ else *up=(Unit)(*up+nib*DECPOWERS[cut]);
+ digits--;
+ if (digits==0) break; // got them all
+ cut++;
+ if (cut==DECDPUN) {
+ up++;
+ cut=0;
+ }
+ last--; // ready for next
+ // nib = *last & 0x0f; // get right nibble
+ // if (nib>9) {decNumberZero(dn); return NULL;}
+ //
+ // // got a digit, in nib
+ // if (cut==0) *up=(Unit)nib;
+ // else *up=(Unit)(*up+nib*DECPOWERS[cut]);
+ // digits--;
+ // if (digits==0) break; // got them all
+ // cut++;
+ // if (cut==DECDPUN) {
+ // up++;
+ // cut=0;
+ // }
+ } // forever
+
+ return dn;
+} // decBCDToNumber
+
+
+static int decCompareMAG(decNumber *lhs, decNumber *rhs, decContext *set)
+{
+ decNumber _cmpm, *cmpm;
+ cmpm = decNumberCompareTotalMag(&_cmpm, lhs, rhs, set);
+
+ if (decNumberIsZero(cmpm))
+ return 0; // lhs == rhs
+
+ if (decNumberIsNegative(cmpm))
+ return -1; // lhs < rhs
+
+ return 1; // lhs > rhs
+}
+
+
+/*
+ * output formatting for DV?X (divide) instructions ....
+ */
+static char * formatDecimalDIV (decContext * set, decNumber * r, int tn,
+ int n, int s, int sf, bool R, decNumber * num,
+ decNumber * den, bool * OVR, bool * TRUNC)
+ {
+
+ bool bDgtN = false;
+
+// this is the sane way to do it.....
+// bool bDgtN = decCompare(num, den, set) == -1;
+// if (s == CSFL && bDgtN)
+// sim_printf("den > num\n");
+
+ // 1) Floating-point quotient
+ // NQ = N2, but if the divisor is greater than the dividend after
+ // operand alignment, the leading zero digit produced is counted and the
+ // effective precision of the result is reduced by one.
+ if (s == CSFL)
+ {
+
+// decNumber _4, _5, _6a, _6b, *op4, *op5, *op6a, *op6b;
+//
+// // we want to make exponents the same so as to align the operands.
+// // ... which one has priority? dividend or divisor? >punt<
+//
+// op4 = decNumberReduce(&_4, num, set);
+// op5 = decNumberReduce(&_5, den, set);
+//
+//// op4 = decNumberCopy(&_4, num);
+//// op5 = decNumberCopy(&_5, den);
+//
+// op4->exponent = 0;
+// op5->exponent = 0;
+// //op6a = decNumberQuantize(&_6a, op4, op5, set);
+// //op6b = decNumberQuantize(&_6b, op5, op4, set);
+//
+// //PRINTALL("align 4 (num/dividend)", op4, set);
+// //PRINTALL("align 5 (den/divisor) ", op5, set);
+//
+// PRINTDEC("align 4 (num/dividend)", op4);
+// PRINTDEC("align 5 (den/divisor) ", op5);
+// //PRINTDEC("align 6a (nd) ", op6a);
+// //PRINTDEC("align 6b (dn) ", op6b);
+//
+// decNumber _cmp, *cmp;
+// cmp = decNumberCompareTotal(&_cmp, op4, op5, set);
+// bool bAdj2 = decNumberIsNegative(cmp); // if denominator is > numerator then remove leading 0
+// // if (bAdj)
+// sim_printf("bAdj2 == %d\n", decNumberToInt32(cmp, set));
+//
+//
+
+ // The dividend mantissa C(AQ) is shifted right and the dividend exponent
+ // C(E) increased accordingly until
+ // | C(AQ)0,63 | < | C(Y-pair)8,71 |
+ // | numerator | < | denominator |
+ // | dividend | < | divisor |
+
+ // start by determining the characteristic(s) of dividend / divisor
+
+ decNumber _dend, _dvsr, *dividend, *divisor;
+
+ dividend = decNumberCopy(&_dend, num);
+ divisor = decNumberCopy(&_dvsr, den);
+
+ // set exponents to zero to yield the characteristic
+ dividend->exponent = 0;
+ divisor->exponent = 0;
+//
+// decNumber _one, *one = decNumberFromInt32(&_one, -1);
+// int c = decCompare(dividend, divisor, set);
+// sim_printf("c0 = %d\n", c);
+
+
+ // we want to do a funky fractional alignment here so we can compare the mantissa's
+
+ unsigned char *c1 = getBCD(num);
+ int f1 = findFirstDigit(c1);
+ dividend = decBCDToNumber(c1+f1, 63, 63, &_dend);
+ PRINTDEC("aligned dividend", dividend);
+
+ unsigned char *c2 = getBCD(den);
+ int f2 = findFirstDigit(c2);
+ divisor = decBCDToNumber(c2+f2, 63, 63, &_dvsr);
+ PRINTDEC("aligned divisor", divisor);
+
+
+// PRINTALL("BCD 1 num/dividend", dividend, set);
+// PRINTALL("BCD 1 den/divisor ", divisor, set);
+//
+// decNumberReduce(dividend, dividend, set);
+// decNumberReduce(divisor, divisor, set);
+//
+// PRINTDEC("dividend", dividend);
+// PRINTDEC("divisor ", divisor);
+// PRINTALL("BCD 2 num/dividend", dividend, set);
+// PRINTALL("BCD 2 den/divisor ", divisor, set);
+
+
+ if (decCompareMAG(dividend, divisor, set) == -1)
+ {
+ bDgtN = true;
+ }
+
+ } // s == CSFL
+
+ if (s == CSFL)
+ sf = 0;
+
+ // XXX what happens if we try to write a negative number to an unsigned field?????
+ // Detection of a character outside the range [0,11]8 in a digit position
+ // or a character outside the range [12,17]8 in a sign position causes an
+ // illegal procedure fault.
+
+ // adjust output length according to type ....
+ //This implies that an unsigned fixed-point receiving field has a minimum
+ //length of 1 character; a signed fixed-point field, 2 characters; and a
+ //floating-point field, haracters.
+
+ int adjLen = n; // adjLen is the adjusted allowed length of the result taking into account signs and/or exponent
+ switch (s)
+ {
+ case CSFL: // we have a leading sign and a trailing exponent.
+ if (tn == CTN9)
+ adjLen -= 2; // a sign and an 1 9-bit exponent
+ else
+ adjLen -= 3; // a sign and 2 4-bit digits making up the exponent
+ break;
+ case CSLS:
+ case CSTS: // take sign into assount. One less char to play with
+ adjLen -= 1;
+ break;
+ case CSNS:
+ break; // no sign to worry about. Use everything
+ }
+
+ sim_debug (DBG_TRACEEXT, & cpu_dev,
+ "\nformatDecimal: adjLen=%d SF=%d S=%s TN=%s\n", adjLen, sf, CS[s], CTN[tn]);
+ sim_debug (DBG_TRACEEXT, & cpu_dev,
+ "formatDecimal: %s r->digits=%d r->exponent=%d\n", getBCD(r), r->digits, r->exponent);
+
+ PRINTDEC("fd(1:r):", r);
+ PRINTALL("pa(1:r):", r, set);
+
+ if (adjLen < 1)
+ {
+ // adjusted length is too small for anything but sign and/or exponent
+ //*OVR = 1;
+
+ // XXX what do we fill in here? Sign and exp?
+ *OVR = true;
+ return (char *)"";
+ }
+
+ // scale result (if not floating)
+
+ decNumber _r2;
+ decNumberZero(&_r2);
+
+ decNumber *r2 = &_r2;
+
+ decNumber _sf; // scaling factor
+ {
+
+
+#ifndef SPEED
+ int scale;
+ char out[256], out2[256];
+ if_sim_debug (DBG_TRACEEXT, & cpu_dev)
+ {
+ memset (out, 0, sizeof (out));
+ memset (out2, 0, sizeof (out2));
+
+ decBCDFromNumber((uint8_t *)out, r->digits, &scale, r);
+ for(int i = 0 ; i < r->digits ; i += 1 )
+ out[i] += '0';
+ sim_printf("formatDecimal(DEBUG): out[]: '%s'\n", out);
+ }
+#endif
+
+ if (s != CSFL)// && sf != 0)
+ {
+ decNumberFromInt32(&_sf, sf);
+ sim_debug (DBG_TRACEEXT, & cpu_dev,
+ "formatDecimal(s != CSFL a): %s r->digits=%d r->exponent=%d\n", getBCD(r), r->digits, r->exponent);
+ r2 = decNumberRescale(&_r2, r, &_sf, set);
+ sim_debug (DBG_TRACEEXT, & cpu_dev,
+ "formatDecimal(s != CSFL b): %s r2->digits=%d r2->exponent=%d\n", getBCD(r2), r2->digits, r2->exponent);
+ }
+ else
+ //*r2 = *r;
+ decNumberCopy(r2, r);
+
+ PRINTDEC("fd(2:r2):", r2);
+
+#ifndef SPEED
+ if_sim_debug (DBG_TRACEEXT, & cpu_dev)
+ {
+ decBCDFromNumber((uint8_t *)out2, r2->digits, &scale, r2);
+ for(int i = 0 ; i < r2->digits ; i += 1 )
+ out2[i] += '0';
+
+ sim_debug (DBG_TRACEEXT, & cpu_dev,
+ "formatDecimal: adjLen=%d E=%d SF=%d S=%s TN=%s digits(r2)=%s E2=%d\n", adjLen, r->exponent, sf, CS[s], CTN[tn], out2, r2->exponent);
+ }
+#endif
+ }
+
+
+
+ int scale;
+
+ uint8_t out[256];
+
+ memset (out, 0, sizeof (out));
+
+ //bool ovr = (r->digits-sf) > adjLen; // is integer portion too large to fit?
+ bool ovr = r2->digits > adjLen; // is integer portion too large to fit?
+ bool trunc = r->digits > r2->digits; // did we loose something along the way?
+
+
+ // now let's check for overflows
+ if (!ovr && !trunc)
+ {
+ sim_debug (DBG_TRACEEXT, & cpu_dev,
+ "formatDecimal(OK): r->digits(%d) <= adjLen(%d) r2->digits(%d)\n", r->digits, adjLen, r2->digits);
+ if (s == CSFL)
+ {
+ if (r2->digits < adjLen)
+ {
+ PRINTDEC("Value 1a", r2)
+
+ decNumber _s, *sc;
+ int rescaleFactor = r2->exponent - (adjLen - r2->digits);
+ sc = decNumberFromInt32(&_s, rescaleFactor);
+
+ PRINTDEC("Value sc", sc)
+ if (rescaleFactor > (adjLen - r2->digits))
+ r2 = decNumberRescale(r2, r2, sc, set);
+
+ PRINTDEC("Value 2a", r2)
+ }
+ else
+ {
+ PRINTDEC("Value 1b", r2)
+ }
+
+ // if it's floating justify it ...
+ /// <remark>
+ /// The dps88 and afterwards would generate a quotient with the maximim number of significant digits.
+ /// Not so the dps8. According the manuals "if the divisor is greater than the dividend after operand alignment,
+ /// the leading zero digit produced is counted and the effective precision of the result is reduced by one."
+ /// No problem. However, according to eis_tester
+ /// desc 1 -sd l -sf 1 -nn 8;
+ /// desc 2 -sd t -sf 2 -nn 8;
+ /// desc 3 -sd f -nn 8;
+ ///
+ /// data 1 "+" (5)"0" "58";
+ /// data 2 "000" "1234" "+";
+ /// data 3 "+" "021275" 376;
+ /// +0001234(00) / +0000058(0) = +021275 e-2
+ /// by as yet an unknown algorithm
+ /// <remark/>
+ // ... if bAdj then we leave a (single?) leading 0
+
+ if (!decNumberIsZero(r2))
+ {
+ char *q = getBCDn(r2, adjLen) ;
+ int lz = 0; // leading 0's
+ while (*q)
+ {
+ if (*q == '0')
+ {
+ lz += 1;
+ q += 1;
+ }
+ else
+ break;
+ }
+
+ if (lz)
+ {
+ decNumber _1;
+ decNumberFromInt32(&_1, lz);
+ decNumberShift(r2, r2, &_1, set);
+ r2->exponent -= lz;
+ }
+ }
+ } // s == CSFL
+
+
+ decBCDFromNumber(out, adjLen, &scale, r2);
+
+ for(int i = 0 ; i < adjLen ; i += 1 )
+ out[i] += '0';
+
+ // add leading 0 and reduce precision if needed
+ if (bDgtN)
+ {
+ for(int i = adjLen - 1 ; i >= 0 ; i -= 1 )
+ out[i + 1] = out[i];
+ out[adjLen] = 0;
+ out[0] = '0';
+ r2->exponent += 1;
+ }
+ } // ! ovr && ! trunc
+ else
+ {
+ PRINTDEC("r2(a):", r2);
+
+ ovr = false;
+ trunc = false;
+
+ // if we get here then we have either overflow or truncation....
+
+ sim_debug (DBG_TRACEEXT, & cpu_dev,
+ "formatDecimal(!OK%s): r2->digits %d adjLen %d\n", R ? " R" : "", r2->digits, adjLen);
+
+ // so, what do we do?
+ if (R)
+ {
+ // NB even with rounding you can have an overflow...
+
+ // if we're in rounding mode then we just make things fit and everything is OK - except if we have an overflow.
+
+ decNumber *ro = r2; //(s == CSFL ? r : r2);
+
+ int safe = set->digits;
+
+ if (ro->digits > adjLen) //(adjLen + 1))
+ {
+ //set->digits = ro->digits + sf + 1;
+ sim_debug (DBG_TRACEEXT, & cpu_dev,
+ "formatDecimal(!OK R1): ro->digits %d adjLen %d\n", ro->digits, adjLen);
+
+ set->digits = adjLen;
+ decNumberPlus(ro, ro, set);
+
+ decBCDFromNumber(out, set->digits, &scale, ro);
+ for(int i = 0 ; i < set->digits ; i += 1 )
+ out[i] += '0';
+
+ // HWR 24 Oct 2013
+ char temp[256];
+ strcpy(temp, (char *) out+set->digits-adjLen);
+ strcpy((char *) out, temp);
+
+ //strcpy(out, out+set->digits-adjLen); // this generates a SIGABRT - probably because of overlapping strings.
+
+ //sim_debug (DBG_TRACEEXT, & cpu_dev, "R OVR\n");
+ //ovr = true; breaks ET MVN 5
+ } // digits > adjlen
+ else
+ {
+ sim_debug (DBG_TRACEEXT, & cpu_dev,
+ "formatDecimal(!OK R2): ro->digits %d adjLen %d\n", ro->digits, adjLen);
+
+ if (s==CSFL)
+ {
+
+ set->digits = adjLen;
+ decNumberPlus(ro, ro, set);
+
+ decBCDFromNumber(out, adjLen, &scale, ro);
+ for(int i = 0 ; i < adjLen ; i += 1 )
+ out[i] += '0';
+ out[adjLen] = 0;
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "formatDecimal(!OK R2a): %s\n", out);
+
+ } // s == CSFL
+ else
+ {
+ int dig = set->digits;
+ set->digits = adjLen;
+ ro = decNumberPlus(ro, ro, set); // round to adjLen digits
+ decBCDFromNumber((uint8_t *)out, adjLen, &scale, ro);
+ set->digits = dig;
+
+ for(int j = 0 ; j < adjLen; j += 1 )
+ out[j] += '0';
+
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "formatDecimal(!OK R2b): %s\n", out);
+ } // s != CSFL
+ ovr = false; // since we've rounded we can have no overflow ?????
+ } // digits <= adjlen
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "formatDecimal(R3): digits:'%s'\n", out);
+
+ set->digits = safe;
+
+ // display int of number
+
+#ifndef SPEED
+ if_sim_debug (DBG_TRACEEXT, & cpu_dev)
+ {
+ decNumber _i;
+ decNumber *i = decNumberToIntegralValue(&_i, ro, set);
+ char outi[256];
+ memset (outi, 0, sizeof (outi));
+ decBCDFromNumber((uint8_t *)outi, adjLen, &scale, i);
+ for(int j = 0 ; j < adjLen; j += 1 )
+ outi[j] += '0';
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "i=%s\n", outi);
+ }
+#endif
+ } // R
+ else
+ {
+ // if we're not in rounding mode then we can either have a truncation or an overflow
+
+ if (s == CSFL)
+ {
+ enum rounding safeR = decContextGetRounding(set); // save rounding mode
+ decContextSetRounding(set, DEC_ROUND_DOWN); // Round towards 0 (truncation).
+
+ PRINTDEC("out[1a]:", r2);
+
+ int safe = set->digits;
+ set->digits = adjLen;
+ decNumberPlus(r2, r2, set);
+
+ PRINTDEC("out[1b]:", r2);
+
+ decBCDFromNumber(out, r2->digits, &scale, r2);
+ for(int i = 0 ; i < adjLen ; i += 1 )
+ out[i] += '0';
+ out[adjLen] = 0;
+
+
+ // 1) Floating-point quotient
+ // NQ = N3, but if the divisor is greater than the dividend after operand alignment, the leading zero digit produced is counted and the effective precision of the result is reduced by one.
+ // -or-
+ // With the divisor (den) greater than the dividend (num), the algorithm generates a leading zero in the quotient.
+
+ if (bDgtN)
+ {
+ for(int i = adjLen - 1 ; i >= 0 ; i -= 1 )
+ out[i + 1] = out[i];
+ out[adjLen] = 0;
+ out[0] = '0';
+ r2->exponent += 1;
+ }
+
+ set->digits = safe;
+ decContextSetRounding(set, safeR); // restore rounding mode
+
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "CSFL TRUNC\n");
+ } // s == CSFL
+ else
+ {
+ if (r2->digits < r->digits)
+ {
+ enum rounding safeR = decContextGetRounding(set); // save rounding mode
+ decContextSetRounding(set, DEC_ROUND_DOWN); // Round towards 0 (truncation).
+
+ // re-rescale r with an eye towards truncation notrounding
+
+ r2 = decNumberRescale(r2, r, &_sf, set);
+
+ trunc = true;
+
+ if (r2->digits <= adjLen)
+ {
+ decBCDFromNumber(out, adjLen, &scale, r2);
+ for(int i = 0 ; i < adjLen; i += 1 )
+ out[i] += '0';
+ out[adjLen] = 0;
+ trunc = false;
+ }
+ else
+ {
+ decBCDFromNumber(out, r2->digits, &scale, r2);
+ for(int i = 0 ; i < r2->digits; i += 1 )
+ out[i] += '0';
+ out[r2->digits] = 0;
+
+ memcpy(out, out + strlen((char *) out) - adjLen, (unsigned long) adjLen);
+ out[adjLen] = 0;
+
+ ovr = true;
+ trunc = false;
+ }
+ decContextSetRounding(set, safeR); // restore rounding mode
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "TRUNC\n");
+
+ // else if ((r2->digits-sf) > adjLen) // HWR 18 July 2014 was (r->digits > adjLen)
+ } // r2 < r
+ else if ((r2->digits) > adjLen) // HWR 18 July 2014 was (r->digits > adjLen)
+ {
+ // OVR
+ decBCDFromNumber(out, r2->digits, &scale, r2);
+ for(int i = 0 ; i < r2->digits ; i += 1 )
+ out[i] += '0';
+ out[r2->digits] = 0;
+
+ // HWR 24 Oct 2013
+ char temp[256];
+ strcpy(temp, (char *) out+r2->digits-adjLen);
+ strcpy((char *) out, temp);
+ //strcpy(out, out+r->digits-adjLen); // this generates a SIGABRT - probably because of overlapping strings.
+
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "OVR\n");
+ ovr = true;
+ } // r2 >= r
+ else
+ sim_printf("formatDecimal(?): How'd we get here?\n");
+ } // s != CSFL
+ } // !R
+ } // ovr || trunc
+ sim_debug (DBG_TRACEEXT, & cpu_dev,
+ "formatDecimal(END): ovrflow=%d trunc=%d R=%d out[]='%s'\n", ovr, trunc, R, out);
+ *OVR = ovr;
+ *TRUNC = trunc;
+
+ decNumberCopy(r, r2);
+ return (char *) out;
+ }
+#endif
+
+/*
+ * dv2d - Divide Using Two Decimal Operands
+ */
+
+void dv2d (void)
+{
+ EISstruct * e = & cpu.currentEISinstruction;
+
+ fault_ipr_subtype_ mod_fault = 0;
+
+#ifndef EIS_SETUP
+ setupOperandDescriptor(1, &mod_fault);
+ setupOperandDescriptor(2, &mod_fault);
+ setupOperandDescriptorCache(3);
+#endif
+
+ parseNumericOperandDescriptor(1, &mod_fault);
+ parseNumericOperandDescriptor(2, &mod_fault);
+
+#ifdef L68
+ // L68 raises it immediately
+ if (mod_fault)
+ {
+ doFault (FAULT_IPR,
+ (_fault_subtype) {.fault_ipr_subtype=mod_fault},
+ "Illegal modifier");
+ }
+#endif
+
+ // Bits 1-8 MBZ
+ // ISOLTS test 840 and RJ78 says bit 9 (T) MBZ as well
+ if (IWB_IRODD & 0377400000000)
+ doFault (FAULT_IPR, (_fault_subtype) {.fault_ipr_subtype=FR_ILL_OP|mod_fault}, "dv2d 1-9 MBZ");
+
+#ifdef DPS8M
+ // DPS8M raises it delayed
+ if (mod_fault)
+ {
+ doFault (FAULT_IPR,
+ (_fault_subtype) {.fault_ipr_subtype=mod_fault},
+ "Illegal modifier");
+ }
+#endif
+
+ e->P = getbits36_1 (cpu.cu.IWB, 0) != 0; // 4-bit data sign character control
+ //bool T = getbits36_1 (cpu.cu.IWB, 9) != 0; // truncation bit
+ bool R = getbits36_1 (cpu.cu.IWB, 10) != 0; // rounding bit
+
+ PNL (L68_ (if (R)
+ DU_CYCLE_FRND;))
+
+ uint srcTN = e->TN1; // type of chars in src
+
+ uint dstTN = e->TN2; // type of chars in dst
+ uint dstCN = e->CN2; // starting at char pos CN
+
+ e->ADDR3 = e->ADDR2;
+
+ int n1 = 0, n2 = 0, sc1 = 0, sc2 = 0;
+
+ /*
+ * Here we need to distinguish between 4 type of numbers.
+ *
+ * CSFL - Floating-point, leading sign
+ * CSLS - Scaled fixed-point, leading sign
+ * CSTS - Scaled fixed-point, trailing sign
+ * CSNS - Scaled fixed-point, unsigned
+ */
+
+ // determine precision
+ switch(e->S1)
+ {
+ case CSFL:
+ n1 = (int) e->N1 - 1; // need to account for the - sign
+ if (srcTN == CTN4)
+ n1 -= 2; // 2 4-bit digits exponent
+ else
+ n1 -= 1; // 1 9-bit digit exponent
+ sc1 = 0; // no scaling factor
+ break;
+
+ case CSLS:
+ case CSTS:
+ n1 = (int) e->N1 - 1; // only 1 sign
+ sc1 = -e->SF1;
+ break;
+
+ case CSNS:
+ n1 = (int) e->N1; // no sign
+ sc1 = -e->SF1;
+ break; // no sign wysiwyg
+ }
+
+ if (n1 < 1)
+ doFault (FAULT_IPR, fst_ill_proc, "dv2d adjusted n1<1");
+
+ switch(e->S2)
+ {
+ case CSFL:
+ n2 = (int) e->N2 - 1; // need to account for the sign
+ if (e->TN2 == CTN4)
+ n2 -= 2; // 2 4-bit digit exponent
+ else
+ n2 -= 1; // 1 9-bit digit exponent
+ sc2 = 0; // no scaling factor
+ break;
+
+ case CSLS:
+ case CSTS:
+ n2 = (int) e->N2 - 1; // 1 sign
+ sc2 = -e->SF2;
+ break;
+
+ case CSNS:
+ n2 = (int) e->N2; // no sign
+ sc2 = -e->SF2;
+ break; // no sign wysiwyg
+ }
+
+ if (n2 < 1)
+ doFault (FAULT_IPR, fst_ill_proc, "dv2d adjusted n2<1");
+
+
+ decContext set;
+ decContextDefaultDPS8(&set);
+
+ set.traps=0;
+
+ decNumber _1, _2, _3;
+
+ EISloadInputBufferNumeric (1); // according to MF1
+
+ decNumber *op1 = decBCD9ToNumber(e->inBuffer, n1, sc1, &_1); // divisor
+ if (e->sign == -1)
+ op1->bits |= DECNEG;
+ if (e->S1 == CSFL)
+ op1->exponent = e->exponent;
+
+ // check for divide by 0!
+ if (decNumberIsZero(op1))
+ {
+ doFault(FAULT_DIV, fst_zero, "dv2d division by 0");
+ }
+
+ word9 inBufferop1 [64];
+ memcpy (inBufferop1,e->inBuffer,64); // save for clz1 calculation later
+
+ EISloadInputBufferNumeric (2); // according to MF2
+
+ decNumber *op2 = decBCD9ToNumber(e->inBuffer, n2, sc2, &_2); // dividend
+ if (e->sign == -1)
+ op2->bits |= DECNEG;
+ if (e->S2 == CSFL)
+ op2->exponent = e->exponent;
+
+ int NQ;
+ if (e->S2 == CSFL)
+ {
+ NQ = n2;
+ }
+ else
+ {
+ // count leading zeroes
+ // TODO optimize - can these be somehow extracted from decNumbers?
+ int clz1, clz2, i;
+ for (i=0; i < op1->digits; i++)
+ if (inBufferop1[i]!=0)
+ break;
+ clz1 = i;
+ for (i=0; i < op2->digits; i++)
+ if (e->inBuffer[i]!=0) // this still holds op2 digits
+ break;
+ clz2 = i;
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "dv2d: clz1 %d clz2 %d\n",clz1,clz2);
+
+ // XXX are clz also valid for CSFL dividend / divisor? probably yes
+ // XXX seems that exponents and scale factors are used interchangeably here ? (RJ78)
+ NQ = (n2-clz2+1) - (n1-clz1) + (-(e->S1==CSFL?op1->exponent:(int)e->SF1));
+
+sim_debug (DBG_TRACEEXT, & cpu_dev, "dv2d S1 %d S2 %d N1 %d N2 %d clz1 %d clz2 %d E1 %d E2 %d SF2 %d NQ %d\n",e->S1,e->S2,e->N1,e->N2,clz1,clz2,op1->exponent,op2->exponent,e->SF2,NQ);
+ }
+ if (NQ > 63)
+ doFault(FAULT_DIV, fst_zero, "dv2d NQ>63");
+ // Note: NQ is currently unused apart from this FAULT_DIV check. decNumber produces more digits than required, but they are then rounded/truncated
+
+ // Yes, they're switched. op1=divisor
+ decNumber *op3 = decNumberDivide(&_3, op2, op1, &set);
+ // Note DPS88 and DPS9000 are different when NQ <= 0
+ // This is a flaw in the DPS8/70 hardware which was corrected in later models
+ // ISOLTS-817 05b
+
+ PRINTDEC("op2", op2);
+ PRINTDEC("op1", op1);
+ PRINTDEC("op3", op3);
+
+ // let's check division results to see for anomalous conditions
+ if (
+ (set.status & DEC_Division_undefined) || // 0/0 will become NaN
+ (set.status & DEC_Invalid_operation) ||
+ (set.status & DEC_Division_by_zero)
+ ) { sim_debug (DBG_TRACEEXT, & cpu_dev, "oops! dv2d anomalous results"); } // divide by zero has already been checked before
+
+ if (e->S2 == CSFL)
+ {
+ // justify CSFL left
+ // This is a flaw in the DPS8/70 hardware which was corrected in later models
+ // Note DPS88 and DPS9000 are different
+ // ISOLTS-817 06c,06e
+
+ decNumber _sf;
+
+ if (n2 - op3->digits > 0)
+ {
+ decNumberFromInt32(&_sf, op3->exponent - (n2 - op3->digits));
+ PRINTDEC("Value 1", op3)
+ PRINTDEC("Value sf", &_sf)
+ op3 = decNumberRescale(op3, op3, &_sf, &set);
+ PRINTDEC("Value 2", op3)
+ }
+ }
+
+ bool Ovr = false, EOvr = false, Trunc = false;
+ uint8_t out[256];
+
+ // CSFL: If the divisor is greater than the dividend after operand
+ // alignment, the leading zero digit produced is counted and the effective
+ // precision of the result is reduced by one.
+ // This is a flaw in the DPS8/70 hardware which was corrected in later models
+ // Note DPS88 and DPS9000 are different
+ //
+ // "greater after operand alignment" means scale until most-significant digits are nonzero, then compare magnitudes ignoring exponents
+ // This passes ISOLTS-817 06e, ET 458,461,483,486
+ char *res;
+ if (e->S2 == CSFL) {
+ decNumber _1a;
+ decNumber _2a;
+ decNumber _sf;
+ if (op1->digits >= op2->digits) {
+ // scale op2
+ decNumberCopy(&_1a, op1);
+ decNumberFromInt32(&_sf, op1->digits - op2->digits);
+ decNumberShift(&_2a, op2, &_sf, &set);
+ } else if (op1->digits < op2->digits) {
+ // scale op1
+ decNumberFromInt32(&_sf, op2->digits - op1->digits);
+ decNumberShift(&_1a, op1, &_sf, &set);
+ decNumberCopy(&_2a, op2);
+ }
+ _1a.exponent = 0;
+ _2a.exponent = 0;
+
+ PRINTDEC("dv2d: op1a", &_1a);
+ PRINTDEC("dv2d: op2a", &_2a);
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "dv2d: exp1 %d exp2 %d digits op1 %d op2 %d op1a %d op2a %d\n",op1->exponent,op2->exponent,op1->digits,op2->digits,_1a.digits,_2a.digits);
+
+ if (decCompareMAG(&_1a, &_2a, &set) > 0) {
+ // shorten the result field to get proper rounding
+ res = formatDecimal(out, &set, op3, n2 -1, (int) e->S2, e->SF2, R, &Ovr, &Trunc);
+
+ // prepend zero digit
+ // ET 458,483 float=float/float, ET 461,486 float=fixed/fixed
+ for (int i = n2; i > 0; i--) // incl.zero terminator
+ res[i] = res[i-1];
+ res[0] = '0';
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "dv2d: addzero n2 %d %s exp %d\n",n2,res,op3->exponent);
+ } else {
+ // full n2 digits are retured
+ res = formatDecimal(out, &set, op3, n2, (int) e->S2, e->SF2, R, &Ovr, &Trunc);
+ }
+ } else {
+ // same as all other decimal instructions
+ res = formatDecimal(out, &set, op3, n2, (int) e->S2, e->SF2, R, &Ovr, &Trunc);
+ }
+
+ if (decNumberIsZero(op3))
+ op3->exponent = 127;
+
+ // now write to memory in proper format.....
+
+ int pos = (int) dstCN;
+
+ // 1st, take care of any leading sign .......
+ switch(e->S2)
+ {
+ case CSFL: // floating-point, leading sign.
+ case CSLS: // fixed-point, leading sign
+ switch(dstTN)
+ {
+ case CTN4:
+ if (e->P) //If TN2 and S2 specify a 4-bit signed number and P = 1, then the 13(8) plus sign character is placed appropriately if the result of the operation is positive.
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, (decNumberIsNegative(op3) && !decNumberIsZero(op3)) ? 015 : 013); // special +
+ else
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, (decNumberIsNegative(op3) && !decNumberIsZero(op3)) ? 015 : 014); // default +
+ break;
+ case CTN9:
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, (decNumberIsNegative(op3) && !decNumberIsZero(op3)) ? '-' : '+');
+ break;
+ }
+ break;
+
+ case CSTS: // nuttin' to do here .....
+ case CSNS:
+ break; // no sign wysiwyg
+ }
+
+ // 2nd, write the digits .....
+ for(int i = 0 ; i < n2 ; i++)
+ switch(dstTN)
+ {
+ case CTN4:
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, (word9) (res[i] - '0'));
+ break;
+ case CTN9:
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, (word9) res[i]);
+ break;
+ }
+
+ // 3rd, take care of any trailing sign or exponent ...
+ switch(e->S2)
+ {
+ case CSTS: // write trailing sign ....
+ switch(dstTN)
+ {
+ case CTN4:
+ if (e->P) //If TN2 and S2 specify a 4-bit signed number and P = 1, then the 13(8) plus sign character is placed appropriately if the result of the operation is positive.
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, (decNumberIsNegative(op3) && !decNumberIsZero(op3)) ? 015 : 013); // special +
+ else
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, (decNumberIsNegative(op3) && !decNumberIsZero(op3)) ? 015 : 014); // default +
+ break;
+ case CTN9:
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, (decNumberIsNegative(op3) && !decNumberIsZero(op3)) ? '-' : '+');
+ break;
+ }
+ break;
+
+ case CSFL: // floating-point, leading sign.
+ // write the exponent
+ switch(dstTN)
+ {
+ case CTN4:
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, (op3->exponent >> 4) & 0xf); // upper 4-bits
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, op3->exponent & 0xf); // lower 4-bits
+ break;
+ case CTN9:
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, op3->exponent & 0xff); // write 8-bit exponent
+ break;
+ }
+ break;
+
+ case CSLS: // fixed point, leading sign - already done
+ case CSNS: // fixed point, unsigned - nuttin' needed to do
+ break;
+ }
+
+ // set flags, etc ...
+ if (e->S2 == CSFL)
+ {
+ if (op3->exponent > 127)
+ {
+ SET_I_EOFL;
+ EOvr = true;
+ }
+ if (op3->exponent < -128)
+ {
+ SET_I_EUFL;
+ EOvr = true;
+ }
+ }
+
+ SC_I_NEG (decNumberIsNegative(op3) && !decNumberIsZero(op3)); // set negative indicator if op3 < 0
+ SC_I_ZERO (decNumberIsZero(op3)); // set zero indicator if op3 == 0
+
+ //SC_I_TRUNC (!R && Trunc); // no truncation flag for divide
+
+ cleanupOperandDescriptor (1);
+ cleanupOperandDescriptor (2);
+ cleanupOperandDescriptor (3);
+
+ //if (TST_I_TRUNC && T && tstOVFfault ())
+ // doFault(FAULT_OFL, fst_zero, "dv2d truncation(overflow) fault");
+ if (EOvr && tstOVFfault ())
+ doFault(FAULT_OFL, fst_zero, "dv2d over/underflow fault");
+ if (Ovr)
+ {
+ SET_I_OFLOW;
+ if (tstOVFfault ())
+ doFault(FAULT_OFL, fst_zero, "dv2d overflow fault");
+ }
+}
+
+/*
+ * dv3d - Divide Using Three Decimal Operands
+ */
+
+void dv3d (void)
+
+{
+ EISstruct * e = & cpu.currentEISinstruction;
+
+ fault_ipr_subtype_ mod_fault = 0;
+
+#ifndef EIS_SETUP
+ setupOperandDescriptor(1, &mod_fault);
+ setupOperandDescriptor(2, &mod_fault);
+ setupOperandDescriptor(3, &mod_fault);
+#endif
+
+ parseNumericOperandDescriptor(1, &mod_fault);
+ parseNumericOperandDescriptor(2, &mod_fault);
+ parseNumericOperandDescriptor(3, &mod_fault);
+
+#ifdef L68
+ // L68 raises it immediately
+ if (mod_fault)
+ {
+ doFault (FAULT_IPR,
+ (_fault_subtype) {.fault_ipr_subtype=mod_fault},
+ "Illegal modifier");
+ }
+#endif
+
+ // Bit 1 MBZ
+ // ISOLTS test 840 and RJ78 says bit 9 (T) MBZ
+ if (IWB_IRODD & 0200400000000)
+ doFault (FAULT_IPR, (_fault_subtype) {.fault_ipr_subtype=FR_ILL_OP|mod_fault}, "dv3d(): 1,9 MBZ");
+
+#ifdef DPS8M
+ // DPS8M raises it delayed
+ if (mod_fault)
+ {
+ doFault (FAULT_IPR,
+ (_fault_subtype) {.fault_ipr_subtype=mod_fault},
+ "Illegal modifier");
+ }
+#endif
+
+ e->P = getbits36_1 (cpu.cu.IWB, 0) != 0; // 4-bit data sign character control
+ //bool T = getbits36_1 (cpu.cu.IWB, 9) != 0; // truncation bit
+ bool R = getbits36_1 (cpu.cu.IWB, 10) != 0; // rounding bit
+
+ PNL (L68_ (if (R)
+ DU_CYCLE_FRND;))
+
+ uint srcTN = e->TN1; // type of chars in src
+
+ uint dstTN = e->TN3; // type of chars in dst
+ uint dstCN = e->CN3; // starting at char pos CN
+
+ int n1 = 0, n2 = 0, n3 = 0, sc1 = 0, sc2 = 0;
+
+ /*
+ * Here we need to distinguish between 4 type of numbers.
+ *
+ * CSFL - Floating-point, leading sign
+ * CSLS - Scaled fixed-point, leading sign
+ * CSTS - Scaled fixed-point, trailing sign
+ * CSNS - Scaled fixed-point, unsigned
+ */
+
+ // determine precision
+ switch(e->S1)
+ {
+ case CSFL:
+ n1 = (int) e->N1 - 1; // need to account for the - sign
+ if (srcTN == CTN4)
+ n1 -= 2; // 2 4-bit digits exponent
+ else
+ n1 -= 1; // 1 9-bit digit exponent
+ sc1 = 0; // no scaling factor
+ break;
+
+ case CSLS:
+ case CSTS:
+ n1 = (int) e->N1 - 1; // only 1 sign
+ sc1 = -e->SF1;
+ break;
+
+ case CSNS:
+ n1 = (int) e->N1; // no sign
+ sc1 = -e->SF1;
+ break; // no sign wysiwyg
+ }
+
+ if (n1 < 1)
+ doFault (FAULT_IPR, fst_ill_proc, "dv3d adjusted n1<1");
+
+ switch(e->S2)
+ {
+ case CSFL:
+ n2 = (int) e->N2 - 1; // need to account for the sign
+ if (e->TN2 == CTN4)
+ n2 -= 2; // 2 4-bit digit exponent
+ else
+ n2 -= 1; // 1 9-bit digit exponent
+ sc2 = 0; // no scaling factor
+ break;
+
+ case CSLS:
+ case CSTS:
+ n2 = (int) e->N2 - 1; // 1 sign
+ sc2 = -e->SF2;
+ break;
+
+ case CSNS:
+ n2 = (int) e->N2; // no sign
+ sc2 = -e->SF2;
+ break; // no sign wysiwyg
+ }
+
+ if (n2 < 1)
+ doFault (FAULT_IPR, fst_ill_proc, "dv3d adjusted n2<1");
+
+ switch(e->S3)
+ {
+ case CSFL:
+ n3 = (int) e->N3 - 1; // need to account for the sign
+ if (dstTN == CTN4)
+ n3 -= 2; // 2 4-bit digit exponent
+ else
+ n3 -= 1; // 1 9-bit digit exponent
+ break;
+
+ case CSLS:
+ case CSTS:
+ n3 = (int) e->N3 - 1; // 1 sign
+ break;
+
+ case CSNS:
+ n3 = (int) e->N3; // no sign
+ break; // no sign wysiwyg
+ }
+ if (n3 < 1)
+ doFault (FAULT_IPR, fst_ill_proc, "dv3d adjusted n3<1");
+
+
+ decContext set;
+ decContextDefaultDPS8(&set);
+
+ set.traps=0;
+
+ decNumber _1, _2, _3;
+
+ EISloadInputBufferNumeric (1); // according to MF1
+
+ decNumber *op1 = decBCD9ToNumber(e->inBuffer, n1, sc1, &_1);
+ //PRINTDEC("op1", op1);
+ if (e->sign == -1)
+ op1->bits |= DECNEG;
+ if (e->S1 == CSFL)
+ op1->exponent = e->exponent;
+
+ // check for divide by 0!
+ if (decNumberIsZero(op1))
+ {
+ doFault(FAULT_DIV, fst_zero, "dv3d division by 0");
+ }
+
+ word9 inBufferop1 [64];
+ memcpy (inBufferop1,e->inBuffer,64); // save for clz1 calculation later
+
+ EISloadInputBufferNumeric (2); // according to MF2
+
+ decNumber *op2 = decBCD9ToNumber(e->inBuffer, n2, sc2, &_2);
+ if (e->sign == -1)
+ op2->bits |= DECNEG;
+ if (e->S2 == CSFL)
+ op2->exponent = e->exponent;
+
+
+ // The number of required quotient digits, NQ, is determined before
+ // division begins as follows:
+ // 1) Floating-point quotient
+ // NQ = N3
+ // 2) Fixed-point quotient
+ // NQ = (N2-LZ2+1) - (N1-LZ1) + (E2-E1-SF3)
+ // where: Nn = given operand field length
+ // LZn = leading zero count for operand n
+ // En = exponent of operand n
+ // SF3 = scaling factor of quotient
+ // 3) Rounding
+ // If rounding is specified (R = 1), then one extra quotient digit is
+ // produced.
+ // Note: rule 3 is already handled by formatDecimal rounding
+ // Nn doesn't represent full field length, but length without sign and exponent (RJ78/DH03 seems like)
+
+ int NQ;
+ if (e->S3 == CSFL)
+ {
+ NQ = n3;
+ }
+ else
+ {
+ // count leading zeroes
+ // TODO optimize - can these be somehow extracted from decNumbers?
+ int clz1, clz2, i;
+ for (i=0; i < op1->digits; i++)
+ if (inBufferop1[i]!=0)
+ break;
+ clz1 = i;
+ for (i=0; i < op2->digits; i++)
+ if (e->inBuffer[i]!=0) // this still holds op2 digits
+ break;
+ clz2 = i;
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "dv3d: clz1 %d clz2 %d\n",clz1,clz2);
+
+ // XXX are clz also valid for CSFL dividend / divisor? probably yes
+ // XXX seems that exponents and scale factors are used interchangeably here ? (RJ78)
+ NQ = (n2-clz2+1) - (n1-clz1) + ((e->S2==CSFL?op2->exponent:(int)e->SF2)-(e->S1==CSFL?op1->exponent:(int)e->SF1)-(int)e->SF3);
+
+sim_debug (DBG_TRACEEXT, & cpu_dev, "dv3d S1 %d S2 %d N1 %d N2 %d clz1 %d clz2 %d E1 %d E2 %d SF3 %d NQ %d\n",e->S1,e->S2,e->N1,e->N2,clz1,clz2,op1->exponent,op2->exponent,e->SF3,NQ);
+ }
+ if (NQ > 63)
+ doFault(FAULT_DIV, fst_zero, "dv3d NQ>63");
+ // Note: NQ is currently unused apart from this FAULT_DIV check. decNumber produces more digits than required, but they are then rounded/truncated
+
+ // Yes, they're switched. op1=divisor
+ decNumber *op3 = decNumberDivide(&_3, op2, op1, &set);
+ // Note DPS88 and DPS9000 are different when NQ <= 0
+ // This is a flaw in the DPS8/70 hardware which was corrected in later models
+ // ISOLTS-817 05b
+
+ PRINTDEC("op2", op2);
+ PRINTDEC("op1", op1);
+ PRINTDEC("op3", op3);
+
+ // let's check division results to see for anomalous conditions
+ if (
+ (set.status & DEC_Division_undefined) || // 0/0 will become NaN
+ (set.status & DEC_Invalid_operation) ||
+ (set.status & DEC_Division_by_zero)
+ ) { sim_debug (DBG_TRACEEXT, & cpu_dev, "oops! dv3d anomalous results"); } // divide by zero has already been checked before
+
+ if (e->S3 == CSFL)
+ {
+ // justify CSFL left
+ // This is a flaw in the DPS8/70 hardware which was corrected in later models
+ // Note DPS88 and DPS9000 are different
+ // ISOLTS-817 06c,06e
+
+ decNumber _sf;
+
+ if (n3 - op3->digits > 0)
+ {
+ decNumberFromInt32(&_sf, op3->exponent - (n3 - op3->digits));
+ PRINTDEC("Value 1", op3)
+ PRINTDEC("Value sf", &_sf)
+ op3 = decNumberRescale(op3, op3, &_sf, &set);
+ PRINTDEC("Value 2", op3)
+ }
+ }
+
+ bool Ovr = false, EOvr = false, Trunc = false;
+ uint8_t out[256];
+
+ // CSFL: If the divisor is greater than the dividend after operand
+ // alignment, the leading zero digit produced is counted and the effective
+ // precision of the result is reduced by one.
+ // This is a flaw in the DPS8/70 hardware which was corrected in later models
+ // Note DPS88 and DPS9000 are different
+ //
+ // "greater after operand alignment" means scale until most-significant digits are nonzero, then compare magnitudes ignoring exponents
+ // This passes ISOLTS-817 06e, ET 458,461,483,486
+ char *res;
+ if (e->S3 == CSFL) {
+ decNumber _1a;
+ decNumber _2a;
+ decNumber _sf;
+ if (op1->digits >= op2->digits) {
+ // scale op2
+ decNumberCopy(&_1a, op1);
+ decNumberFromInt32(&_sf, op1->digits - op2->digits);
+ decNumberShift(&_2a, op2, &_sf, &set);
+ } else if (op1->digits < op2->digits) {
+ // scale op1
+ decNumberFromInt32(&_sf, op2->digits - op1->digits);
+ decNumberShift(&_1a, op1, &_sf, &set);
+ decNumberCopy(&_2a, op2);
+ }
+ _1a.exponent = 0;
+ _2a.exponent = 0;
+
+ PRINTDEC("dv3d: op1a", &_1a);
+ PRINTDEC("dv3d: op2a", &_2a);
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "dv3d: exp1 %d exp2 %d digits op1 %d op2 %d op1a %d op2a %d\n",op1->exponent,op2->exponent,op1->digits,op2->digits,_1a.digits,_2a.digits);
+
+ if (decCompareMAG(&_1a, &_2a, &set) > 0) {
+ // shorten the result field to get proper rounding
+ res = formatDecimal(out, &set, op3, n3 -1, (int) e->S3, e->SF3, R, &Ovr, &Trunc);
+
+ // prepend zero digit
+ // ET 458,483 float=float/float, ET 461,486 float=fixed/fixed
+ for (int i = n3; i > 0; i--) // incl.zero terminator
+ res[i] = res[i-1];
+ res[0] = '0';
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "dv3d: addzero n3 %d %s exp %d\n",n3,res,op3->exponent);
+ } else {
+ // full n3 digits are retured
+ res = formatDecimal(out, &set, op3, n3, (int) e->S3, e->SF3, R, &Ovr, &Trunc);
+ }
+ } else {
+ // same as all other decimal instructions
+ res = formatDecimal(out, &set, op3, n3, (int) e->S3, e->SF3, R, &Ovr, &Trunc);
+ }
+
+ if (decNumberIsZero(op3))
+ op3->exponent = 127;
+
+ //printf("%s\r\n", res);
+
+ // now write to memory in proper format.....
+
+ int pos = (int) dstCN;
+
+ // 1st, take care of any leading sign .......
+ switch(e->S3)
+ {
+ case CSFL: // floating-point, leading sign.
+ case CSLS: // fixed-point, leading sign
+ switch(dstTN)
+ {
+ case CTN4:
+ if (e->P) //If TN2 and S2 specify a 4-bit signed number and P = 1, then the 13(8) plus sign character is placed appropriately if the result of the operation is positive.
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, (decNumberIsNegative(op3) && !decNumberIsZero(op3)) ? 015 : 013); // special +
+ else
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, (decNumberIsNegative(op3) && !decNumberIsZero(op3)) ? 015 : 014); // default +
+ break;
+ case CTN9:
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, (decNumberIsNegative(op3) && !decNumberIsZero(op3)) ? '-' : '+');
+ break;
+ }
+ break;
+
+ case CSTS: // nuttin' to do here .....
+ case CSNS:
+ break; // no sign wysiwyg
+ }
+
+ // 2nd, write the digits .....
+ for(int i = 0 ; i < n3 ; i++)
+ switch(dstTN)
+ {
+ case CTN4:
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, (word9) (res[i] - '0'));
+ break;
+ case CTN9:
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, (word9) res[i]);
+ break;
+ }
+
+ // 3rd, take care of any trailing sign or exponent ...
+ switch(e->S3)
+ {
+ case CSTS: // write trailing sign ....
+ switch(dstTN)
+ {
+ case CTN4:
+ if (e->P) //If TN2 and S2 specify a 4-bit signed number and P = 1, then the 13(8) plus sign character is placed appropriately if the result of the operation is positive.
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, (decNumberIsNegative(op3) && !decNumberIsZero(op3)) ? 015 : 013); // special +
+ else
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, (decNumberIsNegative(op3) && !decNumberIsZero(op3)) ? 015 : 014); // default +
+ break;
+ case CTN9:
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, (decNumberIsNegative(op3) && !decNumberIsZero(op3)) ? '-' : '+');
+ break;
+ }
+ break;
+
+ case CSFL: // floating-point, leading sign.
+ // write the exponent
+ switch(dstTN)
+ {
+ case CTN4:
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, (op3->exponent >> 4) & 0xf); // upper 4-bits
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, op3->exponent & 0xf); // lower 4-bits
+ break;
+ case CTN9:
+ EISwrite49(&e->ADDR3, &pos, (int) dstTN, op3->exponent & 0xff); // write 8-bit exponent
+ break;
+ }
+ break;
+
+ case CSLS: // fixed point, leading sign - already done
+ case CSNS: // fixed point, unsigned - nuttin' needed to do
+ break;
+ }
+
+ // set flags, etc ...
+ if (e->S3 == CSFL)
+ {
+ if (op3->exponent > 127)
+ {
+ SET_I_EOFL;
+ EOvr = true;
+ }
+ if (op3->exponent < -128)
+ {
+ SET_I_EUFL;
+ EOvr = true;
+ }
+ }
+
+ SC_I_NEG (decNumberIsNegative(op3) && !decNumberIsZero(op3)); // set negative indicator if op3 < 0
+ SC_I_ZERO (decNumberIsZero(op3)); // set zero indicator if op3 == 0
+
+ // SC_I_TRUNC(!R && Trunc); // no truncation flag for divide
+
+ cleanupOperandDescriptor (1);
+ cleanupOperandDescriptor (2);
+ cleanupOperandDescriptor (3);
+
+ //if (TST_I_TRUNC && T && tstOVFfault ())
+ // doFault(FAULT_OFL, fst_zero, "dv3d truncation(overflow) fault");
+ if (EOvr && tstOVFfault ())
+ doFault(FAULT_OFL, fst_zero, "dv3d over/underflow fault");
+ if (Ovr)
+ {
+ SET_I_OFLOW;
+ if (tstOVFfault ())
+ doFault(FAULT_OFL, fst_zero, "dv3d overflow fault");
+ }
+}
--- /dev/null
+/*
+ Copyright (c) 2007-2013 Michael Mondy
+ Copyright 2012-2016 by Harry Reed
+ Copyright 2013-2016 by Charles Anthony
+
+ All rights reserved.
+
+ This software is made available under the terms of the
+ ICU License -- ICU 1.8.1 and later.
+ See the LICENSE file at the top-level directory of this distribution and
+ at https://sourceforge.net/p/dps8m/code/ci/master/tree/LICENSE
+ */
+
+void setupEISoperands (void);
+void abd (void);
+void awd (void);
+void a4bd (void);
+void axbd (uint sz);
+
+void sbd (void);
+void swd (void);
+void s4bd (void);
+void s9bd (void);
+void sxbd (uint sz);
+void asxbd (uint sz, bool sub);
+
+void s4bd (void);
+void cmpc (void);
+void scd (void);
+void scdr (void);
+void scm (void);
+void scmr (void);
+void tct (void);
+void tctr (void);
+void mlr (void);
+void mrl (void);
+void mve (void);
+void mvne (void);
+void mvt (void);
+void cmpn (void);
+void mvn (void);
+//void csl (bool isSZTL);
+void csl (void);
+void sztl (void);
+void csr (void);
+void sztr (void);
+void cmpb (void);
+void btd (void);
+void dtb (void);
+void ad2d (void);
+void ad3d (void);
+void sb2d (void);
+void sb3d (void);
+void mp2d (void);
+void mp3d (void);
+void dv2d (void);
+void dv3d (void);
--- /dev/null
+/*
+ Copyright (c) 2007-2013 Michael Mondy
+ Copyright 2012-2016 by Harry Reed
+ Copyright 2013-2018 by Charles Anthony
+
+ All rights reserved.
+
+ This software is made available under the terms of the
+ ICU License -- ICU 1.8.1 and later.
+ See the LICENSE file at the top-level directory of this distribution and
+ at https://sourceforge.net/p/dps8m/code/ci/master/tree/LICENSE
+ */
+
+#ifndef DPS8_EM_CONSTS_H
+#define DPS8_EM_CONSTS_H
+
+////////////////
+//
+// System components: SCU, IOM, CPU
+//
+////////////////
+
+// SCU
+enum { N_SCU_UNITS_MAX = 8 };
+
+// IOM
+enum { N_IOM_UNITS_MAX = 4 };
+
+// CPU
+enum { N_CPU_UNITS_MAX = 8 };
+
+
+////////////////
+//
+// Controllers
+//
+////////////////
+
+// Unit record processor
+enum { N_URP_UNITS_MAX = 16 };
+
+// ABSI
+enum { N_ABSI_UNITS_MAX = 1 };
+
+// FNP
+enum { N_FNP_UNITS_MAX = 16 };
+
+// Operator console
+enum { N_OPC_UNITS_MAX = 8 };
+
+// MTP
+enum { N_MTP_UNITS_MAX = 16 };
+
+// MSP
+enum { N_MSP_UNITS_MAX = 16 };
+
+// IPC
+enum { N_IPC_UNITS_MAX = 16 };
+
+// DIA
+enum { N_DIA_UNITS_MAX = 16 };
+
+////////////////
+//
+// Peripherals
+//
+////////////////
+
+// Tape drive
+enum { N_MT_UNITS_MAX = 34 };
+
+// Printer
+enum { N_PRT_UNITS_MAX = 34 };
+
+// Socket controller
+enum { N_SKC_UNITS_MAX = 64 };
+
+// Card reader
+enum { N_RDR_UNITS_MAX = 16 };
+
+// Card punch
+enum { N_PUN_UNITS_MAX = 16 };
+
+// Disk
+enum { N_DSK_UNITS_MAX = 64 };
+
+//
+// Memory
+//
+
+enum { MEMSIZE = MEM_SIZE_MAX };
+
+//
+// Controller ports
+//
+
+enum { MAX_CTLR_PORTS = 8 };
+
+//
+// CPU ports
+//
+
+#ifdef DPS8M
+enum { N_CPU_PORTS = 4 };
+#endif
+#ifdef L68
+enum { N_CPU_PORTS = 8 };
+#endif
+
+#endif // DPS8_EM_CONSTS_H
+
--- /dev/null
+/*
+ Copyright (c) 2007-2013 Michael Mondy
+ Copyright 2012-2016 by Harry Reed
+ Copyright 2013-2016 by Charles Anthony
+ Copyright 2017 by Michal Tomek
+
+ All rights reserved.
+
+ This software is made available under the terms of the
+ ICU License -- ICU 1.8.1 and later.
+ See the LICENSE file at the top-level directory of this distribution and
+ at https://sourceforge.net/p/dps8m/code/ci/master/tree/LICENSE
+ */
+
+//
+// dps8_faults.c
+// dps8
+//
+// Created by Harry Reed on 6/11/13.
+// Copyright (c) 2013 Harry Reed. All rights reserved.
+//
+
+#include <stdio.h>
+
+#include "dps8.h"
+#include "dps8_sys.h"
+#include "dps8_faults.h"
+//#include "dps8_scu.h"
+//#include "dps8_iom.h"
+//#include "dps8_cable.h"
+#include "dps8_cpu.h"
+#include "dps8_append.h"
+#include "dps8_ins.h"
+#include "dps8_utils.h"
+#if defined(THREADZ) || defined(LOCKLESS)
+#include "threadz.h"
+#endif
+
+#define DBG_CTR cpu.cycleCnt
+
+/*
+ FAULT RECOGNITION
+ For the discussion following, the term "function" is defined as a major processor functional cycle. Examples are: APPEND CYCLE, CA CYCLE, INSTRUCTION FETCH CYCLE, OPERAND STORE CYCLE, DIVIDE EXECUTION CYCLE. Some of these cycles are discussed in various sections of this manual.
+ Faults in groups 1 and 2 cause the processor to abort all functions immediately by entering a FAULT CYCLE.
+ Faults in group 3 cause the processor to "close out" current functions without taking any irrevocable action (such as setting PTW.U in an APPEND CYCLE or modifying an indirect word in a CA CYCLE), then to discard any pending functions (such as an APPEND CYCLE needed during a CA CYCLE), and to enter a FAULT CYCLE.
+ Faults in group 4 cause the processor to suspend overlapped operation, to complete current and pending functions for the current instruction, and then to enter a FAULT CYCLE.
+ Faults in groups 5 or 6 are normally detected during virtual address formation and instruction decode. These faults cause the processor to suspend overlapped operation, to complete the current and pending instructions, and to enter a FAULT CYCLE. If a fault in a higher priority group is generated by the execution of the current or pending instructions, that higher priority fault will take precedence and the group 5 or 6 fault will be lost. If a group 5 or 6 fault is detected during execution of the current instruction (e.g., an access violation, out of segment bounds, fault
+ 
+ during certain interruptible EIS instructions), the instruction is considered "complete" upon detection of the fault.
+ Faults in group 7 are held and processed (with interrupts) at the completion of the current instruction pair. Group 7 faults are inhibitable by setting bit 28 of the instruction word.
+ Faults in groups 3 through 6 must wait for the system controller to acknowledge the last access request before entering the FAULT CYCLE.
+ */
+
+/*
+
+ Table 7-1. List of Faults
+
+ Decimal fault Octal (1) Fault Fault name Priority Group
+ number fault address mnemonic
+ 0 ; 0 ; sdf ; Shutdown ; 27 ; 7
+ 1 ; 2 ; str ; Store ; 10 ; 4
+ 2 ; 4 ; mme ; Master mode entry 1 ; 11 ; 5
+ 3 ; 6 ; f1 ; Fault tag 1 ; 17 ; 5
+ 4 ; 10 ; tro ; Timer runout ; 26 ; 7
+ 5 ; 12 ; cmd ; Command ; 9 ; 4
+ 6 ; 14 ; drl ; Derail ; 15 ; 5
+ 7 ; 16 ; luf ; Lockup ; 5 ; 4
+ 8 ; 20 ; con ; Connect ; 25 ; 7
+ 9 ; 22 ; par ; Parity ; 8 ; 4
+ 10 ; 24 ; ipr ; Illegal procedure ; 16 ; 5
+ 11 ; 26 ; onc ; Operation not complete ; 4 ; 2
+ 12 ; 30 ; suf ; Startup ; 1 ; 1
+ 13 ; 32 ; ofl ; Overflow ; 7 ; 3
+ 14 ; 34 ; div ; Divide check ; 6 ; 3
+ 15 ; 36 ; exf ; Execute ; 2 ; 1
+ 16 ; 40 ; df0 ; Directed fault 0 ; 20 ; 6
+ 17 ; 42 ; df1 ; Directed fault 1 ; 21 ; 6
+ 18 ; 44 ; df2 ; Directed fault 2 ; 22 ; 6
+ 19 ; 46 ; df3 ; Directed fault 3 ; 23 ; 6
+ 20 ; 50 ; acv ; Access violation ; 24 ; 6
+ 21 ; 52 ; mme2 ; Master mode entry 2 ; 12 ; 5
+ 22 ; 54 ; mme3 ; Master mode entry 3 ; 13 ; 5
+ 23 ; 56 ; mme4 ; Master mode entry 4 ; 14 ; 5
+ 24 ; 60 ; f2 ; Fault tag 2 ; 18 ; 5
+ 25 ; 62 ; f3 ; Fault tag 3 ; 19 ; 5
+ 26 ; 64 ; ; Unassigned ; ;
+ 27 ; 66 ; ; Unassigned ; ;
+
+*/
+
+#ifndef QUIET_UNUSED
+static dps8faults _faultsP[] = { // sorted by priority
+// number address mnemonic name Priority Group
+ { 12, 030, "suf", "Startup", 1, 1, false },
+ { 15, 036, "exf", "Execute", 2, 1, false },
+ { 31, 076, "trb", "Trouble", 3, 2, false },
+ { 11, 026, "onc", "Operation not complete", 4, 2, false },
+ { 7, 016, "luf", "Lockup", 5, 4, false },
+ { 14, 034, "div", "Divide check", 6, 3, false },
+ { 13, 032, "ofl", "Overflow", 7, 3, false },
+ { 9, 022, "par", "Parity", 8, 4, false },
+ { 5, 012, "cmd", "Command", 9, 4, false },
+ { 1, 2 , "str", "Store", 10, 4, false },
+ { 2, 4 , "mme", "Master mode entry 1", 11, 5, false },
+ { 21, 052, "mme2", "Master mode entry 2", 12, 5, false },
+ { 22, 054, "mme3", "Master mode entry 3", 13, 5, false },
+ { 23, 056, "mme4", "Master mode entry 4", 14, 5, false },
+ { 6, 014, "drl", "Derail", 15, 5, false },
+ { 10, 024, "ipr", "Illegal procedure", 16, 5, false },
+ { 3, 06, "f1", "Fault tag 1", 17, 5, false },
+ { 24, 060, "f2", "Fault tag 2", 18, 5, false },
+ { 25, 062, "f3", "Fault tag 3", 19, 5, false },
+ { 16, 040, "df0", "Directed fault 0", 20, 6, false },
+ { 17, 042, "df1", "Directed fault 1", 21, 6, false },
+ { 18, 044, "df2", "Directed fault 2", 22, 6, false },
+ { 19, 046, "df3", "Directed fault 3", 23, 6, false },
+ { 20, 050, "acv", "Access violation", 24, 6, false },
+ { 8, 020, "con", "Connect", 25, 7, false },
+ { 4, 010, "tro", "Timer runout", 26, 7, false },
+ { 0, 0 , "sdf", "Shutdown", 27, 7, false },
+ { 26, 064, "???", "Unassigned", -1, -1, false },
+ { 27, 066, "???", "Unassigned", -1, -1, false },
+ { -1, -1, NULL, NULL, -1, -1, false }
+};
+#endif
+#ifndef QUIET_UNUSED
+static dps8faults _faults[] = { // sorted by number
+ // number address mnemonic name Priority Group
+ { 0, 0 , "sdf", "Shutdown", 27, 7, false },
+ { 1, 2 , "str", "Store", 10, 4, false },
+ { 2, 4 , "mme", "Master mode entry 1", 11, 5, false },
+ { 3, 06, "f1", "Fault tag 1", 17, 5, false },
+ { 4, 010, "tro", "Timer runout", 26, 7, false },
+ { 5, 012, "cmd", "Command", 9, 4, false },
+ { 6, 014, "drl", "Derail", 15, 5, false },
+ { 7, 016, "luf", "Lockup", 5, 4, false },
+ { 8, 020, "con", "Connect", 25, 7, false },
+ { 9, 022, "par", "Parity", 8, 4, false },
+ { 10, 024, "ipr", "Illegal procedure", 16, 5, false },
+ { 11, 026, "onc", "Operation not complete", 4, 2, false },
+ { 12, 030, "suf", "Startup", 1, 1, false },
+ { 13, 032, "ofl", "Overflow", 7, 3, false },
+ { 14, 034, "div", "Divide check", 6, 3, false },
+ { 15, 036, "exf", "Execute", 2, 1, false },
+ { 16, 040, "df0", "Directed fault 0", 20, 6, false },
+ { 17, 042, "df1", "Directed fault 1", 21, 6, false },
+ { 18, 044, "df2", "Directed fault 2", 22, 6, false },
+ { 19, 046, "df3", "Directed fault 3", 23, 6, false },
+ { 20, 050, "acv", "Access violation", 24, 6, false },
+ { 21, 052, "mme2", "Master mode entry 2", 12, 5, false },
+ { 22, 054, "mme3", "Master mode entry 3", 13, 5, false },
+ { 23, 056, "mme4", "Master mode entry 4", 14, 5, false },
+ { 24, 060, "f2", "Fault tag 2", 18, 5, false },
+ { 25, 062, "f3", "Fault tag 3", 19, 5, false },
+ { 26, 064, "???", "Unassigned", -1, -1, false },
+ { 27, 066, "???", "Unassigned", -1, -1, false },
+ { 28, 070, "???", "Unassigned", -1, -1, false },
+ { 29, 072, "???", "Unassigned", -1, -1, false },
+ { 30, 074, "???", "Unassigned", -1, -1, false },
+ { 31, 076, "trb", "Trouble", 3, 2, false },
+
+ { -1, -1, NULL, NULL, -1, -1, false }
+};
+#endif
+
+char * faultNames [N_FAULTS] =
+ {
+ "Shutdown",
+ "Store",
+ "Master mode entry 1",
+ "Fault tag 1",
+ "Timer runout",
+ "Command",
+ "Derail",
+ "Lockup",
+ "Connect",
+ "Parity",
+ "Illegal procedure",
+ "Operation not complete",
+ "Startup",
+ "Overflow",
+ "Divide check",
+ "Execute",
+ "Directed fault 0",
+ "Directed fault 1",
+ "Directed fault 2",
+ "Directed fault 3",
+ "Access violation",
+ "Master mode entry 2",
+ "Master mode entry 3",
+ "Master mode entry 4",
+ "Fault tag 2",
+ "Fault tag 3",
+ "Unassigned 26",
+ "Unassigned 27",
+ "Unassigned 28",
+ "Unassigned 29",
+ "Unassigned 30",
+ "Trouble"
+ };
+//bool pending_fault = false; // true when a fault has been signalled, but not processed
+
+
+#ifndef QUIET_UNUSED
+static bool port_interrupts[8] = {false, false, false, false, false, false, false, false };
+#endif
+
+//-----------------------------------------------------------------------------
+// *** Constants, unchanging lookup tables, etc
+
+#ifndef QUIET_UNUSED
+static int fault2group[32] = {
+ // from AL39, page 7-3
+ 7, 4, 5, 5, 7, 4, 5, 4,
+ 7, 4, 5, 2, 1, 3, 3, 1,
+ 6, 6, 6, 6, 6, 5, 5, 5,
+ 5, 5, 0, 0, 0, 0, 0, 2
+};
+
+static int fault2prio[32] = {
+ // from AL39, page 7-3
+ 27, 10, 11, 17, 26, 9, 15, 5,
+ 25, 8, 16, 4, 1, 7, 6, 2,
+ 20, 21, 22, 23, 24, 12, 13, 14,
+ 18, 19, 0, 0, 0, 0, 0, 3
+};
+#endif
+
+/*
+ * fault handler(s).
+ */
+
+#ifdef TESTING
+// We stash a few things for debugging; they are accessed by emCall.
+static word18 fault_ic;
+static word15 fault_psr;
+static char fault_msg [1024];
+
+
+void emCallReportFault (void)
+ {
+ sim_printf ("fault report:\n");
+ sim_printf (" fault number %d (%o)\n", cpu . faultNumber, cpu . faultNumber);
+ sim_printf (" subfault number %"PRIu64" (%"PRIo64")\n", cpu.subFault.bits, cpu.subFault.bits);
+ sim_printf (" faulting address %05o:%06o\n", fault_psr, fault_ic);
+ sim_printf (" msg %s\n", fault_msg);
+ }
+#endif
+
+
+void clearFaultCycle (void)
+ {
+ cpu . bTroubleFaultCycle = false;
+ }
+
+/*
+
+Faults in groups 1 and 2 cause the processor to abort all functions immediately
+by entering a FAULT CYCLE.
+
+Faults in group 3 cause the processor to "close out" current functions without
+taking any irrevocable action (such as setting PTW.U in an APPEND CYCLE or
+modifying an indirect word in a CA CYCLE), then to discard any pending
+functions (such as an APPEND CYCLE needed during a CA CYCLE), and to enter a
+FAULT CYCLE.
+
+Faults in group 4 cause the processor to suspend overlapped operation, to
+complete current and pending functions for the current instruction, and then to
+enter a FAULT CYCLE.
+
+Faults in groups 5 or 6 are normally detected during virtual address formation
+and instruction decode. These faults cause the processor to suspend overlapped
+operation, to complete the current and pending instructions, and to enter a
+FAULT CYCLE. If a fault in a higher priority group is generated by the
+execution of the current or pending instructions, that higher priority fault
+will take precedence and the group 5 or 6 fault will be lost. If a group 5 or 6
+fault is detected during execution of the current instruction (e.g., an access
+violation, out of segment bounds, fault during certain interruptible EIS
+instructions), the instruction is considered "complete" upon detection of the
+fault.
+
+Faults in group 7 are held and processed (with interrupts) at the completion
+of the current instruction pair.
+
+Group 7 faults are inhibitable by setting bit 28 of the instruction word.
+
+Faults in groups 3 through 6 must wait for the system controller to acknowledge
+the last access request before entering the FAULT CYCLE.
+
+After much rumination here are my thoughts for fault processing .....
+
+For now, at least, we must remember a few things:
+
+1) We only have 1 cpu so we have few & limited async faults - shutdown, TRO,
+etc.
+2) We have no overlapping instruction execution
+3) Becuase of 2) we have no pending instructions
+4) We have no system controller to wait for
+
+Group 1 & 2 faults can be processed immediately and then proceed to next
+instruction as long as no transfer prevents us from returing from the XED pair.
+
+Group 3 faults will probably also execute immediately since a G3 fault causes
+"the processor to "close out" current functions without taking any irrevocable
+action (such as setting PTW.U in an APPEND CYCLE or modifying an indirect word
+in a CA CYCLE), then to discard any pending functions (such as an APPEND CYCLE
+needed during a CA CYCLE), and to enter a FAULT CYCLE."
+
+Group 4 faults will probably also execute immediately since a G4 fault causes
+"the processor to suspend overlapped operation, to complete current and pending
+functions for the current instruction, and then to enter a FAULT CYCLE."
+
+Group 5 & 6 faults will probably also execute immediately because "if a group 5
+or 6 fault is detected during execution of the current instruction (e.g., an
+access violation, out of segment bounds, fault during certain interruptible EIS
+instructions), the instruction is considered "complete" upon detection of the
+fault." However, remember "If a fault in a higher priority group is generated
+by the execution of the current or pending instructions, that higher priority
+fault will take precedence and the group 5 or 6 fault will be lost. If a group
+5 or 6 fault is detected during execution of the current instruction (e.g., an
+access violation, out of segment bounds, fault during certain interruptible EIS
+instructions), the instruction is considered "complete" upon detection of the
+fault."
+
+For furter justification of immediate execution since "Faults in groups 3
+through 6 must wait for the system controller to acknowledge the last access
+request before entering the FAULT CYCLE."
+
+Group 7 faults will be processed after next even instruction decode instruction
+decode, but before instruction execution. In this way we can actually use
+bit-28 tp inhibit interrupts
+
+*/
+
+#ifdef LOOPTRC
+#include <time.h>
+void elapsedtime (void);
+#endif
+
+#ifndef NEED_128
+const _fault_subtype fst_zero = (_fault_subtype) {.bits=0};
+const _fault_subtype fst_acv9 = (_fault_subtype) {.fault_acv_subtype=ACV9};
+const _fault_subtype fst_acv15 = (_fault_subtype) {.fault_acv_subtype=ACV15};
+const _fault_subtype fst_ill_mod = (_fault_subtype) {.fault_ipr_subtype=FR_ILL_MOD};
+const _fault_subtype fst_ill_proc = (_fault_subtype) {.fault_ipr_subtype=FR_ILL_PROC};
+const _fault_subtype fst_ill_dig = (_fault_subtype) {.fault_ipr_subtype=FR_ILL_DIG};
+const _fault_subtype fst_ill_op = (_fault_subtype) {.fault_ipr_subtype=FR_ILL_OP};
+const _fault_subtype fst_str_oob = (_fault_subtype) {.fault_str_subtype=flt_str_oob};
+const _fault_subtype fst_str_nea = (_fault_subtype) {.fault_str_subtype=flt_str_nea};
+const _fault_subtype fst_str_ptr = (_fault_subtype) {.fault_str_subtype=flt_str_ill_ptr};
+const _fault_subtype fst_cmd_lprpn = (_fault_subtype) {.fault_cmd_subtype=flt_cmd_lprpn_bits};
+const _fault_subtype fst_cmd_ctl = (_fault_subtype) {.fault_cmd_subtype=flt_cmd_not_control};
+const _fault_subtype fst_onc_nem = (_fault_subtype) {.fault_onc_subtype=flt_onc_nem};
+#endif
+// CANFAULT
+void doFault (_fault faultNumber, _fault_subtype subFault,
+ const char * faultMsg)
+ {
+#ifdef LOOPTRC
+if (faultNumber == FAULT_TRO)
+{
+ elapsedtime ();
+ sim_printf (" TRO PSR:IC %05o:%06o\r\n", cpu.PPR.PSR, cpu.PPR.IC);
+}
+else if (faultNumber == FAULT_ACV)
+{
+ elapsedtime ();
+ sim_printf (" ACV %012llo PSR:IC %05o:%06o\r\n", subFault.bits, cpu.PPR.PSR, cpu.PPR.IC);
+}
+#endif
+//if (current_running_cpu_idx)
+ //sim_printf ("Fault %d(0%0o), sub %ld(0%lo), dfc %c, '%s'\n",
+ //faultNumber, faultNumber, subFault, subFault,
+ //cpu . bTroubleFaultCycle ? 'Y' : 'N', faultMsg);
+//if (current_running_cpu_idx)
+ //sim_printf ("xde %d xdo %d\n", cpu.cu.xde, cpu.cu.xdo);
+ sim_debug (DBG_FAULT, & cpu_dev,
+ "Fault %d(0%0o), sub %"PRIu64"(0%"PRIo64"), dfc %c, '%s'\n",
+ faultNumber, faultNumber, subFault.bits, subFault.bits,
+ cpu . bTroubleFaultCycle ? 'Y' : 'N', faultMsg);
+#ifdef HDBG
+ hdbgFault (faultNumber, subFault, faultMsg);
+#endif
+#ifndef SPEED
+ if_sim_debug (DBG_FAULT, & cpu_dev)
+ traceInstruction (DBG_FAULT);
+#endif
+
+ PNL (cpu.DACVpDF = faultNumber >= FAULT_DF0 && faultNumber <= FAULT_ACV;)
+
+#ifdef TESTING
+ // some debugging support stuff
+ fault_psr = cpu . PPR.PSR;
+ fault_ic = cpu . PPR.IC;
+ strcpy (fault_msg, faultMsg);
+#endif
+
+ //if (faultNumber < 0 || faultNumber > 31)
+ if (faultNumber & ~037U) // quicker?
+ {
+ sim_printf ("fault(out-of-range): %d %"PRIo64" '%s'\n",
+ faultNumber, subFault.bits, faultMsg ? faultMsg : "?");
+ sim_warn ("fault out-of-range\n");
+ faultNumber = FAULT_TRB;
+ }
+
+ cpu.faultNumber = faultNumber;
+ cpu.subFault = subFault;
+ cpu.faultCnt [faultNumber] ++;
+
+ // "The occurrence of a fault or interrupt sets the cache-to-register mode bit to OFF." a:AL39/cmr1
+ CPTUR (cptUseCMR);
+ cpu.CMR.csh_reg = 0;
+
+ // Increment FCT
+
+ word3 FCT = cpu.cu.APUCycleBits & MASK3;
+ FCT = (FCT + 1u) & MASK3;
+ cpu.cu.APUCycleBits = (word12) ((cpu.cu.APUCycleBits & 07770) | FCT);
+
+ // Set fault register bits
+
+ CPTUR (cptUseFR);
+ if (faultNumber == FAULT_IPR)
+ {
+#if 0
+ if (subFault == flt_ipr_ill_op)
+ cpu . faultRegister [0] |= FR_ILL_OP;
+ else if (subFault == flt_ipr_ill_mod)
+ cpu . faultRegister [0] |= FR_ILL_MOD;
+ else if (subFault == flt_ipr_ill_dig)
+ cpu . faultRegister [0] |= FR_ILL_DIG;
+ else /* if (subFault == flt_ipr_ill_proc) */ // and all others
+ cpu . faultRegister [0] |= FR_ILL_PROC;
+#else
+ cpu . faultRegister [0] |= subFault.bits;
+#endif
+ }
+ else if (faultNumber == FAULT_ONC && subFault.fault_onc_subtype == flt_onc_nem)
+ {
+ cpu . faultRegister [0] |= FR_NEM;
+ }
+ else if (faultNumber == FAULT_STR)
+ {
+ if (subFault.fault_str_subtype == flt_str_oob)
+ cpu . faultRegister [0] |= FR_OOB;
+ //else if (subFault.fault_str_subtype == flt_str_ill_ptr)
+ //cpu . faultRegister [0] |= ?; // XXX
+ //else if (subFault.fault_str_subtype == flt_str_nea)
+ //cpu . faultRegister [0] |= ?; // XXX
+ }
+ else if (faultNumber == FAULT_CON)
+ {
+ switch (subFault.fault_con_subtype)
+ {
+ case con_a:
+ cpu . faultRegister [0] |= FR_CON_A;
+ break;
+ case con_b:
+ cpu . faultRegister [0] |= FR_CON_B;
+ break;
+ case con_c:
+ cpu . faultRegister [0] |= FR_CON_C;
+ break;
+ case con_d:
+ cpu . faultRegister [0] |= FR_CON_D;
+ break;
+ default:
+ sim_warn ("FAULT_CON can't map port %lo\n", (long unsigned) subFault.fault_con_subtype);
+ break;
+ }
+ }
+
+ // Set cu word1 fault bits
+
+ cpu . cu . IRO_ISN = 0;
+ cpu . cu . OEB_IOC = 0;
+ cpu . cu . EOFF_IAIM = 0;
+ cpu . cu . ORB_ISP = 0;
+ cpu . cu . ROFF_IPR = 0;
+ cpu . cu . OWB_NEA = 0;
+ cpu . cu . WOFF_OOB = 0;
+ cpu . cu . NO_GA = 0;
+ cpu . cu . OCB = 0;
+ cpu . cu . OCALL = 0;
+ cpu . cu . BOC = 0;
+#ifdef DPS8M
+ cpu . cu . PTWAM_ER = 0;
+#endif
+ cpu . cu . CRT = 0;
+ cpu . cu . RALR = 0;
+ cpu . cu . SDWAM_ER = 0;
+ cpu . cu . OOSB = 0;
+ cpu . cu . PARU = 0;
+ cpu . cu . PARL = 0;
+ cpu . cu . ONC1 = 0;
+ cpu . cu . ONC2 = 0;
+ cpu . cu . IA = 0;
+ cpu . cu . IACHN = 0;
+ cpu . cu . CNCHN = (faultNumber == FAULT_CON) ? subFault.fault_con_subtype & MASK3 : 0;
+
+ // Set control unit 'fault occured during instruction fetch' flag
+ cpu . cu . FIF = cpu . cycle == FETCH_cycle ? 1 : 0;
+ cpu . cu . FI_ADDR = (word5) faultNumber;
+
+ // XXX Under what conditions should this be set?
+ // Assume no
+ // Reading Multics source, it seems like Multics is setting this bit; I'm going
+ // to assume that the h/w also sets it to 0, and the s/w has to explicitly set it on.
+ cpu . cu . rfi = 0;
+
+// Try to decide if this a MIF fault (fault during EIS instruction)
+// EIS instructions are not used in fault/interrupt pairs, so the
+// only time an EIS instruction could be executing is during EXEC_cycle.
+// I am also assuming that only multi-word EIS instructions are of interest.
+// Testing faultNumber fixes ISOLTS 890-04a
+ // fixes 890-04a and 791 / 792
+ SC_I_MIF (cpu.cycle == EXEC_cycle &&
+ (cpu.currentInstruction.info->ndes > 0 ||
+ (faultNumber == FAULT_IPR && (subFault.fault_ipr_subtype & FR_ILL_OP) &&
+ cpu.currentInstruction.opcodeX &&
+ (cpu.currentInstruction.opcode & 0410) == 0)));
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "MIF %o\n", TST_I_MIF);
+#if 0
+sim_debug (DBG_FAULT, & cpu_dev, "cycle %u ndes %u fn %u v %u\n", cpu.cycle, cpu.currentInstruction.info->ndes, faultNumber, (cpu . cycle == EXEC_cycle && cpu . currentInstruction . info -> ndes > 0) || faultNumber == FAULT_IPR);
+ SC_I_MIF (cpu . cycle == EXEC_cycle &&
+ cpu . currentInstruction . info -> ndes > 0);
+ //SC_I_MIF ((cpu . cycle == EXEC_cycle &&
+ //cpu . currentInstruction . info -> ndes > 0) ||
+ //faultNumber == FAULT_IPR);
+#endif
+
+#ifdef ISOLTS
+//if (current_running_cpu_idx && faultNumber == FAULT_LUF) hdbgPrint ();
+#endif
+ if (faultNumber == FAULT_ACV)
+ {
+ // This is annoyingly inefficent since the subFault value
+ // is bitwise the same as the upper half of CU word1;
+ // if the upperhalf were not broken out, then this would be
+ // cpu . cu . word1_upper_half = subFault.
+
+ if (subFault.fault_acv_subtype & ACV0)
+ cpu . cu . IRO_ISN = 1;
+ if (subFault.fault_acv_subtype & ACV1)
+ cpu . cu . OEB_IOC = 1;
+ if (subFault.fault_acv_subtype & ACV2)
+ cpu . cu . EOFF_IAIM = 1;
+ if (subFault.fault_acv_subtype & ACV3)
+ cpu . cu . ORB_ISP = 1;
+ if (subFault.fault_acv_subtype & ACV4)
+ cpu . cu . ROFF_IPR = 1;
+ if (subFault.fault_acv_subtype & ACV5)
+ cpu . cu . OWB_NEA = 1;
+ if (subFault.fault_acv_subtype & ACV6)
+ cpu . cu . WOFF_OOB = 1;
+ if (subFault.fault_acv_subtype & ACV7)
+ cpu . cu . NO_GA = 1;
+ if (subFault.fault_acv_subtype & ACV8)
+ cpu . cu . OCB = 1;
+ if (subFault.fault_acv_subtype & ACV9)
+ cpu . cu . OCALL = 1;
+ if (subFault.fault_acv_subtype & ACV10)
+ cpu . cu . BOC = 1;
+ if (subFault.fault_acv_subtype & ACV11)
+ cpu . cu . PTWAM_ER = 1;
+ if (subFault.fault_acv_subtype & ACV12)
+ cpu . cu . CRT = 1;
+ if (subFault.fault_acv_subtype & ACV13)
+ cpu . cu . RALR = 1;
+ if (subFault.fault_acv_subtype & ACV14)
+ cpu . cu . SDWAM_ER = 1;
+ if (subFault.fault_acv_subtype & ACV15)
+ cpu . cu . OOSB = 1;
+ }
+ else if (faultNumber == FAULT_STR)
+ {
+ if (subFault.fault_str_subtype == flt_str_oob)
+ cpu . cu . WOFF_OOB = 1;
+ //else if (subFault.fault_str_subtype == flt_str_ill_ptr)
+ //cpu . cu . ??? = 1; // XXX
+ else if (subFault.fault_str_subtype == flt_str_nea)
+ cpu . cu . OWB_NEA = 1;
+ }
+ else if (faultNumber == FAULT_IPR)
+ {
+ if (subFault.fault_ipr_subtype & FR_ILL_OP)
+ cpu . cu . OEB_IOC = 1;
+ if (subFault.fault_ipr_subtype & FR_ILL_MOD)
+ cpu . cu . EOFF_IAIM = 1;
+ if (subFault.fault_ipr_subtype & FR_ILL_SLV)
+ cpu . cu . ORB_ISP = 1;
+ if (subFault.fault_ipr_subtype & FR_ILL_DIG)
+ cpu . cu . ROFF_IPR = 1;
+ }
+ else if (faultNumber == FAULT_CMD)
+ {
+ if (subFault.fault_cmd_subtype == flt_cmd_lprpn_bits)
+ cpu . cu . IA = 0;
+ else if (subFault.fault_cmd_subtype == flt_cmd_not_control)
+ cpu . cu . IA = 010;
+ }
+
+#ifdef L68
+ // History registers
+ // IHRRS; AL39 pg 47
+ // History register lock control. If this bit is set ON, set STROBE ¢
+ // (bit 30, key k) OFF, locking the history registers for all faults
+ // including the floating faults.
+ CPTUR (cptUseMR);
+ if (cpu.MR.emr && cpu.MR.ihrrs)
+ {
+ cpu.MR.ihr = 0;
+ }
+#endif
+#ifdef DPS8M
+ // History registers
+ // IHRRS; AL39 pg 49
+ // Additional resetting of bit 30. If bit 31 = 1, the following faults also
+ // reset bit 30:
+ // Lock Up
+ // Parity
+ // Command
+ // Store
+ // Illegal Procedure
+ // Shutdown
+ if (cpu.MR.emr && cpu.MR.ihrrs)
+ {
+ if (faultNumber == FAULT_LUF ||
+ faultNumber == FAULT_PAR ||
+ faultNumber == FAULT_CMD ||
+ faultNumber == FAULT_STR ||
+ faultNumber == FAULT_IPR ||
+ faultNumber == FAULT_SDF)
+ {
+ cpu.MR.ihr = 0;
+ }
+ }
+ // Enable History Registers. This bit will be reset by ... an Op Not
+ // Complete fault. It may be reset by other faults (see bit 31).
+ if (faultNumber == FAULT_ONC)
+ {
+ cpu.MR.ihr = 0;
+ }
+#endif
+
+ // If already in a FAULT CYCLE then signal trouble fault
+
+ if (cpu.cycle == FAULT_EXEC_cycle)
+ {
+ sim_debug (DBG_CYCLE, & cpu_dev, "Changing fault number to Trouble fault\n");
+
+ cpu.faultNumber = FAULT_TRB;
+ cpu.cu.FI_ADDR = FAULT_TRB;
+ cpu.subFault.bits = 0; // XXX ???
+ // XXX Does the CU or FR need fixing? ticket #36
+ if (cpu . bTroubleFaultCycle)
+ {
+ abort();
+//#if !defined(THREADZ) && !defined(LOCKLESS)
+//#ifndef PANEL
+//#ifndef ROUND_ROBIN
+// if ((! sample_interrupts ()) &&
+// (sim_qcount () == 0)) // XXX If clk_svc is implemented it will
+// // break this logic
+// {
+// sim_printf ("Fault cascade @0%06o with no interrupts pending and no events in queue\n", cpu . PPR.IC);
+// sim_printf("\nCycles = %"PRId64"\n", cpu.cycleCnt);
+// sim_printf("\nInstructions = %"PRId64"\n", cpu.instrCnt);
+// //stop_reason = STOP_FLT_CASCADE;
+// longjmp (cpu.jmpMain, JMP_STOP);
+// }
+//#endif
+//#endif
+//#endif
+ }
+ else
+ {
+//-- f = &_faults[FAULT_TRB];
+ cpu . bTroubleFaultCycle = true;
+ }
+ }
+ else
+ {
+ cpu . bTroubleFaultCycle = false;
+ }
+
+ // If doInstruction faults, the instruction cycle counter doesn't get
+ // bumped.
+ if (cpu . cycle == EXEC_cycle)
+ cpu.instrCnt ++;
+
+ cpu . cycle = FAULT_cycle;
+ sim_debug (DBG_CYCLE, & cpu_dev, "Setting cycle to FAULT_cycle\n");
+ longjmp (cpu.jmpMain, JMP_REENTRY);
+}
+
+#ifdef L68
+void do_FFV_fault (uint fault_number, const char * fault_msg)
+ {
+ sim_debug (DBG_FAULT, & cpu_dev,
+ "Floating fault %d '%s'\n",
+ fault_number, fault_msg);
+#ifndef SPEED
+ if_sim_debug (DBG_FAULT, & cpu_dev)
+ traceInstruction (DBG_FAULT);
+#endif
+
+ if (fault_number < 1 || fault_number > 3)
+ {
+ sim_printf ("floating fault(out-of-range): %d '%s'\n",
+ fault_number, fault_msg ? fault_msg : "?");
+ sim_warn ("fault out-of-range\n");
+ }
+
+ cpu.FFV_fault_number = fault_number;
+ cpu.faultNumber = fault_number;
+
+ // "The occurrence of a fault or interrupt sets the cache-to-register mode bit to OFF." a:AL39/cmr1
+ CPTUR (cptUseCMR);
+ cpu.CMR.csh_reg = 0;
+
+ // Increment FCT
+
+ word3 FCT = cpu.cu.APUCycleBits & MASK3;
+ FCT = (FCT + 1) & MASK3;
+ cpu.cu.APUCycleBits = (word12) ((cpu.cu.APUCycleBits & 07770) | FCT);
+
+ // Set fault register bits
+ CPTUR (cptUseFR);
+ cpu.faultRegister [0] = 0;
+
+ // Set cu word1 fault bits
+
+ cpu.cu.IRO_ISN = 0;
+ cpu.cu.OEB_IOC = 0;
+ cpu.cu.EOFF_IAIM = 0;
+ cpu.cu.ORB_ISP = 0;
+ cpu.cu.ROFF_IPR = 0;
+ cpu.cu.OWB_NEA = 0;
+ cpu.cu.WOFF_OOB = 0;
+ cpu.cu.NO_GA = 0;
+ cpu.cu.OCB = 0;
+ cpu.cu.OCALL = 0;
+ cpu.cu.BOC = 0;
+#ifdef DPS8M
+ cpu.cu.PTWAM_ER = 0;
+#endif
+ cpu.cu.CRT = 0;
+ cpu.cu.RALR = 0;
+ cpu.cu.SDWAM_ER = 0;
+ cpu.cu.OOSB = 0;
+ cpu.cu.PARU = 0;
+ cpu.cu.PARL = 0;
+ cpu.cu.ONC1 = 0;
+ cpu.cu.ONC2 = 0;
+ cpu.cu.IA = 0;
+ cpu.cu.IACHN = 0;
+ cpu.cu.CNCHN = 0;
+
+ // Set control unit 'fault occured during instruction fetch' flag
+ cpu.cu.FIF = 0;
+ cpu.cu.FI_ADDR = (word5) fault_number & MASK5;
+
+ // XXX Under what conditions should this be set?
+ // Assume no
+ // Reading Multics source, it seems like Multics is setting this bit; I'm going
+ // to assume that the h/w also sets it to 0, and the s/w has to explicitly set it on.
+ cpu.cu.rfi = 0;
+
+// Try to decide if this a MIF fault (fault during EIS instruction)
+// EIS instructions are not used in fault/interrupt pairs, so the
+// only time an EIS instruction could be executing is during EXEC_cycle.
+// I am also assuming that only multi-word EIS instructions are of interest.
+#if 1
+ SC_I_MIF (cpu.cycle == EXEC_cycle &&
+ cpu.currentInstruction.info->ndes > 0);
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "MIF %o\n", TST_I_MIF);
+#endif
+
+ // History registers
+ // IHRRS; AL39 pg 47
+ // History register lock control. If this bit is set ON, set STROBE ¢
+ // (bit 30, key k) OFF, locking the history registers for all faults
+ // including the floating faults.
+ CPTUR (cptUseMR);
+ if (cpu.MR.emr && cpu.MR.ihrrs)
+ {
+ cpu.MR.ihr = 0;
+ }
+
+ if (cpu.cycle == FAULT_EXEC_cycle)
+ {
+ cpu.faultNumber = FAULT_TRB;
+ cpu.cu.FI_ADDR = FAULT_TRB;
+ cpu.subFault.bits = 0; // XXX ???
+ // XXX Does the CU or FR need fixing? ticket #36
+ if (cpu.bTroubleFaultCycle)
+ {
+#if !defined(THREADZ) && !defined(LOCKLESS)
+#ifndef PANEL
+#ifndef ROUND_ROBIN
+ if ((! sample_interrupts ()) &&
+ (sim_qcount () == 0)) // XXX If clk_svc is implemented it will
+ // break this logic
+ {
+ sim_printf ("Fault cascade @0%06o with no interrupts pending and no events in queue\n", cpu.PPR.IC);
+ sim_printf("\nCycles = %"PRId64"\n", cpu.cycleCnt);
+ sim_printf("\nInstructions = %"PRId64"\n", cpu.instrCnt);
+ //stop_reason = STOP_FLT_CASCADE;
+ longjmp (cpu.jmpMain, JMP_STOP);
+ }
+#endif
+#endif
+#endif
+ }
+ else
+ {
+ cpu.bTroubleFaultCycle = true;
+ }
+ cpu.cycle = FAULT_cycle;
+ sim_debug (DBG_CYCLE, & cpu_dev, "Setting cycle to FAULT_cycle\n");
+ longjmp (cpu.jmpMain, JMP_REENTRY);
+ }
+ cpu.bTroubleFaultCycle = false;
+
+ // If doInstruction faults, the instruction cycle counter doesn't get
+ // bumped.
+ if (cpu . cycle == EXEC_cycle)
+ cpu.instrCnt ++;
+
+ cpu.is_FFV = true;
+ cpu.cycle = FAULT_cycle;
+ longjmp (cpu.jmpMain, JMP_REENTRY);
+}
+#endif
+
+void dlyDoFault (_fault faultNumber, _fault_subtype subFault,
+ const char * faultMsg)
+ {
+ cpu.dlyFlt = true;
+ cpu.dlyFltNum = faultNumber;
+ cpu.dlySubFltNum = subFault;
+ cpu.dlyCtx = faultMsg;
+ }
+
+//
+// return true if group 7 faults are pending ...
+//
+
+// Note: The DIS code assumes that the only G7 fault is TRO. Adding any
+// other G7 faults will potentailly require changing the DIS code.
+
+bool bG7Pending (void)
+ {
+#ifdef DPS8M
+ return cpu.g7Faults != 0;
+#endif
+#ifdef L68
+ return cpu.g7Faults != 0 || cpu.FFV_faults != 0;
+#endif
+ }
+
+bool bG7PendingNoTRO (void)
+ {
+#ifdef DPS8M
+ return (cpu.g7Faults & (~ (1u << FAULT_TRO))) != 0;
+#endif
+#ifdef L68
+ return (cpu.g7Faults & (~ (1u << FAULT_TRO))) != 0 || cpu.FFV_faults != 0;
+#endif
+ }
+
+void setG7fault (uint cpuNo, _fault faultNo, _fault_subtype subFault)
+ {
+ sim_debug (DBG_FAULT, & cpu_dev, "setG7fault CPU %d fault %d (%o) sub %"PRId64" %"PRIo64"\n",
+ cpuNo, faultNo, faultNo, subFault.bits, subFault.bits);
+ cpus[cpuNo].g7FaultsPreset |= (1u << faultNo);
+ //cpu.g7SubFaultsPreset [faultNo] = subFault;
+ cpus[cpuNo].g7SubFaults [faultNo] = subFault;
+#if defined(THREADZ) || defined(LOCKLESS)
+ wakeCPU(cpuNo);
+#endif
+ }
+
+#ifdef L68
+void set_FFV_fault (uint f_fault_no)
+ {
+ sim_debug (DBG_FAULT, & cpu_dev, "set_FFV_fault CPU f_fault_no %u\n",
+ f_fault_no);
+ // Map fault number (2/4/6) to bit mask 01/02/04
+ cpu.FFV_faults_preset |= 1u << ((f_fault_no / 2) - 1);
+ }
+#endif
+
+void clearTROFault (void)
+ {
+ cpu . g7Faults &= ~(1u << FAULT_TRO);
+ }
+
+void doG7Fault (bool allowTR)
+ {
+ // sim_printf ("doG7fault %08o [%"PRId64"]\n", cpu . g7Faults, cpu.cycleCnt);
+ // if (cpu . g7Faults)
+ // {
+ // sim_debug (DBG_FAULT, & cpu_dev, "doG7Fault %08o\n", cpu . g7Faults);
+ // }
+ // According AL39, Table 7-1. List of Faults, priority of connect is 25
+ // and priority of Timer runout is 26, lower number means higher priority
+#if defined(THREADZ) || defined(LOCKLESS)
+ lock_scu ();
+#endif
+ if (cpu.g7Faults & (1u << FAULT_CON))
+ {
+ cpu.g7Faults &= ~(1u << FAULT_CON);
+
+#if defined(THREADZ) || defined(LOCKLESS)
+ unlock_scu ();
+#endif
+ doFault (FAULT_CON, cpu.g7SubFaults [FAULT_CON], "Connect");
+ }
+
+ if (allowTR && cpu . g7Faults & (1u << FAULT_TRO))
+ {
+ cpu . g7Faults &= ~(1u << FAULT_TRO);
+
+ //sim_printf("timer runout %12o\n",cpu.PPR.IC);
+#if defined(THREADZ) || defined(LOCKLESS)
+ unlock_scu ();
+#endif
+ doFault (FAULT_TRO, fst_zero, "Timer runout");
+ }
+
+ // Strictly speaking EXF isn't a G7 fault, put if we treat is as one,
+ // we are allowing the current instruction to complete, simplifying
+ // implementation
+ if (cpu . g7Faults & (1u << FAULT_EXF))
+ {
+ cpu . g7Faults &= ~(1u << FAULT_EXF);
+
+#if defined(THREADZ) || defined(LOCKLESS)
+ unlock_scu ();
+#endif
+ doFault (FAULT_EXF, fst_zero, "Execute fault");
+ }
+
+#ifdef L68
+ if (cpu.FFV_faults & 1u) // FFV + 2 OC TRAP
+ {
+ cpu.FFV_faults &= ~1u;
+#if defined(THREADZ) || defined(LOCKLESS)
+ unlock_scu ();
+#endif
+ do_FFV_fault (1, "OC TRAP");
+ }
+ if (cpu.FFV_faults & 2u) // FFV + 4 CU HISTORY OVERFLOW TRAP
+ {
+ cpu.FFV_faults &= ~2u;
+#if defined(THREADZ) || defined(LOCKLESS)
+ unlock_scu ();
+#endif
+ do_FFV_fault (2, "CU HIST OVF TRAP");
+ }
+ if (cpu.FFV_faults & 4u) // FFV + 6 ADR TRAP
+ {
+ cpu.FFV_faults &= ~4u;
+#if defined(THREADZ) || defined(LOCKLESS)
+ unlock_scu ();
+#endif
+ do_FFV_fault (3, "ADR TRAP");
+ }
+#endif
+#if defined(THREADZ) || defined(LOCKLESS)
+ unlock_scu ();
+#endif
+ doFault (FAULT_TRB, (_fault_subtype) {.bits=cpu.g7Faults}, "Dazed and confused in doG7Fault");
+ }
+
+void advanceG7Faults (void)
+ {
+#if defined(THREADZ) || defined(LOCKLESS)
+ lock_scu ();
+#endif
+ cpu.g7Faults |= cpu.g7FaultsPreset;
+ cpu.g7FaultsPreset = 0;
+ //memcpy (cpu.g7SubFaults, cpu.g7SubFaultsPreset, sizeof (cpu.g7SubFaults));
+#ifdef L68
+ cpu.FFV_faults |= cpu.FFV_faults_preset;
+ cpu.FFV_faults_preset = 0;
+#endif
+#if defined(THREADZ) || defined(LOCKLESS)
+ unlock_scu ();
+#endif
+ }
+
--- /dev/null
+/*
+ Copyright (c) 2007-2013 Michael Mondy
+ Copyright 2012-2016 by Harry Reed
+ Copyright 2013-2016 by Charles Anthony
+
+ All rights reserved.
+
+ This software is made available under the terms of the
+ ICU License -- ICU 1.8.1 and later.
+ See the LICENSE file at the top-level directory of this distribution and
+ at https://sourceforge.net/p/dps8m/code/ci/master/tree/LICENSE
+ */
+
+#ifndef QUIET_UNUSED
+struct _fault_register {
+ // even word
+ bool ILL_OP; // IPR fault. An illegal operation code has been detected.
+ bool ILL_MOD; // IPR fault. An illegal address modifier has been detected.
+ bool ILL_SLV; // IPR fault. An illegal BAR mode procedure has been encountered.
+ bool ILL_PROC; // IPR fault. An illegal procedure other than the three above has been encountered.
+ bool NEM; // ONC fault. A nonexistent main memory address has been requested.
+ bool OOB; // STR fault. A BAR mode boundary violation has occurred.
+ bool ILL_DIG; // IPR fault. An illegal decimal digit or sign has been detected by the decimal unit.
+ bool PROC_PARU; // PAR fault. A parity error has been detected in the upper 36 bits of data. (Yeah, right)
+ bool PROC_PARL; // PAR fault. A parity error has been detected in the lower 36 bits of data. (Yeah, right)
+ bool CON_A; // CON fault. A $CONNECT signal has been received through port A.
+ bool CON_B; // CON fault. A $CONNECT signal has been received through port B.
+ bool CON_C; // CON fault. A $CONNECT signal has been received through port C.
+ bool CON_D; // CON fault. A $CONNECT signal has been received through port D.
+ bool DA_ERR; // ONC fault. Operation not complete. Processor/system controller interface sequence error 1 has been detected. (Yeah, right)
+ bool DA_ERR2; // ONC fault. Operation not completed. Processor/system controller interface sequence error 2 has been detected.
+ int IA_A; // Coded illegal action, port A. (See Table 3-2)
+ int IA_B; // Coded illegal action, port B. (See Table 3-2)
+ int IA_C; // Coded illegal action, port C. (See Table 3-2)
+ int IA_D; // Coded illegal action, port D. (See Table 3-2)
+ bool CPAR_DIV; // A parity error has been detected in the cache memory directory. (Not likely)
+ bool CPAR_STR; // PAR fault. A data parity error has been detected in the cache memory.
+ bool CPAR_IA; // PAR fault. An illegal action has been received from a system controller during a store operation with cache memory enabled.
+ bool CPAR_BLK; // PAR fault. A cache memory parity error has occurred during a cache memory data block load.
+
+ // odd word
+ // Cache Duplicate Directory WNO Buffer Overflow
+ bool PORT_A;
+ bool PORT_B;
+ bool PORT_C;
+ bool PORT_D;
+
+ bool CPD; // Cache Primary Directory WNO Buffer Overflow
+ // Write Notify (WNO) Parity Error on Port A, B, C, or D.
+
+ // Cache Duplicate Directory Parity Error
+ bool LEVEL_0;
+ bool LEVEL_1;
+ bool LEVEL_2;
+ bool LEVEL_3;
+
+ // Cache Duplicate Directory Multiple Match
+ bool CDD;
+
+ bool PAR_SDWAM; // A parity error has been detected in the SDWAM.
+ bool PAR_PTWAM; // A parity error has been detected in the PTWAM.
+};
+#endif
+
+#ifndef QUIET_UNUSED
+struct dps8faults
+{
+ int fault_number;
+ int fault_address;
+ const char *fault_mnemonic;
+ const char *fault_name;
+ int fault_priority;
+ int fault_group;
+};
+typedef struct dps8faults dps8faults;
+#endif
+
+extern char * faultNames [N_FAULTS];
+void check_events (void);
+void clearFaultCycle (void);
+void emCallReportFault (void);
+
+void cu_safe_restore (void);
+
+void doG7Fault(bool allowTR) NO_RETURN;
+
+#ifdef NEED_128
+#define fst_zero (_fault_subtype) {.bits=0}
+#define fst_acv9 (_fault_subtype) {.fault_acv_subtype=ACV9}
+#define fst_acv15 (_fault_subtype) {.fault_acv_subtype=ACV15}
+#define fst_ill_mod (_fault_subtype) {.fault_ipr_subtype=FR_ILL_MOD}
+#define fst_ill_proc (_fault_subtype) {.fault_ipr_subtype=FR_ILL_PROC}
+#define fst_ill_dig (_fault_subtype) {.fault_ipr_subtype=FR_ILL_DIG}
+#define fst_ill_op (_fault_subtype) {.fault_ipr_subtype=FR_ILL_OP}
+#define fst_str_oob (_fault_subtype) {.fault_str_subtype=flt_str_oob}
+#define fst_str_nea (_fault_subtype) {.fault_str_subtype=flt_str_nea}
+#define fst_str_ptr (_fault_subtype) {.fault_str_subtype=flt_str_ill_ptr}
+#define fst_cmd_lprpn (_fault_subtype) {.fault_cmd_subtype=flt_cmd_lprpn_bits}
+#define fst_cmd_ctl (_fault_subtype) {.fault_cmd_subtype=flt_cmd_not_control}
+#define fst_onc_nem (_fault_subtype) {.fault_onc_subtype=flt_onc_nem}
+#else
+extern const _fault_subtype fst_zero;
+extern const _fault_subtype fst_acv9;
+extern const _fault_subtype fst_acv15;
+extern const _fault_subtype fst_ill_mod;
+extern const _fault_subtype fst_ill_proc;
+extern const _fault_subtype fst_ill_dig;
+extern const _fault_subtype fst_ill_op;
+extern const _fault_subtype fst_str_oob;
+extern const _fault_subtype fst_str_nea;
+extern const _fault_subtype fst_str_ptr;
+extern const _fault_subtype fst_cmd_lprpn;
+extern const _fault_subtype fst_cmd_ctl;
+extern const _fault_subtype fst_onc_nem;
+#endif
+
+void doFault (_fault faultNumber, _fault_subtype faultSubtype,
+ const char * faultMsg) NO_RETURN;
+void dlyDoFault (_fault faultNumber, _fault_subtype subFault,
+ const char * faultMsg);
+bool bG7PendingNoTRO (void);
+bool bG7Pending (void);
+void setG7fault (uint cpuNo, _fault faultNo, _fault_subtype subFault);
+//void doG7Fault (void);
+void clearTROFault (void);
+void advanceG7Faults (void);
+#ifdef L68
+void set_FFV_fault (uint f_fault_no);
+void do_FFV_fault (uint fault_number, const char * fault_msg);
+#endif
+
+
--- /dev/null
+/*
+ Copyright (c) 2007-2013 Michael Mondy
+ Copyright 2012-2016 by Harry Reed
+ Copyright 2013-2016 by Charles Anthony
+
+ All rights reserved.
+
+ This software is made available under the terms of the
+ ICU License -- ICU 1.8.1 and later.
+ See the LICENSE file at the top-level directory of this distribution and
+ at https://sourceforge.net/p/dps8m/code/ci/master/tree/LICENSE
+ */
+
+#ifndef DPS8_HW_CONSTS_H
+#define DPS8_HW_CONSTS_H
+
+#include "dps8_math128.h"
+
+/////////////////////////////////////
+//
+// SCU/Memory
+//
+
+#define XXX_TEMP_SCU_SUBPORT 0
+
+enum { N_SCU_PORTS = 8 };
+enum { N_SCU_SUBPORTS = 4 };
+enum { N_ASSIGNMENTS = 2 };
+// Number of interrupts in an interrupt cell register
+enum { N_CELL_INTERRUPTS = 32 };
+
+#define PASIZE 24 /* phys addr width */
+#define PAMASK ((1U << PASIZE) - 1U)
+// Mask out low bit
+#define PAEVEN (((1U << (PASIZE - 1)) - 1U) << 1)
+#define MEM_SIZE_MAX (1U << PASIZE) /* maximum memory */
+
+// The minimum allocation size of a SCU is 64K (2^16)
+// (2 banks of 32K). Call it an SCBANK
+#define SCBANK (1U << 16)
+
+// Maximum memory size is MEM_SIZE_MAX, number of
+// scbanks is:
+#define N_SCBANKS ((MEM_SIZE_MAX) / (SCBANK))
+
+//
+// Memory addressing
+//
+
+
+#define VASIZE 18 // virtual addr width
+#define AMASK ((1U << VASIZE) - 1U) // virtual addr mask
+#define SEGSIZE (1U << VASIZE) // size of segment in words
+
+
+/////////////////////////////////////
+//
+// Append unit memory paging
+//
+
+#define PGSZ 1024u
+#define PGMK 1023u
+
+//
+// IOM
+//
+
+
+enum { MAX_CHANNELS = 64 };
+enum { N_IOM_PORTS = 8 };
+// The number of devices that a dev_code can address (6 bit number)
+enum { N_DEV_CODES = 64 };
+enum { IOM_CONNECT_CHAN = 2 };
+
+/////////////////////////////////////
+//
+// Words
+//
+
+
+#define MAX18 0777777U
+#define MAX18POS 0377777U // 2**17-1
+#define MAX18NEG 0400000U // -2**17
+#define SIGN18 0400000U
+// NB. these 2 use the wrong bit number convention
+#define BIT19 01000000U // carry out bit from 18 bit arithmetic
+#define BIT20 02000000U // carry out bit from 19 bit arithmetic
+#define MASK36 0777777777777LLU // data mask
+#define DMASK MASK36
+#define MASK10 0001777U // 10-bit data mask
+#define MASK14 0037777U // 14-bit data mask
+#define MASK16 0177777U // 16-bit data mask
+#define MASK17 0377777U // 17-bit data mask
+#define MASK18 0777777U // 18-bit data mask
+#define WMASK MASK18 // WORDNO mask
+#define MASKLO18 0000000777777LLU
+#define MASKHI18 0777777000000LLU
+#define MASK20 03777777U // 20-bit data mask
+#define MASK24 077777777U // 24-bit data mask
+#define MASK28 01777777777U // 28-bit data mask
+#define SIGN24 040000000U
+#define SIGN36 0400000000000LLU // sign bit of a 36-bit word
+// NB. these 3 use the wrong bit number convention
+#define BIT37 01000000000000LLU // carry out bit from 36 bit arithmetic
+#define BIT38 02000000000000LLU // carry out bit from 37 bit arithmetic
+#define BIT35 0200000000000LLU // next to the sign bit
+#define MASK32 037777777777U
+#define MASK15 077777U
+#define SMASK MASK15 // Segment number mask
+#define SIGN15 040000U // sign mask 15-bit number
+#define MAGMASK 0377777777777LLU // magnitude mask
+#define ONES 0777777777777LLU
+#define NEG136 0777777777777LLU // -1
+#define MAXPOS 0377777777777LLU // 2**35-1
+#define MAXNEG 0400000000000LLU // -2**35
+#define MAX36 0777777777777LLU // 2**36
+#define MAX72 (((word72)1U << 72) - 1U) // 72 1's
+
+#define CARRY 01000000000000LLU // carry from 2 36-bit additions/subs
+#define ZEROEXT 0777777777777LLU // mask to zero extend a 36 => 64-bit int
+#define ZEROEXT18 0777777U // mask to zero extend a 18 => 32-bit int
+
+#ifdef NEED_128
+
+#define SIGN72 (construct_128 (0200U, 0U))
+// NB. these use the wrong bit number convention
+#define BIT68 (construct_128 (010U, 0U))
+#define BIT69 (construct_128 (020U, 0U))
+#define BIT70 (construct_128 (040U, 0U))
+#define BIT71 (construct_128 (0100U, 0U)) // next to the sign bit
+#define BIT73 (construct_128 (0400U, 0U)) // carry out bit from 72 bit arithmetic
+#define BIT74 (construct_128 (01000U, 0U)) // carry out bit from 73 bit arithmetic
+#define MASK63 0x7FFFFFFFFFFFFFFF
+#define MASK64 0xFFFFFFFFFFFFFFFF
+#define MASK68 (construct_128 (017U, MASK64)) // Hex mode mantissa normalization mask
+#define MASK70 (construct_128 (0077U, MASK64))
+#define MASK71 (construct_128 (0177U, MASK64))
+#define MASK72 (construct_128 (0377U, MASK64))
+
+#else
+
+#define SIGN72 ((word72)1U << 71)
+// NB. these use the wrong bit number convention
+#define BIT68 ((word72)1U << 67)
+#define BIT69 ((word72)1U << 68)
+#define BIT70 ((word72)1U << 69)
+#define BIT71 ((word72)1U << 70) // next to the sign bit
+#define BIT73 ((word72)1U << 72) // carry out bit from 72 bit arithmetic
+#define BIT74 ((word72)1U << 73) // carry out bit from 73 bit arithmetic
+
+#define MASK68 (((word72)1U << 68) - 1U) // Hex mode mantissa normalization mask
+#define MASK70 (((word72)1U << 70) - 1U)
+#define MASK71 (((word72)1U << 71) - 1U)
+#define MASK72 (((word72)1U << 72) - 1U)
+#define ZEROEXT72 (((word72)1U << 72) - 1U) // mask to zero extend a 72 => 128 int
+
+#endif
+
+#define SIGN64 ((uint64)1U << 63)
+
+#define MASK2 03U
+#define MASK3 07U
+#define MASK4 017U
+#define MASK5 037U
+#define MASK6 077U
+#define MASK7 0177U
+
+#define SIGN8 0200U // sign mask 8-bit number
+#define MASK8 0377U // 8-bit mask
+#define MASK9 0777U // 9-bit mask
+
+#define MASK11 03777U
+
+#define SIGN12 0x800U // sign mask 12-bit number
+#define MASK12 07777U
+
+#define SIGN6 0040U // sign bit of 6-bit signed numfer (e.g. Scaling Factor)
+
+#define MASK35 0377777777777llu
+
+#define MASKBITS(x) ( ~(~((uint64)0)<<x) ) // lower (x) bits all ones
+#define MASKBITS18(x) ( ~(~((word18)0)<<x) ) // lower (x) bits all ones
+#define MASKBITS72(x) ( ~(~((word72)0)<<x) ) // lower (x) bits all ones
+
+#define GETHI36(a) ((word18) (((a) >> 18) & MASK18))
+#define GETLO36(a) ((word18) ((a) & MASK18))
+#define SETHI36(a,b) (((a) &= MASKLO18), ((a) |= ((((word36)(b) & MASKLO18) << 18))))
+#define SETLO36(a,b) (((a) &= MASKHI18), ((a) |= ((word36)(b) & MASKLO18)))
+#define GETHI(a) GETHI36((a))
+#define GETLO(a) GETLO36((a))
+#define SETHI(a,b) SETHI36((a),(b))
+#define SETLO(a,b) SETLO36((a),(b))
+
+#define GETHI72(a) ((word36) (((a) >> 36) & MASK36))
+#define GETLO72(a) ((word36) ((a) & MASK36))
+#define SETHI72(a,b) ((a) &= MASK36, (a) |= ((((word72)(b) & MASK36)) << 36))
+#define SETLO72(a,b) ((a) &= MASK36 << 36, (a) |= ((word72)(b) & MASK36))
+
+#define GET24(a) ((word24) ((a) & MASK24))
+#define MASK21 07777777llu
+#define SIGN21 04000000llu
+#define MASK22 017777777llu
+#define SIGN22 010000000llu
+#define MASK27 0777777777llu
+
+
+// Sign extend DPS8M words into host words
+
+static inline int SIGNEXT6_int (word6 w)
+ {
+ if (w & SIGN6)
+ {
+ return ((int) w) | (int) (((uint) -1) << 6);
+ }
+ return w & MASK6;
+ }
+
+static inline int SIGNEXT8_int (word8 w)
+ {
+ if (w & SIGN8)
+ {
+ return ((int) w) | (int) (((uint) -1) << 8);
+ }
+ return w & MASK8;
+ }
+
+static inline int32 SIGNEXT15_32 (word15 w)
+ {
+ if (w & SIGN15)
+ {
+ return ((int32) w) | (int32) (((uint32) -1) << 15);
+ }
+ return w & MASK15;
+ }
+
+static inline int32 SIGNEXT18_32 (word18 w)
+ {
+ if (w & SIGN18)
+ {
+ return ((int32) w) | (int32) (((uint32) -1) << 18);
+ }
+ return w & MASK18;
+ }
+
+static inline int32 SIGNEXT21_32 (word21 w)
+ {
+ if (w & SIGN21)
+ {
+ return ((int32) w) | (int32) (((uint32) -1) << 21);
+ }
+ return w & MASK21;
+ }
+
+static inline int32 SIGNEXT22_32 (word22 w)
+ {
+ if (w & SIGN22)
+ {
+ return ((int32) w) | (int32) (((uint32) -1) << 22);
+ }
+ return w & MASK22;
+ }
+
+static inline int32 SIGNEXT24_32 (word24 w)
+ {
+ if (w & SIGN24)
+ {
+ return ((int32) w) | (int32) (((uint32) -1) << 24);
+ }
+ return w & MASK24;
+ }
+
+static inline t_int64 SIGNEXT36_64 (word36 w)
+ {
+ if (w & SIGN36)
+ {
+ return ((t_int64) w) | (t_int64) (((t_uint64) -1ll) << 36);
+ }
+ return w & MASK36;
+ }
+
+static inline t_int64 SIGNEXT18_64 (word36 w)
+ {
+ if (w & SIGN18)
+ {
+ return ((t_int64) w) | (t_int64) (((t_uint64) -1ll) << 18);
+ }
+ return w & MASK18;
+ }
+
+static inline t_int64 SIGNEXT21_64 (word36 w)
+ {
+ if (w & SIGN21)
+ {
+ return ((t_int64) w) | (t_int64) (((t_uint64) -1ll) << 21);
+ }
+ return w & MASK21;
+ }
+
+static inline t_int64 SIGNEXT22_64 (word36 w)
+ {
+ if (w & SIGN22)
+ {
+ return ((t_int64) w) | (t_int64) (((t_uint64) -1ll) << 22);
+ }
+ return w & MASK22;
+ }
+
+static inline t_int64 SIGNEXT24_64 (word36 w)
+ {
+ if (w & SIGN24)
+ {
+ return ((t_int64) w) | (t_int64) (((t_uint64) -1ll) << 24);
+ }
+ return w & MASK24;
+ }
+
+static inline int128 SIGNEXT72_128 (word72 w)
+ {
+#ifdef NEED_128
+ if (isnonzero_128 (and_128 (w, SIGN72)))
+ {
+ uint128 v = or_128 (w, construct_128 (0xFFFFFFFFFFFFFF80, 0));
+ return cast_s128 (v);
+ }
+ uint128 v = and_128 (w, MASK72);
+ return (int128) { (__int64_t) v.h, v.l};
+#else
+ if (w & SIGN72)
+ {
+ return ((int128) w) | (int128) (((uint128) -1ll) << 72);
+ }
+ return w & MASK72;
+#endif
+ }
+
+#ifdef NEED_128
+static inline int128 SIGNEXT36_128 (word36 w)
+ {
+ if (w & SIGN36)
+ {
+ return construct_s128 ((int64_t) MASK64, w | 0xFFFFFFF000000000);
+ }
+ return construct_s128 (0, w);
+ }
+#endif
+
+// Sign extend DPS8M words into DPS8M words
+// NB: The high order bits in the host container will
+// set to 0; you cannot do host math with
+// these results.
+
+static inline word18 SIGNEXT15_18 (word15 w)
+ {
+ if (w & SIGN15)
+ {
+ return (w | ((word18) -1) << 15) & MASK18;
+ }
+ return w & MASK15;
+ }
+
+static inline word24 SIGNEXT18_24 (word18 w)
+ {
+ if (w & SIGN18)
+ {
+ return (w | ((word24) -1) << 18) & MASK24;
+ }
+ return w & MASK18;
+ }
+
+static inline word72 SIGNEXT36_72 (word36 w)
+ {
+#ifdef NEED_128
+ if (w & SIGN36)
+ {
+ //return (w | ((word72) DMASK) << 36) & MASK72;
+ return construct_128 (0377U, (w & MASK36) | 0xFFFFFFF000000000);
+ }
+ //return w & MASK36;
+ return construct_128 (0, w & MASK36);
+#else
+ if (w & SIGN36)
+ {
+ return (w | ((word72) DMASK) << 36) & MASK72;
+ }
+ return w & MASK36;
+#endif
+ }
+
+#define SETS36(x) ((x) | SIGN36)
+#define CLRS36(x) ((x) & ~SIGN36)
+#define TSTS36(x) ((x) & SIGN36)
+
+/////////////////////////////////////
+//
+// Instruction format
+//
+
+#define INST_V_TAG 0 // Tag
+#define INST_M_TAG 077U
+#define INST_V_A 6 // Indirect via pointer
+#define INST_M_A 1U
+#define INST_V_I 7 // Interrupt Inhibit
+#define INST_M_I 1U
+#define INST_V_OP 9 // opcode
+#define INST_M_OP 0777U
+#define INST_V_OPX 8 // opcode etension
+#define INST_M_OPX 1U
+
+#define INST_V_ADDR 18 // Address
+#define INST_M_ADDR 0777777U
+#define INST_V_OFFSET 18 // Offset (Bit29=1)
+#define INST_M_OFFSET 077777U
+#define INST_V_PRN 33 // n of PR[n] (Bit29=1)
+#define INST_M_PRN 07U
+#define INST_V_ARN 33 // n of AR[n] (Bit29=1)
+#define INST_M_ARN 07U
+
+
+#define GET_TAG(x) ((word6) ( (x) & INST_M_TAG ))
+#define GET_A(x) ((word1) (((x) >> INST_V_A) & INST_M_A ))
+#define GET_I(x) ((int32) (((x) >> INST_V_I) & INST_M_I ))
+#define GET_OP(x) ((word9) (((x) >> INST_V_OP) & INST_M_OP ))
+#define GET_OPX(x) ((bool) (((x) >> INST_V_OPX) & INST_M_OPX))
+
+#define GET_OFFSET(x) ((word15) (((x) >> INST_V_OFFSET) & INST_M_OFFSET))
+#define GET_PRN(x) ((word3) (((x) >> INST_V_PRN) & INST_M_PRN))
+#define GET_ARN(x) ((word3) (((x) >> INST_V_ARN) & INST_M_ARN))
+
+#define GET_TM(x) ( (GET_TAG(x) & 060U))
+#define GET_TD(x) ( (GET_TAG(x) & 017U))
+
+#define GET_ADDR(x) ((uint32) (((x) >> INST_V_ADDR) & INST_M_ADDR))
+
+// tag defines ...
+#define TAG_R 0U
+#define TAG_RI 1U
+#define TAG_IT 2U
+#define TAG_IR 3U
+
+
+#define _TD(tag) ((tag) & 017U)
+#define _TM(tag) ((tag) & 060U)
+
+enum {
+ TD_N = 000U,
+ TD_AU = 001U,
+ TD_QU = 002U,
+ TD_DU = 003U,
+ TD_IC = 004U,
+ TD_AL = 005U,
+ TD_QL = 006U,
+ TD_DL = 007U,
+ TD_X0 = 010U,
+ TD_X1 = 011U,
+ TD_X2 = 012U,
+ TD_X3 = 013U,
+ TD_X4 = 014U,
+ TD_X5 = 015U,
+ TD_X6 = 016U,
+ TD_X7 = 017U
+};
+
+enum {
+ TM_R = 000U,
+ TM_RI = 020U,
+ TM_IT = 040U, // HWR - next 2 had values swapped
+ TM_IR = 060U
+};
+
+/* see AL39, pp 6-13, tbl 6-3 */
+enum {
+ IT_F1 = 000U,
+ IT_SD = 004U,
+ IT_SCR = 005U,
+ IT_F2 = 006U,
+ IT_F3 = 007U,
+ IT_CI = 010U,
+ IT_I = 011U,
+ IT_SC = 012U,
+ IT_AD = 013U,
+ IT_DI = 014U,
+ IT_DIC = 015U,
+ IT_ID = 016U,
+ IT_IDC = 017U,
+
+ // not really IT, but they're in it's namespace
+ SPEC_ITP = 001U,
+ SPEC_ITS = 003U
+};
+
+#define GET_TB(tag) ((tag) & 040U)
+#define GET_CF(tag) ((tag) & 007U)
+
+#define _TB(tag) GET_TB((tag))
+#define _CF(tag) GET_CF((tag))
+
+#define TB6 000U // 6-bit characters
+#define TB9 040U // 9-bit characters
+
+/////////////////////////////////////
+//
+// ITS/ITP
+//
+
+#define ISITP(x) (((x) & INST_M_TAG) == 041U)
+#define GET_ITP_PRNUM(Ypair) ((word3) (((Ypair)[0] >> 33) & 07U))
+#define GET_ITP_WORDNO(Ypair) ((word18) (((Ypair)[1] >> 18) & WMASK))
+#define GET_ITP_BITNO(Ypair) ((word6) (((Ypair)[1] >> 9) & 077U))
+#define GET_ITP_MOD(Ypair) (GET_TAG((Ypair)[1]))
+
+#define ISITS(x) (((x) & INST_M_TAG) == 043U)
+#define GET_ITS_SEGNO(Ypair) ((word15) (((Ypair)[0] >> 18) & SMASK))
+#define GET_ITS_RN(Ypair) ((word3) (((Ypair)[0] >> 15) & 07))
+#define GET_ITS_WORDNO(Ypair) ((word18) (((Ypair)[1] >> 18) & WMASK))
+#define GET_ITS_BITNO(Ypair) ((word6) (((Ypair)[1] >> 9) & 077))
+#define GET_ITS_MOD(Ypair) (GET_TAG((Ypair)[1]))
+
+/////////////////////////////////////
+//
+// Indicator register bits
+//
+
+#define F_V_A 17 // Zero
+#define F_V_B 16 // Negative
+#define F_V_C 15 // Carry
+#define F_V_D 14 // Overflow
+#define F_V_E 13 // Exponent Overflow
+#define F_V_F 12 // Exponent Underflow
+#define F_V_G 11 // Overflow Mask
+#define F_V_H 10 // Tally Runout
+#define F_V_I 9 // Parity Error
+#define F_V_J 8 // Parity Mask
+#define F_V_K 7 // Not BAR mode
+#define F_V_L 6 // Truncation
+#define F_V_M 5 // Mid Instruction Interrupt Fault
+#define F_V_N 4 // Absolute Mode
+#define F_V_O 3 // Hex Mode
+
+#define F_A (1LLU << F_V_A)
+#define F_B (1LLU << F_V_B)
+#define F_C (1LLU << F_V_C)
+#define F_D (1LLU << F_V_D)
+#define F_E (1LLU << F_V_E)
+#define F_F (1LLU << F_V_F)
+#define F_G (1LLU << F_V_G)
+#define F_H (1LLU << F_V_H)
+#define F_I (1LLU << F_V_I)
+#define F_J (1LLU << F_V_J)
+#define F_K (1LLU << F_V_K)
+#define F_L (1LLU << F_V_L)
+#define F_M (1LLU << F_V_M)
+#define F_N (1LLU << F_V_N)
+#define F_O (1LLU << F_V_O)
+
+#ifdef DPS8M
+#define I_HEX F_O // base-16 exponent 0000010
+#endif
+#define I_ABS F_N // absolute mode 0000020
+#define I_MIF F_M // mid-instruction interrupt fault 0000040
+#define I_TRUNC F_L // truncation 0000100
+#define I_NBAR F_K // not BAR mode 0000200
+#define I_PMASK F_J // parity mask 0000400
+#define I_PERR F_I // parity error 0001000
+#define I_TALLY F_H // tally runout 0002000
+#define I_OMASK F_G // overflow mask 0004000
+#define I_EUFL F_F // exponent underflow 0010000
+#define I_EOFL F_E // exponent overflow 0020000
+#define I_OFLOW F_D // overflow 0040000
+#define I_CARRY F_C // carry 0100000
+#define I_NEG F_B // negative 0200000
+#define I_ZERO F_A // zero 0400000
+
+#define I_ZNOC (I_ZERO | I_NEG | I_OFLOW | I_CARRY)
+#define I_ZNC (I_ZERO | I_NEG | I_CARRY)
+
+#define CLR_I_ABS CLRF (cpu.cu.IR, I_ABS)
+#define CLR_I_MIF CLRF (cpu.cu.IR, I_MIF)
+#define CLR_I_TRUNC CLRF (cpu.cu.IR, I_TRUNC)
+#define CLR_I_NBAR CLRF (cpu.cu.IR, I_NBAR)
+#define CLR_I_TALLY CLRF (cpu.cu.IR, I_TALLY)
+#define CLR_I_PMASK CLRF (cpu.cu.IR, I_PMASK)
+#define CLR_I_EOFL CLRF (cpu.cu.IR, I_EOFL)
+#define CLR_I_EUFL CLRF (cpu.cu.IR, I_EUFL)
+#define CLR_I_OFLOW CLRF (cpu.cu.IR, I_OFLOW)
+#define CLR_I_CARRY CLRF (cpu.cu.IR, I_CARRY)
+#define CLR_I_NEG CLRF (cpu.cu.IR, I_NEG)
+#define CLR_I_ZERO CLRF (cpu.cu.IR, I_ZERO)
+
+#define SET_I_ABS SETF (cpu.cu.IR, I_ABS)
+#define SET_I_NBAR SETF (cpu.cu.IR, I_NBAR)
+#define SET_I_TRUNC SETF (cpu.cu.IR, I_TRUNC)
+#define SET_I_TALLY SETF (cpu.cu.IR, I_TALLY)
+#define SET_I_EOFL SETF (cpu.cu.IR, I_EOFL)
+#define SET_I_EUFL SETF (cpu.cu.IR, I_EUFL)
+#define SET_I_OFLOW SETF (cpu.cu.IR, I_OFLOW)
+#define SET_I_CARRY SETF (cpu.cu.IR, I_CARRY)
+#define SET_I_NEG SETF (cpu.cu.IR, I_NEG)
+#define SET_I_ZERO SETF (cpu.cu.IR, I_ZERO)
+
+#define TST_I_ABS TSTF (cpu.cu.IR, I_ABS)
+#define TST_I_MIF TSTF (cpu.cu.IR, I_MIF)
+#define TST_I_NBAR TSTF (cpu.cu.IR, I_NBAR)
+#define TST_I_PMASK TSTF (cpu.cu.IR, I_PMASK)
+#define TST_I_TRUNC TSTF (cpu.cu.IR, I_TRUNC)
+#define TST_I_TALLY TSTF (cpu.cu.IR, I_TALLY)
+#define TST_I_OMASK TSTF (cpu.cu.IR, I_OMASK)
+#define TST_I_EUFL TSTF (cpu.cu.IR, I_EUFL )
+#define TST_I_EOFL TSTF (cpu.cu.IR, I_EOFL )
+#define TST_I_OFLOW TSTF (cpu.cu.IR, I_OFLOW)
+#define TST_I_CARRY TSTF (cpu.cu.IR, I_CARRY)
+#define TST_I_NEG TSTF (cpu.cu.IR, I_NEG)
+#define TST_I_ZERO TSTF (cpu.cu.IR, I_ZERO)
+#define TST_I_HEX TSTF (cpu.cu.IR, I_HEX)
+
+#ifdef DPS8M
+#define SC_I_HEX(v) SCF (v, cpu.cu.IR, I_HEX)
+#endif
+#define SC_I_MIF(v) SCF (v, cpu.cu.IR, I_MIF)
+#define SC_I_TALLY(v) SCF (v, cpu.cu.IR, I_TALLY)
+#define SC_I_NEG(v) SCF (v, cpu.cu.IR, I_NEG)
+#define SC_I_ZERO(v) SCF (v, cpu.cu.IR, I_ZERO)
+#define SC_I_CARRY(v) SCF (v, cpu.cu.IR, I_CARRY);
+#define SC_I_OFLOW(v) SCF (v, cpu.cu.IR, I_OFLOW);
+#define SC_I_EOFL(v) SCF (v, cpu.cu.IR, I_EOFL);
+#define SC_I_EUFL(v) SCF (v, cpu.cu.IR, I_EUFL);
+#define SC_I_OMASK(v) SCF (v, cpu.cu.IR, I_OMASK);
+#define SC_I_PERR(v) SCF (v, cpu.cu.IR, I_PERR);
+#define SC_I_PMASK(v) SCF (v, cpu.cu.IR, I_PMASK);
+#define SC_I_TRUNC(v) SCF (v, cpu.cu.IR, I_TRUNC);
+
+
+
+/////////////////////////////////////
+//
+// floating-point constants
+//
+
+#define FLOAT36MASK 01777777777LLU ///< user to extract mantissa from single precision C(CEAQ)
+#define FLOAT72MASK 01777777777777777777777LLU ///< use to extract mastissa from double precision C(EAQ)
+#define FLOAT72SIGN (1LLU << 63) ///< mantissa sign mask for full precision C(EAQ)
+// XXX beware the 72's are not what they seem!
+
+
+/////////////////////////////////////
+//
+// Faults
+//
+
+#define N_FAULT_GROUPS 7
+#define N_FAULTS 32
+
+enum _fault
+ {
+ FAULT_SDF = 0U, // shutdown fault
+ FAULT_STR = 1U, // store fault
+ FAULT_MME = 2U, // master mode entry
+ FAULT_F1 = 3U, // fault tag 1
+ FAULT_TRO = 4U, // timer runout fault
+ FAULT_CMD = 5U, // command
+ FAULT_DRL = 6U, // derail
+ FAULT_LUF = 7U, // lockup
+ FAULT_CON = 8U, // connect
+ FAULT_PAR = 9U, // parity
+ FAULT_IPR = 10U, // illegal proceedure
+ FAULT_ONC = 11U, // operation not complete
+ FAULT_SUF = 12U, // startup
+ FAULT_OFL = 13U, // overflow
+ FAULT_DIV = 14U, // divide check
+ FAULT_EXF = 15U, // execute
+ FAULT_DF0 = 16U, // directed fault 0
+ FAULT_DF1 = 17U, // directed fault 1
+ FAULT_DF2 = 18U, // directed fault 2
+ FAULT_DF3 = 19U, // directed fault 3
+ FAULT_ACV = 20U, // access violation
+ FAULT_MME2 = 21U, // Master mode entry 2
+ FAULT_MME3 = 22U, // Master mode entry 3
+ FAULT_MME4 = 23U, // Master mode entry 4
+ FAULT_F2 = 24U, // fault tag 2
+ FAULT_F3 = 25U, // fault tag 3
+ FAULT_UN1 = 26U, // unassigned
+ FAULT_UN2 = 27U, // unassigned
+ FAULT_UN3 = 28U, // unassigned
+ FAULT_UN4 = 29U, // unassigned
+ FAULT_UN5 = 30U, // unassigned
+ FAULT_TRB = 31U // Trouble
+ };
+
+#define FAULTBASE_MASK 07740U ///< mask off all but top 7 msb
+
+
+typedef enum _fault _fault;
+
+#if 0
+ //no_fault_subtype = 0,
+
+ // FAULT_IPR
+
+ //flt_ipr_ill_op, // An illegal operation code has been detected.
+ //flt_ipr_ill_mod, // An illegal address modifier has been detected.
+ //flt_ipr_ill_slv, // An illegal BAR mode procedure has been encountered.
+ //flt_ipr_ill_dig, // An illegal decimal digit or sign has been detected by the decimal unit.
+ //flt_ipr_ill_proc, // An illegal procedure other than the four above has been encountered.
+
+
+ // FAULT_PAR
+
+ //proc_paru, // A parity error has been detected in the upper 36 bits of data. (Yeah, right)
+ //proc_parl, // A parity error has been detected in the lower 36 bits of data. (Yeah, right)
+
+
+
+ // FAULT_ONC
+
+ //da_err, // Operation not complete. Processor/system controller interface sequence error 1 has been detected. (Yeah, right)
+ //da_err2, // Operation not completed. Processor/system controller interface sequence error 2 has been detected.
+
+ // Misc
+
+ //cpar_dir, // A parity error has been detected in the cache memory directory. (Not likely)
+ //cpar_str, // PAR fault. A data parity error has been detected in the cache memory.
+ //cpar_ia, // PAR fault. An illegal action has been received from a system controller during a store operation with cache memory enabled.
+ //cpar_blk, // PAR fault. A cache memory parity error has occurred during a cache memory data block load.
+
+ // odd word
+ // Cache Duplicate Directory WNO Buffer Overflow
+ //port_a,
+ //port_b,
+ //port_c,
+ //port_d,
+
+ //cpd, // Cache Primary Directory WNO Buffer Overflow
+ // Write Notify (WNO) Parity Error on Port A, B, C, or D.
+
+ // Cache Duplicate Directory Parity Error
+ //level_0,
+ ////level_1,
+ ////level_2,
+ ////level_3,
+
+ // Cache Duplicate Directory Multiple Match
+ //cdd,
+
+ //par_sdwam, // A parity error has been detected in the SDWAM.
+ //par_ptwam, // A parity error has been detected in the PTWAM.
+
+
+};
+typedef enum _fault_subtype _fault_subtype;
+#endif
+
+typedef enum fault_onc_subtype_
+ {
+ flt_onc_nem, // A nonexistent main memory address has been requested.
+ flt_onc_FORCE = 0400000000000llu // Force enum size to 36 bits.
+ } fault_onc_subtype_;
+
+typedef enum fault_str_subtype_
+ {
+ flt_str_oob, // A BAR mode boundary violation has occurred.
+ flt_str_ill_ptr, // SPRPn illegal ptr.
+ flt_str_nea, // non-existent address
+ flt_str_FORCE = 0400000000000llu // Force enum size to 36 bits.
+ } fault_str_subtype_;
+
+typedef enum fault_con_subtype_
+ {
+ con_a = 0, // A $CONNECT signal has been received through port A.
+ con_b = 1, // A $CONNECT signal has been received through port B.
+ con_c = 2, // A $CONNECT signal has been received through port C.
+ con_d = 3, // A $CONNECT signal has been received through port D.
+ flt_con_FORCE = 0400000000000llu // Force enum size to 36 bits.
+ } fault_con_subtype_;
+
+typedef enum fault_acv_subtype_
+ {
+ ACV0 = (1U << 15), ///< 15.Illegal ring order (ACV0=IRO)
+ ACV1 = (1U << 14), ///< 3. Not in execute bracket (ACV1=OEB)
+ ACV2 = (1U << 13), ///< 6. No execute permission (ACV2=E-OFF)
+ ACV3 = (1U << 12), ///< 1. Not in read bracket (ACV3=ORB)
+ ACV4 = (1U << 11), ///< 4. No read permission (ACV4=R-OFF)
+ ACV5 = (1U << 10), ///< 2. Not in write bracket (ACV5=OWB)
+ ACV6 = (1U << 9), ///< 5. No write permission (ACV6=W-OFF)
+ ACV7 = (1U << 8), ///< 8. Call limiter fault (ACV7=NO GA)
+ ACV8 = (1U << 7), ///< 16.Out of call brackets (ACV8=OCB)
+ ACV9 = (1U << 6), ///< 9. Outward call (ACV9=OCALL)
+ ACV10 = (1U << 5), ///< 10.Bad outward call (ACV10=BOC)
+ ACV11 = (1U << 4), ///< 11.Inward return (ACV11=INRET)
+ ACV12 = (1U << 3), ///< 7. Invalid ring crossing (ACV12=CRT)
+ ACV13 = (1U << 2), ///< 12.Ring alarm (ACV13=RALR)
+ ACV14 = (1U << 1), ///< 13.Associative memory error
+ ACV15 = (1U << 0), ///< 14.Out of segment bounds (ACV15=OOSB)
+ flt_acv_FORCE = 0400000000000llu // Force enum size to 36 bits.
+ } fault_acv_subtype_;
+
+typedef enum fault_ipr_subtype_
+ {
+ FR_ILL_OP = 0400000000000llu, // 0 a ILL OP
+ FR_ILL_MOD = 0200000000000llu, // 1 b ILL MOD
+ FR_ILL_SLV = 0100000000000llu, // 2 c ILL SLV
+ FR_ILL_PROC = 0040000000000llu, // 3 d ILL PROC
+ FR_ILL_PROC_MOD = 0240000000000llu, // 1,3 d ILL PROC | ILL MOD
+ FR_NEM = 0020000000000llu, // 4 e NEM
+ FR_OOB = 0010000000000llu, // 5 f OOB
+ FR_ILL_DIG = 0004000000000llu, // 6 g ILL DIG
+ FR_PROC_PARU = 0002000000000llu, // 7 h PROC PARU
+ FR_PROC_PARL = 0001000000000llu, // 8 i PROC PARU
+ FR_CON_A = 0000400000000llu, // 9 j $CON A
+ FR_CON_B = 0000200000000llu, // 10 k $CON B
+ FR_CON_C = 0000100000000llu, // 11 l $CON C
+ FR_CON_D = 0000040000000llu, // 12 m $CON D
+ FR_DA_ERR = 0000020000000llu, // 13 n DA ERR
+ FR_DA_ERR2 = 0000010000000llu // 14 o DA ERR2
+ } fault_ipr_subtype_;
+
+typedef enum fault_cmd_subtype_
+ {
+ flt_cmd_lprpn_bits, // illegal bits in lprpn instruction
+ flt_cmd_not_control, // not control
+ flt_cmd_FORCE = 0400000000000llu // Force enum size to 36 bits.
+ } fault_cmd_subtype_;
+
+typedef union _fault_subtype
+ {
+ fault_onc_subtype_ fault_onc_subtype;
+ fault_str_subtype_ fault_str_subtype;
+ fault_con_subtype_ fault_con_subtype;
+ fault_acv_subtype_ fault_acv_subtype;
+ fault_ipr_subtype_ fault_ipr_subtype;
+ fault_cmd_subtype_ fault_cmd_subtype;
+ word36 bits;
+ } _fault_subtype;
+
+// Fault Register bits
+enum _faultRegisterBits0
+ {
+ FR_IA_MASK = 017,
+ FR_IAA_SHIFT = 16, // 0000003600000llu,
+ FR_IAB_SHIFT = 12, // 0000000170000llu,
+ FR_IAC_SHIFT = 8, // 0000000007400llu,
+ FR_IAD_SHIFT = 4, // 0000000000360llu,
+
+ FR_CPAR_DIR = 0000000000010llu, // 32 p CPAR DIR
+ FR_CPAR_STR = 0000000000004llu, // 33 q CPAR STR
+ FR_CPAR_IA = 0000000000002llu, // 34 r CPAR IA
+ FR_CPAR_BLK = 0000000000001llu // 35 s CPAR BLK
+ };
+
+enum _faultRegisterBits1
+ {
+ FR_PORT_A = 0400000000000llu, // 0 t PORT A
+ FR_PORT_B = 0200000000000llu, // 1 u PORT B
+ FR_PORT_C = 0100000000000llu, // 2 v PORT C
+ FR_PORT_D = 0040000000000llu, // 3 w PORT D
+ FR_WNO_BO = 0020000000000llu, // 4 x Cache Primary Directory WNO Buffer Overflow
+ FR_WNO_PAR = 0010000000000llu, // 5 y Write Notify (WNO) Parity Error on Port A, B, C or D.
+ FR_LEVEL_0 = 0004000000000llu, // 6 z Level 0
+ FR_LEVEL_1 = 0002000000000llu, // 7 A Level 1
+ FR_LEVEL_2 = 0001000000000llu, // 8 B Level 2
+ FR_LEVEL_3 = 0000400000000llu, // 0 C Level 3
+ FR_CDDMM = 0000200000000llu, // 10 D Cache Duplicate Directory Multiple Match
+ FR_PAR_SDWAM = 0000100000000llu, // 11 E SDWAM parity error
+ FR_PAR_PTWAM = 0000040000000llu // 12 F PTWAM parity error
+ };
+
+enum _systemControllerIllegalActionCodes
+ {
+ SCIAC_NONE = 000,
+ SCIAC_NEA = 002,
+ SCIAC_SOC = 003,
+ SCIAC_PAR5 = 005,
+ SCIAC_PAR6 = 006,
+ SCIAC_PAR7 = 007,
+ SCIAC_NC = 010,
+ SCIAC_PNE = 011,
+ SCIAC_ILL_CMD = 012,
+ SCIAC_NR = 013,
+ SCIAC_PAR14 = 014,
+ SCIAC_PAR15 = 015,
+ SCIAC_PAR16 = 016,
+ SCIAC_PAR17 = 017
+ };
+
+
+/////////////////////////////////////
+//
+// Interrupts
+//
+
+#define N_INTERRUPTS 32
+
+/////////////////////////////////////
+//
+// Memory map
+//
+
+#define IOM_MBX_LOW 01200
+#define IOM_MBX_LEN 02200
+#define DN355_MBX_LOW 03400
+#define DN355_MBX_LEN 03000
+
+/////////////////////////////////////
+//
+// Opcodes
+//
+
+// MM's opcode stuff ...
+
+// Opcodes with low bit (bit 27) == 0. Enum value is value of upper 9 bits.
+typedef enum {
+ opcode0_mme = 0001U, // (1 decimal)
+ opcode0_drl = 0002U, // (2 decimal)
+ opcode0_mme2 = 0004U, // (4 decimal)
+ opcode0_mme3 = 0005U, // (5 decimal)
+ opcode0_mme4 = 0007U, // (7 decimal)
+ opcode0_nop = 0011U, // (9 decimal)
+ opcode0_puls1 = 0012U, // (10 decimal)
+ opcode0_puls2 = 0013U, // (11 decimal)
+ opcode0_cioc = 0015U, // (13 decimal)
+ opcode0_adlx0 = 0020U, // (16 decimal)
+ opcode0_adlx1 = 0021U, // (17 decimal)
+ opcode0_adlx2 = 0022U, // (18 decimal)
+ opcode0_adlx3 = 0023U, // (19 decimal)
+ opcode0_adlx4 = 0024U, // (20 decimal)
+ opcode0_adlx5 = 0025U, // (21 decimal)
+ opcode0_adlx6 = 0026U, // (22 decimal)
+ opcode0_adlx7 = 0027U, // (23 decimal)
+ opcode0_ldqc = 0032U, // (26 decimal)
+ opcode0_adl = 0033U, // (27 decimal)
+ opcode0_ldac = 0034U, // (28 decimal)
+ opcode0_adla = 0035U, // (29 decimal)
+ opcode0_adlq = 0036U, // (30 decimal)
+ opcode0_adlaq = 0037U, // (31 decimal)
+ opcode0_asx0 = 0040U, // (32 decimal)
+ opcode0_asx1 = 0041U, // (33 decimal)
+ opcode0_asx2 = 0042U, // (34 decimal)
+ opcode0_asx3 = 0043U, // (35 decimal)
+ opcode0_asx4 = 0044U, // (36 decimal)
+ opcode0_asx5 = 0045U, // (37 decimal)
+ opcode0_asx6 = 0046U, // (38 decimal)
+ opcode0_asx7 = 0047U, // (39 decimal)
+ opcode0_adwp0 = 0050U, // (40 decimal)
+ opcode0_adwp1 = 0051U, // (41 decimal)
+ opcode0_adwp2 = 0052U, // (42 decimal)
+ opcode0_adwp3 = 0053U, // (43 decimal)
+ opcode0_aos = 0054U, // (44 decimal)
+ opcode0_asa = 0055U, // (45 decimal)
+ opcode0_asq = 0056U, // (46 decimal)
+ opcode0_sscr = 0057U, // (47 decimal)
+ opcode0_adx0 = 0060U, // (48 decimal)
+ opcode0_adx1 = 0061U, // (49 decimal)
+ opcode0_adx2 = 0062U, // (50 decimal)
+ opcode0_adx3 = 0063U, // (51 decimal)
+ opcode0_adx4 = 0064U, // (52 decimal)
+ opcode0_adx5 = 0065U, // (53 decimal)
+ opcode0_adx6 = 0066U, // (54 decimal)
+ opcode0_adx7 = 0067U, // (55 decimal)
+ opcode0_awca = 0071U, // (57 decimal)
+ opcode0_awcq = 0072U, // (58 decimal)
+ opcode0_lreg = 0073U, // (59 decimal)
+ opcode0_ada = 0075U, // (61 decimal)
+ opcode0_adq = 0076U, // (62 decimal)
+ opcode0_adaq = 0077U, // (63 decimal)
+ opcode0_cmpx0 = 0100U, // (64 decimal)
+ opcode0_cmpx1 = 0101U, // (65 decimal)
+ opcode0_cmpx2 = 0102U, // (66 decimal)
+ opcode0_cmpx3 = 0103U, // (67 decimal)
+ opcode0_cmpx4 = 0104U, // (68 decimal)
+ opcode0_cmpx5 = 0105U, // (69 decimal)
+ opcode0_cmpx6 = 0106U, // (70 decimal)
+ opcode0_cmpx7 = 0107U, // (71 decimal)
+ opcode0_cwl = 0111U, // (73 decimal)
+ opcode0_cmpa = 0115U, // (77 decimal)
+ opcode0_cmpq = 0116U, // (78 decimal)
+ opcode0_cmpaq = 0117U, // (79 decimal)
+ opcode0_sblx0 = 0120U, // (80 decimal)
+ opcode0_sblx1 = 0121U, // (81 decimal)
+ opcode0_sblx2 = 0122U, // (82 decimal)
+ opcode0_sblx3 = 0123U, // (83 decimal)
+ opcode0_sblx4 = 0124U, // (84 decimal)
+ opcode0_sblx5 = 0125U, // (85 decimal)
+ opcode0_sblx6 = 0126U, // (86 decimal)
+ opcode0_sblx7 = 0127U, // (87 decimal)
+ opcode0_sbla = 0135U, // (93 decimal)
+ opcode0_sblq = 0136U, // (94 decimal)
+ opcode0_sblaq = 0137U, // (95 decimal)
+ opcode0_ssx0 = 0140U, // (96 decimal)
+ opcode0_ssx1 = 0141U, // (97 decimal)
+ opcode0_ssx2 = 0142U, // (98 decimal)
+ opcode0_ssx3 = 0143U, // (99 decimal)
+ opcode0_ssx4 = 0144U, // (100 decimal)
+ opcode0_ssx5 = 0145U, // (101 decimal)
+ opcode0_ssx6 = 0146U, // (102 decimal)
+ opcode0_ssx7 = 0147U, // (103 decimal)
+ opcode0_adwp4 = 0150U, // (104 decimal)
+ opcode0_adwp5 = 0151U, // (105 decimal)
+ opcode0_adwp6 = 0152U, // (106 decimal)
+ opcode0_adwp7 = 0153U, // (107 decimal)
+ opcode0_sdbr = 0154U, // (108 decimal)
+ opcode0_ssa = 0155U, // (109 decimal)
+ opcode0_ssq = 0156U, // (110 decimal)
+ opcode0_sbx0 = 0160U, // (112 decimal)
+ opcode0_sbx1 = 0161U, // (113 decimal)
+ opcode0_sbx2 = 0162U, // (114 decimal)
+ opcode0_sbx3 = 0163U, // (115 decimal)
+ opcode0_sbx4 = 0164U, // (116 decimal)
+ opcode0_sbx5 = 0165U, // (117 decimal)
+ opcode0_sbx6 = 0166U, // (118 decimal)
+ opcode0_sbx7 = 0167U, // (119 decimal)
+ opcode0_swca = 0171U, // (121 decimal)
+ opcode0_swcq = 0172U, // (122 decimal)
+ opcode0_lpri = 0173U, // (123 decimal)
+ opcode0_sba = 0175U, // (125 decimal)
+ opcode0_sbq = 0176U, // (126 decimal)
+ opcode0_sbaq = 0177U, // (127 decimal)
+ opcode0_cnax0 = 0200U, // (128 decimal)
+ opcode0_cnax1 = 0201U, // (129 decimal)
+ opcode0_cnax2 = 0202U, // (130 decimal)
+ opcode0_cnax3 = 0203U, // (131 decimal)
+ opcode0_cnax4 = 0204U, // (132 decimal)
+ opcode0_cnax5 = 0205U, // (133 decimal)
+ opcode0_cnax6 = 0206U, // (134 decimal)
+ opcode0_cnax7 = 0207U, // (135 decimal)
+ opcode0_cmk = 0211U, // (137 decimal)
+ opcode0_absa = 0212U, // (138 decimal)
+ opcode0_epaq = 0213U, // (139 decimal)
+ opcode0_sznc = 0214U, // (140 decimal)
+ opcode0_cnaa = 0215U, // (141 decimal)
+ opcode0_cnaq = 0216U, // (142 decimal)
+ opcode0_cnaaq = 0217U, // (143 decimal)
+ opcode0_ldx0 = 0220U, // (144 decimal)
+ opcode0_ldx1 = 0221U, // (145 decimal)
+ opcode0_ldx2 = 0222U, // (146 decimal)
+ opcode0_ldx3 = 0223U, // (147 decimal)
+ opcode0_ldx4 = 0224U, // (148 decimal)
+ opcode0_ldx5 = 0225U, // (149 decimal)
+ opcode0_ldx6 = 0226U, // (150 decimal)
+ opcode0_ldx7 = 0227U, // (151 decimal)
+ opcode0_lbar = 0230U, // (152 decimal)
+ opcode0_rsw = 0231U, // (153 decimal)
+ opcode0_ldbr = 0232U, // (154 decimal)
+ opcode0_rmcm = 0233U, // (155 decimal)
+ opcode0_szn = 0234U, // (156 decimal)
+ opcode0_lda = 0235U, // (157 decimal)
+ opcode0_ldq = 0236U, // (158 decimal)
+ opcode0_ldaq = 0237U, // (159 decimal)
+ opcode0_orsx0 = 0240U, // (160 decimal)
+ opcode0_orsx1 = 0241U, // (161 decimal)
+ opcode0_orsx2 = 0242U, // (162 decimal)
+ opcode0_orsx3 = 0243U, // (163 decimal)
+ opcode0_orsx4 = 0244U, // (164 decimal)
+ opcode0_orsx5 = 0245U, // (165 decimal)
+ opcode0_orsx6 = 0246U, // (166 decimal)
+ opcode0_orsx7 = 0247U, // (167 decimal)
+ opcode0_spri0 = 0250U, // (168 decimal)
+ opcode0_spbp1 = 0251U, // (169 decimal)
+ opcode0_spri2 = 0252U, // (170 decimal)
+ opcode0_spbp3 = 0253U, // (171 decimal)
+ opcode0_spri = 0254U, // (172 decimal)
+ opcode0_orsa = 0255U, // (173 decimal)
+ opcode0_orsq = 0256U, // (174 decimal)
+ opcode0_lsdp = 0257U, // (175 decimal)
+ opcode0_orx0 = 0260U, // (176 decimal)
+ opcode0_orx1 = 0261U, // (177 decimal)
+ opcode0_orx2 = 0262U, // (178 decimal)
+ opcode0_orx3 = 0263U, // (179 decimal)
+ opcode0_orx4 = 0264U, // (180 decimal)
+ opcode0_orx5 = 0265U, // (181 decimal)
+ opcode0_orx6 = 0266U, // (182 decimal)
+ opcode0_orx7 = 0267U, // (183 decimal)
+ opcode0_tsp0 = 0270U, // (184 decimal)
+ opcode0_tsp1 = 0271U, // (185 decimal)
+ opcode0_tsp2 = 0272U, // (186 decimal)
+ opcode0_tsp3 = 0273U, // (187 decimal)
+ opcode0_ora = 0275U, // (189 decimal)
+ opcode0_orq = 0276U, // (190 decimal)
+ opcode0_oraq = 0277U, // (191 decimal)
+ opcode0_canx0 = 0300U, // (192 decimal)
+ opcode0_canx1 = 0301U, // (193 decimal)
+ opcode0_canx2 = 0302U, // (194 decimal)
+ opcode0_canx3 = 0303U, // (195 decimal)
+ opcode0_canx4 = 0304U, // (196 decimal)
+ opcode0_canx5 = 0305U, // (197 decimal)
+ opcode0_canx6 = 0306U, // (198 decimal)
+ opcode0_canx7 = 0307U, // (199 decimal)
+ opcode0_eawp0 = 0310U, // (200 decimal)
+ opcode0_easp0 = 0311U, // (201 decimal)
+ opcode0_eawp2 = 0312U, // (202 decimal)
+ opcode0_easp2 = 0313U, // (203 decimal)
+ opcode0_cana = 0315U, // (205 decimal)
+ opcode0_canq = 0316U, // (206 decimal)
+ opcode0_canaq = 0317U, // (207 decimal)
+ opcode0_lcx0 = 0320U, // (208 decimal)
+ opcode0_lcx1 = 0321U, // (209 decimal)
+ opcode0_lcx2 = 0322U, // (210 decimal)
+ opcode0_lcx3 = 0323U, // (211 decimal)
+ opcode0_lcx4 = 0324U, // (212 decimal)
+ opcode0_lcx5 = 0325U, // (213 decimal)
+ opcode0_lcx6 = 0326U, // (214 decimal)
+ opcode0_lcx7 = 0327U, // (215 decimal)
+ opcode0_eawp4 = 0330U, // (216 decimal)
+ opcode0_easp4 = 0331U, // (217 decimal)
+ opcode0_eawp6 = 0332U, // (218 decimal)
+ opcode0_easp6 = 0333U, // (219 decimal)
+ opcode0_lca = 0335U, // (221 decimal)
+ opcode0_lcq = 0336U, // (222 decimal)
+ opcode0_lcaq = 0337U, // (223 decimal)
+ opcode0_ansx0 = 0340U, // (224 decimal)
+ opcode0_ansx1 = 0341U, // (225 decimal)
+ opcode0_ansx2 = 0342U, // (226 decimal)
+ opcode0_ansx3 = 0343U, // (227 decimal)
+ opcode0_ansx4 = 0344U, // (228 decimal)
+ opcode0_ansx5 = 0345U, // (229 decimal)
+ opcode0_ansx6 = 0346U, // (230 decimal)
+ opcode0_ansx7 = 0347U, // (231 decimal)
+ opcode0_epp0 = 0350U, // (232 decimal)
+ opcode0_epbp1 = 0351U, // (233 decimal)
+ opcode0_epp2 = 0352U, // (234 decimal)
+ opcode0_epbp3 = 0353U, // (235 decimal)
+ opcode0_stac = 0354U, // (236 decimal)
+ opcode0_ansa = 0355U, // (237 decimal)
+ opcode0_ansq = 0356U, // (238 decimal)
+ opcode0_stcd = 0357U, // (239 decimal)
+ opcode0_anx0 = 0360U, // (240 decimal)
+ opcode0_anx1 = 0361U, // (241 decimal)
+ opcode0_anx2 = 0362U, // (242 decimal)
+ opcode0_anx3 = 0363U, // (243 decimal)
+ opcode0_anx4 = 0364U, // (244 decimal)
+ opcode0_anx5 = 0365U, // (245 decimal)
+ opcode0_anx6 = 0366U, // (246 decimal)
+ opcode0_anx7 = 0367U, // (247 decimal)
+ opcode0_epp4 = 0370U, // (248 decimal)
+ opcode0_epbp5 = 0371U, // (249 decimal)
+ opcode0_epp6 = 0372U, // (250 decimal)
+ opcode0_epbp7 = 0373U, // (251 decimal)
+ opcode0_ana = 0375U, // (253 decimal)
+ opcode0_anq = 0376U, // (254 decimal)
+ opcode0_anaq = 0377U, // (255 decimal)
+ opcode0_mpf = 0401U, // (257 decimal)
+ opcode0_mpy = 0402U, // (258 decimal)
+ opcode0_cmg = 0405U, // (261 decimal)
+ opcode0_lde = 0411U, // (265 decimal)
+ opcode0_rscr = 0413U, // (267 decimal)
+ opcode0_ade = 0415U, // (269 decimal)
+ opcode0_ufm = 0421U, // (273 decimal)
+ opcode0_dufm = 0423U, // (275 decimal)
+ opcode0_fcmg = 0425U, // (277 decimal)
+ opcode0_dfcmg = 0427U, // (279 decimal)
+ opcode0_fszn = 0430U, // (280 decimal)
+ opcode0_fld = 0431U, // (281 decimal)
+ opcode0_dfld = 0433U, // (283 decimal)
+ opcode0_ufa = 0435U, // (285 decimal)
+ opcode0_dufa = 0437U, // (287 decimal)
+ opcode0_sxl0 = 0440U, // (288 decimal)
+ opcode0_sxl1 = 0441U, // (289 decimal)
+ opcode0_sxl2 = 0442U, // (290 decimal)
+ opcode0_sxl3 = 0443U, // (291 decimal)
+ opcode0_sxl4 = 0444U, // (292 decimal)
+ opcode0_sxl5 = 0445U, // (293 decimal)
+ opcode0_sxl6 = 0446U, // (294 decimal)
+ opcode0_sxl7 = 0447U, // (295 decimal)
+ opcode0_stz = 0450U, // (296 decimal)
+ opcode0_smic = 0451U, // (297 decimal)
+ opcode0_scpr = 0452U, // (298 decimal)
+ opcode0_stt = 0454U, // (300 decimal)
+ opcode0_fst = 0455U, // (301 decimal)
+ opcode0_ste = 0456U, // (302 decimal)
+ opcode0_dfst = 0457U, // (303 decimal)
+ opcode0_fmp = 0461U, // (305 decimal)
+ opcode0_dfmp = 0463U, // (307 decimal)
+ opcode0_fstr = 0470U, // (312 decimal)
+ opcode0_frd = 0471U, // (313 decimal)
+ opcode0_dfstr = 0472U, // (314 decimal)
+ opcode0_dfrd = 0473U, // (315 decimal)
+ opcode0_fad = 0475U, // (317 decimal)
+ opcode0_dfad = 0477U, // (319 decimal)
+ opcode0_rpl = 0500U, // (320 decimal)
+ opcode0_bcd = 0505U, // (325 decimal)
+ opcode0_div = 0506U, // (326 decimal)
+ opcode0_dvf = 0507U, // (327 decimal)
+ opcode0_fneg = 0513U, // (331 decimal)
+ opcode0_fcmp = 0515U, // (333 decimal)
+ opcode0_dfcmp = 0517U, // (335 decimal)
+ opcode0_rpt = 0520U, // (336 decimal)
+ opcode0_fdi = 0525U, // (341 decimal)
+ opcode0_dfdi = 0527U, // (343 decimal)
+ opcode0_neg = 0531U, // (345 decimal)
+ opcode0_cams = 0532U, // (346 decimal)
+ opcode0_negl = 0533U, // (347 decimal)
+ opcode0_ufs = 0535U, // (349 decimal)
+ opcode0_dufs = 0537U, // (351 decimal)
+ opcode0_sprp0 = 0540U, // (352 decimal)
+ opcode0_sprp1 = 0541U, // (353 decimal)
+ opcode0_sprp2 = 0542U, // (354 decimal)
+ opcode0_sprp3 = 0543U, // (355 decimal)
+ opcode0_sprp4 = 0544U, // (356 decimal)
+ opcode0_sprp5 = 0545U, // (357 decimal)
+ opcode0_sprp6 = 0546U, // (358 decimal)
+ opcode0_sprp7 = 0547U, // (359 decimal)
+ opcode0_sbar = 0550U, // (360 decimal)
+ opcode0_stba = 0551U, // (361 decimal)
+ opcode0_stbq = 0552U, // (362 decimal)
+ opcode0_smcm = 0553U, // (363 decimal)
+ opcode0_stc1 = 0554U, // (364 decimal)
+ opcode0_ssdp = 0557U, // (367 decimal)
+ opcode0_rpd = 0560U, // (368 decimal)
+ opcode0_fdv = 0565U, // (373 decimal)
+ opcode0_dfdv = 0567U, // (375 decimal)
+ opcode0_fno = 0573U, // (379 decimal)
+ opcode0_fsb = 0575U, // (381 decimal)
+ opcode0_dfsb = 0577U, // (383 decimal)
+ opcode0_tze = 0600U, // (384 decimal)
+ opcode0_tnz = 0601U, // (385 decimal)
+ opcode0_tnc = 0602U, // (386 decimal)
+ opcode0_trc = 0603U, // (387 decimal)
+ opcode0_tmi = 0604U, // (388 decimal)
+ opcode0_tpl = 0605U, // (389 decimal)
+ opcode0_ttf = 0607U, // (391 decimal)
+ opcode0_rtcd = 0610U, // (392 decimal)
+ opcode0_rcu = 0613U, // (395 decimal)
+ opcode0_teo = 0614U, // (396 decimal)
+ opcode0_teu = 0615U, // (397 decimal)
+ opcode0_dis = 0616U, // (398 decimal)
+ opcode0_tov = 0617U, // (399 decimal)
+ opcode0_eax0 = 0620U, // (400 decimal)
+ opcode0_eax1 = 0621U, // (401 decimal)
+ opcode0_eax2 = 0622U, // (402 decimal)
+ opcode0_eax3 = 0623U, // (403 decimal)
+ opcode0_eax4 = 0624U, // (404 decimal)
+ opcode0_eax5 = 0625U, // (405 decimal)
+ opcode0_eax6 = 0626U, // (406 decimal)
+ opcode0_eax7 = 0627U, // (407 decimal)
+ opcode0_ret = 0630U, // (408 decimal)
+ opcode0_rccl = 0633U, // (411 decimal)
+ opcode0_ldi = 0634U, // (412 decimal)
+ opcode0_eaa = 0635U, // (413 decimal)
+ opcode0_eaq = 0636U, // (414 decimal)
+ opcode0_ldt = 0637U, // (415 decimal)
+ opcode0_ersx0 = 0640U, // (416 decimal)
+ opcode0_ersx1 = 0641U, // (417 decimal)
+ opcode0_ersx2 = 0642U, // (418 decimal)
+ opcode0_ersx3 = 0643U, // (419 decimal)
+ opcode0_ersx4 = 0644U, // (420 decimal)
+ opcode0_ersx5 = 0645U, // (421 decimal)
+ opcode0_ersx6 = 0646U, // (422 decimal)
+ opcode0_ersx7 = 0647U, // (423 decimal)
+ opcode0_spri4 = 0650U, // (424 decimal)
+ opcode0_spbp5 = 0651U, // (425 decimal)
+ opcode0_spri6 = 0652U, // (426 decimal)
+ opcode0_spbp7 = 0653U, // (427 decimal)
+ opcode0_stacq = 0654U, // (428 decimal)
+ opcode0_ersa = 0655U, // (429 decimal)
+ opcode0_ersq = 0656U, // (430 decimal)
+ opcode0_scu = 0657U, // (431 decimal)
+ opcode0_erx0 = 0660U, // (432 decimal)
+ opcode0_erx1 = 0661U, // (433 decimal)
+ opcode0_erx2 = 0662U, // (434 decimal)
+ opcode0_erx3 = 0663U, // (435 decimal)
+ opcode0_erx4 = 0664U, // (436 decimal)
+ opcode0_erx5 = 0665U, // (437 decimal)
+ opcode0_erx6 = 0666U, // (438 decimal)
+ opcode0_erx7 = 0667U, // (439 decimal)
+ opcode0_tsp4 = 0670U, // (440 decimal)
+ opcode0_tsp5 = 0671U, // (441 decimal)
+ opcode0_tsp6 = 0672U, // (442 decimal)
+ opcode0_tsp7 = 0673U, // (443 decimal)
+ opcode0_lcpr = 0674U, // (444 decimal)
+ opcode0_era = 0675U, // (445 decimal)
+ opcode0_erq = 0676U, // (446 decimal)
+ opcode0_eraq = 0677U, // (447 decimal)
+ opcode0_tsx0 = 0700U, // (448 decimal)
+ opcode0_tsx1 = 0701U, // (449 decimal)
+ opcode0_tsx2 = 0702U, // (450 decimal)
+ opcode0_tsx3 = 0703U, // (451 decimal)
+ opcode0_tsx4 = 0704U, // (452 decimal)
+ opcode0_tsx5 = 0705U, // (453 decimal)
+ opcode0_tsx6 = 0706U, // (454 decimal)
+ opcode0_tsx7 = 0707U, // (455 decimal)
+ opcode0_tra = 0710U, // (456 decimal)
+ opcode0_call6 = 0713U, // (459 decimal)
+ opcode0_tss = 0715U, // (461 decimal)
+ opcode0_xec = 0716U, // (462 decimal)
+ opcode0_xed = 0717U, // (463 decimal)
+ opcode0_lxl0 = 0720U, // (464 decimal)
+ opcode0_lxl1 = 0721U, // (465 decimal)
+ opcode0_lxl2 = 0722U, // (466 decimal)
+ opcode0_lxl3 = 0723U, // (467 decimal)
+ opcode0_lxl4 = 0724U, // (468 decimal)
+ opcode0_lxl5 = 0725U, // (469 decimal)
+ opcode0_lxl6 = 0726U, // (470 decimal)
+ opcode0_lxl7 = 0727U, // (471 decimal)
+ opcode0_ars = 0731U, // (473 decimal)
+ opcode0_qrs = 0732U, // (474 decimal)
+ opcode0_lrs = 0733U, // (475 decimal)
+ opcode0_als = 0735U, // (477 decimal)
+ opcode0_qls = 0736U, // (478 decimal)
+ opcode0_lls = 0737U, // (479 decimal)
+ opcode0_stx0 = 0740U, // (480 decimal)
+ opcode0_stx1 = 0741U, // (481 decimal)
+ opcode0_stx2 = 0742U, // (482 decimal)
+ opcode0_stx3 = 0743U, // (483 decimal)
+ opcode0_stx4 = 0744U, // (484 decimal)
+ opcode0_stx5 = 0745U, // (485 decimal)
+ opcode0_stx6 = 0746U, // (486 decimal)
+ opcode0_stx7 = 0747U, // (487 decimal)
+ opcode0_stc2 = 0750U, // (488 decimal)
+ opcode0_stca = 0751U, // (489 decimal)
+ opcode0_stcq = 0752U, // (490 decimal)
+ opcode0_sreg = 0753U, // (491 decimal)
+ opcode0_sti = 0754U, // (492 decimal)
+ opcode0_sta = 0755U, // (493 decimal)
+ opcode0_stq = 0756U, // (494 decimal)
+ opcode0_staq = 0757U, // (495 decimal)
+ opcode0_lprp0 = 0760U, // (496 decimal)
+ opcode0_lprp1 = 0761U, // (497 decimal)
+ opcode0_lprp2 = 0762U, // (498 decimal)
+ opcode0_lprp3 = 0763U, // (499 decimal)
+ opcode0_lprp4 = 0764U, // (500 decimal)
+ opcode0_lprp5 = 0765U, // (501 decimal)
+ opcode0_lprp6 = 0766U, // (502 decimal)
+ opcode0_lprp7 = 0767U, // (503 decimal)
+ opcode0_arl = 0771U, // (505 decimal)
+ opcode0_qrl = 0772U, // (506 decimal)
+ opcode0_lrl = 0773U, // (507 decimal)
+ opcode0_gtb = 0774U, // (508 decimal)
+ opcode0_alr = 0775U, // (509 decimal)
+ opcode0_qlr = 0776U, // (510 decimal)
+ opcode0_llr = 0777U // (511 decimal)
+} opcode0_t;
+
+// Opcodes with low bit (bit 27) == 1. Enum value is value of upper 9 bits.
+typedef enum {
+ opcode1_mve = 0020U, // (16 decimal)
+ opcode1_mvne = 0024U, // (20 decimal)
+ opcode1_csl = 0060U, // (48 decimal)
+ opcode1_csr = 0061U, // (49 decimal)
+ opcode1_sztl = 0064U, // (52 decimal)
+ opcode1_sztr = 0065U, // (53 decimal)
+ opcode1_cmpb = 0066U, // (54 decimal)
+ opcode1_mlr = 0100U, // (64 decimal)
+ opcode1_mrl = 0101U, // (65 decimal)
+ opcode1_cmpc = 0106U, // (70 decimal)
+ opcode1_scd = 0120U, // (80 decimal)
+ opcode1_scdr = 0121U, // (81 decimal)
+ opcode1_scm = 0124U, // (84 decimal)
+ opcode1_scmr = 0125U, // (85 decimal)
+ opcode1_sptr = 0154U, // (108 decimal)
+ opcode1_mvt = 0160U, // (112 decimal)
+ opcode1_tct = 0164U, // (116 decimal)
+ opcode1_tctr = 0165U, // (117 decimal)
+ opcode1_lptr = 0173U, // (123 decimal)
+ opcode1_ad2d = 0202U, // (130 decimal)
+ opcode1_sb2d = 0203U, // (131 decimal)
+ opcode1_mp2d = 0206U, // (134 decimal)
+ opcode1_dv2d = 0207U, // (135 decimal)
+ opcode1_ad3d = 0222U, // (146 decimal)
+ opcode1_sb3d = 0223U, // (147 decimal)
+ opcode1_mp3d = 0226U, // (150 decimal)
+ opcode1_dv3d = 0227U, // (151 decimal)
+ opcode1_lsdr = 0232U, // (154 decimal)
+ opcode1_spbp0 = 0250U, // (168 decimal)
+ opcode1_spri1 = 0251U, // (169 decimal)
+ opcode1_spbp2 = 0252U, // (170 decimal)
+ opcode1_spri3 = 0253U, // (171 decimal)
+ opcode1_ssdr = 0254U, // (172 decimal)
+ opcode1_lptp = 0257U, // (175 decimal)
+ opcode1_mvn = 0300U, // (192 decimal)
+ opcode1_btd = 0301U, // (193 decimal)
+ opcode1_cmpn = 0303U, // (195 decimal)
+ opcode1_dtb = 0305U, // (197 decimal)
+ opcode1_easp1 = 0310U, // (200 decimal)
+ opcode1_eawp1 = 0311U, // (201 decimal)
+ opcode1_easp3 = 0312U, // (202 decimal)
+ opcode1_eawp3 = 0313U, // (203 decimal)
+ opcode1_easp5 = 0330U, // (216 decimal)
+ opcode1_eawp5 = 0331U, // (217 decimal)
+ opcode1_easp7 = 0332U, // (218 decimal)
+ opcode1_eawp7 = 0333U, // (219 decimal)
+ opcode1_epbp0 = 0350U, // (232 decimal)
+ opcode1_epp1 = 0351U, // (233 decimal)
+ opcode1_epbp2 = 0352U, // (234 decimal)
+ opcode1_epp3 = 0353U, // (235 decimal)
+ opcode1_epbp4 = 0370U, // (248 decimal)
+ opcode1_epp5 = 0371U, // (249 decimal)
+ opcode1_epbp6 = 0372U, // (250 decimal)
+ opcode1_epp7 = 0373U, // (251 decimal)
+ opcode1_sareg = 0443U, // (291 decimal)
+ opcode1_spl = 0447U, // (295 decimal)
+ opcode1_lareg = 0463U, // (307 decimal)
+ opcode1_lpl = 0467U, // (311 decimal)
+ opcode1_a9bd = 0500U, // (320 decimal)
+ opcode1_a6bd = 0501U, // (321 decimal)
+ opcode1_a4bd = 0502U, // (322 decimal)
+ opcode1_abd = 0503U, // (323 decimal)
+ opcode1_awd = 0507U, // (327 decimal)
+ opcode1_s9bd = 0520U, // (336 decimal)
+ opcode1_s6bd = 0521U, // (337 decimal)
+ opcode1_s4bd = 0522U, // (338 decimal)
+ opcode1_sbd = 0523U, // (339 decimal)
+ opcode1_swd = 0527U, // (343 decimal)
+ opcode1_camp = 0532U, // (346 decimal)
+ opcode1_ara0 = 0540U, // (352 decimal)
+ opcode1_ara1 = 0541U, // (353 decimal)
+ opcode1_ara2 = 0542U, // (354 decimal)
+ opcode1_ara3 = 0543U, // (355 decimal)
+ opcode1_ara4 = 0544U, // (356 decimal)
+ opcode1_ara5 = 0545U, // (357 decimal)
+ opcode1_ara6 = 0546U, // (358 decimal)
+ opcode1_ara7 = 0547U, // (359 decimal)
+ opcode1_sptp = 0557U, // (367 decimal)
+ opcode1_aar0 = 0560U, // (368 decimal)
+ opcode1_aar1 = 0561U, // (369 decimal)
+ opcode1_aar2 = 0562U, // (370 decimal)
+ opcode1_aar3 = 0563U, // (371 decimal)
+ opcode1_aar4 = 0564U, // (372 decimal)
+ opcode1_aar5 = 0565U, // (373 decimal)
+ opcode1_aar6 = 0566U, // (374 decimal)
+ opcode1_aar7 = 0567U, // (375 decimal)
+ opcode1_trtn = 0600U, // (384 decimal)
+ opcode1_trtf = 0601U, // (385 decimal)
+ opcode1_tmoz = 0604U, // (388 decimal)
+ opcode1_tpnz = 0605U, // (389 decimal)
+ opcode1_ttn = 0606U, // (390 decimal)
+ opcode1_arn0 = 0640U, // (416 decimal)
+ opcode1_arn1 = 0641U, // (417 decimal)
+ opcode1_arn2 = 0642U, // (418 decimal)
+ opcode1_arn3 = 0643U, // (419 decimal)
+ opcode1_arn4 = 0644U, // (420 decimal)
+ opcode1_arn5 = 0645U, // (421 decimal)
+ opcode1_arn6 = 0646U, // (422 decimal)
+ opcode1_arn7 = 0647U, // (423 decimal)
+ opcode1_spbp4 = 0650U, // (424 decimal)
+ opcode1_spri5 = 0651U, // (425 decimal)
+ opcode1_spbp6 = 0652U, // (426 decimal)
+ opcode1_spri7 = 0653U, // (427 decimal)
+ opcode1_nar0 = 0660U, // (432 decimal)
+ opcode1_nar1 = 0661U, // (433 decimal)
+ opcode1_nar2 = 0662U, // (434 decimal)
+ opcode1_nar3 = 0663U, // (435 decimal)
+ opcode1_nar4 = 0664U, // (436 decimal)
+ opcode1_nar5 = 0665U, // (437 decimal)
+ opcode1_nar6 = 0666U, // (438 decimal)
+ opcode1_nar7 = 0667U, // (439 decimal)
+ opcode1_sar0 = 0740U, // (480 decimal)
+ opcode1_sar1 = 0741U, // (481 decimal)
+ opcode1_sar2 = 0742U, // (482 decimal)
+ opcode1_sar3 = 0743U, // (483 decimal)
+ opcode1_sar4 = 0744U, // (484 decimal)
+ opcode1_sar5 = 0745U, // (485 decimal)
+ opcode1_sar6 = 0746U, // (486 decimal)
+ opcode1_sar7 = 0747U, // (487 decimal)
+ opcode1_sra = 0754U, // (492 decimal)
+ opcode1_lar0 = 0760U, // (496 decimal)
+ opcode1_lar1 = 0761U, // (497 decimal)
+ opcode1_lar2 = 0762U, // (498 decimal)
+ opcode1_lar3 = 0763U, // (499 decimal)
+ opcode1_lar4 = 0764U, // (500 decimal)
+ opcode1_lar5 = 0765U, // (501 decimal)
+ opcode1_lar6 = 0766U, // (502 decimal)
+ opcode1_lar7 = 0767U, // (503 decimal)
+ opcode1_lra = 0774U // (508 decimal)
+} opcode1_t;
+
+/////////////////////////////////////
+//
+// History registers
+//
+
+enum { N_HIST_SETS = 4 };
+#ifdef DPS8M
+enum { N_HIST_SIZE = 64 };
+#endif
+#ifdef L68
+enum { N_HIST_SIZE = 16 };
+#endif
+
+// Bit in CU history register word 0
+
+// cu_hist_t flags
+enum
+ {
+ CU_HIST_PIA = 0400000000000, // 0 Prepare instruction address
+ CU_HIST_POA = 0200000000000, // 1 Prepare operand address
+ CU_HIST_RIW = 0100000000000, // 2 Request indirect word
+ CU_HIST_SIW = 0040000000000, // 3 Restore indirect word
+ CU_HIST_POT = 0020000000000, // 4 Prepare operand tally (indirect tally chain)
+ CU_HIST_PON = 0010000000000, // 5 Prepare operand no tally (as for POT except no chain)
+ CU_HIST_RAW = 0004000000000, // 6 Request read-alter-rewrite word
+ CU_HIST_SAW = 0002000000000, // 7 Restore read-later-rewrite word
+ CU_HIST_TRGO = 0001000000000, // 8 Transfer GO (conditions met)
+ CU_HIST_XDE = 0000400000000, // 9 Execute XED even instruction
+ CU_HIST_XDO = 0000200000000, // 10 Execute XED odd instruction
+ CU_HIST_IC = 0000100000000, // 11 Execute odd instruction of the current pair
+ CU_HIST_RPTS = 0000040000000, // 12 Execute a repeat instruction
+ CU_HIST_PORTF = 0000020000000, // 13 Memory cycle to port on previous cycle
+ CU_HIST_INTERNAL = 0000010000000, // 14 Memory cycle to cache or direct on previous cycle
+ CU_HIST_PAI = 0000004000000, // 15 Prepare interrupt address
+ CU_HIST_PFA = 0000002000000, // 16 Prepare fault address
+ CU_HIST_PRIV = 0000001000000 // 17 In privileged mode
+ };
+
+// cu_hist_t flags2
+enum
+ {
+ CU_HIST_XINT = 0100, // 29 Execute instruction
+ CU_HIST_IFT = 0040, // 30 Perform an instruction fetch
+ CU_HIST_CRD = 0020, // 31 Cache read, this CU cycle
+ CU_HIST_MRD = 0010, // 32 Memory read, this CU cycle
+ CU_HIST_MSTO = 0004, // 33 Memory store, this CU cycle
+ CU_HIST_PIB = 0002, // 34 Memory port interface busy
+ };
+
+enum
+ {
+ DU_FANLD1 = 0400000000000, // 0 Alpha-num load desc l (complemented)
+ DU_FANLD2 = 0200000000000, // 1 Alpha-num load desc 2 (complemented)
+ DU_FANSTR = 0100000000000, // 2 Alpha-num store (complemented)
+ DU_FLDWRT1 = 0040000000000, // 3 Load re-write reg l (complemented)
+ DU_FLDWRT2 = 0020000000000, // 4 Load re-write reg 2 (complemented)
+ DU_FNLD1 = 0010000000000, // 5 Numeric load desc l (complemented)
+ DU_FNLD2 = 0004000000000, // 6 Numeric load desc 2 (complemented)
+ DU_NOSEQF = 0002000000000, // 7 End sequence flag
+ DU_FDUD = 0001000000000, // 8 Decimal unit idle (complemented)
+ DU_FGSTR = 0000400000000, // 9 General store flag (complemented)
+ DU_NOSEQ = 0000200000000, // 10 End of sequence (complemented)
+ DU_NINE = 0000100000000, // 11 9-bit character operation
+ DU_SIX = 0000040000000, // 12 6-bit character operation
+ DU_FOUR = 0000020000000, // 13 4-bit character operation
+ DU_DUBIT = 0000010000000, // 14 Bit operation
+ DU_UWORD = 0000004000000, // 15 Word operation
+ DU_PTR1 = 0000002000000, // 16 Select ptr l
+ DU_PTR2 = 0000001000000, // 17 Select ptr 2
+ DU_PRT3 = 0000000400000, // 18 Select ptr 3
+ DU_FPOP = 0000000200000, // 19 Prepare operand pointer
+ DU_GEAM = 0000000100000, // 20 Add timing gates (complemented)
+ DU_LPD12 = 0000000040000, // 21 Load pointer l or 2 (complemented)
+ DU_GEMAE = 0000000020000, // 22 Multiply gates A E (complemented)
+ DU_BTDS = 0000000010000, // 23 Binary to decimal gates (complemented)
+ DU_SP15 = 0000000004000, // 24 Align cycles (complemented)
+ DU_FSWEQ = 0000000002000, // 25 Single word sequence flag (complemented)
+ DU_FGCH = 0000000001000, // 26 Character cycle (complemented)
+ DU_DFRST = 0000000000400, // 27 Processing descriptor for first time
+ DU_EXH = 0000000000200, // 28 Exhaust
+ DU_FGADO = 0000000000100, // 29 Add cycle (complemented)
+ DU_INTRPTD = 0000000000040, // 30 Interrupted
+ DU_GLDP2 = 0000000000020, // 31 Load DP2
+ DU_GEMC = 0000000000010, // 32 Multiply gate C
+ DU_GBDA = 0000000000004, // 33 Binary to decimal gate A
+ DU_GSP5 = 0000000000002 // 34 Final align cycle
+ };
+
+// apu_hist_t flags
+enum
+ {
+ APU_PIA_OVF = 04000000, // 15 PIA Page overflow
+ APU_PIA_OOB = 02000000, // 16 PIA out of segment bounds
+ APU_FDSPTW = 01000000, // 17 Fetch descriptor segment PTW
+ APU_MDSPTW = 00400000, // 18 Descriptor segment PTW is modified
+ APU_FSDW = 00200000, // 19 Fetch SDW
+ APU_FPTW = 00100000, // 20 Fetch PTW
+ APU_FPTW2 = 00040000, // 21 Fetch pre-page PTW
+ APU_MPTW = 00020000, // 22 PTW modified
+ APU_FANP = 00010000, // 23 Final address nonpaged
+ APU_FAP = 00004000, // 24 Final address paged
+ APU_MTCHSDW = 00002000, // 25 SDW match found
+ APU_SDWMF = 00001000, // 26 SDW match found and used
+ // BSY Data source for ESN
+ APU_BSY_IC = 00000000, // 27-28 00 = from ppr.ic
+ APU_BSY_TSE = 00000200, // 27-28 01 = from prn.tsr
+ APU_BSY_SWR = 00000400, // 27-28 10 = from tpr.swr
+ APU_BSY_CA = 00000600, // 27-28 11 = from tpr.ca
+ APU_MTCHPTW = 00000100, // 29 PTW match found (AM)
+ //APU_PTWMF = 00000000, // 30-31 PTW match found (AM) and used
+ //APU_PTWAM = 00000000 // 32-35 PTW AM direct address (ZCA bits 4-7)
+ };
+
+// apu_hist_t flags2
+enum
+ {
+ APU_SDWME = 0400, // 27 SDW match error
+ //APU_SDWLVL = 0000, // 28-29 SDW match level count (0 = Level A)
+ APU_CACHE = 0040, // 30 Cache used this cycle
+ APU_PTW = 0020, // 31 match error
+ //APU_PTWLVL = 0000, // 32-33 PTW match level count (0 = level A)
+ APU_FLTHLD = 0002, // 34 A directed fault or access violation fault is waiting
+ // bit 35 is marked 'w' but undocumented
+ };
+
+
+#ifdef DPS8M
+enum { CU_HIST_REG = 0, DU_OU_HIST_REG = 1, APU_HIST_REG = 2, EAPU_HIST_REG = 3 };
+#endif
+#ifdef L68
+enum { CU_HIST_REG = 0, DU_HIST_REG = 1, OU_HIST_REG = 2, APU_HIST_REG = 3 };
+#endif
+
+
+#endif // DPS8_HW_CONSTS_H
--- /dev/null
+/*
+ Copyright (c) 2007-2013 Michael Mondy
+ Copyright 2012-2016 by Harry Reed
+ Copyright 2013-2016 by Charles Anthony
+ Copyright 2015 by Eric Swenson
+ Copyright 2017 by Michal Tomek
+
+ All rights reserved.
+
+ This software is made available under the terms of the
+ ICU License -- ICU 1.8.1 and later.
+ See the LICENSE file at the top-level directory of this distribution and
+ at https://sourceforge.net/p/dps8m/code/ci/master/tree/LICENSE
+ */
+
+/**
+ * \file dps8_iefp.c
+ * \project dps8
+ * \date 01/11/14
+ * \copyright Copyright (c) 2014 Harry Reed. All rights reserved.
+ */
+
+#include "dps8.h"
+#include "dps8_sys.h"
+#include "dps8_faults.h"
+//#include "dps8_scu.h"
+//#include "dps8_iom.h"
+//#include "dps8_cable.h"
+#include "dps8_cpu.h"
+#include "dps8_append.h"
+#include "dps8_iefp.h"
+#include "dps8_addrmods.h"
+#include "dps8_utils.h"
+
+#define DBG_CTR cpu.cycleCnt
+
+// new Read/Write stuff ...
+
+void Read (word18 address, word36 * result, processor_cycle_type cyctyp)
+ {
+ cpu.TPR.CA = cpu.iefpFinalAddress = address;
+ bool isBAR = get_bar_mode ();
+
+ //if (get_went_appending () ||
+ if (cpu.cu.XSF || (cyctyp != INSTRUCTION_FETCH && cpu.currentInstruction.b29))
+ {
+ goto B29;
+ }
+
+ switch (get_addr_mode ())
+ {
+ case ABSOLUTE_mode:
+ {
+ if (isBAR)
+ {
+ set_apu_status (apuStatus_FABS); // XXX maybe...
+ cpu.iefpFinalAddress = get_BAR_address (address);
+ fauxDoAppendCycle (cyctyp);
+#ifdef LOCKLESS
+ if (cyctyp == OPERAND_RMW || cyctyp == APU_DATA_RMW)
+ core_read_lock (cpu.iefpFinalAddress, result, __func__);
+ else
+ core_read (cpu.iefpFinalAddress, result, __func__);
+#else
+ core_read (cpu.iefpFinalAddress, result, __func__);
+#endif
+ sim_debug (DBG_FINAL, & cpu_dev,
+ "Read (Actual) Read: bar address=%08o "
+ "readData=%012"PRIo64"\n", address, *result);
+ HDBGMRead (cpu.iefpFinalAddress, * result);
+ return;
+ }
+ else
+ {
+ set_apu_status (apuStatus_FABS);
+ fauxDoAppendCycle (cyctyp);
+#ifdef LOCKLESS
+ if (cyctyp == OPERAND_RMW || cyctyp == APU_DATA_RMW)
+ core_read_lock (address, result, __func__);
+ else
+ core_read (address, result, __func__);
+#else
+ core_read (address, result, __func__);
+#endif
+ sim_debug (DBG_FINAL, & cpu_dev,
+ "Read (Actual) Read: abs address=%08o "
+ "readData=%012"PRIo64"\n", address, *result);
+ HDBGMRead (address, * result);
+ return;
+ }
+ }
+
+ case APPEND_mode:
+ {
+B29:;
+ if (isBAR)
+ {
+ cpu.TPR.CA = get_BAR_address (address);
+ cpu.TPR.TSR = cpu.PPR.PSR;
+ cpu.TPR.TRR = cpu.PPR.PRR;
+ cpu.iefpFinalAddress = do_append_cycle (cyctyp, result, 1);
+ sim_debug (DBG_APPENDING | DBG_FINAL, & cpu_dev,
+ "Read (Actual) Read: bar iefpFinalAddress=%08o "
+ "readData=%012"PRIo64"\n",
+ cpu.iefpFinalAddress, * result);
+ HDBGMRead (cpu.iefpFinalAddress, * result);
+
+ return;
+ }
+ else
+ {
+ cpu.iefpFinalAddress = do_append_cycle (cyctyp, result, 1);
+ // XXX Don't trace Multics idle loop
+ if (cpu.PPR.PSR != 061 && cpu.PPR.IC != 0307)
+ {
+ sim_debug (DBG_APPENDING | DBG_FINAL, & cpu_dev,
+ "Read (Actual) Read: iefpFinalAddress=%08o "
+ "readData=%012"PRIo64"\n",
+ cpu.iefpFinalAddress, * result);
+ HDBGMRead (cpu.iefpFinalAddress, * result);
+ }
+ }
+ return;
+ }
+ }
+ return ;//SCPE_UNK;
+ }
+
+void Read2 (word18 address, word36 * result, processor_cycle_type cyctyp)
+ {
+ cpu.TPR.CA = cpu.iefpFinalAddress = address;
+
+ bool isBAR = get_bar_mode ();
+
+ //if (get_went_appending () ||
+ if (cpu.cu.XSF || (cyctyp != INSTRUCTION_FETCH && cpu.currentInstruction.b29) ||
+ cyctyp == RTCD_OPERAND_FETCH) // ISOLTS-886
+ // Another option would be to set_went_appending in ReadRTCDOp
+ {
+ goto B29;
+ }
+
+ switch (get_addr_mode ())
+ {
+ case ABSOLUTE_mode:
+ {
+ if (isBAR)
+ {
+ set_apu_status (apuStatus_FABS); // XXX maybe...
+ cpu.iefpFinalAddress = get_BAR_address (address);
+
+ fauxDoAppendCycle (cyctyp);
+ core_read2 (cpu.iefpFinalAddress, result + 0, result + 1,
+ __func__);
+ if_sim_debug (DBG_FINAL, & cpu_dev)
+ {
+ for (uint i = 0; i < 2; i ++)
+ sim_debug (DBG_FINAL, & cpu_dev,
+ "Read2 (Actual) Read: bar address=%08o"
+ " readData=%012"PRIo64"\n",
+ address + i, result [i]);
+ }
+ HDBGMRead (cpu.iefpFinalAddress, * result);
+ HDBGMRead (cpu.iefpFinalAddress+1, * (result+1));
+ return;
+ }
+ else
+ {
+ set_apu_status (apuStatus_FABS);
+ fauxDoAppendCycle (cyctyp);
+ core_read2 (address, result + 0, result + 1, __func__);
+ if_sim_debug (DBG_FINAL, & cpu_dev)
+ {
+ for (uint i = 0; i < 2; i ++)
+ sim_debug (DBG_FINAL, & cpu_dev,
+ "Read2 (Actual) Read: abs address=%08o"
+ " readData=%012"PRIo64"\n",
+ address + i, result [i]);
+ }
+ HDBGMRead (cpu.iefpFinalAddress, * result);
+ HDBGMRead (cpu.iefpFinalAddress+1, * (result+1));
+ return;
+ }
+ }
+
+ case APPEND_mode:
+ {
+B29:;
+ if (isBAR)
+ {
+ cpu.TPR.CA = get_BAR_address (address);
+ cpu.TPR.TSR = cpu.PPR.PSR;
+ cpu.TPR.TRR = cpu.PPR.PRR;
+ cpu.iefpFinalAddress = do_append_cycle (cyctyp, result, 2);
+ if_sim_debug (DBG_APPENDING | DBG_FINAL, & cpu_dev)
+ {
+ for (uint i = 0; i < 2; i ++)
+ sim_debug (DBG_APPENDING | DBG_FINAL, & cpu_dev,
+ "Read2 (Actual) Read: bar iefpFinalAddress="
+ "%08o readData=%012"PRIo64"\n",
+ cpu.iefpFinalAddress + i, result [i]);
+ }
+ HDBGMRead (cpu.iefpFinalAddress, * result);
+ HDBGMRead (cpu.iefpFinalAddress+1, * (result+1));
+ return;
+ }
+ else
+ {
+ cpu.iefpFinalAddress = do_append_cycle (cyctyp, result, 2);
+ if_sim_debug (DBG_APPENDING | DBG_FINAL, & cpu_dev)
+ {
+ for (uint i = 0; i < 2; i ++)
+ sim_debug (DBG_APPENDING | DBG_FINAL, & cpu_dev,
+ "Read2 (Actual) Read: iefpFinalAddress=%08o"
+ " readData=%012"PRIo64"\n",
+ cpu.iefpFinalAddress + i, result [i]);
+ }
+ HDBGMRead (cpu.iefpFinalAddress, * result);
+ HDBGMRead (cpu.iefpFinalAddress+1, * (result+1));
+ }
+ return;
+ }
+ }
+ return ;//SCPE_UNK;
+ }
+
+void Read8 (word18 address, word36 * result, bool isAR)
+ {
+ cpu.TPR.CA = cpu.iefpFinalAddress = address;
+
+ bool isBAR = get_bar_mode ();
+
+ if (isAR || cpu.cu.XSF /*get_went_appending ()*/)
+ {
+ goto B29;
+ }
+
+ switch (get_addr_mode ())
+ {
+ case ABSOLUTE_mode:
+ {
+ if (isBAR)
+ {
+ set_apu_status (apuStatus_FABS); // XXX maybe...
+ cpu.iefpFinalAddress = get_BAR_address (address);
+
+ fauxDoAppendCycle (APU_DATA_READ);
+ core_readN (cpu.iefpFinalAddress, result, 8, __func__);
+ if_sim_debug (DBG_FINAL, & cpu_dev)
+ {
+ for (uint i = 0; i < 8; i ++)
+ sim_debug (DBG_FINAL, & cpu_dev,
+ "Read8 (Actual) Read: bar address=%08o"
+ " readData=%012"PRIo64"\n",
+ address + i, result [i]);
+ }
+#ifdef HDBG
+ for (uint i = 0; i < 8; i ++)
+ HDBGMRead (cpu.iefpFinalAddress + i, result [i]);
+#endif
+ return;
+ }
+ else
+ {
+ set_apu_status (apuStatus_FABS);
+ fauxDoAppendCycle (APU_DATA_READ);
+ core_readN (address, result, 8, __func__);
+ if_sim_debug (DBG_FINAL, & cpu_dev)
+ {
+ for (uint i = 0; i < 8; i ++)
+ sim_debug (DBG_FINAL, & cpu_dev,
+ "Read8 (Actual) Read: abs address=%08o"
+ " readData=%012"PRIo64"\n",
+ address + i, result [i]);
+ }
+#ifdef HDBG
+ for (uint i = 0; i < 8; i ++)
+ HDBGMRead (address + i, result [i]);
+#endif
+ return;
+ }
+ }
+
+ case APPEND_mode:
+ {
+B29:;
+ if (isBAR)
+ {
+ cpu.TPR.CA = get_BAR_address (address);
+ cpu.TPR.TSR = cpu.PPR.PSR;
+ cpu.TPR.TRR = cpu.PPR.PRR;
+ cpu.iefpFinalAddress = do_append_cycle (APU_DATA_READ, result,
+ 8);
+ if_sim_debug (DBG_APPENDING | DBG_FINAL, & cpu_dev)
+ {
+ for (uint i = 0; i < 8; i ++)
+ sim_debug (DBG_APPENDING | DBG_FINAL, & cpu_dev,
+ "Read8 (Actual) Read: bar iefpFinalAddress="
+ "%08o readData=%012"PRIo64"\n",
+ cpu.iefpFinalAddress + i, result [i]);
+ }
+#ifdef HDBG
+ for (uint i = 0; i < 8; i ++)
+ HDBGMRead (cpu.iefpFinalAddress + i, result[i]);
+#endif
+ return;
+ }
+ else
+ {
+ cpu.iefpFinalAddress = do_append_cycle (APU_DATA_READ, result,
+ 8);
+ // XXX Don't trace Multics idle loop
+ if (cpu.PPR.PSR != 061 && cpu.PPR.IC != 0307)
+ {
+ if_sim_debug (DBG_APPENDING | DBG_FINAL, & cpu_dev)
+ {
+ for (uint i = 0; i < 8; i ++)
+ sim_debug (DBG_APPENDING | DBG_FINAL, & cpu_dev,
+ "Read8 (Actual) Read: iefpFinalAddress="
+ "%08o readData=%012"PRIo64"\n",
+ cpu.iefpFinalAddress + i, result [i]);
+ }
+#ifdef HDBG
+ for (uint i = 0; i < 8; i ++)
+ HDBGMRead (cpu.iefpFinalAddress + i, result [i]);
+#endif
+ }
+ }
+ return;
+ }
+ }
+ return ;//SCPE_UNK;
+ }
+
+void Read16 (word18 address, word36 * result)
+ {
+ address &= paragraphMask; // Round to 8 word boundary
+ Read8 (address, result, cpu.currentInstruction.b29);
+ Read8 (address + 8, result + 8, cpu.currentInstruction.b29);
+ return;
+ }
+
+void ReadPage (word18 address, word36 * result, bool isAR)
+ {
+ if ((address & PGMK) != 0)
+ {
+ sim_warn ("ReadPage not on boundary %06o\n", address);
+ }
+ address &= (word18) ~PGMK; // Round to page boundary
+ cpu.TPR.CA = cpu.iefpFinalAddress = address;
+
+ bool isBAR = get_bar_mode ();
+
+ if (isAR || cpu.cu.XSF /*get_went_appending ()*/)
+ {
+ goto B29;
+ }
+
+ switch (get_addr_mode ())
+ {
+ case ABSOLUTE_mode:
+ {
+ if (isBAR)
+ {
+ set_apu_status (apuStatus_FABS); // XXX maybe...
+ cpu.iefpFinalAddress = get_BAR_address (address);
+
+ fauxDoAppendCycle (APU_DATA_READ);
+ core_readN (cpu.iefpFinalAddress, result, PGSZ, __func__);
+ if_sim_debug (DBG_FINAL, & cpu_dev)
+ {
+ for (uint i = 0; i < PGSZ; i ++)
+ sim_debug (DBG_FINAL, & cpu_dev,
+ "ReadPage (Actual) Read: bar address="
+ "%08o readData=%012"PRIo64"\n",
+ address + i, result [i]);
+ }
+#ifdef HDBG
+ for (uint i = 0; i < PGSZ; i ++)
+ HDBGMRead (cpu.iefpFinalAddress + i, result [i]);
+#endif
+ return;
+ }
+ else
+ {
+ set_apu_status (apuStatus_FABS);
+ fauxDoAppendCycle (APU_DATA_READ);
+ core_readN (address, result, PGSZ, __func__);
+ if_sim_debug (DBG_FINAL, & cpu_dev)
+ {
+ for (uint i = 0; i < PGSZ; i ++)
+ sim_debug (DBG_FINAL, & cpu_dev,
+ "ReadPage (Actual) Read: abs address="
+ "%08o readData=%012"PRIo64"\n",
+ address + i, result [i]);
+ }
+#ifdef HDBG
+ for (uint i = 0; i < PGSZ; i ++)
+ HDBGMRead (address + i, result [i]);
+#endif
+ return;
+ }
+ }
+
+ case APPEND_mode:
+ {
+B29:;
+ if (isBAR)
+ {
+ cpu.TPR.CA = get_BAR_address (address);
+ cpu.TPR.TSR = cpu.PPR.PSR;
+ cpu.TPR.TRR = cpu.PPR.PRR;
+ cpu.iefpFinalAddress = do_append_cycle (APU_DATA_READ, result,
+ PGSZ);
+ if_sim_debug (DBG_APPENDING | DBG_FINAL, & cpu_dev)
+ {
+ for (uint i = 0; i < PGSZ; i ++)
+ sim_debug (DBG_APPENDING | DBG_FINAL, & cpu_dev,
+ "ReadPage (Actual) Read: bar iefpFinalAddress="
+ "%08o readData=%012"PRIo64"\n",
+ cpu.iefpFinalAddress + i, result [i]);
+ }
+#ifdef HDBG
+ for (uint i = 0; i < PGSZ; i ++)
+ HDBGMRead (cpu.iefpFinalAddress + i, result [i]);
+#endif
+
+ return;
+ }
+ else
+ {
+ cpu.iefpFinalAddress = do_append_cycle (APU_DATA_READ, result,
+ PGSZ);
+ // XXX Don't trace Multics idle loop
+ if (cpu.PPR.PSR != 061 && cpu.PPR.IC != 0307)
+ {
+ if_sim_debug (DBG_APPENDING | DBG_FINAL, & cpu_dev)
+ {
+ for (uint i = 0; i < PGSZ; i ++)
+ sim_debug (DBG_APPENDING | DBG_FINAL, & cpu_dev,
+ "ReadPage (Actual) Read: iefpFinalAddress"
+ "=%08o readData=%012"PRIo64"\n",
+ cpu.iefpFinalAddress + i, result [i]);
+ }
+#ifdef HDBG
+ for (uint i = 0; i < PGSZ; i ++)
+ HDBGMRead (cpu.iefpFinalAddress + i, result [i]);
+#endif
+ }
+ }
+ return;
+ }
+ }
+ return ;//SCPE_UNK;
+ }
+
+void Write (word18 address, word36 data, processor_cycle_type cyctyp)
+ {
+ cpu.TPR.CA = cpu.iefpFinalAddress = address;
+
+ bool isBAR = get_bar_mode ();
+
+ if (cpu.cu.XSF /*get_went_appending ()*/ || (cyctyp != INSTRUCTION_FETCH && cpu.currentInstruction.b29))
+ goto B29;
+
+
+ switch (get_addr_mode ())
+ {
+ case ABSOLUTE_mode:
+ {
+ if (isBAR)
+ {
+ cpu.iefpFinalAddress = get_BAR_address (address);
+ set_apu_status (apuStatus_FABS); // XXX maybe...
+ fauxDoAppendCycle (cyctyp);
+ if (cyctyp == OPERAND_STORE && cpu.useZone)
+ {
+ core_write_zone (cpu.iefpFinalAddress, data, __func__);
+ }
+ else
+ {
+ core_write (cpu.iefpFinalAddress, data, __func__);
+ }
+ sim_debug (DBG_FINAL, & cpu_dev,
+ "Write(Actual) Write: bar address=%08o "
+ "writeData=%012"PRIo64"\n", address, data);
+ HDBGMWrite (cpu.iefpFinalAddress, data);
+ return;
+ }
+ else
+ {
+ set_apu_status (apuStatus_FABS);
+ fauxDoAppendCycle (cyctyp);
+ if (cyctyp == OPERAND_STORE && cpu.useZone)
+ {
+ core_write_zone (address, data, __func__);
+ }
+ else
+ {
+ core_write (address, data, __func__);
+ }
+ sim_debug (DBG_FINAL, & cpu_dev,
+ "Write(Actual) Write: abs address=%08o "
+ "writeData=%012"PRIo64"\n",
+ address, data);
+ HDBGMWrite (address, data);
+ return;
+ }
+ }
+
+ case APPEND_mode:
+ {
+B29:
+ if (isBAR)
+ {
+ cpu.TPR.CA = get_BAR_address (address);
+ cpu.TPR.TSR = cpu.PPR.PSR;
+ cpu.TPR.TRR = cpu.PPR.PRR;
+ cpu.iefpFinalAddress = do_append_cycle (cyctyp, & data, 1);
+ sim_debug (DBG_APPENDING | DBG_FINAL, & cpu_dev,
+ "Write(Actual) Write: bar iefpFinalAddress=%08o "
+ "writeData=%012"PRIo64"\n",
+ cpu.iefpFinalAddress, data);
+ HDBGMWrite (cpu.iefpFinalAddress, data);
+ return;
+ }
+ else
+ {
+ cpu.iefpFinalAddress = do_append_cycle (cyctyp, & data, 1);
+ sim_debug (DBG_APPENDING | DBG_FINAL, & cpu_dev,
+ "Write(Actual) Write: iefpFinalAddress=%08o "
+ "writeData=%012"PRIo64"\n",
+ cpu.iefpFinalAddress, data);
+ HDBGMWrite (cpu.iefpFinalAddress, data);
+ return;
+ }
+ }
+ }
+
+ return ;//SCPE_UNK;
+ }
+
+
+void Write2 (word18 address, word36 * data, processor_cycle_type cyctyp)
+ {
+ cpu.TPR.CA = cpu.iefpFinalAddress = address;
+ bool isBAR = get_bar_mode ();
+
+ if (cpu.cu.XSF /*get_went_appending ()*/ || (cyctyp != INSTRUCTION_FETCH && cpu.currentInstruction.b29))
+ goto B29;
+
+ switch (get_addr_mode ())
+ {
+ case ABSOLUTE_mode:
+ {
+ if (isBAR)
+ {
+ cpu.iefpFinalAddress = get_BAR_address (address);
+ set_apu_status (apuStatus_FABS); // XXX maybe...
+ fauxDoAppendCycle (cyctyp);
+ core_write2 (cpu.iefpFinalAddress, data [0], data [1],
+ __func__);
+ HDBGMWrite (cpu.iefpFinalAddress, data [0]);
+ HDBGMWrite (cpu.iefpFinalAddress+1, data [1]);
+ sim_debug (DBG_FINAL, & cpu_dev,
+ "Write2 (Actual) Write: bar address=%08o "
+ "writeData=%012"PRIo64" %012"PRIo64"\n",
+ address, data [0], data [1]);
+ }
+ else
+ {
+ set_apu_status (apuStatus_FABS);
+ fauxDoAppendCycle (cyctyp);
+ core_write2 (address, data [0], data [1], __func__);
+ HDBGMWrite (address, data [0]);
+ HDBGMWrite (address+1, data [1]);
+ sim_debug (DBG_FINAL, & cpu_dev,
+ "Write2 (Actual) Write: abs address=%08o "
+ "writeData=%012"PRIo64" %012"PRIo64"\n",
+ address, data [0], data [1]);
+ }
+ }
+ break;
+
+ case APPEND_mode:
+ {
+B29:
+ if (isBAR)
+ {
+ cpu.TPR.CA = get_BAR_address (address);
+ cpu.TPR.TSR = cpu.PPR.PSR;
+ cpu.TPR.TRR = cpu.PPR.PRR;
+ cpu.iefpFinalAddress = do_append_cycle (cyctyp, data, 2);
+ sim_debug (DBG_APPENDING | DBG_FINAL, & cpu_dev,
+ "Write2 (Actual) Write: bar iefpFinalAddress=%08o "
+ "writeData=%012"PRIo64" %012"PRIo64"\n",
+ address, data [0], data [1]);
+ HDBGMWrite (cpu.iefpFinalAddress, data[0]);
+ HDBGMWrite (cpu.iefpFinalAddress+1, data[1]);
+ }
+ else
+ {
+ cpu.iefpFinalAddress = do_append_cycle (cyctyp, data, 2);
+ sim_debug (DBG_APPENDING | DBG_FINAL, & cpu_dev,
+ "Write2 (Actual) Write: iefpFinalAddress=%08o "
+ "writeData=%012"PRIo64" %012"PRIo64"\n",
+ address, data [0], data [1]);
+ HDBGMWrite (cpu.iefpFinalAddress, data[0]);
+ HDBGMWrite (cpu.iefpFinalAddress+1, data[1]);
+ }
+ }
+ break;
+ }
+ return ;//SCPE_UNK;
+ }
+
+#ifdef CWO
+void Write1 (word18 address, word36 data, bool isAR)
+ {
+ cpu.TPR.CA = cpu.iefpFinalAddress = address;
+ bool isBAR = get_bar_mode ();
+ if (isAR || cpu.cu.XSF /*get_went_appending ()*/)
+ goto B29;
+ switch (get_addr_mode ())
+ {
+ case ABSOLUTE_mode:
+ {
+ if (isBAR)
+ {
+ cpu.iefpFinalAddress = get_BAR_address (address);
+ set_apu_status (apuStatus_FABS); // XXX maybe...
+ fauxDoAppendCycle (APU_DATA_STORE);
+ core_write (cpu.iefpFinalAddress, data, __func__);
+ sim_debug (DBG_FINAL, & cpu_dev,
+ "Write1(Actual) Write: bar address=%08o "
+ "writeData=%012"PRIo64"\n",
+ address, data);
+ HDBGMWrite (cpu.iefpFinalAddress, data);
+ return;
+ }
+ else
+ {
+ set_apu_status (apuStatus_FABS);
+ fauxDoAppendCycle (APU_DATA_STORE);
+ core_write (address, data, __func__);
+ sim_debug (DBG_FINAL, & cpu_dev,
+ "Write1(Actual) Write: abs address=%08o "
+ "writeData=%012"PRIo64"\n",
+ address, data);
+ HDBGMWrite (address, data);
+ return;
+ }
+ }
+
+ case APPEND_mode:
+ {
+B29:
+ if (isBAR)
+ {
+ cpu.TPR.CA = get_BAR_address (address);
+ cpu.TPR.TSR = cpu.PPR.PSR;
+ cpu.TPR.TRR = cpu.PPR.PRR;
+ cpu.iefpFinalAddress = do_append_cycle (APU_DATA_STORE, & data,
+ 1);
+ sim_debug (DBG_APPENDING | DBG_FINAL, & cpu_dev,
+ "Write8(Actual) Write: bar iefpFinalAddress="
+ "%08o writeData=%012"PRIo64"\n",
+ cpu.iefpFinalAddress, data);
+ HDBGMWrite (cpu.iefpFinalAddress, data);
+ return;
+ }
+ else
+ {
+ cpu.iefpFinalAddress = do_append_cycle (APU_DATA_STORE, & data,
+ 1);
+ sim_debug (DBG_APPENDING | DBG_FINAL, & cpu_dev,
+ "Write(Actual) Write: iefpFinalAddress=%08o "
+ "writeData=%012"PRIo64"\n",
+ cpu.iefpFinalAddress, data);
+ HDBGMWrite (cpu.iefpFinalAddress, data);
+ return;
+ }
+ }
+ }
+ return ;//SCPE_UNK;
+ }
+#endif
+
+void Write8 (word18 address, word36 * data, bool isAR)
+ {
+ address &= paragraphMask; // Round to 8 word boundarryt
+ cpu.TPR.CA = cpu.iefpFinalAddress = address;
+
+ bool isBAR = get_bar_mode ();
+
+ if (isAR || cpu.cu.XSF /*get_went_appending ()*/)
+ goto B29;
+
+
+ switch (get_addr_mode ())
+ {
+ case ABSOLUTE_mode:
+ {
+ if (isBAR)
+ {
+ cpu.iefpFinalAddress = get_BAR_address (address);
+ set_apu_status (apuStatus_FABS); // XXX maybe...
+ fauxDoAppendCycle (APU_DATA_STORE);
+ core_writeN (cpu.iefpFinalAddress, data, 8, __func__);
+ if_sim_debug (DBG_FINAL, & cpu_dev)
+ {
+ for (uint i = 0; i < 8; i ++)
+ sim_debug (DBG_FINAL, & cpu_dev,
+ "Write8(Actual) Write: bar address=%08o "
+ "writeData=%012"PRIo64"\n",
+ address + i, data [i]);
+ }
+#ifdef HDBG
+ for (uint i = 0; i < 8; i ++)
+ HDBGMWrite (cpu.iefpFinalAddress + i, data [i]);
+#endif
+ return;
+ }
+ else
+ {
+ set_apu_status (apuStatus_FABS);
+ fauxDoAppendCycle (APU_DATA_STORE);
+ core_writeN (address, data, 8, __func__);
+ if_sim_debug (DBG_FINAL, & cpu_dev)
+ {
+ for (uint i = 0; i < 8; i ++)
+ sim_debug (DBG_FINAL, & cpu_dev,
+ "Write8(Actual) Write: abs address=%08o "
+ "writeData=%012"PRIo64"\n",
+ address + i, data [i]);
+ }
+#ifdef HDBG
+ for (uint i = 0; i < 8; i ++)
+ HDBGMWrite (address + i, data [i]);
+#endif
+ return;
+ }
+ }
+
+ case APPEND_mode:
+ {
+B29:
+ if (isBAR)
+ {
+ cpu.TPR.CA = get_BAR_address (address);
+ cpu.TPR.TSR = cpu.PPR.PSR;
+ cpu.TPR.TRR = cpu.PPR.PRR;
+ cpu.iefpFinalAddress = do_append_cycle (APU_DATA_STORE, data,
+ 8);
+ if_sim_debug (DBG_APPENDING | DBG_FINAL, & cpu_dev)
+ {
+ for (uint i = 0; i < 8; i ++)
+ sim_debug (DBG_APPENDING | DBG_FINAL, & cpu_dev,
+ "Write8(Actual) Write: bar iefpFinalAddress="
+ "%08o writeData=%012"PRIo64"\n",
+ cpu.iefpFinalAddress + i, data [i]);
+ }
+#ifdef HDBG
+ for (uint i = 0; i < 8; i ++)
+ HDBGMWrite (cpu.iefpFinalAddress + i, data [i]);
+#endif
+
+ return;
+ }
+ else
+ {
+ cpu.iefpFinalAddress = do_append_cycle (APU_DATA_STORE, data,
+ 8);
+ if_sim_debug (DBG_APPENDING | DBG_FINAL, & cpu_dev)
+ {
+ for (uint i = 0; i < 8; i ++)
+ sim_debug (DBG_APPENDING | DBG_FINAL, & cpu_dev,
+ "Write8(Actual) Write: iefpFinalAddress=%08o "
+ "writeData=%012"PRIo64"\n",
+ cpu.iefpFinalAddress + i, data [i]);
+ }
+#ifdef HDBG
+ for (uint i = 0; i < 8; i ++)
+ HDBGMWrite (cpu.iefpFinalAddress + i, data [i]);
+#endif
+
+ return;
+ }
+ }
+ }
+ return ;//SCPE_UNK;
+ }
+
+void Write16 (word18 address, word36 * data)
+ {
+ address &= paragraphMask; // Round to 8 word boundary
+ Write8 (address, data, cpu.currentInstruction.b29);
+ Write8 (address + 8, data + 8, cpu.currentInstruction.b29);
+ return;
+ }
+
+void Write32 (word18 address, word36 * data)
+ {
+//#define paragraphMask 077777770
+ //address &= paragraphMask; // Round to 8 word boundary
+ address &= 077777740; // Round to 32 word boundary
+ Write8 (address, data, cpu.currentInstruction.b29);
+ Write8 (address + 8, data + 8, cpu.currentInstruction.b29);
+ Write8 (address + 16, data + 16, cpu.currentInstruction.b29);
+ Write8 (address + 24, data + 24, cpu.currentInstruction.b29);
+ return;
+ }
+
+void WritePage (word18 address, word36 * data, bool isAR)
+ {
+ if ((address & PGMK) != 0)
+ {
+ sim_warn ("WritePage not on boundary %06o\n", address);
+ }
+ address &= (word18) ~PGMK; // Round to page boundary
+ cpu.TPR.CA = cpu.iefpFinalAddress = address;
+
+ bool isBAR = get_bar_mode ();
+
+ if (isAR || cpu.cu.XSF /*get_went_appending ()*/)
+ goto B29;
+
+
+ switch (get_addr_mode ())
+ {
+ case ABSOLUTE_mode:
+ {
+ if (isBAR)
+ {
+ cpu.iefpFinalAddress = get_BAR_address (address);
+ set_apu_status (apuStatus_FABS); // XXX maybe...
+ fauxDoAppendCycle (APU_DATA_STORE);
+ core_writeN (cpu.iefpFinalAddress, data, PGSZ, __func__);
+ if_sim_debug (DBG_FINAL, & cpu_dev)
+ {
+ for (uint i = 0; i < PGSZ; i ++)
+ sim_debug (DBG_FINAL, & cpu_dev,
+ "WritePage(Actual) Write: bar address="
+ "%08o writeData=%012"PRIo64"\n",
+ address + i, data [i]);
+ }
+#ifdef HDBG
+ for (uint i = 0; i < PGSZ; i ++)
+ HDBGMWrite (cpu.iefpFinalAddress + i, data [i]);
+#endif
+ return;
+ }
+ else
+ {
+ set_apu_status (apuStatus_FABS);
+ fauxDoAppendCycle (APU_DATA_STORE);
+ core_writeN (address, data, PGSZ, __func__);
+ if_sim_debug (DBG_FINAL, & cpu_dev)
+ {
+ for (uint i = 0; i < PGSZ; i ++)
+ sim_debug (DBG_FINAL, & cpu_dev,
+ "WritePage(Actual) Write: abs address="
+ "%08o writeData=%012"PRIo64"\n",
+ address + i, data [i]);
+ }
+#ifdef HDBG
+ for (uint i = 0; i < PGSZ; i ++)
+ HDBGMWrite (address + i, data [i]);
+#endif
+ return;
+ }
+ }
+
+ case APPEND_mode:
+ {
+B29:
+ if (isBAR)
+ {
+ cpu.TPR.CA = get_BAR_address (address);
+ cpu.TPR.TSR = cpu.PPR.PSR;
+ cpu.TPR.TRR = cpu.PPR.PRR;
+ cpu.iefpFinalAddress = do_append_cycle (APU_DATA_STORE, data,
+ PGSZ);
+ if_sim_debug (DBG_APPENDING | DBG_FINAL, & cpu_dev)
+ {
+ for (uint i = 0; i < PGSZ; i ++)
+ sim_debug (DBG_APPENDING | DBG_FINAL, & cpu_dev,
+ "WritePage(Actual) Write: bar "
+ "iefpFinalAddress=%08o writeData=%012"PRIo64
+ "\n",
+ cpu.iefpFinalAddress + i, data [i]);
+ }
+ return;
+ }
+ else
+ {
+ cpu.iefpFinalAddress = do_append_cycle (APU_DATA_STORE, data,
+ PGSZ);
+ if_sim_debug (DBG_APPENDING | DBG_FINAL, & cpu_dev)
+ {
+ for (uint i = 0; i < PGSZ; i ++)
+ sim_debug (DBG_APPENDING | DBG_FINAL, & cpu_dev,
+ "WritePage(Actual) Write: iefpFinalAddress="
+ "%08o writeData=%012"PRIo64"\n",
+ cpu.iefpFinalAddress + i, data [i]);
+ }
+#ifdef HDBG
+ for (uint i = 0; i < PGSZ; i ++)
+ HDBGMWrite (cpu.iefpFinalAddress + i, data [i]);
+#endif
+
+ return;
+ }
+ }
+ }
+ return ;//SCPE_UNK;
+ }
+
+
+void ReadIndirect (void)
+ {
+ if (cpu.TPR.CA & 1) // is odd?
+ {
+ Read (cpu.TPR.CA, cpu.itxPair, INDIRECT_WORD_FETCH);
+ cpu.itxPair[1] = MASK36; // fill with ones for debugging
+ }
+ else
+ {
+ Read2 (cpu.TPR.CA, cpu.itxPair, INDIRECT_WORD_FETCH);
+ }
+ return;
+ }
+
--- /dev/null
+dps8_iefp.o dps8_iefp.d : dps8_iefp.c dps8.h ../simh-master/sim_defs.h \
+ ../simh-master/scp.h ../simh-master/sim_console.h \
+ ../simh-master/sim_timer.h ../simh-master/sim_fio.h \
+ ../simh-master/sim_tape.h dps8_simh.h dps8_math128.h dps8_hw_consts.h \
+ dps8_em_consts.h dps8_sys.h uvutil.h dps8_faults.h dps8_scu.h \
+ dps8_iom.h dps8_cable.h dps8_cpu.h hdbg.h dps8_append.h dps8_iefp.h \
+ dps8_addrmods.h dps8_utils.h
--- /dev/null
+/*
+ Copyright (c) 2007-2013 Michael Mondy
+ Copyright 2012-2016 by Harry Reed
+ Copyright 2013-2016 by Charles Anthony
+ Copyright 2015 by Eric Swenson
+
+ All rights reserved.
+
+ This software is made available under the terms of the
+ ICU License -- ICU 1.8.1 and later.
+ See the LICENSE file at the top-level directory of this distribution and
+ at https://sourceforge.net/p/dps8m/code/ci/master/tree/LICENSE
+ */
+
+void Read (word18 addr, word36 *dat, processor_cycle_type cyctyp);
+void Read2 (word18 addr, word36 *dat, processor_cycle_type cyctyp);
+void Write (word18 addr, word36 dat, processor_cycle_type cyctyp);
+void Write2 (word18 address, word36 * data, processor_cycle_type cyctyp);
+#ifdef CWO
+void Write1 (word18 address, word36 data, bool isAR);
+#endif
+void Write8 (word18 address, word36 * data, bool isAR);
+void Write16 (word18 address, word36 * data);
+void Write32 (word18 address, word36 * data);
+void Read8 (word18 address, word36 * result, bool isAR);
+void Read16 (word18 address, word36 * result);
+void WritePage (word18 address, word36 * data, bool isAR);
+void ReadPage (word18 address, word36 * result, bool isAR);
+void ReadIndirect (void);
--- /dev/null
+/*
+ Copyright (c) 2007-2013 Michael Mondy
+ Copyright 2012-2016 by Harry Reed
+ Copyright 2013-2016 by Charles Anthony
+ Copyright 2016 by Michal Tomek
+
+ All rights reserved.
+
+ This software is made available under the terms of the
+ ICU License -- ICU 1.8.1 and later.
+ See the LICENSE file at the top-level directory of this distribution and
+ at https://sourceforge.net/p/dps8m/code/ci/master/tree/LICENSE
+ */
+
+//#define ISOLTS_BITNO
+
+/**
+ * \file dps8_ins.c
+ * \project dps8
+ * \date 9/22/12
+ * \copyright Copyright (c) 2012 Harry Reed. All rights reserved.
+*/
+
+#include <stdio.h>
+
+#include "dps8.h"
+#include "dps8_addrmods.h"
+#include "dps8_sys.h"
+#include "dps8_faults.h"
+//#include "dps8_scu.h"
+//#include "dps8_iom.h"
+//#include "dps8_cable.h"
+#include "dps8_cpu.h"
+#include "dps8_append.h"
+#include "dps8_eis.h"
+#include "dps8_ins.h"
+#include "dps8_math.h"
+#include "dps8_opcodetable.h"
+#include "dps8_decimal.h"
+#include "dps8_iefp.h"
+#include "dps8_utils.h"
+
+#if defined(THREADZ) || defined(LOCKLESS)
+//#include "threadz.h"
+#endif
+
+#define DBG_CTR cpu.cycleCnt
+
+// Forward declarations
+
+static int doABSA (word36 * result);
+static t_stat doInstruction (void);
+#ifdef TESTING
+#if EMULATOR_ONLY
+static int emCall (void);
+#endif
+#endif
+
+#ifdef LOOPTRC
+void elapsedtime (void)
+ {
+ static bool init = false;
+ static struct timespec t0;
+ struct timespec now, delta;
+
+ if (! init)
+ {
+ init = true;
+ clock_gettime (CLOCK_REALTIME, & t0);
+ }
+ clock_gettime (CLOCK_REALTIME, & now);
+ timespec_diff (& t0, & now, & delta);
+ sim_printf ("%5ld.%03ld", delta.tv_sec, delta.tv_nsec/1000000);
+ }
+#endif
+
+// CANFAULT
+static void writeOperands (void)
+{
+ char buf [256];
+ CPT (cpt2U, 0); // write operands
+ DCDstruct * i = & cpu.currentInstruction;
+
+ sim_debug (DBG_ADDRMOD, & cpu_dev,
+ "%s (%s):mne=%s flags=%x\n",
+ __func__, disassemble (buf, IWB_IRODD), i->info->mne, i->info->flags);
+
+ PNL (cpu.prepare_state |= ps_RAW);
+
+ word6 rTAG = 0;
+ if (! (i->info->flags & NO_TAG))
+ rTAG = GET_TAG (cpu.cu.IWB);
+ word6 Td = GET_TD (rTAG);
+ word6 Tm = GET_TM (rTAG);
+
+//
+// IT CI/SC/SCR
+//
+
+ if (Tm == TM_IT && (Td == IT_CI || Td == IT_SC || Td == IT_SCR))
+ {
+ //
+ // Put the character into the data word
+ //
+
+#ifdef LOCKLESS
+ word36 tmpdata;
+ core_read(cpu.char_word_address, &tmpdata, __func__);
+ if (tmpdata != cpu.ou.character_data)
+ sim_warn("write char: data changed from %llo to %llo at %o\n", cpu.ou.character_data, tmpdata, cpu.char_word_address);
+#endif
+
+ switch (cpu.ou.characterOperandSize)
+ {
+ case TB6:
+ putChar (& cpu.ou.character_data, cpu.CY & 077, cpu.ou.characterOperandOffset);
+ break;
+
+ case TB9:
+ putByte (& cpu.ou.character_data, cpu.CY & 0777, cpu.ou.characterOperandOffset);
+ break;
+ }
+
+ //
+ // Write it
+ //
+
+ PNL (cpu.prepare_state |= ps_SAW);
+
+#ifdef LOCKLESSXXX
+ // gives warnings as another lock is aquired in between
+ core_write_unlock (cpu.char_word_address, cpu.ou.character_data, __func__);
+#else
+ Write (cpu.ou.character_address, cpu.ou.character_data, OPERAND_STORE);
+#endif
+
+ sim_debug (DBG_ADDRMOD, & cpu_dev,
+ "%s IT wrote char/byte %012"PRIo64" to %06o "
+ "tTB=%o tCF=%o\n",
+ __func__, cpu.ou.character_data, cpu.ou.character_address,
+ cpu.ou.characterOperandSize, cpu.ou.characterOperandOffset);
+
+ // Restore the CA; Read/Write() updates it.
+ //cpu.TPR.CA = indwordAddress;
+ cpu.TPR.CA = cpu.ou.character_address;
+ return;
+ } // IT
+
+ write_operand (cpu.TPR.CA, OPERAND_STORE);
+
+ return;
+}
+
+// CANFAULT
+static void readOperands (void)
+{
+ char buf [256];
+ CPT (cpt2U, 3); // read operands
+ DCDstruct * i = & cpu.currentInstruction;
+
+ sim_debug (DBG_ADDRMOD, &cpu_dev,
+ "%s (%s):mne=%s flags=%x\n",
+ __func__, disassemble (buf, cpu.cu.IWB), i->info->mne, i->info->flags);
+ sim_debug (DBG_ADDRMOD, &cpu_dev,
+ "%s a %d address %08o\n", __func__, i->b29, cpu.TPR.CA);
+
+ PNL (cpu.prepare_state |= ps_POA);
+
+ word6 rTAG = 0;
+ if (! (i->info->flags & NO_TAG))
+ rTAG = GET_TAG (cpu.cu.IWB);
+ word6 Td = GET_TD (rTAG);
+ word6 Tm = GET_TM (rTAG);
+
+//
+// DU
+//
+
+ if (Tm == TM_R && Td == TD_DU)
+ {
+ cpu.CY = 0;
+ SETHI (cpu.CY, cpu.TPR.CA);
+ sim_debug (DBG_ADDRMOD, & cpu_dev,
+ "%s DU CY=%012"PRIo64"\n", __func__, cpu.CY);
+ return;
+ }
+
+//
+// DL
+//
+
+ if (Tm == TM_R && Td == TD_DL)
+ {
+ cpu.CY = 0;
+ SETLO (cpu.CY, cpu.TPR.CA);
+ sim_debug (DBG_ADDRMOD, & cpu_dev,
+ "%s DL CY=%012"PRIo64"\n", __func__, cpu.CY);
+ return;
+ }
+
+//
+// IT CI/SC/SCR
+//
+
+ if (Tm == TM_IT && (Td == IT_CI || Td == IT_SC || Td == IT_SCR))
+ {
+ //
+ // Get the character from the data word
+ //
+
+ switch (cpu.ou.characterOperandSize)
+ {
+ case TB6:
+ cpu.CY = GETCHAR (cpu.ou.character_data, cpu.ou.characterOperandOffset);
+ break;
+
+ case TB9:
+ cpu.CY = GETBYTE (cpu.ou.character_data, cpu.ou.characterOperandOffset);
+ break;
+ }
+
+ sim_debug (DBG_ADDRMOD, & cpu_dev,
+ "%s IT read operand %012"PRIo64" from"
+ " %06o char/byte=%"PRIo64"\n",
+ __func__, cpu.ou.character_data, cpu.ou.character_address, cpu.CY);
+
+ // Restore the CA; Read/Write() updates it.
+ cpu.TPR.CA = cpu.ou.character_address;
+ return;
+ } // IT
+
+#ifdef LOCKLESS
+ read_operand (cpu.TPR.CA, ((i->info->flags & RMW) == RMW) ? OPERAND_RMW : OPERAND_READ);
+#else
+ read_operand (cpu.TPR.CA, OPERAND_READ);
+#endif
+
+ return;
+ }
+
+static void read_tra_op (void)
+ {
+ if (cpu.TPR.CA & 1)
+ Read (cpu.TPR.CA, &cpu.CY, OPERAND_READ);
+ else
+ Read2 (cpu.TPR.CA, cpu.Ypair, OPERAND_READ);
+ if (! (get_addr_mode () == APPEND_mode || cpu.cu.TSN_VALID [0] ||
+ cpu.cu.XSF || cpu.currentInstruction.b29 /*get_went_appending ()*/))
+ {
+ if (cpu.currentInstruction.info->flags & TSPN_INS)
+ {
+ word3 n;
+ if (cpu.currentInstruction.opcode <= 0273)
+ n = (cpu.currentInstruction.opcode & 3);
+ else
+ n = (cpu.currentInstruction.opcode & 3) + 4;
+
+ // C(PPR.PRR) -> C(PRn.RNR)
+ // C(PPR.PSR) -> C(PRn.SNR)
+ // C(PPR.IC) -> C(PRn.WORDNO)
+ // 000000 -> C(PRn.BITNO)
+ cpu.PR[n].RNR = cpu.PPR.PRR;
+// According the AL39, the PSR is 'undefined' in absolute mode.
+// ISOLTS thinks means don't change the operand
+ if (get_addr_mode () == APPEND_mode)
+ cpu.PR[n].SNR = cpu.PPR.PSR;
+ cpu.PR[n].WORDNO = (cpu.PPR.IC + 1) & MASK18;
+ SET_PR_BITNO (n, 0);
+ HDBGRegPR (n);
+ }
+ cpu.PPR.IC = cpu.TPR.CA;
+ // ISOLTS 870-02f
+ //cpu.PPR.PSR = 0;
+ }
+ sim_debug (DBG_TRACE, & cpu_dev, "%s %05o:%06o\n",
+ __func__, cpu.PPR.PSR, cpu.PPR.IC);
+ if (cpu.PPR.IC & 1)
+ {
+ cpu.cu.IWB = cpu.CY;
+ cpu.cu.IRODD = cpu.CY;
+ }
+ else
+ {
+ cpu.cu.IWB = cpu.Ypair[0];
+ cpu.cu.IRODD = cpu.Ypair[1];
+ }
+ }
+
+static void dump_words (word36 * words)
+ {
+ sim_debug (DBG_FAULT, & cpu_dev, "CU: P %d IR %#o PSR %0#o IC %0#o TSR %0#o\n",
+ getbits36_1 (words[0], 18), getbits36_18 (words[4], 18),
+ getbits36_15 (words[0], 3), getbits36_18 (words[4], 0), getbits36_15 (words[2], 3));
+ sim_debug (DBG_FAULT, & cpu_dev, "CU: xsf %d rf %d rpt %d rd %d rl %d pot %d xde %d xdo %d itp %d rfi %d its %d fif %d hold %0#o\n",
+ getbits36_1 (words[0], 19),
+ getbits36_1 (words[5], 18), getbits36_1 (words[5], 19), getbits36_1 (words[5], 20), getbits36_1 (words[5], 21),
+ getbits36_1 (words[5], 22), getbits36_1 (words[5], 24), getbits36_1 (words[5], 25), getbits36_1 (words[5], 26),
+ getbits36_1 (words[5], 27), getbits36_1 (words[5], 28), getbits36_1 (words[5], 29), getbits36_6 (words[5], 30));
+ sim_debug (DBG_FAULT, & cpu_dev, "CU: iwb %012"PRIo64" irodd %012"PRIo64"\n",
+ words[6], words[7]);
+ }
+
+static void scu2words (word36 *words)
+ {
+ CPT (cpt2U, 6); // scu2words
+ memset (words, 0, 8 * sizeof (* words));
+
+ // words[0]
+
+ putbits36_3 (& words[0], 0, cpu.PPR.PRR);
+ putbits36_15 (& words[0], 3, cpu.PPR.PSR);
+ putbits36_1 (& words[0], 18, cpu.PPR.P);
+ putbits36_1 (& words[0], 19, cpu.cu.XSF);
+ // 20, 1 SDWAMM Match on SDWAM
+ putbits36_1 (& words[0], 21, cpu.cu.SD_ON);
+ // 22, 1 PTWAMM Match on PTWAM
+ putbits36_1 (& words[0], 23, cpu.cu.PT_ON);
+#if 0
+ putbits36_1 (& words[0], 24, cpu.cu.PI_AP); // 24 PI-AP
+ putbits36_1 (& words[0], 25, cpu.cu.DSPTW); // 25 DSPTW
+ putbits36_1 (& words[0], 26, cpu.cu.SDWNP); // 26 SDWNP
+ putbits36_1 (& words[0], 27, cpu.cu.SDWP); // 27 SDWP
+ putbits36_1 (& words[0], 28, cpu.cu.PTW); // 28 PTW
+ putbits36_1 (& words[0], 29, cpu.cu.PTW2); // 29 PTW2
+ putbits36_1 (& words[0], 30, cpu.cu.FAP); // 30 FAP
+ putbits36_1 (& words[0], 31, cpu.cu.FANP); // 31 FANP
+ putbits36_1 (& words[0], 32, cpu.cu.FABS); // 32 FABS
+#else
+ // XXX Only the top 9 bits are used in APUCycleBits, so this is
+ // zeroing the 3 FTC bits at the end of the word; on the
+ // other hand this keeps the values in apuStatusBits clearer.
+ // If FTC is ever used, be sure to put it's save code after this
+ // line.
+ putbits36_12 (& words[0], 24, cpu.cu.APUCycleBits);
+#endif
+
+ // words[1]
+
+ putbits36_1 (& words[1], 0, cpu.cu.IRO_ISN);
+ putbits36_1 (& words[1], 1, cpu.cu.OEB_IOC);
+ putbits36_1 (& words[1], 2, cpu.cu.EOFF_IAIM);
+ putbits36_1 (& words[1], 3, cpu.cu.ORB_ISP);
+ putbits36_1 (& words[1], 4, cpu.cu.ROFF_IPR);
+ putbits36_1 (& words[1], 5, cpu.cu.OWB_NEA);
+ putbits36_1 (& words[1], 6, cpu.cu.WOFF_OOB);
+ putbits36_1 (& words[1], 7, cpu.cu.NO_GA);
+ putbits36_1 (& words[1], 8, cpu.cu.OCB);
+ putbits36_1 (& words[1], 9, cpu.cu.OCALL);
+ putbits36_1 (& words[1], 10, cpu.cu.BOC);
+ putbits36_1 (& words[1], 11, cpu.cu.PTWAM_ER);
+ putbits36_1 (& words[1], 12, cpu.cu.CRT);
+ putbits36_1 (& words[1], 13, cpu.cu.RALR);
+ putbits36_1 (& words[1], 14, cpu.cu.SDWAM_ER);
+ putbits36_1 (& words[1], 15, cpu.cu.OOSB);
+ putbits36_1 (& words[1], 16, cpu.cu.PARU);
+ putbits36_1 (& words[1], 17, cpu.cu.PARL);
+ putbits36_1 (& words[1], 18, cpu.cu.ONC1);
+ putbits36_1 (& words[1], 19, cpu.cu.ONC2);
+ putbits36_4 (& words[1], 20, cpu.cu.IA);
+ putbits36_3 (& words[1], 24, cpu.cu.IACHN);
+ putbits36_3 (& words[1], 27, cpu.cu.CNCHN);
+ putbits36_5 (& words[1], 30, cpu.cu.FI_ADDR);
+ putbits36_1 (& words[1], 35, cpu.cycle == INTERRUPT_cycle ? 0 : 1);
+
+ // words[2]
+
+ putbits36_3 (& words[2], 0, cpu.TPR.TRR);
+ putbits36_15 (& words[2], 3, cpu.TPR.TSR);
+ // 18, 4 PTWAM levels enabled
+ // 22, 4 SDWAM levels enabled
+ // 26, 1 0
+ putbits36_3 (& words[2], 27, (word3) cpu.switches.cpu_num);
+ putbits36_6 (& words[2], 30, cpu.cu.delta);
+
+ // words[3]
+
+ putbits36_3 (& words[3], 18, cpu.cu.TSN_VALID[0] ? cpu.cu.TSN_PRNO[0] : 0);
+ putbits36_1 (& words[3], 21, cpu.cu.TSN_VALID[0]);
+ putbits36_3 (& words[3], 22, cpu.cu.TSN_VALID[1] ? cpu.cu.TSN_PRNO[1] : 0);
+ putbits36_1 (& words[3], 25, cpu.cu.TSN_VALID[1]);
+ putbits36_3 (& words[3], 26, cpu.cu.TSN_VALID[2] ? cpu.cu.TSN_PRNO[2] : 0);
+ putbits36_1 (& words[3], 29, cpu.cu.TSN_VALID[2]);
+ putbits36_6 (& words[3], 30, cpu.TPR.TBR);
+
+ // words[4]
+
+ putbits36_18 (& words[4], 0, cpu.PPR.IC);
+
+// According the AL39, the Hex Mode bit should be 0, but ISOLTS pas2 exec checks it; this code does not set it to zero and indicated by AL39.
+
+ putbits36_18 (& words[4], 18, cpu.cu.IR);
+
+ // ISOLTS 887 test-03a
+ // Adding this makes test03 hang instead of errorign;
+ // presumably it's stuck on some later test.
+ // An 'Add Delta' addressing mode will alter the TALLY bit;
+ // restore it.
+
+ // Breaks ISOLTS 768
+ //putbits36_1 (& words[4], 25, cpu.currentInstruction.stiTally);
+
+#ifdef ISOLTS
+//testing for ipr fault by attempting execution of
+//the illegal opcode 000 and bit 27 not set
+//in privileged-append-bar mode.
+//
+//expects ABS to be clear....
+//
+//testing for ipr fault by attempting execution of
+//the illegal opcode 000 and bit 27 not set
+//in absolute-bar mode.
+//
+//expects ABS to be set
+
+//if (cpu.PPR.P && TST_I_NBAR == 0) fails 101007 absolute-bar mode; s/b set
+//if (cpu.PPR.P == 0 && TST_I_NBAR == 0)
+//if (TST_I_NBAR == 0 && TST_I_ABS == 1) // If ABS BAR
+//{
+ //putbits36 (& words[4], 31, 1, 0);
+// putbits36 (& words[4], 31, 1, cpu.PPR.P ? 0 : 1);
+//if (current_running_cpu_idx)
+//sim_printf ("cleared ABS\n");
+//}
+#endif
+
+ // words[5]
+
+ putbits36 (& words[5], 0, 18, cpu.TPR.CA);
+ putbits36 (& words[5], 18, 1, cpu.cu.repeat_first);
+ putbits36 (& words[5], 19, 1, cpu.cu.rpt);
+ putbits36 (& words[5], 20, 1, cpu.cu.rd);
+ putbits36 (& words[5], 21, 1, cpu.cu.rl);
+ putbits36 (& words[5], 22, 1, cpu.cu.pot);
+ // 23, 1 PON Prepare operand no tally
+ putbits36_1 (& words[5], 24, cpu.cu.xde);
+ putbits36_1 (& words[5], 25, cpu.cu.xdo);
+ putbits36_1 (& words[5], 26, cpu.cu.itp);
+ putbits36_1 (& words[5], 27, cpu.cu.rfi);
+ putbits36_1 (& words[5], 28, cpu.cu.its);
+ putbits36_1 (& words[5], 29, cpu.cu.FIF);
+ putbits36_6 (& words[5], 30, cpu.cu.CT_HOLD);
+
+ // words[6]
+
+ words[6] = cpu.cu.IWB;
+
+ // words[7]
+
+ words[7] = cpu.cu.IRODD;
+//sim_printf ("scu2words %lld %012llo\n", cpu.cycleCnt, words [6]);
+
+ if_sim_debug (DBG_FAULT, & cpu_dev)
+ dump_words (words);
+
+#ifdef ISOLTS
+ if (current_running_cpu_idx != 0)
+ {
+ struct
+ {
+ word36 should_be[8];
+ word36 was[8];
+ char *name;
+ }
+ rewrite_table[] =
+ {
+ { { 0000001400021, 0000000000011, 0000001000100, 0000000000000, 0000016400000, 0110015000500, 0110015011000, 0110015011000 },
+ { 0000001400011, 0000000000011, 0000001000100, 0000000000000, 0000016400000, 0110015000100, 0110015011000, 0110015011000 },
+ "pa865 test-03a inhibit", // rfi
+ },
+ { { 0000000401001, 0000000000041, 0000001000100, 0000000000000, 0101175000220, 0000006000000, 0100006235100, 0100006235100 },
+ { 0000000601001, 0000000000041, 0000001000100, 0000000000000, 0101175000220, 0000006000000, 0100006235100, 0100006235100 },
+ "pa870 test-01a dir. fault",
+ },
+ { { 0000000451001, 0000000000041, 0000001000100, 0000000000000, 0000000200200, 0000003000000, 0200003716100, 0000005755000 },
+ { 0000000651001, 0000000000041, 0000001000100, 0000000000000, 0000000200200, 0000003000000, 0200003716100, 0000005755000 },
+ "pa885 test-05a xec inst",
+ },
+ { { 0000000451001, 0000000000041, 0000001000100, 0000000000000, 0000000200200, 0000002000000, 0200002717100, 0110002001000 },
+ { 0000000651001, 0000000000041, 0000001000100, 0000000000000, 0000000200200, 0000002000000, 0200002717100, 0110002001000 },
+ "pa885 test-05b xed inst",
+ },
+ { { 0000000451001, 0000000000041, 0000001000100, 0000000000000, 0000000200200, 0000004004000, 0200004235100, 0000005755000 },
+ { 0000000451001, 0000000000041, 0000001000100, 0000000000000, 0000000200200, 0000004002000, 0200004235100, 0000005755000 },
+ "pa885 test-05c xed inst", // xde/xdo
+ },
+ { { 0000000451001, 0000000000041, 0000001000100, 0000000000000, 0000001200200, 0000004006000, 0200004235100, 0000005755000 },
+ { 0000000451001, 0000000000041, 0000001000100, 0000000000000, 0000001200200, 0000004002000, 0200004235100, 0000005755000 },
+ "pa885 test-05d xed inst", // xde/xdo
+ },
+ { { 0000000454201, 0000000000041, 0000000000100, 0000000000000, 0001777200200, 0002000000500, 0005600560201, 0005600560201 },
+ { 0000000450201, 0000000000041, 0000000000100, 0000000000000, 0001777200200, 0002000000000, 0005600560201, 0005600560201 },
+ "pa885 test-06a rpd inst", // rfi/fif
+ },
+ { { 0000000451001, 0000000000041, 0000001000101, 0000000000000, 0002000200200, 0000003500001, 0200003235111, 0002005755012 },
+ { 0000000651001, 0000000000041, 0000001000101, 0000000000000, 0002000202200, 0000003500000, 0200003235111, 0002005755012 },
+ "pa885 test-06b rpd inst", // tro ct-hold
+ },
+ { { 0000000450201, 0000000000041, 0000000000101, 0000000000000, 0001776200200, 0002015500001, 0002015235031, 0002017755032 },
+ { 0000000450201, 0000000000041, 0000000000101, 0000000000000, 0001776202200, 0002015500000, 0002015235031, 0002017755032 },
+ "pa885 test-06c rpd inst", // tro ct-hold
+ },
+ { { 0000000450201, 0000000000041, 0000000000101, 0000000000000, 0001776000200, 0002000100012, 0001775235011, 0001775755012 },
+ { 0000000450201, 0000000000041, 0000000000101, 0000000000000, 0001776000200, 0002000100000, 0001775235011, 0001775755012 },
+ "pa885 test-06d rpd inst", // ct-hold
+ },
+ { { 0000000404202, 0000000000041, 0000000000100, 0000000000000, 0002000202200, 0002000000500, 0001773755000, 0001773755000 },
+ { 0000000400202, 0000000000041, 0000000000100, 0000000000000, 0002000202200, 0002000000100, 0001773755000, 0001773755000 },
+ "pa885 test-10a scu snap (acv fault)", // rfi
+ }
+ };
+ int i;
+ for (i=0; i < 11; i++)
+ {
+ if (memcmp (words, rewrite_table[i].was, 8*sizeof (word36)) == 0)
+ {
+ memcpy (words, rewrite_table[i].should_be, 8*sizeof (word36));
+ sim_warn("%s: scu rewrite %d: %s\n", __func__, i, rewrite_table[i].name);
+ break;
+ }
+ }
+ }
+#endif
+
+ }
+
+
+void cu_safe_store (void)
+{
+ // Save current Control Unit Data in hidden temporary so a later SCU
+ // instruction running in FAULT mode can save the state as it existed at
+ // the time of the fault rather than as it exists at the time the SCU
+ // instruction is executed.
+ scu2words (cpu.scu_data);
+
+ cpu.cu_data.PSR = cpu.PPR.PSR;
+ cpu.cu_data.PRR = cpu.PPR.PRR;
+ cpu.cu_data.IC = cpu.PPR.IC;
+
+ tidy_cu ();
+
+}
+
+void tidy_cu (void)
+ {
+// The only places this is called is in fault and interrupt processing;
+// once the CU is saved, it needs to be set to a usable state. Refactoring
+// that code here so that there is only a single copy to maintain.
+
+ cpu.cu.delta = 0;
+ cpu.cu.repeat_first = false;
+ cpu.cu.rpt = false;
+ cpu.cu.rd = false;
+ cpu.cu.rl = false;
+ cpu.cu.pot = false;
+ cpu.cu.itp = false;
+ cpu.cu.its = false;
+ cpu.cu.xde = false;
+ cpu.cu.xdo = false;
+ }
+
+static void words2scu (word36 * words)
+{
+ CPT (cpt2U, 7); // words2scu
+ // BUG: We don't track much of the data that should be tracked
+
+ // words[0]
+
+ cpu.PPR.PRR = getbits36_3 (words[0], 0);
+ cpu.PPR.PSR = getbits36_15 (words[0], 3);
+ cpu.PPR.P = getbits36_1 (words[0], 18);
+ cpu.cu.XSF = getbits36_1 (words[0], 19);
+sim_debug (DBG_TRACEEXT, & cpu_dev, "%s sets XSF to %o\n", __func__, cpu.cu.XSF);
+ //cpu.cu.SDWAMM = getbits36_1 (words[0], 20);
+ //cpu.cu.SD_ON = getbits36_1 (words[0], 21);
+ //cpu.cu.PTWAMM = getbits36_1 (words[0], 22);
+ //cpu.cu.PT_ON = getbits36_1 (words[0], 23);
+#if 0
+ //cpu.cu.PI_AP = getbits36_1 (words[0], 24);
+ //cpu.cu.DSPTW = getbits36_1 (words[0], 25);
+ //cpu.cu.SDWNP = getbits36_1 (words[0], 26);
+ //cpu.cu.SDWP = getbits36_1 (words[0], 27);
+ //cpu.cu.PTW = getbits36_1 (words[0], 28);
+ //cpu.cu.PTW2 = getbits36_1 (words[0], 29);
+ //cpu.cu.FAP = getbits36_1 (words[0], 30);
+ //cpu.cu.FANP = getbits36_1 (words[0], 31);
+ //cpu.cu.FABS = getbits36_1 (words[0], 32);
+#else
+ //cpu.cu.APUCycleBits = getbits36_12 (words[0], 24);
+#endif
+ // The FCT is stored in APUCycleBits
+ cpu.cu.APUCycleBits = (word12) ((cpu.cu.APUCycleBits & 07770) | (word12) getbits36_3 (words[0], 33));
+
+ // words[1]
+
+#if 0
+ cpu.cu.IRO_ISN = getbits36_1 (words[1], 0);
+ cpu.cu.OEB_IOC = getbits36_1 (words[1], 1);
+ cpu.cu.EOFF_IAIM = getbits36_1 (words[1], 2);
+ cpu.cu.ORB_ISP = getbits36_1 (words[1], 3);
+ cpu.cu.ROFF_IPR = getbits36_1 (words[1], 4);
+ cpu.cu.OWB_NEA = getbits36_1 (words[1], 5);
+ cpu.cu.WOFF_OOB = getbits36_1 (words[1], 6);
+ cpu.cu.NO_GA = getbits36_1 (words[1], 7);
+ cpu.cu.OCB = getbits36_1 (words[1], 8);
+ cpu.cu.OCALL = getbits36_1 (words[1], 9);
+ cpu.cu.BOC = getbits36_1 (words[1], 10);
+ cpu.cu.PTWAM_ER = getbits36_1 (words[1], 11);
+ cpu.cu.CRT = getbits36_1 (words[1], 12);
+ cpu.cu.RALR = getbits36_1 (words[1], 13);
+ cpu.cu.SDWAM_ER = getbits36_1 (words[1], 14);
+ cpu.cu.OOSB = getbits36_1 (words[1], 15);
+ cpu.cu.PARU = getbits36_1 (words[1], 16);
+ cpu.cu.PARL = getbits36_1 (words[1], 17);
+ cpu.cu.ONC1 = getbits36_1 (words[1], 18);
+ cpu.cu.ONC2 = getbits36_1 (words[1], 19);
+ cpu.cu.IA = getbits36_4 (words[1], 20);
+ cpu.cu.IACHN = getbits36_3 (words[1], 24);
+ cpu.cu.CNCHN = getbits36_3 (words[1], 27);
+ cpu.cu.FI_ADDR = getbits36_5 (words[1], 30);
+ cpu.cu.FLT_INT = getbits36_1 (words[1], 35);
+#endif
+
+ // words[2]
+
+ cpu.TPR.TRR = getbits36_3 (words[2], 0);
+ cpu.TPR.TSR = getbits36_15 (words[2], 3);
+ // 18-21 PTW
+ // 22-25 SDW
+ // 26 0
+ // 27-29 CPU number
+ cpu.cu.delta = getbits36_6 (words[2], 30);
+
+ // words[3]
+
+ // 0-17 0
+
+ cpu.cu.TSN_PRNO[0] = getbits36_3 (words[3], 18);
+ cpu.cu.TSN_VALID[0] = getbits36_1 (words[3], 21);
+ cpu.cu.TSN_PRNO[1] = getbits36_3 (words[3], 22);
+ cpu.cu.TSN_VALID[1] = getbits36_1 (words[3], 25);
+ cpu.cu.TSN_PRNO[2] = getbits36_3 (words[3], 26);
+ cpu.cu.TSN_VALID[2] = getbits36_1 (words[3], 29);
+ cpu.TPR.TBR = getbits36_6 (words[3], 30);
+
+ // words[4]
+
+ cpu.cu.IR = getbits36_18 (words[4], 18); // HWR
+ cpu.PPR.IC = getbits36_18 (words[4], 0);
+
+ // words[5]
+
+// AL39 pg 75, RCU does not restore CA
+ //cpu.TPR.CA = getbits36_18 (words[5], 0);
+ cpu.cu.repeat_first = getbits36_1 (words[5], 18);
+ cpu.cu.rpt = getbits36_1 (words[5], 19);
+ cpu.cu.rd = getbits36_1 (words[5], 20);
+ cpu.cu.rl = getbits36_1 (words[5], 21);
+ cpu.cu.pot = getbits36_1 (words[5], 22);
+ // 23 PON
+ cpu.cu.xde = getbits36_1 (words[5], 24);
+ cpu.cu.xdo = getbits36_1 (words[5], 25);
+ cpu.cu.itp = getbits36_1 (words[5], 26);
+ cpu.cu.rfi = getbits36_1 (words[5], 27);
+ cpu.cu.its = getbits36_1 (words[5], 28);
+ cpu.cu.FIF = getbits36_1 (words[5], 29);
+ cpu.cu.CT_HOLD = getbits36_6 (words[5], 30);
+
+ // words[6]
+
+ cpu.cu.IWB = words[6];
+
+ // words[7]
+
+ cpu.cu.IRODD = words[7];
+}
+
+void cu_safe_restore (void)
+ {
+ words2scu (cpu.scu_data);
+ decode_instruction (IWB_IRODD, & cpu.currentInstruction);
+ }
+
+static void du2words (word36 * words)
+ {
+ CPT (cpt2U, 7); // du2words
+
+#ifdef ISOLTS
+ for (int i = 0; i < 8; i ++)
+ {
+ words[i] = cpu.du.image[i];
+ }
+#else
+ memset (words, 0, 8 * sizeof (* words));
+#endif
+ // Word 0
+
+ putbits36_1 (& words[0], 9, cpu.du.Z);
+ putbits36_1 (& words[0], 10, cpu.du.NOP);
+ putbits36_24 (& words[0], 12, cpu.du.CHTALLY);
+
+ // Word 1
+
+#ifdef ISOLTS
+ words[1] = words[0];
+#endif
+ // Word 2
+
+ putbits36_18 (& words[2], 0, cpu.du.D1_PTR_W);
+ putbits36_6 (& words[2], 18, cpu.du.D1_PTR_B);
+ putbits36_2 (& words[2], 25, cpu.du.TAk[0]);
+ putbits36_1 (& words[2], 31, cpu.du.F1);
+ putbits36_1 (& words[2], 32, cpu.du.Ak[0]);
+
+ // Word 3
+
+ putbits36_10 (& words[3], 0, cpu.du.LEVEL1);
+ putbits36_24 (& words[3], 12, cpu.du.D1_RES);
+
+ // Word 4
+
+ putbits36_18 (& words[4], 0, cpu.du.D2_PTR_W);
+ putbits36_6 (& words[4], 18, cpu.du.D2_PTR_B);
+ putbits36_2 (& words[4], 25, cpu.du.TAk[1]);
+ putbits36_1 (& words[4], 30, cpu.du.R);
+ putbits36_1 (& words[4], 31, cpu.du.F2);
+ putbits36_1 (& words[4], 32, cpu.du.Ak[1]);
+
+ // Word 5
+
+ putbits36_10 (& words[5], 0, cpu.du.LEVEL2);
+ putbits36_24 (& words[5], 12, cpu.du.D2_RES);
+
+ // Word 6
+
+ putbits36_18 (& words[6], 0, cpu.du.D3_PTR_W);
+ putbits36_6 (& words[6], 18, cpu.du.D3_PTR_B);
+ putbits36_2 (& words[6], 25, cpu.du.TAk[2]);
+ putbits36_1 (& words[6], 31, cpu.du.F3);
+ putbits36_1 (& words[6], 32, cpu.du.Ak[2]);
+ putbits36_3 (& words[6], 33, cpu.du.JMP);
+
+ // Word 7
+
+ putbits36_24 (& words[7], 12, cpu.du.D3_RES);
+
+ }
+
+static void words2du (word36 * words)
+ {
+ CPT (cpt2U, 8); // words2du
+ // Word 0
+
+ cpu.du.Z = getbits36_1 (words[0], 9);
+ cpu.du.NOP = getbits36_1 (words[0], 10);
+ cpu.du.CHTALLY = getbits36_24 (words[0], 12);
+ // Word 1
+
+ // Word 2
+
+ cpu.du.D1_PTR_W = getbits36_18 (words[2], 0);
+ cpu.du.D1_PTR_B = getbits36_6 (words[2], 18);
+ cpu.du.TAk[0] = getbits36_2 (words[2], 25);
+ cpu.du.F1 = getbits36_1 (words[2], 31);
+ cpu.du.Ak[0] = getbits36_1 (words[2], 32);
+
+ // Word 3
+
+ cpu.du.LEVEL1 = getbits36_10 (words[3], 0);
+ cpu.du.D1_RES = getbits36_24 (words[3], 12);
+
+ // Word 4
+
+ cpu.du.D2_PTR_W = getbits36_18 (words[4], 0);
+ cpu.du.D2_PTR_B = getbits36_6 (words[4], 18);
+ cpu.du.TAk[1] = getbits36_2 (words[4], 25);
+ cpu.du.F2 = getbits36_1 (words[4], 31);
+ cpu.du.Ak[1] = getbits36_1 (words[4], 32);
+
+ // Word 5
+
+ cpu.du.LEVEL2 = getbits36_1 (words[5], 9);
+ cpu.du.D2_RES = getbits36_24 (words[5], 12);
+
+ // Word 6
+
+ cpu.du.D3_PTR_W = getbits36_18 (words[6], 0);
+ cpu.du.D3_PTR_B = getbits36_6 (words[6], 18);
+ cpu.du.TAk[2] = getbits36_2 (words[6], 25);
+ cpu.du.F3 = getbits36_1 (words[6], 31);
+ cpu.du.Ak[2] = getbits36_1 (words[6], 32);
+ cpu.du.JMP = getbits36_3 (words[6], 33);
+
+ // Word 7
+
+ cpu.du.D3_RES = getbits36_24 (words[7], 12);
+
+#ifdef ISOLTS
+ for (int i = 0; i < 8; i ++)
+ {
+ cpu.du.image[i] = words[i];
+ }
+#endif
+ }
+
+static char *PRalias[] = {"ap", "ab", "bp", "bb", "lp", "lb", "sp", "sb" };
+
+
+//=============================================================================
+
+// illegal modifications for various instructions
+
+/*
+
+ 00 01 02 03 04 05 06 07
+
+ 00 -- au qu du ic al ql dl R
+ 10 0 1 2 3 4 5 6 7
+
+ 20 n* au* qu* -- ic* al* al* -- RI
+ 30 0* 1* 2* 3* 4* 5* 6* 7*
+
+ 40 f1 itp -- its sd scr f2 f3 IT
+ 50 ci i sc ad di dic id idc
+
+ 60 *n *au *qu -- *ic *al *al -- IR
+ 70 *0 *1 *2 *3 *4 *5 *6 *7
+
+
+ bool _allowed[] = {
+ // Tm = 0 (register) R
+ // -- au qu du ic al ql dl
+ true, false, false, false, false, false, false, false,
+ // 0 1 2 3 4 5 6 7
+ false, false, false, false, false, false, false, false,
+ // Tm = 1 (register then indirect) RI
+ // n* au* qu* -- ic* al* al* --
+ false, false, false, true, false, false, false, true,
+ // 0* 1* 2* 3* 4* 5* 6* 7*
+ false, false, false, false, false, false, false, false,
+ // Tm = 2 (indirect then tally) IT
+ // f1 itp -- its sd scr f2 f3
+ false, false, true, false, false, false, false, false,
+ // ci i sc ad di dic id idc
+ false, false, false, false, false, false, false, false,
+ // Tm = 3 (indirect then register) IR
+ // *n *au *qu -- *ic *al *al --
+ false, false, false, true, false, false, false, true,
+ // *0 *1 *2 *3 *4 *5 *6 *7
+ false, false, false, false, false, false, false, false,
+ };
+
+ */
+// No DUDL
+
+static bool _nodudl[] = {
+ // Tm = 0 (register) R
+ // -- au qu du ic al ql dl
+ false, false, false, true, false, false, false, true,
+ // 0 1 2 3 4 5 6 7
+ false, false, false, false, false, false, false, false,
+ // Tm = 1 (register then indirect) RI
+ // n* au* qu* -- ic* al* al* --
+ false, false, false, true, false, false, false, true,
+ // 0* 1* 2* 3* 4* 5* 6* 7*
+ false, false, false, false, false, false, false, false,
+ // Tm = 2 (indirect then tally) IT
+ // f1 itp -- its sd scr f2 f3
+ false, false, true, false, false, false, false, false,
+ // ci i sc ad di dic id idc
+ false, false, false, false, false, false, false, false,
+ // Tm = 3 (indirect then register) IR
+ // *n *au *qu -- *ic *al *al --
+ false, false, false, true, false, false, false, true,
+ // *0 *1 *2 *3 *4 *5 *6 *7
+ false, false, false, false, false, false, false, false,
+};
+
+// No DU
+// No DL
+
+
+
+// (NO_CI | NO_SC | NO_SCR)
+static bool _nocss[] = {
+ // Tm = 0 (register) R
+ // * au qu du ic al ql dl
+ false, false, false, false, false, false, false, false,
+ // 0 1 2 3 4 5 6 7
+ false, false, false, false, false, false, false, false,
+ // Tm = 1 (register then indirect) RI
+ // n* au* qu* -- ic* al* al* --
+ false, false, false, true, false, false, false, true,
+ // 0* 1* 2* 3* 4* 5* 6* 7*
+ false, false, false, false, false, false, false, false,
+ // Tm = 2 (indirect then tally) IT
+ // f1 itp -- its sd scr f2 f3
+ false, false, true, false, false, true, false, false,
+ // ci i sc ad di dic id idc
+ true, false, true, false, false, false, false, false,
+ // Tm = 3 (indirect then register) IR
+ // *n *au *qu -- *ic *al *al --
+ false, false, false, true, false, false, false, true,
+ // *0 *1 *2 *3 *4 *5 *6 *7
+ false, false, false, false, false, false, false, false,
+};
+
+// (NO_DUDL | NO_CISCSCR)
+static bool _noddcss[] = {
+ // Tm = 0 (register) R
+ // * au qu du ic al ql dl
+ false, false, false, true, false, false, false, true,
+ // 0 1 2 3 4 5 6 7
+ false, false, false, false, false, false, false, false,
+ // Tm = 1 (register then indirect) RI
+ // n* au* qu* -- ic* al* al* --
+ false, false, false, true, false, false, false, true,
+ // 0* 1* 2* 3* 4* 5* 6* 7*
+ false, false, false, false, false, false, false, false,
+ // Tm = 2 (indirect then tally) IT
+ // f1 itp -- its sd scr f2 f3
+ false, false, true, false, false, true, false, false,
+ // ci i sc ad di dic id idc
+ true, false, true, false, false, false, false, false,
+ // Tm = 3 (indirect then register) IR
+ // *n *au *qu -- *ic *al *al --
+ false, false, false, true, false, false, false, true,
+ // *0 *1 *2 *3 *4 *5 *6 *7
+ false, false, false, false, false, false, false, false,
+};
+
+// (NO_DUDL | NO_CISCSCR)
+static bool _nodlcss[] = {
+ // Tm = 0 (register) R
+ // * au qu du ic al ql dl
+ false, false, false, false, false, false, false, true,
+ // 0 1 2 3 4 5 6 7
+ false, false, false, false, false, false, false, false,
+ // Tm = 1 (register then indirect) RI
+ // n* au* qu* -- ic* al* al* --
+ false, false, false, true, false, false, false, true,
+ // 0* 1* 2* 3* 4* 5* 6* 7*
+ false, false, false, false, false, false, false, false,
+ // Tm = 2 (indirect then tally) IT
+ // f1 itp -- its sd scr f2 f3
+ false, false, true, false, false, true, false, false,
+ // ci i sc ad di dic id idc
+ true, false, true, false, false, false, false, false,
+ // Tm = 3 (indirect then register) IR
+ // *n *au *qu -- *ic *al *al --
+ false, false, false, true, false, false, false, true,
+ // *0 *1 *2 *3 *4 *5 *6 *7
+ false, false, false, false, false, false, false, false,
+};
+
+static bool _onlyaqxn[] = {
+ // Tm = 0 (register) R
+ // -- au qu du ic al ql dl
+ false, false, false, true, true, false, false, true,
+ // 0 1 2 3 4 5 6 7
+ false, false, false, false, false, false, false, false,
+ // Tm = 1 (register then indirect) RI
+ // n* au* qu* -- ic* al* al* --
+ false, false, false, true, false, false, false, true,
+ // 0* 1* 2* 3* 4* 5* 6* 7*
+ false, false, false, false, false, false, false, false,
+ // Tm = 2 (indirect then tally) IT
+ // f1 itp -- its sd scr f2 f3
+ false, false, true, false, false, false, false, false,
+ // ci i sc ad di dic id idc
+ false, false, false, false, false, false, false, false,
+ // Tm = 3 (indirect then register) IR
+ // *n *au *qu -- *ic *al *al --
+ false, false, false, true, false, false, false, true,
+ // *0 *1 *2 *3 *4 *5 *6 *7
+ false, false, false, false, false, false, false, false,
+};
+
+#ifndef QUIET_UNUSED
+static bool _illmod[] = {
+ // Tm = 0 (register) R
+ // * au qu du ic al ql dl
+ false, false, false, false, false, false, false, false,
+ // 0 1 2 3 4 5 6 7
+ false, false, false, false, false, false, false, false,
+ // Tm = 1 (register then indirect) RI
+ // n* au* qu* -- ic* al* al* --
+ false, false, false, true, false, false, false, true,
+ // 0* 1* 2* 3* 4* 5* 6* 7*
+ false, false, false, false, false, false, false, false,
+ // Tm = 2 (indirect then tally) IT
+ // f1 itp -- its sd scr f2 f3
+ false, false, true, false, false, false, false, false,
+ // ci i sc ad di dic id idc
+ false, false, false, false, false, false, false, false,
+ // Tm = 3 (indirect then register) IR
+ // *n *au *qu -- *ic *al *al --
+ // *0 *1 *2 *3 *4 *5 *6 *7
+ false, false, false, true, false, false, false, true,
+ false, false, false, false, false, false, false, false,
+};
+#endif
+
+//=============================================================================
+
+#ifdef MATRIX
+
+static long long theMatrix[1024] // 1024 opcodes (2^10)
+ [2] // opcode extension
+ [2] // bit 29
+ [64]; // Tag
+
+void initializeTheMatrix (void)
+{
+ memset (theMatrix, 0, sizeof (theMatrix));
+}
+
+void addToTheMatrix (uint32 opcode, bool opcodeX, bool a, word6 tag)
+{
+ // safety
+ uint _opcode = opcode & 01777;
+ int _opcodeX = opcodeX ? 1 : 0;
+ int _a = a ? 1 : 0;
+ int _tag = tag & 077;
+ theMatrix[_opcode][_opcodeX][_a][_tag] ++;
+}
+
+t_stat display_the_matrix (UNUSED int32 arg, UNUSED const char * buf)
+{
+ long long count;
+
+ for (int opcode = 0; opcode < 01000; opcode ++)
+ for (int opcodeX = 0; opcodeX < 2; opcodeX ++)
+ {
+ long long total = 0;
+ for (int a = 0; a < 2; a ++)
+ for (int tag = 0; tag < 64; tag ++)
+ if ((count = theMatrix[opcode][opcodeX][a][tag]))
+ {
+ // disassemble doesn't quite do what we want so copy the good bits
+ static char result[132] = "???";
+ strcpy (result, "???");
+ // get mnemonic ...
+ if (opcodes10 [opcode | (opcodeX ? 01000 : 0)].mne)
+ strcpy (result, opcodes10[opcode | (opcodeX ? 01000 : 0)].mne);
+ if (a)
+ strcat (result, " prn|nnnn");
+ else
+ strcat (result, " nnnn");
+
+ // get mod
+ if (extMods[tag].mod)
+ {
+ strcat (result, ",");
+ strcat (result, extMods[tag].mod);
+ }
+ if (result[0] == '?')
+ sim_printf ("%20"PRId64": ? opcode 0%04o X %d a %d tag 0%02do\n",
+ count, opcode, opcodeX, a, tag);
+ else
+ sim_printf ("%20"PRId64": %s\n", count, result);
+ total += count;
+ }
+ static char result[132] = "???";
+ strcpy (result, "???");
+ if (total) {
+ // get mnemonic ...
+ if (opcodes10 [opcode | (opcodeX ? 01000 : 0)].mne)
+ strcpy (result, opcodes10[opcode | (opcodeX ? 01000 : 0)].mne);
+ sim_printf ("%20"PRId64": %s\n", total, result);
+ }
+ }
+ return SCPE_OK;
+}
+#endif
+
+
+// fetch instrcution at address
+// CANFAULT
+void fetchInstruction (word18 addr)
+{
+ CPT (cpt2U, 9); // fetchInstruction
+
+ if (get_addr_mode () == ABSOLUTE_mode)
+ {
+ cpu.TPR.TRR = 0;
+ cpu.RSDWH_R1 = 0;
+ //cpu.PPR.P = 1; // XXX this should be already set by set_addr_mode, so no worry here
+ }
+
+ if (cpu.cu.rd && ((cpu.PPR.IC & 1) != 0))
+ {
+ if (cpu.cu.repeat_first)
+ {
+ CPT (cpt2U, 10); // fetch rpt odd
+ //Read (addr, & cpu.cu.IRODD, INSTRUCTION_FETCH);
+ }
+ }
+ else if (cpu.cu.rpt || cpu.cu.rd || cpu.cu.rl)
+ {
+ if (cpu.cu.repeat_first)
+ {
+ CPT (cpt2U, 11); // fetch rpt even
+ if (addr & 1)
+ Read (addr, & cpu.cu.IWB, INSTRUCTION_FETCH);
+ else
+ {
+ word36 tmp[2];
+ Read2 (addr, tmp, INSTRUCTION_FETCH);
+ cpu.cu.IWB = tmp[0];
+ cpu.cu.IRODD = tmp[1];
+ }
+ }
+ }
+ else
+ {
+ CPT (cpt2U, 12); // fetch
+// ISOLTS test pa870 expects IRODD to be set up.
+// If we are fetching an even instruction, also fetch the odd.
+// If we are fetching an odd instruction, copy it to IRODD as
+// if that was where we got it from.
+ //Read (addr, & cpu.cu.IWB, INSTRUCTION_FETCH);
+ if ((cpu.PPR.IC & 1) == 0) // Even
+ {
+ word36 tmp[2];
+ Read2 (addr, tmp, INSTRUCTION_FETCH);
+ cpu.cu.IWB = tmp[0];
+ cpu.cu.IRODD = tmp[1];
+ }
+ else // Odd
+ {
+ Read (addr, & cpu.cu.IWB, INSTRUCTION_FETCH);
+ cpu.cu.IRODD = cpu.cu.IWB;
+ }
+ }
+}
+
+#ifdef TESTING
+void traceInstruction (uint flag)
+ {
+ char buf [256];
+ if (! flag) goto force;
+ if_sim_debug (flag, &cpu_dev)
+ {
+force:;
+ char * compname;
+ word18 compoffset;
+ char * where = lookup_address (cpu.PPR.PSR, cpu.PPR.IC, & compname,
+ & compoffset);
+ bool isBAR = TST_I_NBAR ? false : true;
+ if (where)
+ {
+ if (get_addr_mode () == ABSOLUTE_mode)
+ {
+ if (isBAR)
+ {
+ sim_debug (flag, &cpu_dev, "%06o|%06o %s\n",
+ cpu.BAR.BASE, cpu.PPR.IC, where);
+ }
+ else
+ {
+ sim_debug (flag, &cpu_dev, "%06o %s\n", cpu.PPR.IC, where);
+ }
+ }
+ else if (get_addr_mode () == APPEND_mode)
+ {
+ if (isBAR)
+ {
+ sim_debug (flag, &cpu_dev, "%05o:%06o|%06o %s\n",
+ cpu.PPR.PSR,
+ cpu.BAR.BASE, cpu.PPR.IC, where);
+ }
+ else
+ {
+ sim_debug (flag, &cpu_dev, "%05o:%06o %s\n",
+ cpu.PPR.PSR, cpu.PPR.IC, where);
+ }
+ }
+ list_source (compname, compoffset, flag);
+ }
+ if (get_addr_mode () == ABSOLUTE_mode)
+ {
+ if (isBAR)
+ {
+ sim_debug (flag, &cpu_dev,
+ "%d: "
+ "%05o|%06o %012"PRIo64" (%s) %06o %03o(%d) %o %o %o %02o\n",
+ current_running_cpu_idx,
+ cpu.BAR.BASE,
+ cpu.PPR.IC,
+ IWB_IRODD,
+ disassemble (buf, IWB_IRODD),
+ cpu.currentInstruction.address,
+ cpu.currentInstruction.opcode,
+ cpu.currentInstruction.opcodeX,
+ cpu.currentInstruction.b29,
+ cpu.currentInstruction.i,
+ GET_TM (cpu.currentInstruction.tag) >> 4,
+ GET_TD (cpu.currentInstruction.tag) & 017);
+ }
+ else
+ {
+ sim_debug (flag, &cpu_dev,
+ "%d: "
+ "%06o %012"PRIo64" (%s) %06o %03o(%d) %o %o %o %02o\n",
+ current_running_cpu_idx,
+ cpu.PPR.IC,
+ IWB_IRODD,
+ disassemble (buf, IWB_IRODD),
+ cpu.currentInstruction.address,
+ cpu.currentInstruction.opcode,
+ cpu.currentInstruction.opcodeX,
+ cpu.currentInstruction.b29,
+ cpu.currentInstruction.i,
+ GET_TM (cpu.currentInstruction.tag) >> 4,
+ GET_TD (cpu.currentInstruction.tag) & 017);
+ }
+ }
+ else if (get_addr_mode () == APPEND_mode)
+ {
+ if (isBAR)
+ {
+ sim_debug (flag, &cpu_dev,
+ "%d: "
+ "%05o:%06o|%06o %o %012"PRIo64" (%s) %06o %03o(%d) %o %o %o %02o\n",
+ current_running_cpu_idx,
+ cpu.PPR.PSR,
+ cpu.BAR.BASE,
+ cpu.PPR.IC,
+ cpu.PPR.PRR,
+ IWB_IRODD,
+ disassemble (buf, IWB_IRODD),
+ cpu.currentInstruction.address,
+ cpu.currentInstruction.opcode,
+ cpu.currentInstruction.opcodeX,
+ cpu.currentInstruction.b29, cpu.currentInstruction.i,
+ GET_TM (cpu.currentInstruction.tag) >> 4,
+ GET_TD (cpu.currentInstruction.tag) & 017);
+ }
+ else
+ {
+ sim_debug (flag, &cpu_dev,
+ "%d: "
+ "%05o:%06o %o %012"PRIo64" (%s) %06o %03o(%d) %o %o %o %02o\n",
+ current_running_cpu_idx,
+ cpu.PPR.PSR,
+ cpu.PPR.IC,
+ cpu.PPR.PRR,
+ IWB_IRODD,
+ disassemble (buf, IWB_IRODD),
+ cpu.currentInstruction.address,
+ cpu.currentInstruction.opcode,
+ cpu.currentInstruction.opcodeX,
+ cpu.currentInstruction.b29,
+ cpu.currentInstruction.i,
+ GET_TM (cpu.currentInstruction.tag) >> 4,
+ GET_TD (cpu.currentInstruction.tag) & 017);
+ }
+ }
+ }
+
+ }
+#endif
+
+bool chkOVF (void)
+ {
+ if (cpu.cu.rpt || cpu.cu.rd || cpu.cu.rl)
+ {
+ // a:AL39/rpd2
+ // Did the repeat instruction inhibit overflow faults?
+ if ((cpu.rX[0] & 00001) == 0)
+ return false;
+ }
+ return true;
+ }
+
+bool tstOVFfault (void)
+ {
+ // Masked?
+ if (TST_I_OMASK)
+ return false;
+ // Doing a RPT/RPD?
+ if (cpu.cu.rpt || cpu.cu.rd || cpu.cu.rl)
+ {
+ // a:AL39/rpd2
+ // Did the repeat instruction inhibit overflow faults?
+ if ((cpu.rX[0] & 00001) == 0)
+ return false;
+ }
+ return true;
+ }
+
+t_stat executeInstruction (void)
+ {
+ CPT (cpt2U, 13); // execute instruction
+
+//
+// Decode the instruction
+//
+// If not restart
+// if xec/xed
+// check for illegal execute
+// if rpt/rpd
+// check for illegal rpt/rpd modifiers
+// check for illegal modifiers
+// check for privilege
+// initialize CA
+//
+// Save tally
+// Debug trace instruction
+// If not restart
+// Initialize TPR
+//
+// Initialize DU.JMP
+// If rpt/rpd
+// If first repeat
+// Initialize Xn
+//
+// If EIS instruction
+// If not restart
+// Initialize DU.CHTALLY, DU.Z
+// Read operands
+// Parse operands
+// Else not EIS instruction
+// If not restart
+// If B29
+// Set TPR from pointer register
+// Else
+// Setup up TPR
+// Initialize CU.CT_HOLD
+// If restart and CU.POT
+// Restore CA from IWB
+// Do CAF if needed
+// Read operand if needed
+//
+// Execute the instruction
+//
+// Write operand if needed
+// Update IT tally if needed
+// If XEC/XED, move instructions into IWB/IRODD
+// If instruction was repeated
+// Update Xn
+// Check for repeat termination
+// Post-instruction debug
+
+
+///
+/// executeInstruction: Decode the instruction
+///
+
+ DCDstruct * ci = & cpu.currentInstruction;
+ decode_instruction (IWB_IRODD, ci);
+ //cpu.isb29 = ci->b29;
+ //ISB29 = ci->b29;
+ const struct opcode_s *info = ci->info;
+
+// Local caches of frequently accessed data
+
+ const uint ndes = info->ndes;
+ const bool restart = cpu.cu.rfi; // instruction is to be restarted
+ cpu.cu.rfi = 0;
+ const opc_flag flags = info->flags;
+ const opc_mod mods = info->mods;
+ const uint32 opcode = ci->opcode; // opcode
+ const bool opcodeX = ci->opcodeX; // opcode extension
+ const word6 tag = ci->tag; // instruction tag
+
+
+#ifdef MATRIX
+ {
+ const uint32 opcode = ci->opcode; // opcode
+ const bool opcodeX = ci->opcodeX; // opcode extension
+ // XXX replace with rY
+ const bool b29 = ci->b29; // bit-29 - addressing via pointer
+ // register
+ const word6 tag = ci->tag; // instruction tag
+ // XXX replace withrTAG
+ addToTheMatrix (opcode, opcodeX, b29, tag);
+ }
+#endif
+
+//#define likely(x) (x)
+//#define unlikely(x) (x)
+#define likely(x) __builtin_expect ((x), 1)
+#define unlikely(x) __builtin_expect ((x), 0)
+
+//sim_debug (DBG_TRACEEXT, & cpu_dev, "isb29 %o\n", ci->b29);
+ if (ci->b29)
+ ci->address = SIGNEXT15_18 (ci->address & MASK15);
+
+#ifdef L68
+ CPTUR (cptUseMR);
+ if (unlikely (cpu.MR.emr && cpu.MR.OC_TRAP))
+ {
+ if (cpu.MR.OPCODE == opcode &&
+ cpu.MR.OPCODEX == opcodeX)
+ {
+ if (cpu.MR.ihrrs)
+ {
+ cpu.MR.ihr = 0;
+ }
+ CPT (cpt2U, 14); // opcode trap
+ //set_FFV_fault (2); // XXX According to AL39
+ do_FFV_fault (1, "OC TRAP");
+ }
+ }
+#endif // L68
+
+///
+/// executeInstruction: Non-restart processing
+///
+
+ if (likely (!restart) || unlikely (ndes > 0)) // until we implement EIS restart
+ {
+ cpu.cu.TSN_VALID[0] = 0;
+ cpu.cu.TSN_VALID[1] = 0;
+ cpu.cu.TSN_VALID[2] = 0;
+ cpu.cu.TSN_PRNO[0] = 0;
+ cpu.cu.TSN_PRNO[1] = 0;
+ cpu.cu.TSN_PRNO[2] = 0;
+ }
+
+ if (unlikely (restart))
+ goto restart_1;
+
+//
+// not restart
+//
+
+ cpu.cu.XSF = 0;
+sim_debug (DBG_TRACEEXT, & cpu_dev, "%s sets XSF to %o\n", __func__, cpu.cu.XSF);
+
+ cpu.cu.pot = 0;
+ cpu.cu.its = 0;
+ cpu.cu.itp = 0;
+
+ CPT (cpt2U, 14); // non-restart processing
+ // Set Address register empty
+ PNL (L68_ (cpu.AR_F_E = false;))
+
+ // Reset the fault counter
+ cpu.cu.APUCycleBits &= 07770;
+
+ //cpu.cu.TSN_VALID[0] = 0;
+ //cpu.cu.TSN_VALID[1] = 0;
+ //cpu.cu.TSN_VALID[2] = 0;
+
+ // If executing the target of XEC/XED, check the instruction is allowed
+ if (unlikely (cpu.isXED))
+ {
+ if (flags & NO_XED)
+ doFault (FAULT_IPR,
+ fst_ill_proc,
+ "Instruction not allowed in XEC/XED");
+ // The even instruction from C(Y-pair) must not alter
+ // C(Y-pair)36,71, and must not be another xed instruction.
+ if (opcode == 0717 && !opcodeX && cpu.cu.xde && cpu.cu.xdo /* even instruction being executed */)
+ doFault (FAULT_IPR,
+ fst_ill_proc,
+ "XED of XED on even word");
+ // ISOLTS 791 03k, 792 03k
+ if (opcode == 0560 && !opcodeX) {
+ // To Execute Double (XED) the RPD instruction, the RPD must be the second
+ // instruction at an odd-numbered address.
+ if (cpu.cu.xde && cpu.cu.xdo /* even instr being executed */)
+ doFault (FAULT_IPR,
+ (_fault_subtype) {.fault_ipr_subtype=FR_ILL_PROC},
+ "XED of RPD on even word");
+ // To execute an instruction pair having an rpd instruction as the odd
+ // instruction, the xed instruction must be located at an odd address.
+ if (!cpu.cu.xde && cpu.cu.xdo /* odd instr being executed */ && !(cpu.PPR.IC & 1))
+ doFault (FAULT_IPR,
+ (_fault_subtype) {.fault_ipr_subtype=FR_ILL_PROC},
+ "XED of RPD on odd word, even IC");
+ }
+ } else if (unlikely (cpu.isExec)) {
+ // To execute a rpd instruction, the xec instruction must be in an odd location.
+ // ISOLTS 768 01w
+ if (opcode == 0560 && !opcodeX && cpu.cu.xde && !(cpu.PPR.IC & 1))
+ doFault (FAULT_IPR,
+ (_fault_subtype) {.fault_ipr_subtype=FR_ILL_PROC},
+ "XEC of RPx on even word");
+ }
+
+ // ISOLTS wants both the not allowed in RPx and RPx illegal modifier
+ // tested.
+ fault_ipr_subtype_ RPx_fault = 0;
+
+ // In BAR mode and not allowed?
+
+#if 0
+ if (TST_I_NBAR == 0)
+ if (flags & NO_BAR)
+ RPx_fault |= FR_ILL_SLV;
+#endif
+
+ // RPT/RPD illegal modifiers
+ // a:AL39/rpd3
+ if (unlikely (cpu.cu.rpt || cpu.cu.rd || cpu.cu.rl))
+ {
+ if (! (flags & NO_TAG))
+ {
+ // check for illegal modifiers:
+ // only R & RI are allowed
+ // only X1..X7
+ switch (GET_TM (tag))
+ {
+ case TM_RI:
+ if (cpu.cu.rl)
+ {
+ RPx_fault |= FR_ILL_MOD;
+ }
+ break;
+ case TM_R:
+ break;
+ default:
+ // generate fault. Only R & RI allowed
+ RPx_fault |= FR_ILL_MOD;
+ }
+
+ word6 Td = GET_TD (tag);
+ if (Td == TD_X0)
+ {
+ RPx_fault |= FR_ILL_MOD;
+ }
+ //if (! cpu.cu.rd && Td < TD_X0)
+ if (Td < TD_X0)
+ {
+ RPx_fault |= FR_ILL_MOD;
+ }
+ }
+
+#ifdef DPS8M
+ // ISOLTS 792 03e
+ // this is really strange. possibly a bug in DPS8M HW (L68 handles it the same as all other instructions)
+ if (RPx_fault && !opcodeX && opcode==0413) // rscr
+ {
+ doFault (FAULT_IPR,
+ (_fault_subtype) {.fault_ipr_subtype=RPx_fault},
+ "DPS8M rscr early raise");
+ }
+#endif
+
+ // Instruction not allowed in RPx?
+
+ if (unlikely (cpu.cu.rpt || cpu.cu.rd || cpu.cu.rl))
+ {
+ if (flags & NO_RPT)
+ {
+ RPx_fault |= FR_ILL_PROC;
+ }
+ }
+
+ if (unlikely (cpu.cu.rl))
+ {
+ if (flags & NO_RPL)
+ {
+ RPx_fault |= FR_ILL_PROC;
+ }
+ }
+
+#ifdef L68
+ // ISOLTS 791 03d, 792 03d
+ // L68 wants ILL_MOD here - stca,stcq,stba,stbq,scpr,lcpr
+ // all these instructions have a nonstandard TAG field interpretation. probably a HW bug in decoder
+ if (RPx_fault && !opcodeX && (opcode==0751 || opcode==0752 || opcode==0551
+ || opcode==0552 || opcode==0452 || opcode==0674))
+ {
+ RPx_fault |= FR_ILL_MOD;
+ }
+#endif
+ }
+
+ if (unlikely (RPx_fault != 0))
+ {
+ doFault (FAULT_IPR,
+ (_fault_subtype) {.fault_ipr_subtype=RPx_fault},
+ "RPx test fail");
+ }
+
+ /// check for illegal addressing mode(s) ...
+ ///
+ // ISOLTS wants both the IPR and illegal modifier tested.
+ fault_ipr_subtype_ mod_fault = 0;
+
+ // No CI/SC/SCR allowed
+ if (mods == NO_CSS)
+ {
+ if (_nocss[tag])
+ mod_fault |= FR_ILL_MOD; // "Illegal CI/SC/SCR modification"
+ }
+ // No DU/DL/CI/SC/SCR allowed
+ else if (mods == NO_DDCSS)
+ {
+ if (_noddcss[tag])
+ mod_fault |= FR_ILL_MOD; // "Illegal DU/DL/CI/SC/SCR modification"
+ }
+ // No DL/CI/SC/SCR allowed
+ else if (mods == NO_DLCSS)
+ {
+ if (_nodlcss[tag])
+ mod_fault |= FR_ILL_MOD; // "Illegal DL/CI/SC/SCR modification"
+ }
+ // No DU/DL allowed
+ else if (mods == NO_DUDL)
+ {
+ if (_nodudl[tag])
+ mod_fault |= FR_ILL_MOD; // "Illegal DU/DL modification"
+ }
+ else if (mods == ONLY_AU_QU_AL_QL_XN)
+ {
+ if (_onlyaqxn[tag])
+ mod_fault |= FR_ILL_MOD; // "Illegal DU/DL/IC modification"
+ }
+
+#ifdef L68
+ // L68 raises it immediately
+ if (mod_fault)
+ {
+ doFault (FAULT_IPR,
+ (_fault_subtype) {.fault_ipr_subtype=mod_fault},
+ "Illegal modifier");
+ }
+#endif
+
+ // check for priv ins - Attempted execution in normal or BAR modes causes a
+ // illegal procedure fault.
+ if (unlikely (flags & PRIV_INS))
+ {
+#ifdef DPS8M
+ // DPS8M illegal instructions lptp,lptr,lsdp,lsdr
+ // ISOLTS 890 05abc
+ if (((opcode == 0232 || opcode == 0173) && opcodeX )
+ || (opcode == 0257))
+ {
+ doFault (FAULT_IPR,
+ (_fault_subtype) {.fault_ipr_subtype=FR_ILL_OP|mod_fault},
+ "Attempted execution of multics privileged instruction.");
+ }
+#endif
+ if (!is_priv_mode ())
+ {
+ // "multics" privileged instructions: absa,ldbr,lra,rcu,scu,sdbr,ssdp,ssdr,sptp,sptr
+ // ISOLTS 890 05abc,06abc
+#ifdef DPS8M
+ if (((opcode == 0212 || opcode == 0232 || opcode == 0613 || opcode == 0657) && !opcodeX )
+ || ((opcode == 0254 || opcode == 0774) && opcodeX )
+ || (opcode == 0557 || opcode == 0154))
+#else // L68
+ // on L68, lptp,lptr,lsdp,lsdr instructions are not illegal, so handle them here
+ if (((opcode == 0212 || opcode == 0232 || opcode == 0613 || opcode == 0657) && !opcodeX )
+ || ((opcode == 0254 || opcode == 0774 || opcode == 0232 || opcode == 0173) && opcodeX )
+ || (opcode == 0557 || opcode == 0154 || opcode == 0257))
+#endif
+ {
+ if (!get_bar_mode ()) {
+ // ISOLTS-890 05ab
+ doFault (FAULT_IPR,
+ (_fault_subtype) {.fault_ipr_subtype=FR_ILL_SLV|mod_fault},
+ "Attempted execution of multics privileged instruction.");
+ } else {
+ doFault (FAULT_IPR,
+ (_fault_subtype) {.fault_ipr_subtype=FR_ILL_OP|mod_fault},
+ "Attempted execution of multics privileged instruction.");
+ }
+ }
+ doFault (FAULT_IPR,
+ (_fault_subtype) {.fault_ipr_subtype=FR_ILL_SLV|mod_fault},
+ "Attempted execution of privileged instruction.");
+ }
+ }
+
+ if (unlikely (flags & NO_BAR))
+ if (get_bar_mode())
+ {
+ // lbar
+ // ISOLTS 890 06a
+ // ISOLTS says that L68 handles this in the same way
+ if (opcode == 0230 && !opcodeX) {
+ doFault (FAULT_IPR,
+ (_fault_subtype) {.fault_ipr_subtype=FR_ILL_SLV|mod_fault},
+ "Attempted BAR execution of nonprivileged instruction.");
+ } else
+ doFault (FAULT_IPR,
+ (_fault_subtype) {.fault_ipr_subtype=FR_ILL_OP|mod_fault},
+ "Attempted BAR execution of nonprivileged instruction.");
+ }
+
+#ifdef DPS8M
+ // DPS8M raises it delayed
+ if (unlikely (mod_fault != 0))
+ {
+ doFault (FAULT_IPR,
+ (_fault_subtype) {.fault_ipr_subtype=mod_fault},
+ "Illegal modifier");
+ }
+#endif
+
+ ///
+ /// executeInstruction: Restart or Non-restart processing
+ /// Initialize address registers
+ ///
+restart_1:
+
+#if 0 // #ifndef CA_REWORK
+#if 1
+ cpu.TPR.CA = ci->address;
+ cpu.iefpFinalAddress = cpu.TPR.CA;
+ //cpu.rY = cpu.TPR.CA;
+#else
+ cpu.iefpFinalAddress = ci->address;
+ cpu.rY = ci->address;
+#endif
+#endif
+
+
+
+ CPT (cpt2U, 15); // instruction processing
+///
+/// executeInstruction: Initialize state saving registers
+///
+
+ // XXX this may be wrong; make sure that the right value is used
+ // if a page fault occurs. (i.e. this may belong above restart_1.
+ // This is also used by the SCU instruction. ISOLTS tst887 does
+ // a 'SCU n,ad' with a tally of 1; the tally is decremented, setting
+ // the IR tally bit as part of the CA calculation; this is not
+ // the machine conditions that the SCU instruction is saving.
+
+ ci->stiTally = TST_I_TALLY; // for sti instruction
+
+///
+/// executeInstruction: simh hooks
+///
+
+#ifndef SPEED
+ // Don't trace Multics idle loop
+ //if (cpu.PPR.PSR != 061 || cpu.PPR.IC != 0307)
+
+ {
+ traceInstruction (DBG_TRACE);
+#ifdef DBGEVENT
+ int dbgevt;
+ if (n_dbgevents && (dbgevt = (dbgevent_lookup (cpu.PPR.PSR, cpu.PPR.IC))) >= 0)
+ {
+ if (dbgevents[dbgevt].t0)
+ clock_gettime (CLOCK_REALTIME, & dbgevent_t0);
+ struct timespec now, delta;
+ clock_gettime (CLOCK_REALTIME, & now);
+ timespec_diff (& dbgevent_t0, & now, & delta);
+ sim_printf ("[%d] %5ld.%03ld %s\r\n", dbgevt, delta.tv_sec, delta.tv_nsec/1000000, dbgevents[dbgevt].tag);
+ }
+#endif
+
+#ifdef HDBG
+ hdbgTrace ();
+#endif // HDBG
+ }
+#else // !SPEED
+#ifdef HDBG
+ // Don't trace Multics idle loop
+ //if (cpu.PPR.PSR != 061 || cpu.PPR.IC != 0307)
+ hdbgTrace ();
+#endif // HDBG
+#endif // !SPEED
+
+///
+/// executeInstruction: Initialize misc.
+///
+
+ cpu.du.JMP = (word3) ndes;
+ cpu.dlyFlt = false;
+
+///
+/// executeInstruction: RPT/RPD/RPL special processing for 'first time'
+///
+
+ if (unlikely (cpu.cu.rpt || cpu.cu.rd || cpu.cu.rl))
+ {
+ CPT (cpt2U, 15); // RPx processing
+//
+// RPT:
+//
+// The computed address, y, of the operand (in the case of R modification) or
+// indirect word (in the case of RI modification) is determined as follows:
+//
+// For the first execution of the repeated instruction:
+// C(C(PPR.IC)+1)0,17 + C(Xn) -> y, y -> C(Xn)
+//
+// For all successive executions of the repeated instruction:
+// C(Xn) + Delta -> y, y -> C(Xn);
+//
+//
+// RPD:
+//
+// The computed addresses, y-even and y-odd, of the operands (in the case of
+// R modification) or indirect words (in the case of RI modification) are
+// determined as follows:
+//
+// For the first execution of the repeated instruction pair:
+// C(C(PPR.IC)+1)0,17 + C(X-even) -> y-even, y-even -> C(X-even)
+// C(C(PPR.IC)+2)0,17 + C(X-odd) -> y-odd, y-odd -> C(X-odd)
+//
+// For all successive executions of the repeated instruction pair:
+// if C(X0)8 = 1, then C(X-even) + Delta -> y-even,
+// y-even -> C(X-even);
+// otherwise, C(X-even) -> y-even
+// if C(X0)9 = 1, then C(X-odd) + Delta -> y-odd,
+// y-odd -> C(X-odd);
+// otherwise, C(X-odd) -> y-odd
+//
+// C(X0)8,9 correspond to control bits A and B, respectively, of the rpd
+// instruction word.
+//
+//
+// RL:
+//
+// The computed address, y, of the operand is determined as follows:
+//
+// For the first execution of the repeated instruction:
+//
+// C(C(PPR.IC)+1)0,17 + C(Xn) -> y, y -> C(Xn)
+//
+// For all successive executions of the repeated instruction:
+//
+// C(Xn) -> y
+//
+// if C(Y)0,17 != 0, then C (y)0,17 -> C(Xn);
+//
+// otherwise, no change to C(Xn)
+//
+// C(Y)0,17 is known as the link address and is the computed address of the
+// next entry in a threaded list of operands to be referenced by the repeated
+// instruction.
+//
+
+ sim_debug (DBG_TRACEEXT, & cpu_dev,
+ "RPT/RPD first %d rpt %d rd %d e/o %d X0 %06o a %d b %d\n",
+ cpu.cu.repeat_first, cpu.cu.rpt, cpu.cu.rd, cpu.PPR.IC & 1,
+ cpu.rX[0], !! (cpu.rX[0] & 01000), !! (cpu.rX[0] & 0400));
+ sim_debug (DBG_TRACEEXT, & cpu_dev,
+ "RPT/RPD CA %06o\n", cpu.TPR.CA);
+
+// Handle first time of a RPT or RPD
+
+ if (cpu.cu.repeat_first)
+ {
+ CPT (cpt2U, 16); // RPx first processing
+ // The semantics of these are that even is the first instruction of
+ // and RPD, and odd the second.
+
+ bool icOdd = !! (cpu.PPR.IC & 1);
+ bool icEven = ! icOdd;
+
+ // If RPT or (RPD and the odd instruction)
+ if (cpu.cu.rpt || (cpu.cu.rd && icOdd) || cpu.cu.rl)
+ cpu.cu.repeat_first = false;
+
+ // a:RJ78/rpd6
+ // For the first execution of the repeated instruction:
+ // C(C(PPR.IC)+1)0,17 + C(Xn) -> y, y -> C(Xn)
+ if (cpu.cu.rpt || // rpt
+ (cpu.cu.rd && icEven) || // rpd & even
+ (cpu.cu.rd && icOdd) || // rpd & odd
+ cpu.cu.rl) // rl
+ {
+#if 0
+ word18 offset;
+ if (ci->b29)
+ {
+{ static bool first = true;
+if (first) {
+first = false;
+sim_printf ("XXX rethink this; bit 29 is finagled below; should this be done in a different order?\n");
+}}
+sim_debug (DBG_TRACEEXT, & cpu_dev, "b29, ci->address %o\n", ci->address);
+ // a:RJ78/rpd4
+ offset = SIGNEXT15_18 (ci->address & MASK15);
+ }
+ else
+ offset = ci->address;
+#else
+ word18 offset = ci->address;
+#endif
+ offset &= AMASK;
+
+ sim_debug (DBG_TRACEEXT, & cpu_dev,
+ "rpt/rd/rl repeat first; offset is %06o\n", offset);
+
+ word6 Td = GET_TD (tag);
+ uint Xn = X (Td); // Get Xn of next instruction
+ sim_debug (DBG_TRACEEXT, & cpu_dev,
+ "rpt/rd/rl repeat first; X%d was %06o\n",
+ Xn, cpu.rX[Xn]);
+ // a:RJ78/rpd5
+ cpu.TPR.CA = (cpu.rX[Xn] + offset) & AMASK;
+ cpu.rX[Xn] = cpu.TPR.CA;
+ HDBGRegX (Xn);
+ sim_debug (DBG_TRACEEXT, & cpu_dev,
+ "rpt/rd/rl repeat first; X%d now %06o\n",
+ Xn, cpu.rX[Xn]);
+ } // rpt or rd or rl
+
+ } // repeat first
+ } // cpu.cu.rpt || cpu.cu.rd || cpu.cu.rl
+
+///
+/// Restart or Non-restart
+///
+
+///
+/// executeInstruction: EIS operand processing
+///
+
+ if (unlikely (ndes > 0))
+ {
+ CPT (cpt2U, 27); // EIS operand processing
+ sim_debug (DBG_APPENDING, &cpu_dev, "initialize EIS descriptors\n");
+ // This must not happen on instruction restart
+ if (!restart)
+ {
+ CPT (cpt2U, 28); // EIS not restart
+ cpu.du.CHTALLY = 0;
+ cpu.du.Z = 1;
+ }
+ for (uint n = 0; n < ndes; n += 1)
+ {
+ CPT (cpt2U, 29 + n); // EIS operand fetch (29, 30, 31)
+// XXX This is a bit of a hack; In general the code is good about
+// setting up for bit29 or PR operations by setting up TPR, but
+// assumes that the 'else' case can be ignored when it should set
+// TPR to the canonical values. Here, in the case of a EIS instruction
+// restart after page fault, the TPR is in an unknown state. Ultimately,
+// this should not be an issue, as this folderol would be in the DU, and
+// we would not be re-executing that code, but until then, set the TPR
+// to the condition we know it should be in.
+ cpu.TPR.TRR = cpu.PPR.PRR;
+ cpu.TPR.TSR = cpu.PPR.PSR;
+#if 0
+{ static bool first = true;
+if (first) {
+first = false;
+sim_printf ("XXX this had b29 of 0; it may be necessary to clear TSN_VALID[0]\n");
+}}
+#else
+ // append cycles updates cpu.PPR.IC to TPR.CA
+ word18 saveIC = cpu.PPR.IC;
+ Read (cpu.PPR.IC + 1 + n, & cpu.currentEISinstruction.op[n],
+ INSTRUCTION_FETCH);
+ cpu.PPR.IC = saveIC;
+ //Read (cpu.PPR.IC + 1 + n, & cpu.currentEISinstruction.op[n],
+ // APU_DATA_READ);
+#endif
+ }
+ PNL (cpu.IWRAddr = cpu.currentEISinstruction.op[0]);
+ setupEISoperands ();
+ }
+
+///
+/// Restart or Non-restart
+///
+
+///
+/// executeInstruction: non-EIS operand processing
+///
+
+ else
+ {
+ CPT (cpt2U, 32); // non-EIS operand processing
+ CPT (cpt2U, 33); // not restart non-EIS operand processing
+ if (ci->b29) // if A bit set set-up TPR stuff ...
+ {
+ CPT (cpt2U, 34); // B29
+
+// AL39 says that RCU does not restore CA, so words to SCU does not.
+// So we do it here, even if restart
+ word3 n = GET_PRN(IWB_IRODD); // get PRn
+ word15 offset = GET_OFFSET(IWB_IRODD);
+ CPTUR (cptUsePRn + n);
+
+ sim_debug (DBG_APPENDING, &cpu_dev,
+ "doPtrReg: PR[%o] SNR=%05o RNR=%o WORDNO=%06o "
+ "BITNO=%02o\n",
+ n, cpu.PAR[n].SNR, cpu.PAR[n].RNR,
+ cpu.PAR[n].WORDNO, GET_PR_BITNO (n));
+
+#if 0 // #ifndef CA_REWORK
+ cpu.TPR.CA = (cpu.PAR[n].WORDNO + SIGNEXT15_18 (offset))
+ & MASK18;
+#endif
+
+// Fix tst880: 'call6 pr1|0'. The instruction does a DF1; the fault handler
+// updates PRR in the CU save data. On restart, TRR is not updated.
+// Removing the 'if' appears to resolve the problem without regressions.
+ //if (!restart)
+ {
+// Not EIS, bit 29 set, !restart
+ cpu.TPR.TBR = GET_PR_BITNO (n);
+
+ cpu.TPR.TSR = cpu.PAR[n].SNR;
+ if (ci->info->flags & TRANSFER_INS)
+ cpu.TPR.TRR = max (cpu.PAR[n].RNR, cpu.PPR.PRR);
+ else
+ cpu.TPR.TRR = max3 (cpu.PAR[n].RNR, cpu.TPR.TRR, cpu.PPR.PRR);
+
+ sim_debug (DBG_APPENDING, &cpu_dev,
+ "doPtrReg: n=%o offset=%05o TPR.CA=%06o "
+ "TPR.TBR=%o TPR.TSR=%05o TPR.TRR=%o\n",
+ n, offset, cpu.TPR.CA, cpu.TPR.TBR,
+ cpu.TPR.TSR, cpu.TPR.TRR);
+ // cpu.cu.XSF = 1;
+ //sim_debug (DBG_TRACEEXT, & cpu_dev, "executeInstruction !restart !EIS sets XSF to %o\n", cpu.cu.XSF);
+ //set_went_appending ();
+ }
+
+// Putting the a29 clear here makes sense, but breaks the emulator for unclear
+// reasons (possibly ABSA?). Do it in updateIWB instead
+// ci->a = false;
+// // Don't clear a; it is needed to detect change to appending
+// // mode
+// //a = false;
+// putbits36_1 (& cpu.cu.IWB, 29, 0);
+ }
+ else
+ {
+// not eis, not bit b29
+ if (!restart)
+ {
+ CPT (cpt2U, 35); // not B29
+ cpu.cu.TSN_VALID [0] = 0;
+ cpu.TPR.TBR = 0;
+ if (get_addr_mode () == ABSOLUTE_mode)
+ {
+ cpu.TPR.TSR = cpu.PPR.PSR;
+ cpu.TPR.TRR = 0;
+ cpu.RSDWH_R1 = 0;
+ }
+ //cpu.cu.XSF = 0;
+sim_debug (DBG_TRACEEXT, & cpu_dev, "executeInstruction not EIS sets XSF to %o\n", cpu.cu.XSF);
+ //clr_went_appending ();
+ }
+ }
+
+ // This must not happen on instruction restart
+ if (!restart)
+ {
+ cpu.cu.CT_HOLD = 0; // Clear interrupted IR mode flag
+ }
+
+
+#if 0 // #ifndef CA_REWORK
+ //
+ // If POT is set, a page fault occured during the fetch of the data word
+ // pointed to by an indirect addressing word, and the saved CA points
+ // to the data word instead of the indirect word; reset the CA correctly
+ //
+
+ if (restart && cpu.cu.pot)
+ {
+ CPT (cpt2L, 0); // POT set
+ cpu.TPR.CA = GET_ADDR (IWB_IRODD);
+ if (getbits36_1 (cpu.cu.IWB, 29) != 0)
+ cpu.TPR.CA &= MASK15;
+ }
+#endif
+
+ // These are set by do_caf
+ cpu.ou.directOperandFlag = false;
+ cpu.ou.directOperand = 0;
+ cpu.ou.characterOperandSize = 0;
+ cpu.ou.characterOperandOffset = 0;
+ cpu.ou.crflag = false;
+
+#define REORDER
+#ifdef REORDER
+ if ((flags & PREPARE_CA) || WRITEOP (ci) || READOP (ci))
+ {
+ CPT (cpt2L, 1); // CAF
+ do_caf ();
+ PNL (L68_ (cpu.AR_F_E = true;))
+ cpu.iefpFinalAddress = cpu.TPR.CA;
+ }
+
+ //if (READOP (ci) && ! ((bool) (flags & TRANSFER_INS)))
+ if (READOP (ci))
+ {
+ CPT (cpt2L, 2); // Read operands
+ readOperands ();
+#ifdef LOCKLESS
+ cpu.rmw_address = cpu.iefpFinalAddress;
+#endif
+ if (cpu.cu.rl)
+ {
+ switch (operand_size ())
+ {
+ case 1:
+ {
+ cpu.lnk = GETHI36 (cpu.CY);
+ cpu.CY &= MASK18;
+ break;
+ }
+ case 2:
+ {
+ cpu.lnk = GETHI36 (cpu.Ypair[0]);
+ cpu.Ypair[0] &= MASK18;
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ }
+ }
+#else
+ if (flags & PREPARE_CA)
+ {
+ do_caf ();
+ L68_ (cpu.AR_F_E = true;)
+ cpu.iefpFinalAddress = cpu.TPR.CA;
+ }
+ else if (READOP (ci))
+ {
+ do_caf ();
+ cpu.iefpFinalAddress = cpu.TPR.CA;
+ readOperands ();
+ }
+#endif
+ PNL (cpu.IWRAddr = 0);
+ }
+
+// Initiialize zone to 'entire word'
+
+ cpu.useZone = false;
+ cpu.zone = MASK36;
+
+///
+/// executeInstruction: Execute the instruction
+///
+
+ t_stat ret = doInstruction ();
+
+///
+/// executeInstruction: Write operand
+///
+
+ cpu.last_write = 0;
+ if (WRITEOP (ci))
+ {
+ CPT (cpt2L, 3); // Write operands
+ cpu.last_write = cpu.TPR.CA;
+#ifndef REORDER
+ if (! READOP (ci))
+ {
+ do_caf ();
+ cpu.iefpFinalAddress = cpu.TPR.CA;
+ }
+#endif
+#ifdef LOCKLESS
+ if ((ci->info->flags & RMW) == RMW)
+ {
+ if (operand_size() != 1)
+ sim_warn("executeInstruction: operand_size!= 1\n");
+ if (cpu.iefpFinalAddress != cpu.rmw_address)
+ sim_warn("executeInstruction: write addr changed %o %d\n", cpu.iefpFinalAddress, cpu.rmw_address);
+ core_write_unlock (cpu.iefpFinalAddress, cpu.CY, __func__);
+ }
+ else
+ writeOperands ();
+#else
+ writeOperands ();
+#endif
+ }
+
+ else if (flags & PREPARE_CA)
+ {
+ // 'EPP ITS; TRA' confuses the APU by leaving last_cycle
+ // at INDIRECT_WORD_FETCH; defoobarize the APU:
+ fauxDoAppendCycle (OPERAND_READ);
+ cpu.TPR.TRR = cpu.PPR.PRR;
+ cpu.TPR.TSR = cpu.PPR.PSR;
+ cpu.TPR.TBR = 0;
+ }
+
+///
+/// executeInstruction: RPT/RPD/RPL processing
+///
+
+
+ // The semantics of these are that even is the first instruction of
+ // and RPD, and odd the second.
+
+ bool icOdd = !! (cpu.PPR.IC & 1);
+ bool icEven = ! icOdd;
+
+ // Here, repeat_first means that the instruction just executed was the
+ // RPT or RPD; but when the even instruction of a RPD is executed,
+ // repeat_first is still set, since repeat_first cannot be cleared
+ // until the odd instruction gets its first execution. Put some
+ // ugly logic in to detect that condition.
+
+ bool rf = cpu.cu.repeat_first;
+ if (rf && cpu.cu.rd && icEven)
+ rf = false;
+
+ if (unlikely ((! rf) && (cpu.cu.rpt || cpu.cu.rd || cpu.cu.rl)))
+ {
+ CPT (cpt2L, 7); // Post execution RPx
+ // If we get here, the instruction just executed was a
+ // RPT, RPD or RPL target instruction, and not the RPT or RPD
+ // instruction itself
+
+ if (cpu.cu.rpt || cpu.cu.rd)
+ {
+ // Add delta to index register.
+
+ bool rptA = !! (cpu.rX[0] & 01000);
+ bool rptB = !! (cpu.rX[0] & 00400);
+
+ sim_debug (DBG_TRACEEXT, & cpu_dev,
+ "RPT/RPD delta first %d rf %d rpt %d rd %d "
+ "e/o %d X0 %06o a %d b %d\n",
+ cpu.cu.repeat_first, rf, cpu.cu.rpt, cpu.cu.rd, icOdd,
+ cpu.rX[0], rptA, rptB);
+
+ if (cpu.cu.rpt) // rpt
+ {
+ CPT (cpt2L, 8); // RPT delta
+ uint Xn = (uint) getbits36_3 (cpu.cu.IWB, 36 - 3);
+ cpu.TPR.CA = (cpu.rX[Xn] + cpu.cu.delta) & AMASK;
+ cpu.rX[Xn] = cpu.TPR.CA;
+ HDBGRegX (Xn);
+ sim_debug (DBG_TRACEEXT, & cpu_dev,
+ "RPT/RPD delta; X%d now %06o\n", Xn, cpu.rX[Xn]);
+ }
+
+ // a:RJ78/rpd6
+ // We know that the X register is not to be incremented until
+ // after both instructions have executed, so the following
+ // if uses icOdd instead of the more sensical icEven.
+ if (cpu.cu.rd && icOdd && rptA) // rpd, even instruction
+ {
+ CPT (cpt2L, 9); // RD even
+ // a:RJ78/rpd7
+ uint Xn = (uint) getbits36_3 (cpu.cu.IWB, 36 - 3);
+ cpu.TPR.CA = (cpu.rX[Xn] + cpu.cu.delta) & AMASK;
+ cpu.rX[Xn] = cpu.TPR.CA;
+ HDBGRegX (Xn);
+ sim_debug (DBG_TRACEEXT, & cpu_dev,
+ "RPT/RPD delta; X%d now %06o\n", Xn, cpu.rX[Xn]);
+ }
+
+ if (cpu.cu.rd && icOdd && rptB) // rpdb, odd instruction
+ {
+ CPT (cpt2L, 10); // RD odd
+ // a:RJ78/rpd8
+ uint Xn = (uint) getbits36_3 (cpu.cu.IRODD, 36 - 3);
+ cpu.TPR.CA = (cpu.rX[Xn] + cpu.cu.delta) & AMASK;
+ cpu.rX[Xn] = cpu.TPR.CA;
+ HDBGRegX (Xn);
+ sim_debug (DBG_TRACEEXT, & cpu_dev,
+ "RPT/RPD delta; X%d now %06o\n", Xn, cpu.rX[Xn]);
+ }
+ } // rpt || rd
+
+#if 0
+ else if (cpu.cu.rl)
+ {
+ CPT (cpt2L, 11); // RL
+ // C(Xn) -> y
+#if 1
+ uint Xn = (uint) getbits36_3 (cpu.cu.IWB, 36 - 3);
+ word18 lnk = GETHI36 (cpu.CY);
+ cpu.CY &= MASK18;
+ cpu.rX[Xn] = lnk;
+ //putbits36 (& cpu.cu.IWB, 0, 18, lnk);
+#else
+ uint Xn = (uint) getbits36_3 (cpu.cu.IWB, 36 - 3);
+ putbits36 (& cpu.cu.IWB, 0, 18, cpu.rX[Xn]);
+#endif
+ }
+#endif
+
+ // Check for termination conditions.
+
+///////
+//
+// ISOLTS test 769 claims in test-02a that 'rpt;div' with a divide
+// fault should delay the divide fault until after the tremination
+// check (it checks that the tally should be decremented) and in test-02b
+// that 'rpl;div' with a divide fault should not due the termination
+// check (the tally should not be decremented).
+//
+// This implies that rpt and rpl are handled differently; as a test
+// trying:
+
+#ifdef DPS8M
+ if (cpu.cu.rl && cpu.dlyFlt)
+#else // L68
+ if ((cpu.cu.rl || cpu.cu.rpt || cpu.cu.rd) && cpu.dlyFlt)
+#endif
+ {
+ CPT (cpt2L, 14); // Delayed fault
+ doFault (cpu.dlyFltNum, cpu.dlySubFltNum, cpu.dlyCtx);
+ }
+
+// Sadly, it fixes ISOLTS 769 test 02a and 02b.
+//
+///////
+
+ if (cpu.cu.rpt || (cpu.cu.rd && icOdd) || cpu.cu.rl)
+ {
+ CPT (cpt2L, 12); // RPx termination check
+ bool exit = false;
+ // The repetition cycle consists of the following steps:
+ // a. Execute the repeated instruction
+ // b. C(X0)0,7 - 1 -> C(X0)0,7
+ // a:AL39/rpd9
+ uint x = (uint) getbits18 (cpu.rX[0], 0, 8);
+ x -= 1;
+ x &= MASK8;
+ putbits18 (& cpu.rX[0], 0, 8, x);
+ HDBGRegX (0);
+
+ //sim_debug (DBG_TRACEEXT, & cpu_dev, "x %03o rX[0] %06o\n", x, rX[0]);
+
+ // a:AL39/rpd10
+ // c. If C(X0)0,7 = 0, then set the tally runout indicator ON
+ // and terminate
+
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "tally %d\n", x);
+ if (x == 0)
+ {
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "tally runout\n");
+ SET_I_TALLY;
+ exit = true;
+ }
+ else
+ {
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "not tally runout\n");
+ CLR_I_TALLY;
+ }
+
+ // d. If a terminate condition has been met, then set
+ // the tally runout indicator OFF and terminate
+
+ if (TST_I_ZERO && (cpu.rX[0] & 0100))
+ {
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "is zero terminate\n");
+ CLR_I_TALLY;
+ exit = true;
+ }
+ if (!TST_I_ZERO && (cpu.rX[0] & 040))
+ {
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "is not zero terminate\n");
+ CLR_I_TALLY;
+ exit = true;
+ }
+ if (TST_I_NEG && (cpu.rX[0] & 020))
+ {
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "is neg terminate\n");
+ CLR_I_TALLY;
+ exit = true;
+ }
+ if (!TST_I_NEG && (cpu.rX[0] & 010))
+ {
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "is not neg terminate\n");
+ CLR_I_TALLY;
+ exit = true;
+ }
+ if (TST_I_CARRY && (cpu.rX[0] & 04))
+ {
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "is carry terminate\n");
+ CLR_I_TALLY;
+ exit = true;
+ }
+ if (!TST_I_CARRY && (cpu.rX[0] & 02))
+ {
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "is not carry terminate\n");
+ CLR_I_TALLY;
+ exit = true;
+ }
+ if (TST_I_OFLOW && (cpu.rX[0] & 01))
+ {
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "is overflow terminate\n");
+// ISOLTS test ps805 says that on overflow the tally should be set.
+ //CLR_I_TALLY;
+ SET_I_TALLY;
+ exit = true;
+ }
+
+ if (exit)
+ {
+ CPT (cpt2L, 13); // RPx terminated
+ cpu.cu.rpt = false;
+ cpu.cu.rd = false;
+ cpu.cu.rl = false;
+ }
+ else
+ {
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "not terminate\n");
+ }
+ } // if (cpu.cu.rpt || cpu.cu.rd & (cpu.PPR.IC & 1))
+
+ if (cpu.cu.rl)
+ {
+ CPT (cpt2L, 11); // RL
+ if (cpu.lnk == 0)
+ {
+ CPT (cpt2L, 13); // RPx terminated
+ cpu.cu.rpt = false;
+ cpu.cu.rd = false;
+ cpu.cu.rl = false;
+ SET_I_TALLY;
+ }
+ else
+ {
+ // C(Xn) -> y
+#if 1
+ uint Xn = (uint) getbits36_3 (cpu.cu.IWB, 36 - 3);
+ //word18 lnk = GETHI36 (cpu.CY);
+ //cpu.CY &= MASK18;
+ cpu.rX[Xn] = cpu.lnk;
+ HDBGRegX (Xn);
+ //putbits36 (& cpu.cu.IWB, 0, 18, lnk);
+#else
+ uint Xn = (uint) getbits36_3 (cpu.cu.IWB, 36 - 3);
+ putbits36 (& cpu.cu.IWB, 0, 18, cpu.rX[Xn]);
+#endif
+ }
+ } // rl
+ } // (! rf) && (cpu.cu.rpt || cpu.cu.rd)
+
+ if (unlikely (cpu.dlyFlt))
+ {
+ CPT (cpt2L, 14); // Delayed fault
+ doFault (cpu.dlyFltNum, cpu.dlySubFltNum, cpu.dlyCtx);
+ }
+///
+/// executeInstruction: simh hooks
+///
+
+ cpu.instrCnt ++;
+
+ if_sim_debug (DBG_REGDUMP, & cpu_dev)
+ {
+ char buf [256];
+ sim_debug (DBG_REGDUMPAQI, &cpu_dev,
+ "A=%012"PRIo64" Q=%012"PRIo64" IR:%s\n",
+ cpu.rA, cpu.rQ, dump_flags (buf, cpu.cu.IR));
+
+#ifndef __MINGW64__
+ sim_debug (DBG_REGDUMPFLT, &cpu_dev,
+ "E=%03o A=%012"PRIo64" Q=%012"PRIo64" %.10Lg\n",
+ cpu.rE, cpu.rA, cpu.rQ, EAQToIEEElongdouble ());
+#else
+ sim_debug (DBG_REGDUMPFLT, &cpu_dev,
+ "E=%03o A=%012"PRIo64" Q=%012"PRIo64" %.10g\n",
+ cpu.rE, cpu.rA, cpu.rQ, EAQToIEEEdouble ());
+#endif
+
+ sim_debug (DBG_REGDUMPIDX, &cpu_dev,
+ "X[0]=%06o X[1]=%06o X[2]=%06o X[3]=%06o\n",
+ cpu.rX[0], cpu.rX[1], cpu.rX[2], cpu.rX[3]);
+ sim_debug (DBG_REGDUMPIDX, &cpu_dev,
+ "X[4]=%06o X[5]=%06o X[6]=%06o X[7]=%06o\n",
+ cpu.rX[4], cpu.rX[5], cpu.rX[6], cpu.rX[7]);
+ for (int n = 0 ; n < 8 ; n++)
+ {
+ sim_debug (DBG_REGDUMPPR, &cpu_dev,
+ "PR%d/%s: SNR=%05o RNR=%o WORDNO=%06o BITNO:%02o ARCHAR:%o ARBITNO:%02o\n",
+ n, PRalias[n], cpu.PR[n].SNR, cpu.PR[n].RNR,
+ cpu.PR[n].WORDNO, GET_PR_BITNO (n),
+ GET_AR_CHAR (n), GET_AR_BITNO (n));
+ }
+ sim_debug (DBG_REGDUMPPPR, &cpu_dev,
+ "PRR:%o PSR:%05o P:%o IC:%06o\n",
+ cpu.PPR.PRR, cpu.PPR.PSR, cpu.PPR.P, cpu.PPR.IC);
+ sim_debug (DBG_REGDUMPDSBR, &cpu_dev,
+ "ADDR:%08o BND:%05o U:%o STACK:%04o\n",
+ cpu.DSBR.ADDR, cpu.DSBR.BND, cpu.DSBR.U, cpu.DSBR.STACK);
+ }
+
+///
+/// executeInstruction: done. (Whew!)
+///
+
+ return ret;
+}
+
+//static t_stat DoBasicInstruction (void);
+//static t_stat DoEISInstruction (void);
+
+static inline void overflow (bool ovf, bool dly, const char * msg)
+ {
+ CPT (cpt2L, 15); // overflow check
+ // If an overflow occured and the repeat instruction is not inhibiting
+ // overflow checking.
+ if (ovf && chkOVF ())
+ {
+ SET_I_OFLOW;
+ // If overflows are not masked
+ if (tstOVFfault ())
+ {
+ CPT (cpt2L, 16); // overflow
+ // ISOLTS test ps768: Overflows set TRO.
+ if (cpu.cu.rpt || cpu.cu.rd || cpu.cu.rl)
+ {
+ SET_I_TALLY;
+ }
+ if (dly)
+ dlyDoFault (FAULT_OFL, fst_zero, msg);
+ else
+ doFault (FAULT_OFL, fst_zero, msg);
+ }
+ }
+ }
+
+// Return values
+// CONT_TRA
+// STOP_UNIMP
+// STOP_ILLOP
+// emCall()
+// STOP_HALT
+// scu_sscr()
+// STOP_BUG
+// STOP_WARN
+// scu_rmcm()
+// STOP_BUG
+// scu_smcm()
+// STOP_DIS
+// simh_hooks()
+// hard to document what this can return....
+// 0
+//
+
+// CANFAULT
+static t_stat doInstruction (void)
+{
+ DCDstruct * i = & cpu.currentInstruction;
+ // AL39 says it is always cleared, but that makes no sense (what good
+ // is an indicator bit if it is always 0 when you check it?). Clear it if
+ // an multiword EIS is at bat.
+ // NB: Never clearing it renders Multics unbootable.
+ if (i->info->ndes > 0)
+ CLR_I_MIF;
+
+#ifdef L68
+ cpu.ou.eac = 0;
+ cpu.ou.RB1_FULL = 0;
+ cpu.ou.RP_FULL = 0;
+ cpu.ou.RS_FULL = 0;
+ cpu.ou.STR_OP = 0;
+ cpu.ou.cycle = 0;
+#endif
+ PNL (cpu.ou.RS = (word9) i->opcode);
+ PNL (L68_ (DU_CYCLE_FDUD;)) // set DU idle
+ cpu.skip_cu_hist = false;
+ memcpy (& cpu.MR_cache, & cpu.MR, sizeof (cpu.MR_cache));
+
+// This mapping keeps nonEIS/EIS ordering, making various tables cleaner
+#define x0(n) (n)
+#define x1(n) (n|01000)
+
+ //t_stat ret = i->opcodeX ? DoEISInstruction () : DoBasicInstruction ();
+ uint32 opcode10 = i->opcode10;
+
+#ifdef PANEL
+ if (insGrp [opcode10])
+ {
+ word8 grp = insGrp [opcode10] - 1;
+ uint row = grp / 36;
+ uint col = grp % 36;
+ CPT (cpt3U + row, col); // 3U 0-35, 3L 0-17
+ }
+#ifdef L68
+ bool is_ou = false;
+#endif
+ if (opcodes10[opcode10].reg_use & is_OU)
+ {
+#ifdef L68
+ is_ou = true;
+#endif
+ // XXX Punt on RP FULL, RS FULL
+ cpu.ou.RB1_FULL = cpu.ou.RP_FULL = cpu.ou.RS_FULL = 1;
+ cpu.ou.cycle |= ou_GIN;
+ cpu.ou.opsz = (opcodes10[i->opcode10].reg_use >> 12) & 037;
+ word10 reguse = (opcodes10[i->opcode10].reg_use) & MASK10;
+ cpu.ou.reguse = reguse;
+ if (reguse & ru_A) CPT (cpt5U, 4);
+ if (reguse & ru_Q) CPT (cpt5U, 5);
+ if (reguse & ru_X0) CPT (cpt5U, 6);
+ if (reguse & ru_X1) CPT (cpt5U, 7);
+ if (reguse & ru_X2) CPT (cpt5U, 8);
+ if (reguse & ru_X3) CPT (cpt5U, 9);
+ if (reguse & ru_X4) CPT (cpt5U, 10);
+ if (reguse & ru_X5) CPT (cpt5U, 11);
+ if (reguse & ru_X6) CPT (cpt5U, 12);
+ if (reguse & ru_X7) CPT (cpt5U, 13);
+ }
+#ifdef L68
+ bool is_du = false;
+ if (opcodes10[opcode10].reg_use & is_DU)
+ {
+ is_du = true;
+ PNL (DU_CYCLE_nDUD;) // set not idle
+ }
+#endif
+#endif // PANEL
+
+ switch (opcode10)
+ {
+
+
+// Operations sorted by frequency of use; should help with caching issues
+
+// Operations counts from booting and build a boot tape from source:
+// 1605873148: eppn
+// 845109778: sprin
+// 702257337: lda
+// 637613648: tra
+// 555520875: ldq
+// 462569862: tze
+// 322979813: tnz
+// 288200618: stq
+// 260400300: cmpq
+// 192454329: anaq
+// 187283749: sta
+// 170691055: lprpn
+// 167568868: eaxn
+// 166842812: tsxn
+// 161542573: stz
+// 155129792: epbpn
+// 153639462: cmpa
+// 144804232: aos
+// 133559646: cana
+// 127230192: ldaq
+// 119988496: tpnz
+// 113295654: lxln
+// 109645303: staq
+// 109417021: tspn
+// 108352453: als
+// 96267840: rtcd
+// 93570029: tmi
+// 93161815: stxn
+// 90485871: ldi
+// 87421892: eraq
+// 76632891: ora
+// 75372023: adq
+// 75036448: tmoz
+// 64921645: spbpn
+// 63595794: ana
+// 62621406: fld
+// 57281513: epaq
+// 56066122: qls
+// 55861962: sti
+// 55186331: mlr
+// 54388393: call6
+// 50000721: lrl
+// 49736026: sbq
+// 49552594: tpl
+// 46097756: cmpb
+// 44484993: szn
+// 41295856: arl
+// 40019677: lrs
+// 39386119: sprpn
+// 36130580: ldxn
+// 32168708: ersa
+// 31817270: cmpxn
+// 31280696: a9bd
+// 29383886: era
+// 29282465: lls
+// 28714658: mpy
+// 28508378: sba
+// 24067324: anq
+// 23963178: asq
+// 23953122: nop
+// 23643534: orsa
+// 23083282: csl
+// 20970795: sbxn
+// 20109045: tct
+// 18504719: stba
+// 18297461: eaq
+// 17130040: eaa
+// 16035441: cmpc
+// 15762874: sxln
+// 15109836: lca
+// 15013924: adxn
+// 14159104: lcq
+// 14049597: div
+// 14043543: cmpaq
+// 13528591: ada
+// 12778888: ansa
+// 12534711: trc
+// 11710149: sbaq
+// 11584853: neg
+// 11456885: ttn
+// 11356918: canq
+// 10797383: rccl
+// 10743245: asa
+// 10100949: ttf
+// 9691628: orq
+// 9332512: adwp0-3
+// 9251904: anxn
+// 8076030: ldac
+// 8061536: scd
+// 7779639: adaq
+// 7586713: xec
+// 7506406: qrl
+// 7442522: adl
+// 6535658: stca
+// 6359531: adlxn
+// 6255134: sbla
+// 5936484: stacq
+// 5673345: eawp2
+// 4671545: tnc
+// 4230412: scm
+// 4040255: sarn
+// 4006015: oraq
+// 3918690: adlq
+// 3912600: stbq
+// 3449053: lcxn
+// 3368670: adla
+// 3290057: qrs
+// 3252438: ars
+// 3143543: qlr
+// 3098158: stac
+// 2838451: mvne
+// 2739787: lde
+// 2680484: btd
+// 2573170: erq
+// 2279433: fno
+// 2273692: smcm
+// 2240713: ersq
+// 2173455: sreg
+// 2173196: lreg
+// 2112784: mrl
+// 2030237: mvt
+// 2010819: stc2
+// 2008675: fmp
+// 1981148: llr
+// 1915081: mvn
+// 1846728: sblxn
+// 1820604: fcmp
+// 1765253: lcpr
+// 1447485: stc1
+// 1373184: ansxn
+// 1337744: negl
+// 1264062: rscr
+// 1201563: adwp4-7
+// 1198321: rmcm
+// 1182814: sznc
+// 1171307: sblq
+// 1140227: spri
+// 1139968: lpri
+// 1133946: dvf
+// 1059600: scpr
+// 958321: stcq
+// 837695: tctr
+// 820615: s9bd
+// 812523: rsw
+// 769275: fad
+// 729737: orsq
+// 651623: scu
+// 651612: rcu
+// 606518: abd
+// 603591: eawp1
+// 555935: orsxn
+// 525680: scmr
+// 467605: spl
+// 467405: lpl
+// 463927: lra
+// 416700: awd
+// 384090: dtb
+// 383544: cmk
+// 382254: fst
+// 378820: ssa
+// 370308: sra
+// 326432: alr
+// 321319: ldt
+// 319911: ldbr
+// 319908: sbar
+// 319907: lbar
+// 310379: cams
+// 303041: eawp7
+// 299122: xed
+// 294724: easp2
+// 270712: sztl
+// 252001: dfst
+// 241844: ste
+// 226970: absa
+// 218891: cioc
+// 184535: dfld
+// 182347: camp
+// 174567: ansq
+// 169317: rpt
+// 124972: erx2
+// 121933: fneg
+// 114697: cnaaq
+// 111728: rpd
+// 106892: dis
+// 96801: tov
+// 92283: fsb
+// 86209: erx4
+// 80564: eawp3
+// 76911: canaq
+// 65706: ufa
+// 65700: dfcmp
+// 64530: fdv
+// 48215: ldqc
+// 45994: dfad
+// 37790: awca
+// 27218: asxn
+// 23203: eawp5
+// 16947: gtb
+// 11431: ersxn
+// 9527: erx3
+// 8888: ssdr
+// 8888: ssdp
+// 8888: sptr
+// 8888: sptp
+// 8170: ssq
+// 7116: mp3d
+// 6969: cmg
+// 6878: dv3d
+// 5615: eawp6
+// 4859: easp1
+// 4726: easp3
+// 3157: ad2d
+// 2807: eawp4
+// 2807: easp4
+// 2411: cwl
+// 1912: teu
+// 1912: teo
+// 1798: cmpn
+// 1625: easp6
+// 931: adlaq
+// 659: erx1
+// 500: ???
+// 388: csr
+// 215: sb3d
+// 176: dfdv
+// 93: stcd
+// 92: mp2d
+// 41: sscr
+// 26: dfmp
+// 14: ad3d
+// 12: mve
+// 11: dfsb
+// 5: sdbr
+// 4: trtf
+// 4: orxn
+// 3: sb2d
+// 2: scdr
+// 1: stt
+// 1: ret
+// 1: drl
+
+ case x0 (0350): // epp0
+ case x1 (0351): // epp1
+ case x0 (0352): // epp2
+ case x1 (0353): // epp3
+ case x0 (0370): // epp4
+ case x1 (0371): // epp5
+ case x0 (0372): // epp6
+ case x1 (0373): // epp7
+ // For n = 0, 1, ..., or 7 as determined by operation code
+ // C(TPR.TRR) -> C(PRn.RNR)
+ // C(TPR.TSR) -> C(PRn.SNR)
+ // C(TPR.CA) -> C(PRn.WORDNO)
+ // C(TPR.TBR) -> C(PRn.BITNO)
+ {
+ // epp0 0350 101 000
+ // epp1 1351 101 001
+ // epp2 0352 101 010
+ // epp3 1353 101 011
+ // epp4 0370 111 000
+ // epp5 1371 111 001
+ // epp6 0372 111 010
+ // epp7 1373 111 011
+ //n = ((opcode10 & 020) ? 4 : 0) + (opcode10 & 03);
+ uint n = ((opcode10 & 020) >> 2) | (opcode10 & 03);
+ CPTUR (cptUsePRn + n);
+ cpu.PR[n].RNR = cpu.TPR.TRR;
+ cpu.PR[n].SNR = cpu.TPR.TSR;
+ cpu.PR[n].WORDNO = cpu.TPR.CA;
+ SET_PR_BITNO (n, cpu.TPR.TBR);
+ HDBGRegPR (n);
+ }
+ break;
+
+ case x0 (0250): // spri0
+ case x1 (0251): // spri1
+ case x0 (0252): // spri2
+ case x1 (0253): // spri3
+ case x0 (0650): // spri4
+ case x1 (0651): // spri5
+ case x0 (0652): // spri6
+ case x1 (0653): // spri7
+
+ // For n = 0, 1, ..., or 7 as determined by operation code
+ // 000 -> C(Y-pair)0,2
+ // C(PRn.SNR) -> C(Y-pair)3,17
+ // C(PRn.RNR) -> C(Y-pair)18,20
+ // 00...0 -> C(Y-pair)21,29
+ // (43)8 -> C(Y-pair)30,35
+ // C(PRn.WORDNO) -> C(Y-pair)36,53
+ // 000 -> C(Y-pair)54,56
+ // C(PRn.BITNO) -> C(Y-pair)57,62
+ // 00...0 -> C(Y-pair)63,71
+ {
+ // spri0 0250 0 010 101 000
+ // spri1 1251 1 010 101 001
+ // spri2 0252 0 010 101 010
+ // spri3 1253 1 010 101 011
+ // spri4 0650 0 110 101 000
+ // spri5 1651 1 110 101 001
+ // spri6 0652 0 110 101 010
+ // spri7 1653 1 110 101 011
+ //uint n = ((opcode10 & 0400) ? 4 : 0) + (opcode10 & 03);
+ uint n = ((opcode10 & 0400) >> 6) | (opcode10 & 03);
+ CPTUR (cptUsePRn + n);
+ cpu.Ypair[0] = 043;
+ cpu.Ypair[0] |= ((word36) cpu.PR[n].SNR) << 18;
+ cpu.Ypair[0] |= ((word36) cpu.PR[n].RNR) << 15;
+
+ cpu.Ypair[1] = (word36) cpu.PR[n].WORDNO << 18;
+ cpu.Ypair[1] |= (word36) GET_PR_BITNO (n) << 9;
+ }
+ break;
+
+ case x0 (0235): // lda
+ cpu.rA = cpu.CY;
+ HDBGRegA ();
+ SC_I_ZERO (cpu.rA == 0);
+ SC_I_NEG (cpu.rA & SIGN36);
+ break;
+
+ case x0 (0710): // tra
+ // C(TPR.CA) -> C(PPR.IC)
+ // C(TPR.TSR) -> C(PPR.PSR)
+ do_caf ();
+ read_tra_op ();
+ return CONT_TRA;
+
+ case x0 (0236): // ldq
+ cpu.rQ = cpu.CY;
+ HDBGRegQ ();
+ SC_I_ZERO (cpu.rQ == 0);
+ SC_I_NEG (cpu.rQ & SIGN36);
+ break;
+
+ case x0 (0600): // tze
+ // If zero indicator ON then
+ // C(TPR.CA) -> C(PPR.IC)
+ // C(TPR.TSR) -> C(PPR.PSR)
+ // otherwise, no change to C(PPR)
+ if (TST_I_ZERO)
+ {
+ do_caf ();
+ read_tra_op ();
+ return CONT_TRA;
+ }
+ break;
+
+ case x0 (0601): // tnz
+ // If zero indicator OFF then
+ // C(TPR.CA) -> C(PPR.IC)
+ // C(TPR.TSR) -> C(PPR.PSR)
+ if (!TST_I_ZERO)
+ {
+ do_caf ();
+ read_tra_op ();
+ return CONT_TRA;
+ }
+ break;
+
+ case x0 (0756): // stq
+ cpu.CY = cpu.rQ;
+ HDBGRegQ ();
+ break;
+
+ case x0 (0116): // cmpq
+ // C(Q) :: C(Y)
+ cmp36 (cpu.rQ, cpu.CY, &cpu.cu.IR);
+ break;
+
+ case x0 (0377): //< anaq
+ // C(AQ)i & C(Y-pair)i -> C(AQ)i for i = (0, 1, ..., 71)
+ {
+ word72 tmp72 = YPAIRTO72 (cpu.Ypair);
+ word72 trAQ = convert_to_word72 (cpu.rA, cpu.rQ);
+#ifdef NEED_128
+ trAQ = and_128 (trAQ, tmp72);
+ trAQ = and_128 (trAQ, MASK72);
+
+ SC_I_ZERO (iszero_128 (trAQ));
+ SC_I_NEG (isnonzero_128 (and_128 (trAQ, SIGN72)));
+#else
+ trAQ = trAQ & tmp72;
+ trAQ &= MASK72;
+
+ SC_I_ZERO (trAQ == 0);
+ SC_I_NEG (trAQ & SIGN72);
+#endif
+ convert_to_word36 (trAQ, &cpu.rA, &cpu.rQ);
+ HDBGRegA ();
+ HDBGRegQ ();
+ }
+ break;
+
+ case x0 (0755): // sta
+ cpu.CY = cpu.rA;
+ HDBGRegA ();
+ break;
+
+ // lprpn
+ case x0 (0760): // lprp0
+ case x0 (0761): // lprp1
+ case x0 (0762): // lprp2
+ case x0 (0763): // lprp3
+ case x0 (0764): // lprp4
+ case x0 (0765): // lprp5
+ case x0 (0766): // lprp6
+ case x0 (0767): // lprp7
+ // For n = 0, 1, ..., or 7 as determined by operation code
+ // C(TPR.TRR) -> C(PRn.RNR)
+ // If C(Y)0,1 != 11, then
+ // C(Y)0,5 -> C(PRn.BITNO);
+ // otherwise,
+ // generate command fault
+ // If C(Y)6,17 = 11...1, then 111 -> C(PRn.SNR)0,2
+ // otherwise,
+ // 000 -> C(PRn.SNR)0,2
+ // C(Y)6,17 -> C(PRn.SNR)3,14
+ // C(Y)18,35 -> C(PRn.WORDNO)
+ {
+ uint32 n = opcode10 & 07; // get n
+ CPTUR (cptUsePRn + n);
+ cpu.PR[n].RNR = cpu.TPR.TRR;
+
+// [CAC] sprpn says: If C(PRn.SNR) 0,2 are nonzero, and C(PRn.SNR) != 11...1,
+// then a store fault (illegal pointer) will occur and C(Y) will not be changed.
+// I interpret this has meaning that only the high bits should be set here
+
+ if (((cpu.CY >> 34) & 3) != 3)
+ {
+ word6 bitno = (cpu.CY >> 30) & 077;
+ SET_PR_BITNO (n, bitno);
+ }
+ else
+ {
+// fim.alm
+// command_fault:
+// eax7 com assume normal command fault
+// ldq bp|mc.scu.port_stat_word check illegal action
+// canq scu.ial_mask,dl
+// tnz fixindex nonzero, treat as normal case
+// ldq bp|scu.even_inst_word check for LPRPxx instruction
+// anq =o770400,dl
+// cmpq lprp_insts,dl
+// tnz fixindex isn't LPRPxx, treat as normal
+
+// ial_mask is checking SCU word 1, field IA: 0 means "no illegal action"
+
+ // Therefore the subfault well no illegal action, and
+ // Multics will peek it the instruction to deduce that it
+ // is a lprpn fault.
+ doFault (FAULT_CMD, fst_cmd_lprpn, "lprpn");
+ }
+// The SPRPn instruction stores only the low 12 bits of the 15 bit SNR.
+// A special case is made for an SNR of all ones; it is stored as 12 1's.
+// The pcode in AL39 handles this awkwardly; I believe this is
+// the same, but in a more straightforward manner
+
+ // Get the 12 bit operand SNR
+ word12 oSNR = getbits36_12 (cpu.CY, 6);
+ // Test for special case
+ if (oSNR == 07777)
+ cpu.PR[n].SNR = 077777;
+ else
+ cpu.PR[n].SNR = oSNR; // usigned word will 0-extend.
+ //C(Y)18,35 -> C(PRn.WORDNO)
+ cpu.PR[n].WORDNO = GETLO (cpu.CY);
+
+ sim_debug (DBG_APPENDING, & cpu_dev,
+ "lprp%d CY 0%012"PRIo64", PR[n].RNR 0%o, "
+ "PR[n].BITNO 0%o, PR[n].SNR 0%o, PR[n].WORDNO %o\n",
+ n, cpu.CY, cpu.PR[n].RNR, GET_PR_BITNO (n),
+ cpu.PR[n].SNR, cpu.PR[n].WORDNO);
+ HDBGRegPR (n);
+ }
+ break;
+
+ // eaxn
+ case x0 (0620): // eax0
+ case x0 (0621): // eax1
+ case x0 (0622): // eax2
+ case x0 (0623): // eax3
+ case x0 (0624): // eax4
+ case x0 (0625): // eax5
+ case x0 (0626): // eax6
+ case x0 (0627): // eax7
+ {
+ uint32 n = opcode10 & 07; // get n
+ cpu.rX[n] = cpu.TPR.CA;
+ HDBGRegX (n);
+
+ SC_I_ZERO (cpu.TPR.CA == 0);
+ SC_I_NEG (cpu.TPR.CA & SIGN18);
+
+ }
+ break;
+
+ // tsxn
+ case x0 (0700): // tsx0
+ case x0 (0701): // tsx1
+ case x0 (0702): // tsx2
+ case x0 (0703): // tsx3
+ case x0 (0704): // tsx4
+ case x0 (0705): // tsx5
+ case x0 (0706): // tsx6
+ case x0 (0707): // tsx7
+ // For n = 0, 1, ..., or 7 as determined by operation code
+ // C(PPR.IC) + 1 -> C(Xn)
+ // C(TPR.CA) -> C(PPR.IC)
+ // C(TPR.TSR) -> C(PPR.PSR)
+ {
+ // We can't set Xn yet as the CAF may refer to Xn
+ word18 ret = (cpu.PPR.IC + 1) & MASK18;
+ do_caf ();
+ read_tra_op ();
+ cpu.rX[opcode10 & 07] = ret;
+ HDBGRegX (opcode10 & 07);
+ }
+ return CONT_TRA;
+
+ case x0 (0450): // stz
+ cpu.CY = 0;
+ break;
+
+ // epbpn
+ case x1 (0350): // epbp0
+ case x0 (0351): // epbp1
+ case x1 (0352): // epbp2
+ case x0 (0353): // epbp3
+ case x1 (0370): // epbp4
+ case x0 (0371): // epbp5
+ case x1 (0372): // epbp6
+ case x0 (0373): // epbp7
+ // For n = 0, 1, ..., or 7 as determined by operation code
+ // C(TPR.TRR) -> C(PRn.RNR)
+ // C(TPR.TSR) -> C(PRn.SNR)
+ // 00...0 -> C(PRn.WORDNO)
+ // 0000 -> C(PRn.BITNO)
+ {
+ // epbp0 1350 101 000
+ // epbp1 0351 101 000
+ // epbp2 1352 101 000
+ // epbp3 0353 101 000
+ // epbp4 1370 111 000
+ // epbp4 0371 111 000
+ // epbp6 1372 111 000
+ // epbp7 0373 111 000
+ //n = ((opcode10 & 020) ? 4 : 0) + (opcode10 & 03);
+ uint n = ((opcode10 & 020) >> 2) | (opcode10 & 03);
+ CPTUR (cptUsePRn + n);
+ cpu.PR[n].RNR = cpu.TPR.TRR;
+ cpu.PR[n].SNR = cpu.TPR.TSR;
+ cpu.PR[n].WORDNO = 0;
+ SET_PR_BITNO (n, 0);
+ HDBGRegPR (n);
+ }
+ break;
+
+ case x0 (0115): // cmpa
+ // C(A) :: C(Y)
+ cmp36 (cpu.rA, cpu.CY, &cpu.cu.IR);
+ break;
+
+ case x0 (0054): // aos
+ {
+ // C(Y)+1->C(Y)
+
+#ifdef L68
+ cpu.ou.cycle |= ou_GOS;
+#endif
+ bool ovf;
+ cpu.CY = Add36b (cpu.CY, 1, 0, I_ZNOC,
+ & cpu.cu.IR, & ovf);
+ overflow (ovf, true, "aos overflow fault");
+#ifdef TEST_OLIN
+ cmpxchg ();
+#endif
+ }
+ break;
+
+
+ case x0 (0315): // cana
+ // C(Z)i = C(A)i & C(Y)i for i = (0, 1, ..., 35)
+ {
+ word36 trZ = cpu.rA & cpu.CY;
+ trZ &= MASK36;
+
+ SC_I_ZERO (trZ == 0);
+ SC_I_NEG (trZ & SIGN36);
+ }
+ break;
+
+ case x0 (0237): // ldaq
+ cpu.rA = cpu.Ypair[0];
+ HDBGRegA ();
+ cpu.rQ = cpu.Ypair[1];
+ HDBGRegQ ();
+ SC_I_ZERO (cpu.rA == 0 && cpu.rQ == 0)
+ SC_I_NEG (cpu.rA & SIGN36);
+ break;
+
+ case x1 (0605): // tpnz
+ // If negative and zero indicators are OFF then
+ // C(TPR.CA) -> C(PPR.IC)
+ // C(TPR.TSR) -> C(PPR.PSR)
+ if (! (cpu.cu.IR & I_NEG) && ! (cpu.cu.IR & I_ZERO))
+ {
+ do_caf ();
+ read_tra_op ();
+ return CONT_TRA;
+ }
+ break;
+
+ // lxln
+ case x0 (0720): // lxl0
+ case x0 (0721): // lxl1
+ case x0 (0722): // lxl2
+ case x0 (0723): // lxl3
+ case x0 (0724): // lxl4
+ case x0 (0725): // lxl5
+ case x0 (0726): // lxl6
+ case x0 (0727): // lxl7
+ {
+ uint32 n = opcode10 & 07; // get n
+ cpu.rX[n] = GETLO (cpu.CY);
+ HDBGRegX (n);
+ SC_I_ZERO (cpu.rX[n] == 0);
+ SC_I_NEG (cpu.rX[n] & SIGN18);
+ }
+ break;
+
+ case x0 (0757): // staq
+ cpu.Ypair[0] = cpu.rA;
+ cpu.Ypair[1] = cpu.rQ;
+ break;
+
+ // tspn
+ case x0 (0270): // tsp0
+ case x0 (0271): // tsp1
+ case x0 (0272): // tsp2
+ case x0 (0273): // tsp3
+ case x0 (0670): // tsp4
+ case x0 (0671): // tsp5
+ case x0 (0672): // tsp6
+ case x0 (0673): // tsp7
+ // For n = 0, 1, ..., or 7 as determined by operation code
+ // C(PPR.PRR) -> C(PRn.RNR)
+ // C(PPR.PSR) -> C(PRn.SNR)
+ // C(PPR.IC) + 1 -> C(PRn.WORDNO)
+ // 00...0 -> C(PRn.BITNO)
+ // C(TPR.CA) -> C(PPR.IC)
+ // C(TPR.TSR) -> C(PPR.PSR)
+ {
+#ifdef PANEL
+ uint32 n;
+ if (opcode10 <= 0273)
+ n = (opcode10 & 3);
+ else
+ n = (opcode10 & 3) + 4;
+ CPTUR (cptUsePRn + n);
+#endif
+
+ do_caf ();
+ // PR[n] is set in read_tra_op().
+ read_tra_op ();
+ }
+ return CONT_TRA;
+
+ case x0 (0735): // als
+ {
+ word36 tmp36 = cpu.TPR.CA & 0177; // CY bits 11-17
+
+ word36 tmpSign = cpu.rA & SIGN36;
+ CLR_I_CARRY;
+
+ for (uint j = 0; j < tmp36; j ++)
+ {
+ cpu.rA <<= 1;
+ if (tmpSign != (cpu.rA & SIGN36))
+ SET_I_CARRY;
+ }
+ cpu.rA &= DMASK; // keep to 36-bits
+ HDBGRegA ();
+
+ SC_I_ZERO (cpu.rA == 0);
+ SC_I_NEG (cpu.rA & SIGN36);
+ }
+ break;
+
+ case x0 (0610): // rtcd
+ // If an access violation fault occurs when fetching the SDW for
+ // the Y-pair, the C(PPR.PSR) and C(PPR.PRR) are not altered.
+
+ do_caf ();
+ Read2 (cpu.TPR.CA, cpu.Ypair, RTCD_OPERAND_FETCH);
+ // RTCD always ends up in append mode.
+ set_addr_mode (APPEND_mode);
+
+ return CONT_RET;
+
+ case x0 (0604): // tmi
+ // If negative indicator ON then
+ // C(TPR.CA) -> C(PPR.IC)
+ // C(TPR.TSR) -> C(PPR.PSR)
+ if (TST_I_NEG)
+ {
+ do_caf ();
+ read_tra_op ();
+ return CONT_TRA;
+ }
+ break;
+
+ // stxn
+ case x0 (0740): // stx0
+ case x0 (0741): // stx1
+ case x0 (0742): // stx2
+ case x0 (0743): // stx3
+ case x0 (0744): // stx4
+ case x0 (0745): // stx5
+ case x0 (0746): // stx6
+ case x0 (0747): // stx7
+ {
+ uint32 n = opcode10 & 07; // get n
+ //SETHI (cpu.CY, cpu.rX[n]);
+ cpu.CY = ((word36) cpu.rX[n]) << 18;
+ cpu.zone = 0777777000000;
+ cpu.useZone = true;
+ }
+#ifdef TEST_OLIN
+ cmpxchg ();
+#endif
+ break;
+
+ case x0 (0634): // ldi
+ {
+ CPTUR (cptUseIR);
+ // C(Y)18,31 -> C(IR)
+
+ // Indicators:
+ // Parity Mask:
+ // If C(Y)27 = 1, and the processor is in absolute or
+ // instruction privileged mode, then ON; otherwise OFF.
+ // This indicator is not affected in the normal or BAR modes.
+ // Not BAR mode:
+ // Cannot be changed by the ldi instruction
+ // MIF:
+ // If C(Y)30 = 1, and the processor is in absolute or
+ // instruction privileged mode, then ON; otherwise OFF.
+ // This indicator is not affected in normal or BAR modes.
+ // Absolute mode:
+ // Cannot be changed by the ldi instruction
+ // All others: If corresponding bit in C(Y) is 1, then ON;
+ // otherwise, OFF
+
+ // upper 14-bits of lower 18-bits
+
+ // AL39 ldi says that HEX is ignored, but the mode register
+ // description says that it isn't
+#ifdef DPS8M
+ word18 tmp18 = GETLO (cpu.CY) & 0777770;
+#endif
+#ifdef L68
+ word18 tmp18 = GETLO (cpu.CY) & 0777760;
+#endif
+
+ bool bAbsPriv = is_priv_mode ();
+
+ SC_I_ZERO (tmp18 & I_ZERO);
+ SC_I_NEG (tmp18 & I_NEG);
+ SC_I_CARRY (tmp18 & I_CARRY);
+ SC_I_OFLOW (tmp18 & I_OFLOW);
+ SC_I_EOFL (tmp18 & I_EOFL);
+ SC_I_EUFL (tmp18 & I_EUFL);
+ SC_I_OMASK (tmp18 & I_OMASK);
+ SC_I_TALLY (tmp18 & I_TALLY);
+ SC_I_PERR (tmp18 & I_PERR);
+ // I_PMASK handled below
+ // LDI cannot change I_NBAR
+ SC_I_TRUNC (tmp18 & I_TRUNC);
+ // I_MIF handled below
+ // LDI cannot change I_ABS
+#ifdef DPS8M
+ SC_I_HEX (tmp18 & I_HEX);
+#endif
+
+#if 0
+ cpu.bar_attempt = false;
+
+#endif
+ if (bAbsPriv)
+ {
+ SC_I_PMASK (tmp18 & I_PMASK);
+ SC_I_MIF (tmp18 & I_MIF);
+#if 0
+ if (! (tmp18 & I_NBAR))
+ {
+ cpu.bar_attempt = true;
+ }
+#endif
+ }
+ else
+ {
+ CLR_I_PMASK;
+ CLR_I_MIF;
+ }
+ }
+ break;
+
+ case x0 (0677): // eraq
+ // C(AQ)i XOR C(Y-pair)i -> C(AQ)i for i = (0, 1, ..., 71)
+ {
+ word72 tmp72 = YPAIRTO72 (cpu.Ypair);
+ word72 trAQ = convert_to_word72 (cpu.rA, cpu.rQ);
+#ifdef NEED_128
+ trAQ = xor_128 (trAQ, tmp72);
+ trAQ = and_128 (trAQ, MASK72);
+
+ SC_I_ZERO (iszero_128 (trAQ));
+ SC_I_NEG (isnonzero_128 (and_128 (trAQ, SIGN72)));
+#else
+ trAQ = trAQ ^ tmp72;
+ trAQ &= MASK72;
+
+ SC_I_ZERO (trAQ == 0);
+ SC_I_NEG (trAQ & SIGN72);
+#endif
+
+ convert_to_word36 (trAQ, &cpu.rA, &cpu.rQ);
+ HDBGRegA ();
+ HDBGRegQ ();
+ }
+ break;
+
+ case x0 (0275): // ora
+ // C(A)i | C(Y)i -> C(A)i for i = (0, 1, ..., 35)
+ cpu.rA = cpu.rA | cpu.CY;
+ cpu.rA &= DMASK;
+ HDBGRegA ();
+
+ SC_I_ZERO (cpu.rA == 0);
+ SC_I_NEG (cpu.rA & SIGN36);
+ break;
+
+ case x0 (0076): // adq
+ {
+#ifdef L68
+ cpu.ou.cycle |= ou_GOS;
+#endif
+ bool ovf;
+ cpu.rQ = Add36b (cpu.rQ, cpu.CY, 0, I_ZNOC,
+ & cpu.cu.IR, & ovf);
+ HDBGRegQ ();
+ overflow (ovf, false, "adq overflow fault");
+ }
+ break;
+
+ case x1 (0604): // tmoz
+ // If negative or zero indicator ON then
+ // C(TPR.CA) -> C(PPR.IC)
+ // C(TPR.TSR) -> C(PPR.PSR)
+ if (cpu.cu.IR & (I_NEG | I_ZERO))
+ {
+ do_caf ();
+ read_tra_op ();
+ return CONT_TRA;
+ }
+ break;
+
+
+ case x1 (0250): // spbp0
+ case x0 (0251): // spbp1
+ case x1 (0252): // spbp2
+ case x0 (0253): // spbp3
+ case x1 (0650): // spbp4
+ case x0 (0651): // spbp5
+ case x1 (0652): // spbp6
+ case x0 (0653): // spbp7
+ // For n = 0, 1, ..., or 7 as determined by operation code
+ // C(PRn.SNR) -> C(Y-pair)3,17
+ // C(PRn.RNR) -> C(Y-pair)18,20
+ // 000 -> C(Y-pair)0,2
+ // 00...0 -> C(Y-pair)21,29
+ // (43)8 -> C(Y-pair)30,35
+ // 00...0 -> C(Y-pair)36,71
+ {
+ // spbp0 1250 010 101 000
+ // spbp1 0251 010 101 001
+ // spbp2 1252 010 101 010
+ // spbp3 0253 010 101 011
+ // spbp4 1650 110 101 000
+ // spbp5 0651 110 101 001
+ // spbp6 1652 110 101 010
+ // spbp8 0653 110 101 011
+ uint n = ((opcode10 & 0400) >> 6) | (opcode10 & 03);
+ CPTUR (cptUsePRn + n);
+ cpu.Ypair[0] = 043;
+ cpu.Ypair[0] |= ((word36) cpu.PR[n].SNR) << 18;
+ cpu.Ypair[0] |= ((word36) cpu.PR[n].RNR) << 15;
+ cpu.Ypair[1] = 0;
+ }
+ break;
+
+ case x0 (0375): // ana
+ // C(A)i & C(Y)i -> C(A)i for i = (0, 1, ..., 35)
+ cpu.rA = cpu.rA & cpu.CY;
+ cpu.rA &= DMASK;
+ HDBGRegA ();
+ SC_I_ZERO (cpu.rA == 0);
+ SC_I_NEG (cpu.rA & SIGN36);
+ break;
+
+ case x0 (0431): // fld
+ // C(Y)0,7 -> C(E)
+ // C(Y)8,35 -> C(AQ)0,27
+ // 00...0 -> C(AQ)30,71
+ // Zero: If C(AQ) = 0, then ON; otherwise OFF
+ // Neg: If C(AQ)0 = 1, then ON; otherwise OFF
+
+ CPTUR (cptUseE);
+ cpu.CY &= DMASK;
+ cpu.rE = (cpu.CY >> 28) & 0377;
+ cpu.rA = (cpu.CY & FLOAT36MASK) << 8;
+ HDBGRegA ();
+ cpu.rQ = 0;
+ HDBGRegQ ();
+
+ SC_I_ZERO (cpu.rA == 0 && cpu.rQ == 0);
+ SC_I_NEG (cpu.rA & SIGN36);
+ break;
+
+ case x0 (0213): // epaq
+ // 000 -> C(AQ)0,2
+ // C(TPR.TSR) -> C(AQ)3,17
+ // 00...0 -> C(AQ)18,32
+ // C(TPR.TRR) -> C(AQ)33,35
+
+ // C(TPR.CA) -> C(AQ)36,53
+ // 00...0 -> C(AQ)54,65
+ // C(TPR.TBR) -> C(AQ)66,71
+
+ cpu.rA = cpu.TPR.TRR & MASK3;
+ cpu.rA |= (word36) (cpu.TPR.TSR & MASK15) << 18;
+ HDBGRegA ();
+
+ cpu.rQ = cpu.TPR.TBR & MASK6;
+ cpu.rQ |= (word36) (cpu.TPR.CA & MASK18) << 18;
+ HDBGRegQ ();
+
+ SC_I_ZERO (cpu.rA == 0 && cpu.rQ == 0);
+
+ break;
+
+ case x0 (0736): // qls
+ // Shift C(Q) left the number of positions given in
+ // C(TPR.CA)11,17; fill vacated positions with zeros.
+ {
+ word36 tmp36 = cpu.TPR.CA & 0177; // CY bits 11-17
+ word36 tmpSign = cpu.rQ & SIGN36;
+ CLR_I_CARRY;
+
+ for (uint j = 0; j < tmp36; j ++)
+ {
+ cpu.rQ <<= 1;
+ if (tmpSign != (cpu.rQ & SIGN36))
+ SET_I_CARRY;
+ }
+ cpu.rQ &= DMASK; // keep to 36-bits
+ HDBGRegQ ();
+
+ SC_I_ZERO (cpu.rQ == 0);
+ SC_I_NEG (cpu.rQ & SIGN36);
+ }
+ break;
+
+ case x0 (0754): // sti
+
+ // C(IR) -> C(Y)18,31
+ // 00...0 -> C(Y)32,35
+
+ // The contents of the indicator register after address
+ // preparation are stored in C(Y)18,31 C(Y)18,31 reflects the
+ // state of the tally runout indicator prior to address
+ // preparation. The relation between C(Y)18,31 and the indicators
+ // is given in Table 4-5.
+
+ CPTUR (cptUseIR);
+ // AL39 sti says that HEX is ignored, but the mode register
+ // description says that it isn't
+#ifdef DPS8M
+ //SETLO (cpu.CY, (cpu.cu.IR & 0000000777770LL));
+ cpu.CY = cpu.cu.IR & 0000000777770LL;
+#endif
+#ifdef L68
+ //SETLO (cpu.CY, (cpu.cu.IR & 0000000777760LL));
+ cpu.CY = cpu.cu.IR & 0000000777760LL;
+#endif
+ cpu.zone = 0000000777777;
+ cpu.useZone = true;
+ SCF (i->stiTally, cpu.CY, I_TALLY);
+#ifdef TEST_OLIN
+ cmpxchg ();
+#endif
+ break;
+
+
+ /// FIXED-POINT ARITHMETIC INSTRUCTIONS
+
+ /// Fixed-Point Data Movement Load
+
+ case x0 (0635): // eaa
+ cpu.rA = 0;
+ SETHI (cpu.rA, cpu.TPR.CA);
+ HDBGRegA ();
+ SC_I_ZERO (cpu.TPR.CA == 0);
+ SC_I_NEG (cpu.TPR.CA & SIGN18);
+
+ break;
+
+ case x0 (0636): // eaq
+ cpu.rQ = 0;
+ SETHI (cpu.rQ, cpu.TPR.CA);
+ HDBGRegQ ();
+
+ SC_I_ZERO (cpu.TPR.CA == 0);
+ SC_I_NEG (cpu.TPR.CA & SIGN18);
+
+ break;
+
+// Optimized to the top of the loop
+// case x0 (0620): // eax0
+// case x0 (0621): // eax1
+// case x0 (0622): // eax2
+// case x0 (0623): // eax3
+// case x0 (0624): // eax4
+// case x0 (0625): // eax5
+// case x0 (0626): // eax6
+// case x0 (0627): // eax7
+
+ case x0 (0335): // lca
+ {
+ bool ovf;
+ cpu.rA = compl36 (cpu.CY, & cpu.cu.IR, & ovf);
+ HDBGRegA ();
+ overflow (ovf, false, "lca overflow fault");
+ }
+ break;
+
+ case x0 (0336): // lcq
+ {
+ bool ovf;
+ cpu.rQ = compl36 (cpu.CY, & cpu.cu.IR, & ovf);
+ HDBGRegQ ();
+ overflow (ovf, false, "lcq overflow fault");
+ }
+ break;
+
+ // lcxn
+ case x0 (0320): // lcx0
+ case x0 (0321): // lcx1
+ case x0 (0322): // lcx2
+ case x0 (0323): // lcx3
+ case x0 (0324): // lcx4
+ case x0 (0325): // lcx5
+ case x0 (0326): // lcx6
+ case x0 (0327): // lcx7
+ {
+ bool ovf;
+ uint32 n = opcode10 & 07; // get n
+ cpu.rX[n] = compl18 (GETHI (cpu.CY), & cpu.cu.IR, & ovf);
+ HDBGRegX (n);
+ overflow (ovf, false, "lcxn overflow fault");
+ }
+ break;
+
+ case x0 (0337): // lcaq
+ {
+ // The lcaq instruction changes the number to its negative while
+ // moving it from Y-pair to AQ. The operation is executed by
+ // forming the twos complement of the string of 72 bits. In twos
+ // complement arithmetic, the value 0 is its own negative. An
+ // overflow condition exists if C(Y-pair) = -2**71.
+
+
+ if (cpu.Ypair[0] == 0400000000000LL && cpu.Ypair[1] == 0)
+ {
+ cpu.rA = cpu.Ypair[0];
+ HDBGRegA ();
+ cpu.rQ = cpu.Ypair[1];
+ HDBGRegQ ();
+ SET_I_NEG;
+ CLR_I_ZERO;
+ overflow (true, false, "lcaq overflow fault");
+ }
+ else if (cpu.Ypair[0] == 0 && cpu.Ypair[1] == 0)
+ {
+ cpu.rA = 0;
+ HDBGRegA ();
+ cpu.rQ = 0;
+ HDBGRegQ ();
+
+ SET_I_ZERO;
+ CLR_I_NEG;
+ }
+ else
+ {
+ word72 tmp72 = convert_to_word72 (cpu.Ypair[0], cpu.Ypair[1]);
+#ifdef NEED_128
+ tmp72 = negate_128 (tmp72);
+#else
+ tmp72 = ~tmp72 + 1;
+#endif
+ convert_to_word36 (tmp72, & cpu.rA, & cpu.rQ);
+ HDBGRegA ();
+ HDBGRegQ ();
+
+ SC_I_ZERO (cpu.rA == 0 && cpu.rQ == 0);
+ SC_I_NEG (cpu.rA & SIGN36);
+ }
+ }
+ break;
+
+// Optimized to the top of the loop
+// case x0 (0235): // lda
+
+ case x0 (0034): // ldac
+ cpu.rA = cpu.CY;
+ HDBGRegA ();
+ SC_I_ZERO (cpu.rA == 0);
+ SC_I_NEG (cpu.rA & SIGN36);
+ cpu.CY = 0;
+#ifdef TEST_OLIN
+ cmpxchg ();
+#endif
+ break;
+
+// Optimized to the top of the loop
+// case x0 (0237): // ldaq
+
+// Optimized to the top of the loop
+// case x0 (0634): // ldi
+
+// Optimized to the top of the loop
+// case x0 (0236): // ldq
+
+ case x0 (0032): // ldqc
+ cpu.rQ = cpu.CY;
+ HDBGRegQ ();
+ SC_I_ZERO (cpu.rQ == 0);
+ SC_I_NEG (cpu.rQ & SIGN36);
+ cpu.CY = 0;
+#ifdef TEST_OLIN
+ cmpxchg ();
+#endif
+ break;
+
+ // ldxn
+ case x0 (0220): // ldx0
+ case x0 (0221): // ldx1
+ case x0 (0222): // ldx2
+ case x0 (0223): // ldx3
+ case x0 (0224): // ldx4
+ case x0 (0225): // ldx5
+ case x0 (0226): // ldx6
+ case x0 (0227): // ldx7
+ {
+ uint32 n = opcode10 & 07; // get n
+ cpu.rX[n] = GETHI (cpu.CY);
+ HDBGRegX (n);
+ SC_I_ZERO (cpu.rX[n] == 0);
+ SC_I_NEG (cpu.rX[n] & SIGN18);
+ }
+ break;
+
+ case x0 (0073): // lreg
+ CPTUR (cptUseE);
+#ifdef L68
+ cpu.ou.cycle |= ou_GOS;
+ cpu.ou.eac = 0;
+#endif
+ cpu.rX[0] = GETHI (cpu.Yblock8[0]);
+ HDBGRegX (0);
+ cpu.rX[1] = GETLO (cpu.Yblock8[0]);
+ HDBGRegX (1);
+#ifdef L68
+ cpu.ou.eac ++;
+#endif
+ cpu.rX[2] = GETHI (cpu.Yblock8[1]);
+ HDBGRegX (2);
+ cpu.rX[3] = GETLO (cpu.Yblock8[1]);
+ HDBGRegX (3);
+#ifdef L68
+ cpu.ou.eac ++;
+#endif
+ cpu.rX[4] = GETHI (cpu.Yblock8[2]);
+ HDBGRegX (4);
+ cpu.rX[5] = GETLO (cpu.Yblock8[2]);
+ HDBGRegX (5);
+#ifdef L68
+ cpu.ou.eac ++;
+#endif
+ cpu.rX[6] = GETHI (cpu.Yblock8[3]);
+ HDBGRegX (6);
+ cpu.rX[7] = GETLO (cpu.Yblock8[3]);
+ HDBGRegX (7);
+#ifdef L68
+ cpu.ou.eac = 0;
+#endif
+ cpu.rA = cpu.Yblock8[4];
+ HDBGRegA ();
+ cpu.rQ = cpu.Yblock8[5];
+ HDBGRegQ ();
+ cpu.rE = (GETHI (cpu.Yblock8[6]) >> 10) & 0377; // need checking
+ break;
+
+// Optimized to the top of the loop
+// // lxln
+// case x0 (0720): // lxl0
+// case x0 (0721): // lxl1
+// case x0 (0722): // lxl2
+// case x0 (0723): // lxl3
+// case x0 (0724): // lxl4
+// case x0 (0725): // lxl5
+// case x0 (0726): // lxl6
+// case x0 (0727): // lxl7
+
+ /// Fixed-Point Data Movement Store
+
+ case x0 (0753): // sreg
+ CPTUR (cptUseE);
+ CPTUR (cptUseRALR);
+ // clear block (changed to memset() per DJ request)
+ //memset (cpu.Yblock8, 0, sizeof (cpu.Yblock8));
+#ifdef L68
+ cpu.ou.cycle |= ou_GOS;
+ cpu.ou.eac = 0;
+#endif
+ SETHI (cpu.Yblock8[0], cpu.rX[0]);
+ SETLO (cpu.Yblock8[0], cpu.rX[1]);
+#ifdef L68
+ cpu.ou.eac ++;
+#endif
+ SETHI (cpu.Yblock8[1], cpu.rX[2]);
+ SETLO (cpu.Yblock8[1], cpu.rX[3]);
+#ifdef L68
+ cpu.ou.eac ++;
+#endif
+ SETHI (cpu.Yblock8[2], cpu.rX[4]);
+ SETLO (cpu.Yblock8[2], cpu.rX[5]);
+#ifdef L68
+ cpu.ou.eac ++;
+#endif
+ SETHI (cpu.Yblock8[3], cpu.rX[6]);
+ SETLO (cpu.Yblock8[3], cpu.rX[7]);
+#ifdef L68
+ cpu.ou.eac = 0;
+#endif
+ cpu.Yblock8[4] = cpu.rA;
+ HDBGRegA ();
+ cpu.Yblock8[5] = cpu.rQ;
+ HDBGRegQ ();
+ cpu.Yblock8[6] = ((word36)(cpu.rE & MASK8)) << 28;
+#ifdef ISOLTS
+ if (current_running_cpu_idx)
+ cpu.Yblock8[7] = (((-- cpu.shadowTR) & MASK27) << 9) | (cpu.rRALR & 07);
+ else
+ cpu.Yblock8[7] = ((cpu.rTR & MASK27) << 9) | (cpu.rRALR & 07);
+
+#else
+ cpu.Yblock8[7] = ((cpu.rTR & MASK27) << 9) | (cpu.rRALR & 07);
+#endif
+ break;
+
+// Optimized to the top of the loop
+// case x0 (0755): // sta
+
+ case x0 (0354): // stac
+ if (cpu.CY == 0)
+ {
+ SET_I_ZERO;
+ cpu.CY = cpu.rA;
+ }
+ else
+ CLR_I_ZERO;
+#ifdef TEST_OLIN
+ cmpxchg ();
+#endif
+ break;
+
+ case x0 (0654): // stacq
+ if (cpu.CY == cpu.rQ)
+ {
+ cpu.CY = cpu.rA;
+ SET_I_ZERO;
+ }
+ else
+ CLR_I_ZERO;
+#ifdef TEST_OLIN
+ cmpxchg ();
+#endif
+ break;
+
+// Optimized to the top of the loop
+// case x0 (0757): // staq
+
+ case x0 (0551): // stba
+ // 9-bit bytes of C(A) -> corresponding bytes of C(Y), the byte
+ // positions affected being specified in the TAG field.
+ //copyBytes ((i->tag >> 2) & 0xf, cpu.rA, &cpu.CY);
+ cpu.CY = cpu.rA;
+ cpu.zone =
+ ((i->tag & 040) ? 0777000000000u : 0) |
+ ((i->tag & 020) ? 0000777000000u : 0) |
+ ((i->tag & 010) ? 0000000777000u : 0) |
+ ((i->tag & 004) ? 0000000000777u : 0);
+ cpu.useZone = true;
+ cpu.ou.crflag = true;
+#ifdef TEST_OLIN
+ cmpxchg ();
+#endif
+ break;
+
+ case x0 (0552): // stbq
+ // 9-bit bytes of C(Q) -> corresponding bytes of C(Y), the byte
+ // positions affected being specified in the TAG field.
+ //copyBytes ((i->tag >> 2) & 0xf, cpu.rQ, &cpu.CY);
+ cpu.CY = cpu.rQ;
+ cpu.zone =
+ ((i->tag & 040) ? 0777000000000u : 0) |
+ ((i->tag & 020) ? 0000777000000u : 0) |
+ ((i->tag & 010) ? 0000000777000u : 0) |
+ ((i->tag & 004) ? 0000000000777u : 0);
+ cpu.useZone = true;
+ cpu.ou.crflag = true;
+#ifdef TEST_OLIN
+ cmpxchg ();
+#endif
+ break;
+
+ case x0 (0554): // stc1
+ // "C(Y)25 reflects the state of the tally runout indicator
+ // prior to modification.
+ SETHI (cpu.CY, (cpu.PPR.IC + 1) & MASK18);
+ // AL39 stc1 says that HEX is ignored, but the mode register
+ // description says that it isn't
+#ifdef DPS8M
+ SETLO (cpu.CY, cpu.cu.IR & 0777770);
+#endif
+#ifdef L68
+ SETLO (cpu.CY, cpu.cu.IR & 0777760);
+#endif
+ SCF (i->stiTally, cpu.CY, I_TALLY);
+ break;
+
+ case x0 (0750): // stc2
+ // AL-39 doesn't specify if the low half is set to zero,
+ // set to IR, or left unchanged
+ // RJ78 specifies unchanged
+ //SETHI (cpu.CY, (cpu.PPR.IC + 2) & MASK18);
+ cpu.CY = ((word36) ((cpu.PPR.IC + 2) & MASK18)) << 18;
+ cpu.zone = 0777777000000;
+ cpu.useZone = true;
+#ifdef TEST_OLIN
+ cmpxchg ();
+#endif
+ break;
+
+ case x0 (0751): // stca
+ // Characters of C(A) -> corresponding characters of C(Y),
+ // the character positions affected being specified in the TAG
+ // field.
+ //copyChars (i->tag, cpu.rA, &cpu.CY);
+ cpu.CY = cpu.rA;
+ cpu.zone =
+ ((i->tag & 040) ? 0770000000000u : 0) |
+ ((i->tag & 020) ? 0007700000000u : 0) |
+ ((i->tag & 010) ? 0000077000000u : 0) |
+ ((i->tag & 004) ? 0000000770000u : 0) |
+ ((i->tag & 002) ? 0000000007700u : 0) |
+ ((i->tag & 001) ? 0000000000077u : 0);
+ cpu.useZone = true;
+ cpu.ou.crflag = true;
+#ifdef TEST_OLIN
+ cmpxchg ();
+#endif
+ break;
+
+ case x0 (0752): // stcq
+ // Characters of C(Q) -> corresponding characters of C(Y), the
+ // character positions affected being specified in the TAG field.
+ //copyChars (i->tag, cpu.rQ, &cpu.CY);
+ cpu.CY = cpu.rQ;
+ cpu.zone =
+ ((i->tag & 040) ? 0770000000000u : 0) |
+ ((i->tag & 020) ? 0007700000000u : 0) |
+ ((i->tag & 010) ? 0000077000000u : 0) |
+ ((i->tag & 004) ? 0000000770000u : 0) |
+ ((i->tag & 002) ? 0000000007700u : 0) |
+ ((i->tag & 001) ? 0000000000077u : 0);
+ cpu.useZone = true;
+ cpu.ou.crflag = true;
+#ifdef TEST_OLIN
+ cmpxchg ();
+#endif
+ break;
+
+ case x0 (0357): //< stcd
+ // C(PPR) -> C(Y-pair) as follows:
+
+ // 000 -> C(Y-pair)0,2
+ // C(PPR.PSR) -> C(Y-pair)3,17
+ // C(PPR.PRR) -> C(Y-pair)18,20
+ // 00...0 -> C(Y-pair)21,29
+ // (43)8 -> C(Y-pair)30,35
+
+ // C(PPR.IC)+2 -> C(Y-pair)36,53
+ // 00...0 -> C(Y-pair)54,71
+
+ // ISOLTS 880 5a has an STCD in an XED in a fault pair;
+ // it reports the wrong ring number. This was fixed by
+ // emulating the SCU instruction (different behavior in fault
+ // pair).
+
+ if (cpu.cycle == EXEC_cycle)
+ {
+ cpu.Ypair[0] = 0;
+ putbits36_15 (& cpu.Ypair[0], 3, cpu.PPR.PSR);
+ putbits36_3 (& cpu.Ypair[0], 18, cpu.PPR.PRR);
+ putbits36_6 (& cpu.Ypair[0], 30, 043);
+
+ cpu.Ypair[1] = 0;
+ putbits36_18 (& cpu.Ypair[1], 0, cpu.PPR.IC + 2);
+ }
+ else
+ {
+ cpu.Ypair[0] = 0;
+ putbits36_15 (& cpu.Ypair[0], 3, cpu.cu_data.PSR);
+ putbits36_3 (& cpu.Ypair[0], 18, cpu.cu_data.PRR);
+ //putbits36_6 (& cpu.Ypair[0], 30, 043);
+
+ cpu.Ypair[1] = 0;
+ putbits36_18 (& cpu.Ypair[1], 0, cpu.cu_data.IC + 2);
+ }
+ break;
+
+
+// Optimized to the top of the loop
+// case x0 (0754): // sti
+
+// Optimized to the top of the loop
+// case x0 (0756): // stq
+
+ case x0 (0454): // stt
+ CPTUR (cptUseTR);
+#ifdef ISOLTS
+ if (current_running_cpu_idx)
+ cpu.CY = ((-- cpu.shadowTR) & MASK27) << 9;
+ else
+ cpu.CY = (cpu.rTR & MASK27) << 9;
+#else
+ cpu.CY = (cpu.rTR & MASK27) << 9;
+#endif
+ break;
+
+
+// Optimized to the top of the loop
+// // stxn
+// case x0 (0740): // stx0
+// case x0 (0741): // stx1
+// case x0 (0742): // stx2
+// case x0 (0743): // stx3
+// case x0 (0744): // stx4
+// case x0 (0745): // stx5
+// case x0 (0746): // stx6
+// case x0 (0747): // stx7
+
+// Optimized to the top of the loop
+// case x0 (0450): // stz
+
+ // sxln
+ case x0 (0440): // sxl0
+ case x0 (0441): // sxl1
+ case x0 (0442): // sxl2
+ case x0 (0443): // sxl3
+ case x0 (0444): // sxl4
+ case x0 (0445): // sxl5
+ case x0 (0446): // sxl6
+ case x0 (0447): // sxl7
+ //SETLO (cpu.CY, cpu.rX[opcode10 & 07]);
+ cpu.CY = cpu.rX[opcode10 & 07];
+ cpu.zone = 0000000777777;
+ cpu.useZone = true;
+ break;
+
+ /// Fixed-Point Data Movement Shift
+
+ case x0 (0775): // alr
+ {
+ word36 tmp36 = cpu.TPR.CA & 0177; // CY bits 11-17
+ for (uint j = 0 ; j < tmp36 ; j++)
+ {
+ bool a0 = cpu.rA & SIGN36; // A0
+ cpu.rA <<= 1; // shift left 1
+ if (a0) // rotate A0 -> A35
+ cpu.rA |= 1;
+ }
+ cpu.rA &= DMASK; // keep to 36-bits
+ HDBGRegA ();
+
+ SC_I_ZERO (cpu.rA == 0);
+ SC_I_NEG (cpu.rA & SIGN36);
+ }
+ break;
+
+// Optimized to the top of the loop
+// case x0 (0735): // als
+
+ case x0 (0771): // arl
+ // Shift C(A) right the number of positions given in
+ // C(TPR.CA)11,17; filling vacated positions with zeros.
+ {
+ cpu.rA &= DMASK; // Make sure the shifted in bits are 0
+ word36 tmp36 = cpu.TPR.CA & 0177; // CY bits 11-17
+
+ cpu.rA >>= tmp36;
+ cpu.rA &= DMASK; // keep to 36-bits
+ HDBGRegA ();
+
+ SC_I_ZERO (cpu.rA == 0);
+ SC_I_NEG (cpu.rA & SIGN36);
+ }
+ break;
+
+ case x0 (0731): // ars
+ {
+ // Shift C(A) right the number of positions given in
+ // C(TPR.CA)11,17; filling vacated positions with initial C(A)0.
+
+ cpu.rA &= DMASK; // Make sure the shifted in bits are 0
+ word18 tmp18 = cpu.TPR.CA & 0177; // CY bits 11-17
+
+ bool a0 = cpu.rA & SIGN36; // A0
+ for (uint j = 0 ; j < tmp18 ; j ++)
+ {
+ cpu.rA >>= 1; // shift right 1
+ if (a0) // propagate sign bit
+ cpu.rA |= SIGN36;
+ }
+ cpu.rA &= DMASK; // keep to 36-bits
+ HDBGRegA ();
+
+ SC_I_ZERO (cpu.rA == 0);
+ SC_I_NEG (cpu.rA & SIGN36);
+ }
+ break;
+
+ case x0 (0777): // llr
+ // Shift C(AQ) left by the number of positions given in
+ // C(TPR.CA)11,17; entering each bit leaving AQ0 into AQ71.
+
+ {
+ word36 tmp36 = cpu.TPR.CA & 0177; // CY bits 11-17
+ for (uint j = 0 ; j < tmp36 ; j++)
+ {
+ bool a0 = cpu.rA & SIGN36; // A0
+
+ cpu.rA <<= 1; // shift left 1
+
+ bool b0 = cpu.rQ & SIGN36; // Q0
+ if (b0)
+ cpu.rA |= 1; // Q0 => A35
+
+ cpu.rQ <<= 1; // shift left 1
+
+ if (a0) // propagate A sign bit
+ cpu.rQ |= 1;
+ }
+
+ cpu.rA &= DMASK; // keep to 36-bits
+ HDBGRegA ();
+ cpu.rQ &= DMASK;
+ HDBGRegQ ();
+
+ SC_I_ZERO (cpu.rA == 0 && cpu.rQ == 0);
+ SC_I_NEG (cpu.rA & SIGN36);
+ }
+ break;
+
+ case x0 (0737): // lls
+ {
+ // Shift C(AQ) left the number of positions given in
+ // C(TPR.CA)11,17; filling vacated positions with zeros.
+
+ CLR_I_CARRY;
+
+ word36 tmp36 = cpu.TPR.CA & 0177; // CY bits 11-17
+ word36 tmpSign = cpu.rA & SIGN36;
+ for (uint j = 0 ; j < tmp36 ; j ++)
+ {
+ cpu.rA <<= 1; // shift left 1
+
+ if (tmpSign != (cpu.rA & SIGN36))
+ SET_I_CARRY;
+
+ bool b0 = cpu.rQ & SIGN36; // Q0
+ if (b0)
+ cpu.rA |= 1; // Q0 => A35
+
+ cpu.rQ <<= 1; // shift left 1
+ }
+
+ cpu.rA &= DMASK; // keep to 36-bits
+ HDBGRegA ();
+ cpu.rQ &= DMASK;
+ HDBGRegQ ();
+
+ SC_I_ZERO (cpu.rA == 0 && cpu.rQ == 0);
+ SC_I_NEG (cpu.rA & SIGN36);
+ }
+ break;
+
+ case x0 (0773): // lrl
+ // Shift C(AQ) right the number of positions given in
+ // C(TPR.CA)11,17; filling vacated positions with zeros.
+ {
+ cpu.rA &= DMASK; // Make sure the shifted in bits are 0
+ cpu.rQ &= DMASK; // Make sure the shifted in bits are 0
+ word36 tmp36 = cpu.TPR.CA & 0177; // CY bits 11-17
+ for (uint j = 0 ; j < tmp36 ; j++)
+ {
+ bool a35 = cpu.rA & 1; // A35
+ cpu.rA >>= 1; // shift right 1
+
+ cpu.rQ >>= 1; // shift right 1
+
+ if (a35) // propagate sign bit
+ cpu.rQ |= SIGN36;
+ }
+ cpu.rA &= DMASK; // keep to 36-bits
+ HDBGRegA ();
+ cpu.rQ &= DMASK;
+ HDBGRegQ ();
+
+ SC_I_ZERO (cpu.rA == 0 && cpu.rQ == 0);
+ SC_I_NEG (cpu.rA & SIGN36);
+ }
+ break;
+
+ case x0 (0733): // lrs
+ {
+ // Shift C(AQ) right the number of positions given in
+ // C(TPR.CA)11,17; filling vacated positions with initial C(AQ)0.
+
+ word36 tmp36 = cpu.TPR.CA & 0177; // CY bits 11-17
+ cpu.rA &= DMASK; // Make sure the shifted in bits are 0
+ cpu.rQ &= DMASK; // Make sure the shifted in bits are 0
+ bool a0 = cpu.rA & SIGN36; // A0
+
+ for (uint j = 0 ; j < tmp36 ; j ++)
+ {
+ bool a35 = cpu.rA & 1; // A35
+
+ cpu.rA >>= 1; // shift right 1
+ if (a0)
+ cpu.rA |= SIGN36;
+
+ cpu.rQ >>= 1; // shift right 1
+ if (a35) // propagate sign bit1
+ cpu.rQ |= SIGN36;
+ }
+ cpu.rA &= DMASK; // keep to 36-bits (probably ain't necessary)
+ HDBGRegA ();
+ cpu.rQ &= DMASK;
+ HDBGRegQ ();
+
+ SC_I_ZERO (cpu.rA == 0 && cpu.rQ == 0);
+ SC_I_NEG (cpu.rA & SIGN36);
+ }
+ break;
+
+ case x0 (0776): // qlr
+ // Shift C(Q) left the number of positions given in
+ // C(TPR.CA)11,17; entering each bit leaving Q0 into Q35.
+ {
+ word36 tmp36 = cpu.TPR.CA & 0177; // CY bits 11-17
+ for (uint j = 0 ; j < tmp36 ; j++)
+ {
+ bool q0 = cpu.rQ & SIGN36; // Q0
+ cpu.rQ <<= 1; // shift left 1
+ if (q0) // rotate A0 -> A35
+ cpu.rQ |= 1;
+ }
+ cpu.rQ &= DMASK; // keep to 36-bits
+ HDBGRegQ ();
+
+ SC_I_ZERO (cpu.rQ == 0);
+ SC_I_NEG (cpu.rQ & SIGN36);
+ }
+ break;
+
+// Optimized to the top of the loop
+// case x0 (0736): // qls
+
+ case x0 (0772): // qrl
+ // Shift C(Q) right the number of positions specified by
+ // Y11,17; fill vacated positions with zeros.
+ {
+ word36 tmp36 = cpu.TPR.CA & 0177; // CY bits 11-17
+
+ cpu.rQ &= DMASK; // Make sure the shifted in bits are 0
+ cpu.rQ >>= tmp36;
+ cpu.rQ &= DMASK; // keep to 36-bits
+ HDBGRegQ ();
+
+ SC_I_ZERO (cpu.rQ == 0);
+ SC_I_NEG (cpu.rQ & SIGN36);
+
+ }
+ break;
+
+ case x0 (0732): // qrs
+ {
+ // Shift C(Q) right the number of positions given in
+ // C(TPR.CA)11,17; filling vacated positions with initial C(Q)0.
+
+ cpu.rQ &= DMASK; // Make sure the shifted in bits are 0
+ word36 tmp36 = cpu.TPR.CA & 0177; // CY bits 11-17
+ bool q0 = cpu.rQ & SIGN36; // Q0
+ for (uint j = 0 ; j < tmp36 ; j++)
+ {
+ cpu.rQ >>= 1; // shift right 1
+ if (q0) // propagate sign bit
+ cpu.rQ |= SIGN36;
+ }
+ cpu.rQ &= DMASK; // keep to 36-bits
+ HDBGRegQ ();
+
+ SC_I_ZERO (cpu.rQ == 0);
+ SC_I_NEG (cpu.rQ & SIGN36);
+ }
+ break;
+
+ /// Fixed-Point Addition
+
+ case x0 (0075): // ada
+ {
+ // C(A) + C(Y) -> C(A)
+ // Modifications: All
+ //
+ // (Indicators not listed are not affected)
+ // ZERO: If C(A) = 0, then ON; otherwise OFF
+ // NEG: If C(A)0 = 1, then ON; otherwise OFF
+ // OVR: If range of A is exceeded, then ON
+ // CARRY: If a carry out of A0 is generated, then ON; otherwise OFF
+
+#ifdef L68
+ cpu.ou.cycle |= ou_GOS;
+#endif
+ bool ovf;
+ cpu.rA = Add36b (cpu.rA, cpu.CY, 0, I_ZNOC, & cpu.cu.IR, & ovf);
+ HDBGRegA ();
+ overflow (ovf, false, "ada overflow fault");
+ }
+#ifdef TEST_OLIN
+ cmpxchg ();
+#endif
+ break;
+
+ case x0 (0077): // adaq
+ {
+ // C(AQ) + C(Y-pair) -> C(AQ)
+#ifdef L68
+ cpu.ou.cycle |= ou_GOS;
+#endif
+ bool ovf;
+ word72 tmp72 = YPAIRTO72 (cpu.Ypair);
+ tmp72 = Add72b (convert_to_word72 (cpu.rA, cpu.rQ), tmp72, 0,
+ I_ZNOC, & cpu.cu.IR, & ovf);
+ convert_to_word36 (tmp72, & cpu.rA, & cpu.rQ);
+ HDBGRegA ();
+ HDBGRegQ ();
+ overflow (ovf, false, "adaq overflow fault");
+ }
+ break;
+
+ case x0 (0033): // adl
+ {
+ // C(AQ) + C(Y) sign extended -> C(AQ)
+#ifdef L68
+ cpu.ou.cycle |= ou_GOS;
+#endif
+ bool ovf;
+ word72 tmp72 = SIGNEXT36_72 (cpu.CY); // sign extend Cy
+ tmp72 = Add72b (convert_to_word72 (cpu.rA, cpu.rQ), tmp72, 0,
+ I_ZNOC,
+ & cpu.cu.IR, & ovf);
+ convert_to_word36 (tmp72, & cpu.rA, & cpu.rQ);
+ HDBGRegA ();
+ HDBGRegQ ();
+ overflow (ovf, false, "adl overflow fault");
+ }
+ break;
+
+
+ case x0 (0037): // adlaq
+ {
+ // The adlaq instruction is identical to the adaq instruction with
+ // the exception that the overflow indicator is not affected by the
+ // adlaq instruction, nor does an overflow fault occur. Operands
+ // and results are treated as unsigned, positive binary integers.
+#ifdef L68
+ cpu.ou.cycle |= ou_GOS;
+#endif
+ bool ovf;
+ word72 tmp72 = YPAIRTO72 (cpu.Ypair);
+
+ tmp72 = Add72b (convert_to_word72 (cpu.rA, cpu.rQ), tmp72, 0,
+ I_ZNC, & cpu.cu.IR, & ovf);
+ convert_to_word36 (tmp72, & cpu.rA, & cpu.rQ);
+ HDBGRegA ();
+ HDBGRegQ ();
+ }
+ break;
+
+ case x0 (0035): // adla
+ {
+#ifdef L68
+ cpu.ou.cycle |= ou_GOS;
+#endif
+ // The adla instruction is identical to the ada instruction with
+ // the exception that the overflow indicator is not affected by the
+ // adla instruction, nor does an overflow fault occur. Operands and
+ // results are treated as unsigned, positive binary integers. */
+
+ bool ovf;
+ cpu.rA = Add36b (cpu.rA, cpu.CY, 0, I_ZNC, & cpu.cu.IR, & ovf);
+ HDBGRegA ();
+ }
+ break;
+
+ case x0 (0036): // adlq
+ {
+ // The adlq instruction is identical to the adq instruction with
+ // the exception that the overflow indicator is not affected by the
+ // adlq instruction, nor does an overflow fault occur. Operands and
+ // results are treated as unsigned, positive binary integers. */
+
+#ifdef L68
+ cpu.ou.cycle |= ou_GOS;
+#endif
+ bool ovf;
+ cpu.rQ = Add36b (cpu.rQ, cpu.CY, 0, I_ZNC, & cpu.cu.IR, & ovf);
+ HDBGRegQ ();
+ }
+ break;
+
+ // adlxn
+ case x0 (0020): // adlx0
+ case x0 (0021): // adlx1
+ case x0 (0022): // adlx2
+ case x0 (0023): // adlx3
+ case x0 (0024): // adlx4
+ case x0 (0025): // adlx5
+ case x0 (0026): // adlx6
+ case x0 (0027): // adlx7
+ {
+#ifdef L68
+ cpu.ou.cycle |= ou_GOS;
+#endif
+ bool ovf;
+ uint32 n = opcode10 & 07; // get n
+ cpu.rX[n] = Add18b (cpu.rX[n], GETHI (cpu.CY), 0, I_ZNC,
+ & cpu.cu.IR, & ovf);
+ HDBGRegX (n);
+ }
+ break;
+
+// Optimized to the top of the loop
+// case x0 (0076): // adq
+
+ // adxn
+ case x0 (0060): // adx0
+ case x0 (0061): // adx1
+ case x0 (0062): // adx2
+ case x0 (0063): // adx3
+ case x0 (0064): // adx4
+ case x0 (0065): // adx5
+ case x0 (0066): // adx6
+ case x0 (0067): // adx7
+ {
+#ifdef L68
+ cpu.ou.cycle |= ou_GOS;
+#endif
+ bool ovf;
+ uint32 n = opcode10 & 07; // get n
+ cpu.rX[n] = Add18b (cpu.rX[n], GETHI (cpu.CY), 0,
+ I_ZNOC,
+ & cpu.cu.IR, & ovf);
+ HDBGRegX (n);
+ overflow (ovf, false, "adxn overflow fault");
+ }
+ break;
+
+// Optimized to the top of the loop
+// case x0 (0054): // aos
+
+ case x0 (0055): // asa
+ {
+ // C(A) + C(Y) -> C(Y)
+
+#ifdef L68
+ cpu.ou.cycle |= ou_GOS;
+#endif
+ bool ovf;
+ cpu.CY = Add36b (cpu.rA, cpu.CY, 0, I_ZNOC,
+ & cpu.cu.IR, & ovf);
+ HDBGRegA ();
+ overflow (ovf, true, "asa overflow fault");
+ }
+#ifdef TEST_OLIN
+ cmpxchg ();
+#endif
+ break;
+
+ case x0 (0056): // asq
+ {
+ // C(Q) + C(Y) -> C(Y)
+#ifdef L68
+ cpu.ou.cycle |= ou_GOS;
+#endif
+ bool ovf;
+ cpu.CY = Add36b (cpu.rQ, cpu.CY, 0, I_ZNOC, & cpu.cu.IR, & ovf);
+ overflow (ovf, true, "asq overflow fault");
+ }
+#ifdef TEST_OLIN
+ cmpxchg ();
+#endif
+ break;
+
+ // asxn
+ case x0 (0040): // asx0
+ case x0 (0041): // asx1
+ case x0 (0042): // asx2
+ case x0 (0043): // asx3
+ case x0 (0044): // asx4
+ case x0 (0045): // asx5
+ case x0 (0046): // asx6
+ case x0 (0047): // asx7
+ {
+ // For n = 0, 1, ..., or 7 as determined by operation code
+ // C(Xn) + C(Y)0,17 -> C(Y)0,17
+#ifdef L68
+ cpu.ou.cycle |= ou_GOS;
+#endif
+ bool ovf;
+ uint32 n = opcode10 & 07; // get n
+ word18 tmp18 = Add18b (cpu.rX[n], GETHI (cpu.CY), 0,
+ I_ZNOC, & cpu.cu.IR, & ovf);
+ SETHI (cpu.CY, tmp18);
+ overflow (ovf, true, "asxn overflow fault");
+#ifdef TEST_OLIN
+ cmpxchg ();
+#endif
+ }
+ break;
+
+ case x0 (0071): // awca
+ {
+ // If carry indicator OFF, then C(A) + C(Y) -> C(A)
+ // If carry indicator ON, then C(A) + C(Y) + 1 -> C(A)
+
+#ifdef L68
+ cpu.ou.cycle |= ou_GOS;
+#endif
+ bool ovf;
+ cpu.rA = Add36b (cpu.rA, cpu.CY, TST_I_CARRY ? 1 : 0,
+ I_ZNOC, & cpu.cu.IR, & ovf);
+ HDBGRegA ();
+ overflow (ovf, false, "awca overflow fault");
+ }
+ break;
+
+ case x0 (0072): // awcq
+ {
+ // If carry indicator OFF, then C(Q) + C(Y) -> C(Q)
+ // If carry indicator ON, then C(Q) + C(Y) + 1 -> C(Q)
+
+#ifdef L68
+ cpu.ou.cycle |= ou_GOS;
+#endif
+ bool ovf;
+ cpu.rQ = Add36b (cpu.rQ, cpu.CY, TST_I_CARRY ? 1 : 0,
+ I_ZNOC, & cpu.cu.IR, & ovf);
+ HDBGRegQ ();
+ overflow (ovf, false, "awcq overflow fault");
+ }
+ break;
+
+ /// Fixed-Point Subtraction
+
+ case x0 (0175): // sba
+ {
+ // C(A) - C(Y) -> C(A)
+
+#ifdef L68
+ cpu.ou.cycle |= ou_GOS;
+#endif
+ bool ovf;
+ cpu.rA = Sub36b (cpu.rA, cpu.CY, 1, I_ZNOC, & cpu.cu.IR, & ovf);
+ HDBGRegA ();
+ overflow (ovf, false, "sba overflow fault");
+ }
+ break;
+
+ case x0 (0177): // sbaq
+ {
+ // C(AQ) - C(Y-pair) -> C(AQ)
+#ifdef L68
+ cpu.ou.cycle |= ou_GOS;
+#endif
+ bool ovf;
+ word72 tmp72 = YPAIRTO72 (cpu.Ypair);
+ tmp72 = Sub72b (convert_to_word72 (cpu.rA, cpu.rQ), tmp72, 1,
+ I_ZNOC, & cpu.cu.IR,
+ & ovf);
+ convert_to_word36 (tmp72, & cpu.rA, & cpu.rQ);
+ HDBGRegA ();
+ HDBGRegQ ();
+ overflow (ovf, false, "sbaq overflow fault");
+ }
+ break;
+
+ case x0 (0135): // sbla
+ {
+ // C(A) - C(Y) -> C(A) logical
+
+#ifdef L68
+ cpu.ou.cycle |= ou_GOS;
+#endif
+ bool ovf;
+ cpu.rA = Sub36b (cpu.rA, cpu.CY, 1, I_ZNC, & cpu.cu.IR, & ovf);
+ HDBGRegA ();
+ }
+ break;
+
+ case x0 (0137): // sblaq
+ {
+ // The sblaq instruction is identical to the sbaq instruction with
+ // the exception that the overflow indicator is not affected by the
+ // sblaq instruction, nor does an overflow fault occur. Operands
+ // and results are treated as unsigned, positive binary integers.
+ // C(AQ) - C(Y-pair) -> C(AQ)
+
+#ifdef L68
+ cpu.ou.cycle |= ou_GOS;
+#endif
+ bool ovf;
+ word72 tmp72 = YPAIRTO72 (cpu.Ypair);
+
+ tmp72 = Sub72b (convert_to_word72 (cpu.rA, cpu.rQ), tmp72, 1,
+ I_ZNC, & cpu.cu.IR, & ovf);
+ convert_to_word36 (tmp72, & cpu.rA, & cpu.rQ);
+ HDBGRegA ();
+ HDBGRegQ ();
+ }
+ break;
+
+ case x0 (0136): // sblq
+ {
+ // C(Q) - C(Y) -> C(Q)
+#ifdef L68
+ cpu.ou.cycle |= ou_GOS;
+#endif
+ bool ovf;
+ cpu.rQ = Sub36b (cpu.rQ, cpu.CY, 1, I_ZNC, & cpu.cu.IR, & ovf);
+ HDBGRegQ ();
+ }
+ break;
+
+ // sblxn
+ case x0 (0120): // sblx0
+ case x0 (0121): // sblx1
+ case x0 (0122): // sblx2
+ case x0 (0123): // sblx3
+ case x0 (0124): // sblx4
+ case x0 (0125): // sblx5
+ case x0 (0126): // sblx6
+ case x0 (0127): // sblx7
+ {
+ // For n = 0, 1, ..., or 7 as determined by operation code
+ // C(Xn) - C(Y)0,17 -> C(Xn)
+
+#ifdef L68
+ cpu.ou.cycle |= ou_GOS;
+#endif
+ bool ovf;
+ uint32 n = opcode10 & 07; // get n
+ cpu.rX[n] = Sub18b (cpu.rX[n], GETHI (cpu.CY), 1,
+ I_ZNC, & cpu.cu.IR, & ovf);
+ HDBGRegX (n);
+ }
+ break;
+
+ case x0 (0176): // sbq
+ {
+ // C(Q) - C(Y) -> C(Q)
+#ifdef L68
+ cpu.ou.cycle |= ou_GOS;
+#endif
+ bool ovf;
+ cpu.rQ = Sub36b (cpu.rQ, cpu.CY, 1, I_ZNOC, & cpu.cu.IR, & ovf);
+ HDBGRegQ ();
+ overflow (ovf, false, "sbq overflow fault");
+ }
+ break;
+
+ // sbxn
+ case x0 (0160): // sbx0
+ case x0 (0161): // sbx1
+ case x0 (0162): // sbx2
+ case x0 (0163): // sbx3
+ case x0 (0164): // sbx4
+ case x0 (0165): // sbx5
+ case x0 (0166): // sbx6
+ case x0 (0167): // sbx7
+ {
+ // For n = 0, 1, ..., or 7 as determined by operation code
+ // C(Xn) - C(Y)0,17 -> C(Xn)
+
+#ifdef L68
+ cpu.ou.cycle |= ou_GOS;
+#endif
+ bool ovf;
+ uint32 n = opcode10 & 07; // get n
+ cpu.rX[n] = Sub18b (cpu.rX[n], GETHI (cpu.CY), 1,
+ I_ZNOC, & cpu.cu.IR, & ovf);
+ HDBGRegX (n);
+ overflow (ovf, false, "sbxn overflow fault");
+ }
+ break;
+
+ case x0 (0155): // ssa
+ {
+ // C(A) - C(Y) -> C(Y)
+
+#ifdef L68
+ cpu.ou.cycle |= ou_GOS;
+#endif
+ bool ovf;
+ cpu.CY = Sub36b (cpu.rA, cpu.CY, 1, I_ZNOC, & cpu.cu.IR, & ovf);
+ overflow (ovf, true, "ssa overflow fault");
+ }
+#ifdef TEST_OLIN
+ cmpxchg ();
+#endif
+ break;
+
+ case x0 (0156): // ssq
+ {
+ // C(Q) - C(Y) -> C(Y)
+
+#ifdef L68
+ cpu.ou.cycle |= ou_GOS;
+#endif
+ bool ovf;
+ cpu.CY = Sub36b (cpu.rQ, cpu.CY, 1, I_ZNOC, & cpu.cu.IR, & ovf);
+ overflow (ovf, true, "ssq overflow fault");
+ }
+#ifdef TEST_OLIN
+ cmpxchg ();
+#endif
+ break;
+
+ // ssxn
+ case x0 (0140): // ssx0
+ case x0 (0141): // ssx1
+ case x0 (0142): // ssx2
+ case x0 (0143): // ssx3
+ case x0 (0144): // ssx4
+ case x0 (0145): // ssx5
+ case x0 (0146): // ssx6
+ case x0 (0147): // ssx7
+ {
+ // For uint32 n = 0, 1, ..., or 7 as determined by operation code
+ // C(Xn) - C(Y)0,17 -> C(Y)0,17
+
+#ifdef L68
+ cpu.ou.cycle |= ou_GOS;
+#endif
+ bool ovf;
+ uint32 n = opcode10 & 07; // get n
+ word18 tmp18 = Sub18b (cpu.rX[n], GETHI (cpu.CY), 1,
+ I_ZNOC, & cpu.cu.IR, & ovf);
+ SETHI (cpu.CY, tmp18);
+ overflow (ovf, true, "ssxn overflow fault");
+ }
+#ifdef TEST_OLIN
+ cmpxchg ();
+#endif
+ break;
+
+
+ case x0 (0171): // swca
+ {
+ // If carry indicator ON, then C(A)- C(Y) -> C(A)
+ // If carry indicator OFF, then C(A) - C(Y) - 1 -> C(A)
+
+#ifdef L68
+ cpu.ou.cycle |= ou_GOS;
+#endif
+ bool ovf;
+ cpu.rA = Sub36b (cpu.rA, cpu.CY, TST_I_CARRY ? 1 : 0,
+ I_ZNOC, & cpu.cu.IR, & ovf);
+ HDBGRegA ();
+ overflow (ovf, false, "swca overflow fault");
+ }
+ break;
+
+ case x0 (0172): // swcq
+ {
+ // If carry indicator ON, then C(Q) - C(Y) -> C(Q)
+ // If carry indicator OFF, then C(Q) - C(Y) - 1 -> C(Q)
+
+#ifdef L68
+ cpu.ou.cycle |= ou_GOS;
+#endif
+ bool ovf;
+ cpu.rQ = Sub36b (cpu.rQ, cpu.CY, TST_I_CARRY ? 1 : 0,
+ I_ZNOC, & cpu.cu.IR, & ovf);
+ HDBGRegQ ();
+ overflow (ovf, false, "swcq overflow fault");
+ }
+ break;
+
+ /// Fixed-Point Multiplication
+
+ case x0 (0401): // mpf
+ {
+ // C(A) * C(Y) -> C(AQ), left adjusted
+ //
+ // Two 36-bit fractional factors (including sign) are multiplied
+ // to form a 71- bit fractional product (including sign), which
+ // is stored left-adjusted in the AQ register. AQ71 contains a
+ // zero. Overflow can occur only in the case of A and Y
+ // containing negative 1 and the result exceeding the range of
+ // the AQ register.
+
+#ifdef L68
+ cpu.ou.cycle |= ou_GD1;
+#endif
+#ifdef NEED_128
+ word72 tmp72 = multiply_128 (SIGNEXT36_72 (cpu.rA), SIGNEXT36_72 (cpu.CY));
+ tmp72 = and_128 (tmp72, MASK72);
+ tmp72 = lshift_128 (tmp72, 1);
+#else
+ word72 tmp72 = SIGNEXT36_72 (cpu.rA) * SIGNEXT36_72 (cpu.CY);
+ tmp72 &= MASK72;
+ tmp72 <<= 1; // left adjust so AQ71 contains 0
+#endif
+#ifdef L68
+ cpu.ou.cycle |= ou_GD2;
+#endif
+ // Overflow can occur only in the case of A and Y containing
+ // negative 1
+ if (cpu.rA == MAXNEG && cpu.CY == MAXNEG)
+ {
+ SET_I_NEG;
+ CLR_I_ZERO;
+ overflow (true, false, "mpf overflow fault");
+ }
+
+ convert_to_word36 (tmp72, &cpu.rA, &cpu.rQ);
+ HDBGRegA ();
+ HDBGRegQ ();
+ SC_I_ZERO (cpu.rA == 0 && cpu.rQ == 0);
+ SC_I_NEG (cpu.rA & SIGN36);
+ }
+ break;
+
+ case x0 (0402): // mpy
+ // C(Q) * C(Y) -> C(AQ), right adjusted
+
+ {
+#ifdef L68
+ cpu.ou.cycle |= ou_GOS;
+#endif
+#ifdef NEED_128
+ int128 prod = multiply_s128 (
+ SIGNEXT36_128 (cpu.rQ & DMASK),
+ SIGNEXT36_128 (cpu.CY & DMASK));
+ convert_to_word36 (cast_128 (prod), &cpu.rA, &cpu.rQ);
+#else
+ int64_t t0 = SIGNEXT36_64 (cpu.rQ & DMASK);
+ int64_t t1 = SIGNEXT36_64 (cpu.CY & DMASK);
+
+ __int128_t prod = (__int128_t) t0 * (__int128_t) t1;
+
+ convert_to_word36 ((word72)prod, &cpu.rA, &cpu.rQ);
+#endif
+ HDBGRegA ();
+ HDBGRegQ ();
+
+ SC_I_ZERO (cpu.rA == 0 && cpu.rQ == 0);
+ SC_I_NEG (cpu.rA & SIGN36);
+ }
+ break;
+
+//#define DIV_TRACE
+
+ /// Fixed-Point Division
+
+ case x0 (0506): // div
+ // C(Q) / (Y) integer quotient -> C(Q), integer remainder -> C(A)
+ //
+ // A 36-bit integer dividend (including sign) is divided by a
+ // 36-bit integer divisor (including sign) to form a 36-bit integer
+ // * quotient (including sign) and a 36-bit integer remainder
+ // * (including sign). The remainder sign is equal to the dividend
+ // * sign unless the remainder is zero.
+ // *
+ // * If the dividend = -2**35 and the divisor = -1 or if the divisor
+ // * = 0, then division does not take place. Instead, a divide check
+ // * fault occurs, C(Q) contains the dividend magnitude, and the
+ // * negative indicator reflects the dividend sign.
+
+#ifdef L68
+ cpu.ou.cycle |= ou_GD1;
+#endif
+ // RJ78: If the dividend = -2**35 and the divisor = +/-1, or if
+ // the divisor is 0
+
+ if ((cpu.rQ == MAXNEG && (cpu.CY == 1 || cpu.CY == NEG136)) ||
+ (cpu.CY == 0))
+ {
+//sim_printf ("DIV Q %012"PRIo64" Y %012"PRIo64"\n", cpu.rQ, cpu.CY);
+// case 1 400000000000 000000000000 --> 000000000000
+// case 2 000000000000 000000000000 --> 400000000000
+ //cpu.rA = 0; // works for case 1
+ cpu.rA = (cpu.rQ & SIGN36) ? 0 : SIGN36; // works for case 1,2
+ HDBGRegA ();
+
+ // no division takes place
+ SC_I_ZERO (cpu.CY == 0);
+ SC_I_NEG (cpu.rQ & SIGN36);
+
+ if (cpu.rQ & SIGN36)
+ {
+ cpu.rQ = (- cpu.rQ) & MASK36;
+ HDBGRegQ ();
+ }
+
+ dlyDoFault (FAULT_DIV,
+ fst_ill_op,
+ "div divide check");
+ }
+ else
+ {
+ t_int64 dividend = (t_int64) (SIGNEXT36_64 (cpu.rQ));
+ t_int64 divisor = (t_int64) (SIGNEXT36_64 (cpu.CY));
+
+#ifdef DIV_TRACE
+ sim_debug (DBG_CAC, & cpu_dev, "\n");
+ sim_debug (DBG_CAC, & cpu_dev,
+ ">>> dividend cpu.rQ %"PRId64" (%012"PRIo64")\n",
+ dividend, cpu.rQ);
+ sim_debug (DBG_CAC, & cpu_dev,
+ ">>> divisor CY %"PRId64" (%012"PRIo64")\n",
+ divisor, cpu.CY);
+#endif
+
+ t_int64 quotient = dividend / divisor;
+#ifdef L68
+ cpu.ou.cycle |= ou_GD2;
+#endif
+ t_int64 remainder = dividend % divisor;
+
+#ifdef DIV_TRACE
+ sim_debug (DBG_CAC, & cpu_dev, ">>> quot 1 %"PRId64"\n", quotient);
+ sim_debug (DBG_CAC, & cpu_dev, ">>> rem 1 %"PRId64"\n", remainder);
+#endif
+
+// Evidence is that DPS8M rounds toward zero; if it turns out that it
+// rounds toward -inf, try this code:
+#if 0
+ // XXX C rounds toward zero; I suspect that DPS8M rounded toward
+ // -inf.
+ // If the remainder is negative, we rounded the wrong way
+ if (remainder < 0)
+ {
+ remainder += divisor;
+ quotient -= 1;
+
+#ifdef DIV_TRACE
+ sim_debug (DBG_CAC, & cpu_dev,
+ ">>> quot 2 %"PRId64"\n", quotient);
+ sim_debug (DBG_CAC, & cpu_dev,
+ ">>> rem 2 %"PRId64"\n", remainder);
+#endif
+ }
+#endif
+
+#ifdef DIV_TRACE
+ // (a/b)*b + a%b is equal to a.
+ sim_debug (DBG_CAC, & cpu_dev,
+ "dividend was = %"PRId64"\n", dividend);
+ sim_debug (DBG_CAC, & cpu_dev,
+ "quotient * divisor + remainder = %"PRId64"\n",
+ quotient * divisor + remainder);
+ if (dividend != quotient * divisor + remainder)
+ {
+ sim_debug (DBG_CAC, & cpu_dev,
+ "---------------------------------^^^^^^^^^^^^^^^\n");
+ }
+#endif
+
+
+ if (dividend != quotient * divisor + remainder)
+ {
+ sim_debug (DBG_ERR, & cpu_dev,
+ "Internal division error;"
+ " rQ %012"PRIo64" CY %012"PRIo64"\n", cpu.rQ, cpu.CY);
+ }
+
+ cpu.rA = (word36) remainder & DMASK;
+ HDBGRegA ();
+ cpu.rQ = (word36) quotient & DMASK;
+ HDBGRegQ ();
+
+#ifdef DIV_TRACE
+ sim_debug (DBG_CAC, & cpu_dev, "rA (rem) %012"PRIo64"\n", cpu.rA);
+ sim_debug (DBG_CAC, & cpu_dev, "rQ (quot) %012"PRIo64"\n", cpu.rQ);
+#endif
+
+ SC_I_ZERO (cpu.rQ == 0);
+ SC_I_NEG (cpu.rQ & SIGN36);
+ }
+
+ break;
+
+ case x0 (0507): // dvf
+ // C(AQ) / (Y)
+ // fractional quotient -> C(A)
+ // fractional remainder -> C(Q)
+
+ // A 71-bit fractional dividend (including sign) is divided by a
+ // 36-bit fractional divisor yielding a 36-bit fractional quotient
+ // (including sign) and a 36-bit fractional remainder (including
+ // sign). C(AQ)71 is ignored; bit position 35 of the remainder
+ // corresponds to bit position 70 of the dividend. The remainder
+ // sign is equal to the dividend sign unless the remainder is zero.
+
+ // If | dividend | >= | divisor | or if the divisor = 0, division
+ // does not take place. Instead, a divide check fault occurs, C(AQ)
+ // contains the dividend magnitude in absolute, and the negative
+ // indicator reflects the dividend sign.
+
+ dvf ();
+
+ break;
+
+ /// Fixed-Point Negate
+
+ case x0 (0531): // neg
+ // -C(A) -> C(A) if C(A) != 0
+
+ cpu.rA &= DMASK;
+ if (cpu.rA == 0400000000000ULL)
+ {
+ CLR_I_ZERO;
+ SET_I_NEG;
+ overflow (true, false, "neg overflow fault");
+ }
+
+ cpu.rA = -cpu.rA;
+
+ cpu.rA &= DMASK; // keep to 36-bits
+ HDBGRegA ();
+
+ SC_I_ZERO (cpu.rA == 0);
+ SC_I_NEG (cpu.rA & SIGN36);
+
+ break;
+
+ case x0 (0533): // negl
+ // -C(AQ) -> C(AQ) if C(AQ) != 0
+ {
+ cpu.rA &= DMASK;
+ cpu.rQ &= DMASK;
+
+ if (cpu.rA == 0400000000000ULL && cpu.rQ == 0)
+ {
+ CLR_I_ZERO;
+ SET_I_NEG;
+ overflow (true, false, "negl overflow fault");
+ }
+
+ word72 tmp72 = convert_to_word72 (cpu.rA, cpu.rQ);
+#ifdef NEED_128
+ tmp72 = negate_128 (tmp72);
+
+ SC_I_ZERO (iszero_128 (tmp72));
+ SC_I_NEG (isnonzero_128 (and_128 (tmp72, SIGN72)));
+#else
+ tmp72 = -tmp72;
+
+ SC_I_ZERO (tmp72 == 0);
+ SC_I_NEG (tmp72 & SIGN72);
+#endif
+
+ convert_to_word36 (tmp72, &cpu.rA, &cpu.rQ);
+ HDBGRegA ();
+ HDBGRegQ ();
+ }
+ break;
+
+ /// Fixed-Point Comparison
+
+ case x0 (0405): // cmg
+ // | C(A) | :: | C(Y) |
+ // Zero: If | C(A) | = | C(Y) | , then ON; otherwise OFF
+ // Negative: If | C(A) | < | C(Y) | , then ON; otherwise OFF
+ {
+ // This is wrong for MAXNEG
+ //word36 a = cpu.rA & SIGN36 ? -cpu.rA : cpu.rA;
+ //word36 y = cpu.CY & SIGN36 ? -cpu.CY : cpu.CY;
+
+ // If we do the 64 math, the MAXNEG case works
+ t_int64 a = SIGNEXT36_64 (cpu.rA);
+ if (a < 0)
+ a = -a;
+ t_int64 y = SIGNEXT36_64 (cpu.CY);
+ if (y < 0)
+ y = -y;
+
+ SC_I_ZERO (a == y);
+ SC_I_NEG (a < y);
+ }
+ break;
+
+ case x0 (0211): // cmk
+ // For i = 0, 1, ..., 35
+ // C(Z)i = ~C(Q)i & ( C(A)i XOR C(Y)i )
+
+ /**
+ * The cmk instruction compares the contents of bit positions of A
+ * and Y for identity that are not masked by a 1 in the
+ * corresponding bit position of Q.
+ *
+ * The zero indicator is set ON if the comparison is successful for
+ * all bit positions; i.e., if for all i = 0, 1, ..., 35 there is
+ * either: C(A)i = C(Y)i (the identical case) or C(Q)i = 1 (the
+ * masked case); otherwise, the zero indicator is set OFF.
+ *
+ * The negative indicator is set ON if the comparison is
+ * unsuccessful for bit position 0; i.e., if C(A)0 XOR C(Y)0 (they
+ * are nonidentical) as well as C(Q)0 = 0 (they are unmasked);
+ * otherwise, the negative indicator is set OFF.
+ */
+ {
+ word36 Z = ~cpu.rQ & (cpu.rA ^ cpu.CY);
+ Z &= DMASK;
+
+// Q A Y ~Q A^Y Z
+// 0 0 0 1 0 0
+// 0 0 1 1 1 1
+// 0 1 0 1 1 1
+// 0 1 1 1 0 0
+// 1 0 0 0 0 0
+// 1 0 1 0 1 0
+// 1 1 0 0 1 0
+// 1 1 1 0 0 0
+
+
+ SC_I_ZERO (Z == 0);
+ SC_I_NEG (Z & SIGN36);
+ }
+ break;
+
+// Optimized to the top of the loop
+// case x0 (0115): // cmpa
+
+ case x0 (0117): // cmpaq
+ // C(AQ) :: C(Y-pair)
+ {
+ word72 tmp72 = YPAIRTO72 (cpu.Ypair);
+ word72 trAQ = convert_to_word72 (cpu.rA, cpu.rQ);
+#ifdef NEED_128
+ trAQ = and_128 (trAQ, MASK72);
+#else
+ trAQ &= MASK72;
+#endif
+ cmp72 (trAQ, tmp72, &cpu.cu.IR);
+ }
+ break;
+
+// Optimized to the top of the loop
+// case x0 (0116): // cmpq
+
+ // cmpxn
+ case x0 (0100): // cmpx0
+ case x0 (0101): // cmpx1
+ case x0 (0102): // cmpx2
+ case x0 (0103): // cmpx3
+ case x0 (0104): // cmpx4
+ case x0 (0105): // cmpx5
+ case x0 (0106): // cmpx6
+ case x0 (0107): // cmpx7
+ // For n = 0, 1, ..., or 7 as determined by operation code
+ // C(Xn) :: C(Y)0,17
+ {
+ uint32 n = opcode10 & 07; // get n
+ cmp18 (cpu.rX[n], GETHI (cpu.CY), &cpu.cu.IR);
+ }
+ break;
+
+ case x0 (0111): // cwl
+ // C(Y) :: closed interval [C(A);C(Q)]
+ /**
+ * The cwl instruction tests the value of C(Y) to determine if it
+ * is within the range of values set by C(A) and C(Q). The
+ * comparison of C(Y) with C(Q) locates C(Y) with respect to the
+ * interval if C(Y) is not contained within the
+ interval.
+ */
+ cmp36wl (cpu.rA, cpu.CY, cpu.rQ, &cpu.cu.IR);
+ break;
+
+ /// Fixed-Point Miscellaneous
+
+ case x0 (0234): // szn
+ // Set indicators according to C(Y)
+ cpu.CY &= DMASK;
+ SC_I_ZERO (cpu.CY == 0);
+ SC_I_NEG (cpu.CY & SIGN36);
+ break;
+
+ case x0 (0214): // sznc
+ // Set indicators according to C(Y)
+ cpu.CY &= DMASK;
+ SC_I_ZERO (cpu.CY == 0);
+ SC_I_NEG (cpu.CY & SIGN36);
+ // ... and clear
+ cpu.CY = 0;
+#ifdef TEST_OLIN
+ cmpxchg ();
+#endif
+ break;
+
+ /// BOOLEAN OPERATION INSTRUCTIONS
+
+ /// Boolean And
+
+// Optimized to the top of the loop
+// case x0 (0375): // ana
+
+// Optimized to the top of the loop
+// case x0 (0377): //< anaq
+
+ case x0 (0376): // anq
+ // C(Q)i & C(Y)i -> C(Q)i for i = (0, 1, ..., 35)
+ cpu.rQ = cpu.rQ & cpu.CY;
+ cpu.rQ &= DMASK;
+ HDBGRegQ ();
+
+ SC_I_ZERO (cpu.rQ == 0);
+ SC_I_NEG (cpu.rQ & SIGN36);
+ break;
+
+ case x0 (0355): // ansa
+ // C(A)i & C(Y)i -> C(Y)i for i = (0, 1, ..., 35)
+ {
+ cpu.CY = cpu.rA & cpu.CY;
+ cpu.CY &= DMASK;
+ HDBGRegA ();
+
+ SC_I_ZERO (cpu.CY == 0);
+ SC_I_NEG (cpu.CY & SIGN36);
+ }
+#ifdef TEST_OLIN
+ cmpxchg ();
+#endif
+ break;
+
+ case x0 (0356): // ansq
+ // C(Q)i & C(Y)i -> C(Y)i for i = (0, 1, ..., 35)
+ {
+ cpu.CY = cpu.rQ & cpu.CY;
+ cpu.CY &= DMASK;
+
+ SC_I_ZERO (cpu.CY == 0);
+ SC_I_NEG (cpu.CY & SIGN36);
+ }
+#ifdef TEST_OLIN
+ cmpxchg ();
+#endif
+ break;
+
+ // ansxn
+ case x0 (0340): // ansx0
+ case x0 (0341): // ansx1
+ case x0 (0342): // ansx2
+ case x0 (0343): // ansx3
+ case x0 (0344): // ansx4
+ case x0 (0345): // ansx5
+ case x0 (0346): // ansx6
+ case x0 (0347): // ansx7
+ // For n = 0, 1, ..., or 7 as determined by operation code
+ // C(Xn)i & C(Y)i -> C(Y)i for i = (0, 1, ..., 17)
+ {
+ uint32 n = opcode10 & 07; // get n
+ word18 tmp18 = cpu.rX[n] & GETHI (cpu.CY);
+ tmp18 &= MASK18;
+
+ SC_I_ZERO (tmp18 == 0);
+ SC_I_NEG (tmp18 & SIGN18);
+
+ SETHI (cpu.CY, tmp18);
+ }
+#ifdef TEST_OLIN
+ cmpxchg ();
+#endif
+
+ break;
+
+ // anxn
+ case x0 (0360): // anx0
+ case x0 (0361): // anx1
+ case x0 (0362): // anx2
+ case x0 (0363): // anx3
+ case x0 (0364): // anx4
+ case x0 (0365): // anx5
+ case x0 (0366): // anx6
+ case x0 (0367): // anx7
+ // For n = 0, 1, ..., or 7 as determined by operation code
+ // C(Xn)i & C(Y)i -> C(Xn)i for i = (0, 1, ..., 17)
+ {
+ uint32 n = opcode10 & 07; // get n
+ cpu.rX[n] &= GETHI (cpu.CY);
+ cpu.rX[n] &= MASK18;
+ HDBGRegX (n);
+
+ SC_I_ZERO (cpu.rX[n] == 0);
+ SC_I_NEG (cpu.rX[n] & SIGN18);
+ }
+ break;
+
+ /// Boolean Or
+
+// Optimized to the top of the loop
+// case x0 (0275): // ora
+
+ case x0 (0277): // oraq
+ // C(AQ)i | C(Y-pair)i -> C(AQ)i for i = (0, 1, ..., 71)
+ {
+ word72 tmp72 = YPAIRTO72 (cpu.Ypair);
+ word72 trAQ = convert_to_word72 (cpu.rA, cpu.rQ);
+#ifdef NEED_128
+ trAQ = or_128 (trAQ, tmp72);
+ trAQ = and_128 (trAQ, MASK72);
+
+ SC_I_ZERO (iszero_128 (trAQ));
+ SC_I_NEG (isnonzero_128 (and_128 (trAQ, SIGN72)));
+#else
+ trAQ = trAQ | tmp72;
+ trAQ &= MASK72;
+
+ SC_I_ZERO (trAQ == 0);
+ SC_I_NEG (trAQ & SIGN72);
+#endif
+ convert_to_word36 (trAQ, &cpu.rA, &cpu.rQ);
+ HDBGRegA ();
+ HDBGRegQ ();
+ }
+ break;
+
+ case x0 (0276): // orq
+ // C(Q)i | C(Y)i -> C(Q)i for i = (0, 1, ..., 35)
+ cpu.rQ = cpu.rQ | cpu.CY;
+ cpu.rQ &= DMASK;
+ HDBGRegQ ();
+
+ SC_I_ZERO (cpu.rQ == 0);
+ SC_I_NEG (cpu.rQ & SIGN36);
+
+ break;
+
+ case x0 (0255): // orsa
+ // C(A)i | C(Y)i -> C(Y)i for i = (0, 1, ..., 35)
+ cpu.CY = cpu.rA | cpu.CY;
+ cpu.CY &= DMASK;
+
+ SC_I_ZERO (cpu.CY == 0);
+ SC_I_NEG (cpu.CY & SIGN36);
+#ifdef TEST_OLIN
+ cmpxchg ();
+#endif
+ break;
+
+ case x0 (0256): // orsq
+ // C(Q)i | C(Y)i -> C(Y)i for i = (0, 1, ..., 35)
+
+ cpu.CY = cpu.rQ | cpu.CY;
+ cpu.CY &= DMASK;
+
+ SC_I_ZERO (cpu.CY == 0);
+ SC_I_NEG (cpu.CY & SIGN36);
+#ifdef TEST_OLIN
+ cmpxchg ();
+#endif
+ break;
+
+ // orsxn
+ case x0 (0240): // orsx0
+ case x0 (0241): // orsx1
+ case x0 (0242): // orsx2
+ case x0 (0243): // orsx3
+ case x0 (0244): // orsx4
+ case x0 (0245): // orsx5
+ case x0 (0246): // orsx6
+ case x0 (0247): // orsx7
+ // For n = 0, 1, ..., or 7 as determined by operation code
+ // C(Xn)i | C(Y)i -> C(Y)i for i = (0, 1, ..., 17)
+ {
+ uint32 n = opcode10 & 07; // get n
+
+ word18 tmp18 = cpu.rX[n] | GETHI (cpu.CY);
+ tmp18 &= MASK18;
+
+ SC_I_ZERO (tmp18 == 0);
+ SC_I_NEG (tmp18 & SIGN18);
+
+ SETHI (cpu.CY, tmp18);
+ }
+#ifdef TEST_OLIN
+ cmpxchg ();
+#endif
+ break;
+
+ // orxn
+ case x0 (0260): // orx0
+ case x0 (0261): // orx1
+ case x0 (0262): // orx2
+ case x0 (0263): // orx3
+ case x0 (0264): // orx4
+ case x0 (0265): // orx5
+ case x0 (0266): // orx6
+ case x0 (0267): // orx7
+ // For n = 0, 1, ..., or 7 as determined by operation code
+ // C(Xn)i | C(Y)i -> C(Xn)i for i = (0, 1, ..., 17)
+ {
+ uint32 n = opcode10 & 07; // get n
+ cpu.rX[n] |= GETHI (cpu.CY);
+ cpu.rX[n] &= MASK18;
+ HDBGRegX (n);
+
+ SC_I_ZERO (cpu.rX[n] == 0);
+ SC_I_NEG (cpu.rX[n] & SIGN18);
+ }
+ break;
+
+ /// Boolean Exclusive Or
+
+ case x0 (0675): // era
+ // C(A)i XOR C(Y)i -> C(A)i for i = (0, 1, ..., 35)
+ cpu.rA = cpu.rA ^ cpu.CY;
+ cpu.rA &= DMASK;
+ HDBGRegA ();
+
+ SC_I_ZERO (cpu.rA == 0);
+ SC_I_NEG (cpu.rA & SIGN36);
+
+ break;
+
+// Optimized to the top of the loop
+// case x0 (0677): // eraq
+
+ case x0 (0676): // erq
+ // C(Q)i XOR C(Y)i -> C(Q)i for i = (0, 1, ..., 35)
+ cpu.rQ = cpu.rQ ^ cpu.CY;
+ cpu.rQ &= DMASK;
+ HDBGRegQ ();
+ SC_I_ZERO (cpu.rQ == 0);
+ SC_I_NEG (cpu.rQ & SIGN36);
+ break;
+
+ case x0 (0655): // ersa
+ // C(A)i XOR C(Y)i -> C(Y)i for i = (0, 1, ..., 35)
+
+ cpu.CY = cpu.rA ^ cpu.CY;
+ cpu.CY &= DMASK;
+
+ SC_I_ZERO (cpu.CY == 0);
+ SC_I_NEG (cpu.CY & SIGN36);
+#ifdef TEST_OLIN
+ cmpxchg ();
+#endif
+ break;
+
+ case x0 (0656): // ersq
+ // C(Q)i XOR C(Y)i -> C(Y)i for i = (0, 1, ..., 35)
+
+ cpu.CY = cpu.rQ ^ cpu.CY;
+ cpu.CY &= DMASK;
+
+ SC_I_ZERO (cpu.CY == 0);
+ SC_I_NEG (cpu.CY & SIGN36);
+#ifdef TEST_OLIN
+ cmpxchg ();
+#endif
+
+ break;
+
+ // ersxn
+ case x0 (0640): // ersx0
+ case x0 (0641): // ersx1
+ case x0 (0642): // ersx2
+ case x0 (0643): // ersx3
+ case x0 (0644): // ersx4
+ case x0 (0645): // ersx5
+ case x0 (0646): // ersx6
+ case x0 (0647): // ersx7
+ // For n = 0, 1, ..., or 7 as determined by operation code
+ // C(Xn)i XOR C(Y)i -> C(Y)i for i = (0, 1, ..., 17)
+ {
+ uint32 n = opcode10 & 07; // get n
+
+ word18 tmp18 = cpu.rX[n] ^ GETHI (cpu.CY);
+ tmp18 &= MASK18;
+
+ SC_I_ZERO (tmp18 == 0);
+ SC_I_NEG (tmp18 & SIGN18);
+
+ SETHI (cpu.CY, tmp18);
+ }
+#ifdef TEST_OLIN
+ cmpxchg ();
+#endif
+ break;
+
+ // erxn
+ case x0 (0660): // erx0
+ case x0 (0661): // erx1
+ case x0 (0662): // erx2
+ case x0 (0663): // erx3
+ case x0 (0664): // erx4
+ case x0 (0665): // erx5
+ case x0 (0666): // erx6 !!!! Beware !!!!
+ case x0 (0667): // erx7
+ // For n = 0, 1, ..., or 7 as determined by operation code
+ // C(Xn)i XOR C(Y)i -> C(Xn)i for i = (0, 1, ..., 17)
+ {
+ uint32 n = opcode10 & 07; // get n
+ cpu.rX[n] ^= GETHI (cpu.CY);
+ cpu.rX[n] &= MASK18;
+ HDBGRegX (n);
+
+ SC_I_ZERO (cpu.rX[n] == 0);
+ SC_I_NEG (cpu.rX[n] & SIGN18);
+ }
+ break;
+
+ /// Boolean Comparative And
+
+// Optimized to the top of the loop
+// case x0 (0315): // cana
+
+ case x0 (0317): // canaq
+ // C(Z)i = C(AQ)i & C(Y-pair)i for i = (0, 1, ..., 71)
+ {
+ word72 tmp72 = YPAIRTO72 (cpu.Ypair);
+ word72 trAQ = convert_to_word72 (cpu.rA, cpu.rQ);
+#ifdef NEED_128
+ trAQ = and_128 (trAQ, tmp72);
+ trAQ = and_128 (trAQ, MASK72);
+
+ SC_I_ZERO (iszero_128 (trAQ));
+ SC_I_NEG (isnonzero_128 (and_128 (trAQ, SIGN72)));
+#else
+ trAQ = trAQ & tmp72;
+ trAQ &= MASK72;
+
+ SC_I_ZERO (trAQ == 0);
+ SC_I_NEG (trAQ & SIGN72);
+#endif
+ }
+ break;
+
+ case x0 (0316): // canq
+ // C(Z)i = C(Q)i & C(Y)i for i = (0, 1, ..., 35)
+ {
+ word36 trZ = cpu.rQ & cpu.CY;
+ trZ &= DMASK;
+
+ SC_I_ZERO (trZ == 0);
+ SC_I_NEG (trZ & SIGN36);
+ }
+ break;
+
+ // canxn
+ case x0 (0300): // canx0
+ case x0 (0301): // canx1
+ case x0 (0302): // canx2
+ case x0 (0303): // canx3
+ case x0 (0304): // canx4
+ case x0 (0305): // canx5
+ case x0 (0306): // canx6
+ case x0 (0307): // canx7
+ // For n = 0, 1, ..., or 7 as determined by operation code
+ // C(Z)i = C(Xn)i & C(Y)i for i = (0, 1, ..., 17)
+ {
+ uint32 n = opcode10 & 07; // get n
+ word18 tmp18 = cpu.rX[n] & GETHI (cpu.CY);
+ tmp18 &= MASK18;
+ sim_debug (DBG_TRACEEXT, & cpu_dev,
+ "n %o rX %06o HI %06o tmp %06o\n",
+ n, cpu.rX[n], (word18) (GETHI (cpu.CY) & MASK18),
+ tmp18);
+
+ SC_I_ZERO (tmp18 == 0);
+ SC_I_NEG (tmp18 & SIGN18);
+ }
+ break;
+
+ /// Boolean Comparative Not
+
+ case x0 (0215): // cnaa
+ // C(Z)i = C(A)i & ~C(Y)i for i = (0, 1, ..., 35)
+ {
+ word36 trZ = cpu.rA & ~cpu.CY;
+ trZ &= DMASK;
+
+ SC_I_ZERO (trZ == 0);
+ SC_I_NEG (trZ & SIGN36);
+ }
+ break;
+
+ case x0 (0217): // cnaaq
+ // C(Z)i = C (AQ)i & ~C(Y-pair)i for i = (0, 1, ..., 71)
+ {
+ word72 tmp72 = YPAIRTO72 (cpu.Ypair); //
+
+ word72 trAQ = convert_to_word72 (cpu.rA, cpu.rQ);
+#ifdef NEED_128
+ trAQ = and_128 (trAQ, complement_128 (tmp72));
+ trAQ = and_128 (trAQ, MASK72);
+
+ SC_I_ZERO (iszero_128 (trAQ));
+ SC_I_NEG (isnonzero_128 (and_128 (trAQ, SIGN72)));
+#else
+ trAQ = trAQ & ~tmp72;
+ trAQ &= MASK72;
+
+ SC_I_ZERO (trAQ == 0);
+ SC_I_NEG (trAQ & SIGN72);
+#endif
+ }
+ break;
+
+ case x0 (0216): // cnaq
+ // C(Z)i = C(Q)i & ~C(Y)i for i = (0, 1, ..., 35)
+ {
+ word36 trZ = cpu.rQ & ~cpu.CY;
+ trZ &= DMASK;
+ SC_I_ZERO (trZ == 0);
+ SC_I_NEG (trZ & SIGN36);
+ }
+ break;
+
+ // cnaxn
+ case x0 (0200): // cnax0
+ case x0 (0201): // cnax1
+ case x0 (0202): // cnax2
+ case x0 (0203): // cnax3
+ case x0 (0204): // cnax4
+ case x0 (0205): // cnax5
+ case x0 (0206): // cnax6
+ case x0 (0207): // cnax7
+ // C(Z)i = C(Xn)i & ~C(Y)i for i = (0, 1, ..., 17)
+ {
+ uint32 n = opcode10 & 07; // get n
+ word18 tmp18 = cpu.rX[n] & ~GETHI (cpu.CY);
+ tmp18 &= MASK18;
+
+ SC_I_ZERO (tmp18 == 0);
+ SC_I_NEG (tmp18 & SIGN18);
+ }
+ break;
+
+ /// FLOATING-POINT ARITHMETIC INSTRUCTIONS
+
+ /// Floating-Point Data Movement Load
+
+ case x0 (0433): // dfld
+ // C(Y-pair)0,7 -> C(E)
+ // C(Y-pair)8,71 -> C(AQ)0,63
+ // 00...0 -> C(AQ)64,71
+ // Zero: If C(AQ) = 0, then ON; otherwise OFF
+ // Neg: If C(AQ)0 = 1, then ON; otherwise OFF
+
+ CPTUR (cptUseE);
+ cpu.rE = (cpu.Ypair[0] >> 28) & MASK8;
+
+ cpu.rA = (cpu.Ypair[0] & FLOAT36MASK) << 8;
+ cpu.rA |= (cpu.Ypair[1] >> 28) & MASK8;
+ HDBGRegA ();
+
+ cpu.rQ = (cpu.Ypair[1] & FLOAT36MASK) << 8;
+ HDBGRegQ ();
+
+ SC_I_ZERO (cpu.rA == 0 && cpu.rQ == 0);
+ SC_I_NEG (cpu.rA & SIGN36);
+ break;
+
+// Optimized to the top of the loop
+// case x0 (0431): // fld
+
+ /// Floating-Point Data Movement Store
+
+ case x0 (0457): // dfst
+ // C(E) -> C(Y-pair)0,7
+ // C(AQ)0,63 -> C(Y-pair)8,71
+
+ CPTUR (cptUseE);
+ cpu.Ypair[0] = ((word36)cpu.rE << 28) |
+ ((cpu.rA & 0777777777400LLU) >> 8);
+ cpu.Ypair[1] = ((cpu.rA & 0377) << 28) |
+ ((cpu.rQ & 0777777777400LLU) >> 8);
+
+ break;
+
+ case x0 (0472): // dfstr
+
+ dfstr (cpu.Ypair);
+ break;
+
+ case x0 (0455): // fst
+ // C(E) -> C(Y)0,7
+ // C(A)0,27 -> C(Y)8,35
+ CPTUR (cptUseE);
+ cpu.rE &= MASK8;
+ cpu.rA &= DMASK;
+ cpu.CY = ((word36)cpu.rE << 28) | (((cpu.rA >> 8) & 01777777777LL));
+ break;
+
+ case x0 (0470): // fstr
+ // The fstr instruction performs a true round and normalization on
+ // C(EAQ) as it is stored.
+
+// frd ();
+//
+// // C(E) -> C(Y)0,7
+// // C(A)0,27 -> C(Y)8,35
+// cpu.CY = ((word36)cpu.rE << 28) |
+// (((cpu.rA >> 8) & 01777777777LL));
+//
+// // Zero: If C(Y) = floating point 0, then ON; otherwise OFF
+// //SC_I_ZERO ((cpu.CY & 01777777777LL) == 0);
+// bool isZero = cpu.rE == -128 && cpu.rA == 0;
+// SC_I_ZERO (isZero);
+//
+// // Neg: If C(Y)8 = 1, then ON; otherwise OFF
+// //SC_I_NEG (cpu.CY & 01000000000LL);
+// SC_I_NEG (cpu.rA & SIGN36);
+//
+// // Exp Ovr: If exponent is greater than +127, then ON
+// // Exp Undr: If exponent is less than -128, then ON
+// // XXX: not certain how these can occur here ....
+
+ fstr (&cpu.CY);
+
+ break;
+
+ /// Floating-Point Addition
+
+ case x0 (0477): // dfad
+ // The dfad instruction may be thought of as a dufa instruction
+ // followed by a fno instruction.
+
+ CPTUR (cptUseE);
+ dufa (false);
+ fno (&cpu.rE, &cpu.rA, &cpu.rQ);
+ HDBGRegA ();
+ HDBGRegQ ();
+ break;
+
+ case x0 (0437): // dufa
+ dufa (false);
+ break;
+
+ case x0 (0475): // fad
+ // The fad instruction may be thought of a an ufa instruction
+ // followed by a fno instruction.
+ // (Heh, heh. We'll see....)
+
+ CPTUR (cptUseE);
+ ufa (false);
+ fno (&cpu.rE, &cpu.rA, &cpu.rQ);
+ HDBGRegA ();
+ HDBGRegQ ();
+
+ break;
+
+ case x0 (0435): // ufa
+ // C(EAQ) + C(Y) -> C(EAQ)
+
+ ufa (false);
+ break;
+
+ /// Floating-Point Subtraction
+
+ case x0 (0577): // dfsb
+ // The dfsb instruction is identical to the dfad instruction with
+ // the exception that the twos complement of the mantissa of the
+ // operand from main memory is used.
+
+ CPTUR (cptUseE);
+ dufa (true);
+ fno (&cpu.rE, &cpu.rA, &cpu.rQ);
+ HDBGRegA ();
+ HDBGRegQ ();
+ break;
+
+ case x0 (0537): // dufs
+ dufa (true);
+ break;
+
+ case x0 (0575): // fsb
+ // The fsb instruction may be thought of as an ufs instruction
+ // followed by a fno instruction.
+ CPTUR (cptUseE);
+ ufa (true);
+ fno (&cpu.rE, &cpu.rA, &cpu.rQ);
+ HDBGRegA ();
+ HDBGRegQ ();
+
+ break;
+
+ case x0 (0535): // ufs
+ // C(EAQ) - C(Y) -> C(EAQ)
+ ufa (true);
+ break;
+
+ /// Floating-Point Multiplication
+
+ case x0 (0463): // dfmp
+ // The dfmp instruction may be thought of as a dufm instruction
+ // followed by a fno instruction.
+
+ CPTUR (cptUseE);
+ dufm ();
+ fno (&cpu.rE, &cpu.rA, &cpu.rQ);
+ HDBGRegA ();
+ HDBGRegQ ();
+ break;
+
+ case x0 (0423): // dufm
+
+ dufm ();
+ break;
+
+ case x0 (0461): // fmp
+ // The fmp instruction may be thought of as a ufm instruction
+ // followed by a fno instruction.
+
+ CPTUR (cptUseE);
+ ufm ();
+ fno (&cpu.rE, &cpu.rA, &cpu.rQ);
+ HDBGRegA ();
+ HDBGRegQ ();
+
+ break;
+
+ case x0 (0421): // ufm
+ // C(EAQ)* C(Y) -> C(EAQ)
+ ufm ();
+ break;
+
+ /// Floating-Point Division
+
+ case x0 (0527): // dfdi
+
+ dfdi ();
+ break;
+
+ case x0 (0567): // dfdv
+
+ dfdv ();
+ break;
+
+ case x0 (0525): // fdi
+ // C(Y) / C(EAQ) -> C(EA)
+
+ fdi ();
+ break;
+
+ case x0 (0565): // fdv
+ // C(EAQ) /C(Y) -> C(EA)
+ // 00...0 -> C(Q)
+ fdv ();
+ break;
+
+ /// Floating-Point Negation
+
+ case x0 (0513): // fneg
+ // -C(EAQ) normalized -> C(EAQ)
+ fneg ();
+ break;
+
+ /// Floating-Point Normalize
+
+ case x0 (0573): // fno
+ // The fno instruction normalizes the number in C(EAQ) if C(AQ)
+ // != 0 and the overflow indicator is OFF.
+ //
+ // A normalized floating number is defined as one whose mantissa
+ // lies in the interval [0.5,1.0) such that 0.5<= |C(AQ)| <1.0
+ // which, in turn, requires that C(AQ)0 != C(AQ)1.list
+ //
+ // !!!! For personal reasons the following 3 lines of comment must
+ // never be removed from this program or any code derived
+ // therefrom. HWR 25 Aug 2014
+ ///Charles Is the coolest
+ ///true story y'all
+ //you should get me darksisers 2 for christmas
+
+ CPTUR (cptUseE);
+ fno (& cpu.rE, & cpu.rA, & cpu.rQ);
+ HDBGRegA ();
+ HDBGRegQ ();
+ break;
+
+ /// Floating-Point Round
+
+ case x0 (0473): // dfrd
+ // C(EAQ) rounded to 64 bits -> C(EAQ)
+ // 0 -> C(AQ)64,71 (See notes in dps8_math.c on dfrd())
+
+ dfrd ();
+ break;
+
+ case x0 (0471): // frd
+ // C(EAQ) rounded to 28 bits -> C(EAQ)
+ // 0 -> C(AQ)28,71 (See notes in dps8_math.c on frd())
+
+ frd ();
+ break;
+
+ /// Floating-Point Compare
+
+ case x0 (0427): // dfcmg
+ // C(E) :: C(Y-pair)0,7
+ // | C(AQ)0,63 | :: | C(Y-pair)8,71 |
+
+ dfcmg ();
+ break;
+
+ case x0 (0517): // dfcmp
+ // C(E) :: C(Y-pair)0,7
+ // C(AQ)0,63 :: C(Y-pair)8,71
+
+ dfcmp ();
+ break;
+
+ case x0 (0425): // fcmg
+ // C(E) :: C(Y)0,7
+ // | C(AQ)0,27 | :: | C(Y)8,35 |
+
+ fcmg ();
+ break;
+
+ case x0 (0515): // fcmp
+ // C(E) :: C(Y)0,7
+ // C(AQ)0,27 :: C(Y)8,35
+
+ fcmp ();
+ break;
+
+ /// Floating-Point Miscellaneous
+
+ case x0 (0415): // ade
+ // C(E) + C(Y)0,7 -> C(E)
+ {
+ CPTUR (cptUseE);
+ int y = SIGNEXT8_int ((cpu.CY >> 28) & 0377);
+ int e = SIGNEXT8_int (cpu.rE);
+ e = e + y;
+
+ cpu.rE = e & 0377;
+ CLR_I_ZERO;
+ CLR_I_NEG;
+
+ if (e > 127)
+ {
+ SET_I_EOFL;
+ if (tstOVFfault ())
+ doFault (FAULT_OFL, fst_zero, "ade exp overflow fault");
+ }
+
+ if (e < -128)
+ {
+ SET_I_EUFL;
+ if (tstOVFfault ())
+ doFault (FAULT_OFL, fst_zero, "ade exp underflow fault");
+ }
+ }
+ break;
+
+ case x0 (0430): // fszn
+
+ // Zero: If C(Y)8,35 = 0, then ON; otherwise OFF
+ // Negative: If C(Y)8 = 1, then ON; otherwise OFF
+
+ SC_I_ZERO ((cpu.CY & 001777777777LL) == 0);
+ SC_I_NEG (cpu.CY & 001000000000LL);
+
+ break;
+
+ case x0 (0411): // lde
+ // C(Y)0,7 -> C(E)
+
+ CPTUR (cptUseE);
+ cpu.rE = (cpu.CY >> 28) & 0377;
+ CLR_I_ZERO;
+ CLR_I_NEG;
+
+ break;
+
+ case x0 (0456): // ste
+ // C(E) -> C(Y)0,7
+ // 00...0 -> C(Y)8,17
+
+ CPTUR (cptUseE);
+ //putbits36_18 (& cpu.CY, 0, ((word18) (cpu.rE & 0377) << 10));
+ cpu.CY = ((word36) (cpu.rE & 0377)) << 28;
+ cpu.zone = 0777777000000;
+ cpu.useZone = true;
+ break;
+
+
+ /// TRANSFER INSTRUCTIONS
+
+ case x0 (0713): // call6
+
+ CPTUR (cptUsePRn + 7);
+
+ do_caf ();
+ read_tra_op ();
+ sim_debug (DBG_TRACEEXT, & cpu_dev,
+ "call6 PRR %o PSR %o\n", cpu.PPR.PRR, cpu.PPR.PSR);
+
+ return CONT_TRA;
+
+
+ case x0 (0630): // ret
+ {
+ // Parity mask: If C(Y)27 = 1, and the processor is in absolute or
+ // mask privileged mode, then ON; otherwise OFF. This indicator is
+ // not affected in the normal or BAR modes.
+ // Not BAR mode: Can be set OFF but not ON by the ret instruction
+ // Absolute mode: Can be set OFF but not ON by the ret instruction
+ // All oter indicators: If corresponding bit in C(Y) is 1, then ON;
+ // otherwise, OFF
+
+ // C(Y)0,17 -> C(PPR.IC)
+ // C(Y)18,31 -> C(IR)
+ do_caf ();
+ Read (cpu.TPR.CA, &cpu.CY, OPERAND_READ);
+
+ cpu.PPR.IC = GETHI (cpu.CY);
+ word18 tempIR = GETLO (cpu.CY) & 0777770;
+ // Assuming 'mask privileged mode' is 'temporary absolute mode'
+ if (is_priv_mode ()) // abs. or temp. abs. or priv.
+ {
+ // if abs, copy existing parity mask to tempIR
+ // According to ISOLTS pm785, not the case.
+ //SCF (TST_I_PMASK, tempIR, I_PMASK);
+ // if abs, copy existing I_MIF to tempIR
+ SCF (TST_I_MIF, tempIR, I_MIF);
+ }
+ else
+ {
+ CLRF (tempIR, I_MIF);
+ }
+ // can be set OFF but not on
+ // IR ret result
+ // off off off
+ // off on off
+ // on on on
+ // on off off
+ // "If it was on, set it to on"
+ //SCF (TST_I_NBAR, tempIR, I_NBAR);
+ if (! (TST_I_NBAR && TSTF (tempIR, I_NBAR)))
+ {
+ CLRF (tempIR, I_NBAR);
+ }
+ if (! (TST_I_ABS && TSTF (tempIR, I_ABS)))
+ {
+ CLRF (tempIR, I_ABS);
+ }
+
+
+ //sim_debug (DBG_TRACEEXT, & cpu_dev,
+ // "RET NBAR was %d now %d\n",
+ // TST_NBAR ? 1 : 0,
+ // TSTF (tempIR, I_NBAR) ? 1 : 0);
+ //sim_debug (DBG_TRACEEXT, & cpu_dev,
+ // "RET ABS was %d now %d\n",
+ // TST_I_ABS ? 1 : 0,
+ // TSTF (tempIR, I_ABS) ? 1 : 0);
+ CPTUR (cptUseIR);
+ cpu.cu.IR = tempIR;
+ return CONT_RET;
+ }
+
+// Optimized to the top of the loop
+// case x0 (0610): // rtcd
+
+ case x0 (0614): // teo
+ // If exponent overflow indicator ON then
+ // C(TPR.CA) -> C(PPR.IC)
+ // C(TPR.TSR) -> C(PPR.PSR)
+ // otherwise, no change to C(PPR)
+ if (TST_I_EOFL)
+ {
+ CLR_I_EOFL;
+ do_caf ();
+ read_tra_op ();
+ return CONT_TRA;
+ }
+ break;
+
+ case x0 (0615): // teu
+ // If exponent underflow indicator ON then
+ // C(TPR.CA) -> C(PPR.IC)
+ // C(TPR.TSR) -> C(PPR.PSR)
+ if (TST_I_EUFL)
+ {
+ CLR_I_EUFL;
+ do_caf ();
+ read_tra_op ();
+ return CONT_TRA;
+ }
+ break;
+
+
+// Optimized to the top of the loop
+// case x0 (0604): // tmi
+
+// Optimized to the top of the loop
+// case x1 (0604): // tmoz
+
+ case x0 (0602): // tnc
+ // If carry indicator OFF then
+ // C(TPR.CA) -> C(PPR.IC)
+ // C(TPR.TSR) -> C(PPR.PSR)
+ if (!TST_I_CARRY)
+ {
+ do_caf ();
+ read_tra_op ();
+ return CONT_TRA;
+ }
+ break;
+
+// Optimized to the top of the loop
+// case x0 (0601): // tnz
+
+ case x0 (0617): // tov
+ // If overflow indicator ON then
+ // C(TPR.CA) -> C(PPR.IC)
+ // C(TPR.TSR) -> C(PPR.PSR)
+ if (TST_I_OFLOW)
+ {
+ CLR_I_OFLOW;
+ do_caf ();
+ read_tra_op ();
+ return CONT_TRA;
+ }
+ break;
+
+ case x0 (0605): // tpl
+ // If negative indicator OFF, then
+ // C(TPR.CA) -> C(PPR.IC)
+ // C(TPR.TSR) -> C(PPR.PSR)
+ if (! (TST_I_NEG))
+ {
+ do_caf ();
+ read_tra_op ();
+ return CONT_TRA;
+ }
+ break;
+
+// Optimized to the top of the loop
+// case x1 (0605): // tpnz
+
+// Optimized to the top of the loop
+// case x0 (0710): // tra
+
+
+ case x0 (0603): // trc
+ // If carry indicator ON then
+ // C(TPR.CA) -> C(PPR.IC)
+ // C(TPR.TSR) -> C(PPR.PSR)
+ if (TST_I_CARRY)
+ {
+ do_caf ();
+ read_tra_op ();
+ return CONT_TRA;
+ }
+ break;
+
+ case x1 (0601): // trtf
+ // If truncation indicator OFF then
+ // C(TPR.CA) -> C(PPR.IC)
+ // C(TPR.TSR) -> C(PPR.PSR)
+ if (!TST_I_TRUNC)
+ {
+ do_caf ();
+ read_tra_op ();
+ return CONT_TRA;
+ }
+ break;
+
+ case x1 (0600): // trtn
+ // If truncation indicator ON then
+ // C(TPR.CA) -> C(PPR.IC)
+ // C(TPR.TSR) -> C(PPR.PSR)
+ if (TST_I_TRUNC)
+ {
+ CLR_I_TRUNC;
+ do_caf ();
+ read_tra_op ();
+ return CONT_TRA;
+ }
+ break;
+
+// Optimized to the top of the loop
+// // tspn
+// case x0 (0270): // tsp0
+// case x0 (0271): // tsp1
+// case x0 (0272): // tsp2
+// case x0 (0273): // tsp3
+// case x0 (0670): // tsp4
+// case x0 (0671): // tsp5
+// case x0 (0672): // tsp6
+// case x0 (0673): // tsp7
+
+ case x0 (0715): // tss
+ CPTUR (cptUseBAR);
+ do_caf ();
+ if (get_bar_mode ())
+ read_tra_op ();
+ else
+ {
+ cpu.TPR.CA = get_BAR_address (cpu.TPR.CA);
+ read_tra_op ();
+ CLR_I_NBAR;
+ }
+ return CONT_TRA;
+
+// Optimized to the top of the loop
+// // tsxn
+// case x0 (0700): // tsx0
+// case x0 (0701): // tsx1
+// case x0 (0702): // tsx2
+// case x0 (0703): // tsx3
+// case x0 (0704): // tsx4
+// case x0 (0705): // tsx5
+// case x0 (0706): // tsx6
+// case x0 (0707): // tsx7
+
+ case x0 (0607): // ttf
+ // If tally runout indicator OFF then
+ // C(TPR.CA) -> C(PPR.IC)
+ // C(TPR.TSR) -> C(PPR.PSR)
+ // otherwise, no change to C(PPR)
+ if (TST_I_TALLY == 0)
+ {
+ do_caf ();
+ read_tra_op ();
+ return CONT_TRA;
+ }
+ break;
+
+ case x1 (0606): // ttn
+ // If tally runout indicator ON then
+ // C(TPR.CA) -> C(PPR.IC)
+ // C(TPR.TSR) -> C(PPR.PSR)
+ // otherwise, no change to C(PPR)
+ if (TST_I_TALLY)
+ {
+ do_caf ();
+ read_tra_op ();
+ return CONT_TRA;
+ }
+ break;
+
+// Optimized to the top of the loop
+// case x0 (0600): // tze
+
+ /// POINTER REGISTER INSTRUCTIONS
+
+ /// Pointer Register Data Movement Load
+
+ // easpn
+
+ case x0 (0311): // easp0
+ // C(TPR.CA) -> C(PRn.SNR)
+ CPTUR (cptUsePRn + 0);
+ cpu.PR[0].SNR = cpu.TPR.CA & MASK15;
+ HDBGRegPR (0);
+ break;
+
+ case x1 (0310): // easp1
+ // C(TPR.CA) -> C(PRn.SNR)
+ CPTUR (cptUsePRn + 1);
+ cpu.PR[1].SNR = cpu.TPR.CA & MASK15;
+ HDBGRegPR (1);
+ break;
+
+ case x0 (0313): // easp2
+ // C(TPR.CA) -> C(PRn.SNR)
+ CPTUR (cptUsePRn + 2);
+ cpu.PR[2].SNR = cpu.TPR.CA & MASK15;
+ HDBGRegPR (2);
+ break;
+
+ case x1 (0312): // easp3
+ // C(TPR.CA) -> C(PRn.SNR)
+ CPTUR (cptUsePRn + 3);
+ cpu.PR[3].SNR = cpu.TPR.CA & MASK15;
+ HDBGRegPR (3);
+ break;
+
+ case x0 (0331): // easp4
+ // C(TPR.CA) -> C(PRn.SNR)
+ CPTUR (cptUsePRn + 4);
+ cpu.PR[4].SNR = cpu.TPR.CA & MASK15;
+ HDBGRegPR (4);
+ break;
+
+ case x1 (0330): // easp5
+ // C(TPR.CA) -> C(PRn.SNR)
+ CPTUR (cptUsePRn + 5);
+ cpu.PR[5].SNR = cpu.TPR.CA & MASK15;
+ HDBGRegPR (5);
+ break;
+
+ case x0 (0333): // easp6
+ // C(TPR.CA) -> C(PRn.SNR)
+ CPTUR (cptUsePRn + 6);
+ cpu.PR[6].SNR = cpu.TPR.CA & MASK15;
+ HDBGRegPR (6);
+ break;
+
+ case x1 (0332): // easp7
+ // C(TPR.CA) -> C(PRn.SNR)
+ CPTUR (cptUsePRn + 7);
+ cpu.PR[7].SNR = cpu.TPR.CA & MASK15;
+ HDBGRegPR (7);
+ break;
+
+ // eawpn
+
+ case x0 (0310): // eawp0
+ // For n = 0, 1, ..., or 7 as determined by operation code
+ // C(TPR.CA) -> C(PRn.WORDNO)
+ // C(TPR.TBR) -> C(PRn.BITNO)
+ CPTUR (cptUsePRn + 0);
+ cpu.PR[0].WORDNO = cpu.TPR.CA;
+ SET_PR_BITNO (0, cpu.TPR.TBR);
+ HDBGRegPR (0);
+ break;
+
+ case x1 (0311): // eawp1
+ // For n = 0, 1, ..., or 7 as determined by operation code
+ // C(TPR.CA) -> C(PRn.WORDNO)
+ // C(TPR.TBR) -> C(PRn.BITNO)
+ CPTUR (cptUsePRn + 1);
+ cpu.PR[1].WORDNO = cpu.TPR.CA;
+ SET_PR_BITNO (1, cpu.TPR.TBR);
+ HDBGRegPR (1);
+ break;
+
+ case x0 (0312): // eawp2
+ // For n = 0, 1, ..., or 7 as determined by operation code
+ // C(TPR.CA) -> C(PRn.WORDNO)
+ // C(TPR.TBR) -> C(PRn.BITNO)
+ CPTUR (cptUsePRn + 2);
+ cpu.PR[2].WORDNO = cpu.TPR.CA;
+ SET_PR_BITNO (2, cpu.TPR.TBR);
+ HDBGRegPR (2);
+ break;
+
+ case x1 (0313): // eawp3
+ // For n = 0, 1, ..., or 7 as determined by operation code
+ // C(TPR.CA) -> C(PRn.WORDNO)
+ // C(TPR.TBR) -> C(PRn.BITNO)
+ CPTUR (cptUsePRn + 3);
+ cpu.PR[3].WORDNO = cpu.TPR.CA;
+ SET_PR_BITNO (3, cpu.TPR.TBR);
+ HDBGRegPR (3);
+ break;
+
+ case x0 (0330): // eawp4
+ // For n = 0, 1, ..., or 7 as determined by operation code
+ // C(TPR.CA) -> C(PRn.WORDNO)
+ // C(TPR.TBR) -> C(PRn.BITNO)
+ CPTUR (cptUsePRn + 4);
+ cpu.PR[4].WORDNO = cpu.TPR.CA;
+ SET_PR_BITNO (4, cpu.TPR.TBR);
+ HDBGRegPR (4);
+ break;
+
+ case x1 (0331): // eawp5
+ // For n = 0, 1, ..., or 7 as determined by operation code
+ // C(TPR.CA) -> C(PRn.WORDNO)
+ // C(TPR.TBR) -> C(PRn.BITNO)
+ CPTUR (cptUsePRn + 5);
+ cpu.PR[5].WORDNO = cpu.TPR.CA;
+ SET_PR_BITNO (5, cpu.TPR.TBR);
+ HDBGRegPR (5);
+ break;
+
+ case x0 (0332): // eawp6
+ // For n = 0, 1, ..., or 7 as determined by operation code
+ // C(TPR.CA) -> C(PRn.WORDNO)
+ // C(TPR.TBR) -> C(PRn.BITNO)
+ CPTUR (cptUsePRn + 6);
+ cpu.PR[6].WORDNO = cpu.TPR.CA;
+ SET_PR_BITNO (6, cpu.TPR.TBR);
+ HDBGRegPR (6);
+ break;
+
+ case x1 (0333): // eawp7
+ // For n = 0, 1, ..., or 7 as determined by operation code
+ // C(TPR.CA) -> C(PRn.WORDNO)
+ // C(TPR.TBR) -> C(PRn.BITNO)
+ CPTUR (cptUsePRn + 7);
+ cpu.PR[7].WORDNO = cpu.TPR.CA;
+ SET_PR_BITNO (7, cpu.TPR.TBR);
+ HDBGRegPR (7);
+ break;
+
+// Optimized to the top of the loop
+// case x1 (0350): // epbp0
+// case x0 (0351): // epbp1
+// case x1 (0352): // epbp2
+// case x0 (0353): // epbp3
+// case x1 (0370): // epbp4
+// case x0 (0371): // epbp5
+// case x1 (0372): // epbp6
+// case x0 (0373): // epbp7
+
+// Optimized to the top of the switch
+// case x0 (0350): // epp0
+// case x1 (0351): // epp1
+// case x0 (0352): // epp2
+// case x1 (0353): // epp3
+// case x0 (0374): // epp4
+// case x1 (0371): // epp5
+// case x0 (0376): // epp6
+// case x1 (0373): // epp7
+
+ case x0 (0173): // lpri
+ // For n = 0, 1, ..., 7
+ // Y-pair = Y-block16 + 2n
+ // Maximum of C(Y-pair)18,20; C(SDW.R1); C(TPR.TRR) -> C(PRn.RNR)
+ // C(Y-pair) 3,17 -> C(PRn.SNR)
+ // C(Y-pair)36,53 -> C(PRn.WORDNO)
+ // C(Y-pair)57,62 -> C(PRn.BITNO)
+
+ for (uint32 n = 0 ; n < 8 ; n ++)
+ {
+ CPTUR (cptUsePRn + n);
+ // Even word of ITS pointer pair
+ cpu.Ypair[0] = cpu.Yblock16[n * 2 + 0];
+ // Odd word of ITS pointer pair
+ cpu.Ypair[1] = cpu.Yblock16[n * 2 + 1];
+
+ // RNR from ITS pair
+ word3 Crr = (GETLO (cpu.Ypair[0]) >> 15) & 07;
+ if (get_addr_mode () == APPEND_mode)
+ cpu.PR[n].RNR = max3 (Crr, cpu.SDW->R1, cpu.TPR.TRR);
+ else
+ cpu.PR[n].RNR = Crr;
+ cpu.PR[n].SNR = (cpu.Ypair[0] >> 18) & MASK15;
+ cpu.PR[n].WORDNO = GETHI (cpu.Ypair[1]);
+ word6 bitno = (GETLO (cpu.Ypair[1]) >> 9) & 077;
+// According to ISOLTS, loading a 077 into bitno results in 037
+// pa851 test-04b lpri test bar-100176
+// test start 105321 patch 105461 subtest loop point 105442
+// s/b 77777737
+// was 77777733
+ if (bitno == 077)
+ bitno = 037;
+ SET_PR_BITNO (n, bitno);
+ HDBGRegPR (n);
+ }
+
+ break;
+
+// Optimized to the top of the loop
+// // lprpn
+// case x0 (0760): // lprp0
+// case x0 (0761): // lprp1
+// case x0 (0762): // lprp2
+// case x0 (0763): // lprp3
+// case x0 (0764): // lprp4
+// case x0 (0765): // lprp5
+// case x0 (0766): // lprp6
+// case x0 (0767): // lprp7
+
+ /// Pointer Register Data Movement Store
+
+// Optimized to the top of the loop
+// case x1 (0250): // spbp0
+// case x0 (0251): // spbp1
+// case x1 (0252): // spbp2
+// case x0 (0253): // spbp3
+// case x1 (0650): // spbp4
+// case x0 (0651): // spbp5
+// case x1 (0652): // spbp6
+// case x0 (0653): // spbp7
+
+ case x0 (0254): // spri
+ // For n = 0, 1, ..., 7
+ // Y-pair = Y-block16 + 2n
+
+ // 000 -> C(Y-pair)0,2
+ // C(PRn.SNR) -> C(Y-pair)3,17
+ // C(PRn.RNR) -> C(Y-pair)18,20
+ // 00...0 -> C(Y-pair)21,29
+ // (43)8 -> C(Y-pair)30,35
+
+ // C(PRn.WORDNO) -> C(Y-pair)36,53
+ // 000 -> C(Y-pair)54,56
+ // C(PRn.BITNO) -> C(Y-pair)57,62
+ // 00...0 -> C(Y-pair)63,71
+
+ for (uint32 n = 0 ; n < 8 ; n++)
+ {
+ CPTUR (cptUsePRn + n);
+ cpu.Yblock16[2 * n] = 043;
+ cpu.Yblock16[2 * n] |= ((word36) cpu.PR[n].SNR) << 18;
+ cpu.Yblock16[2 * n] |= ((word36) cpu.PR[n].RNR) << 15;
+
+ cpu.Yblock16[2 * n + 1] = (word36) cpu.PR[n].WORDNO << 18;
+ cpu.Yblock16[2 * n + 1] |= (word36) GET_PR_BITNO(n) << 9;
+ }
+
+ break;
+
+// Optimized to the top of the loop
+// case x0 (0250): // spri0
+// case x1 (0251): // spri1
+// case x0 (0252): // spri2
+// case x1 (0253): // spri3
+// case x0 (0650): // spri4
+// case x1 (0255): // spri5
+// case x0 (0652): // spri6
+// case x1 (0257): // spri7
+
+ // sprpn
+ case x0 (0540): // sprp0
+ case x0 (0541): // sprp1
+ case x0 (0542): // sprp2
+ case x0 (0543): // sprp3
+ case x0 (0544): // sprp4
+ case x0 (0545): // sprp5
+ case x0 (0546): // sprp6
+ case x0 (0547): // sprp7
+ // For n = 0, 1, ..., or 7 as determined by operation code
+ // C(PRn.BITNO) -> C(Y)0,5
+ // C(PRn.SNR)3,14 -> C(Y)6,17
+ // C(PRn.WORDNO) -> C(Y)18,35
+ {
+ uint32 n = opcode10 & 07; // get n
+ CPTUR (cptUsePRn + n);
+
+ // If C(PRn.SNR)0,2 are nonzero, and C(PRn.SNR) != 11...1, then
+ // a store fault (illegal pointer) will occur and C(Y) will not
+ // be changed.
+
+ if ((cpu.PR[n].SNR & 070000) != 0 && cpu.PR[n].SNR != MASK15)
+ doFault (FAULT_STR, fst_str_ptr, "sprpn");
+
+ cpu.CY = ((word36) (GET_PR_BITNO(n) & 077)) << 30;
+ // lower 12- of 15-bits
+ cpu.CY |= ((word36) (cpu.PR[n].SNR & 07777)) << 18;
+ cpu.CY |= cpu.PR[n].WORDNO & PAMASK;
+ cpu.CY &= DMASK; // keep to 36-bits
+ }
+ break;
+
+ /// Pointer Register Address Arithmetic
+
+ // adwpn
+ case x0 (0050): // adwp0
+ case x0 (0051): // adwp1
+ case x0 (0052): // adwp2
+ case x0 (0053): // adwp3
+ // For n = 0, 1, ..., or 7 as determined by operation code
+ // C(Y)0,17 + C(PRn.WORDNO) -> C(PRn.WORDNO)
+ // 00...0 -> C(PRn.BITNO)
+ {
+ uint32 n = opcode10 & 03; // get n
+ CPTUR (cptUsePRn + n);
+ cpu.PR[n].WORDNO += GETHI (cpu.CY);
+ cpu.PR[n].WORDNO &= MASK18;
+ SET_PR_BITNO (n, 0);
+ HDBGRegPR (n);
+ }
+ break;
+
+ case x0 (0150): // adwp4
+ case x0 (0151): // adwp5
+ case x0 (0152): // adwp6
+ case x0 (0153): // adwp7
+ // For n = 0, 1, ..., or 7 as determined by operation code
+ // C(Y)0,17 + C(PRn.WORDNO) -> C(PRn.WORDNO)
+ // 00...0 -> C(PRn.BITNO)
+ {
+ uint32 n = (opcode10 & MASK3) + 4U; // get n
+ CPTUR (cptUsePRn + n);
+ cpu.PR[n].WORDNO += GETHI (cpu.CY);
+ cpu.PR[n].WORDNO &= MASK18;
+ SET_PR_BITNO (n, 0);
+ HDBGRegPR (n);
+ }
+ break;
+
+ /// Pointer Register Miscellaneous
+
+// Optimized to the top of the loop
+// case x0 (0213): // epaq
+
+ /// MISCELLANEOUS INSTRUCTIONS
+
+// case x0 (0633): // rccl
+// // 00...0 -> C(AQ)0,19
+// // C(calendar clock) -> C(AQ)20,71
+// {
+//// XXX see ticket #23
+// // For the rccl instruction, the first 2 or 3 bits of the addr
+// // field of the instruction are used to specify which SCU.
+// // init_processor.alm systematically steps through the SCUs,
+// // using addresses 000000 100000 200000 300000.
+//#ifdef DPS8M
+// uint cpu_port_num = (cpu.TPR.CA >> 15) & 03;
+//#endif
+//#ifdef L68
+// uint cpu_port_num = (cpu.TPR.CA >> 15) & 07;
+//#endif
+// if (! get_scu_in_use (current_running_cpu_idx, cpu_port_num))
+// {
+// sim_warn ("rccl on CPU %u port %d has no SCU; faulting\n",
+// current_running_cpu_idx, cpu_port_num);
+// doFault (FAULT_ONC, fst_onc_nem, "(rccl)");
+// }
+// uint scuUnitIdx = get_scu_idx (current_running_cpu_idx, cpu_port_num);
+//
+// t_stat rc = scu_rscr ((uint) scuUnitIdx, current_running_cpu_idx,
+// 040, & cpu.rA, & cpu.rQ);
+// HDBGRegA ();
+// HDBGRegQ ();
+// if (rc > 0)
+// return rc;
+//#ifndef SPEED
+// if_sim_debug (DBG_TRACEEXT, & cpu_dev)
+// {
+// // Clock at initialization
+// // date -d "Tue Jul 22 16:39:38 PDT 1999" +%s
+// // 932686778
+// uint64 UnixSecs = 932686778;
+// uint64 UnixuSecs = UnixSecs * 1000000LL;
+// // now determine uSecs since Jan 1, 1901 ...
+// uint64 MulticsuSecs = 2177452800000000LL + UnixuSecs;
+//
+// // Back into 72 bits
+// word72 big = convert_to_word72 (cpu.rA, cpu.rQ);
+//#ifdef NEED_128
+// // Convert to time since boot
+// big = subtract_128 (big, construct_128 (0, MulticsuSecs));
+// uint32_t remainder;
+// uint128 bigsecs = divide_128_32 (big, 1000000u, & remainder);
+// uint64_t uSecs = remainder;
+// uint64_t secs = bigsecs.l;
+// sim_debug (DBG_TRACEEXT, & cpu_dev,
+// "Clock time since boot %4llu.%06llu seconds\n",
+// secs, uSecs);
+//#else
+// // Convert to time since boot
+// big -= MulticsuSecs;
+// unsigned long uSecs = big % 1000000u;
+// unsigned long secs = (unsigned long) (big / 1000000u);
+// sim_debug (DBG_TRACEEXT, & cpu_dev,
+// "Clock time since boot %4lu.%06lu seconds\n",
+// secs, uSecs);
+//#endif
+// }
+//#endif
+// }
+// break;
+
+ case x0 (0002): // drl
+ // Causes a fault which fetches and executes, in absolute mode, the
+ // instruction pair at main memory location C+(14)8. The value of C
+ // is obtained from the FAULT VECTOR switches on the processor
+ // configuration panel.
+
+ if (cpu.switches.drl_fatal)
+ {
+ return STOP_STOP;
+ }
+ doFault (FAULT_DRL, fst_zero, "drl");
+
+ case x0 (0716): // xec
+ cpu.cu.xde = 1;
+ cpu.cu.xdo = 0;
+// XXX NB. This used to be done in executeInstruction post-execution
+// processing; moving it here means that post-execution code cannot inspect IWB
+// to determine what the instruction or it flags were.
+ cpu.cu.IWB = cpu.CY;
+ return CONT_XEC;
+
+ case x0 (0717): // xed
+ // The xed instruction itself does not affect any indicator.
+ // However, the execution of the instruction pair from C(Y-pair)
+ // may affect indicators.
+ //
+ // The even instruction from C(Y-pair) must not alter
+ // C(Y-pair)36,71, and must not be another xed instruction.
+ //
+ // If the execution of the instruction pair from C(Y-pair) alters
+ // C(PPR.IC), then a transfer of control occurs; otherwise, the
+ // next instruction to be executed is fetched from C(PPR.IC)+1. If
+ // the even instruction from C(Y-pair) alters C(PPR.IC), then the
+ // transfer of control is effective immediately and the odd
+ // instruction is not executed.
+ //
+ // To execute an instruction pair having an rpd instruction as the
+ // odd instruction, the xed instruction must be located at an odd
+ // address. The instruction pair repeated is that instruction pair
+ // at C PPR.IC)+1, that is, the instruction pair immediately
+ // following the xed instruction. C(PPR.IC) is adjusted during the
+ // execution of the repeated instruction pair so the the next
+ // instruction fetched for execution is from the first word
+ // following the repeated instruction pair.
+ //
+ // The instruction pair at C(Y-pair) may cause any of the processor
+ // defined fault conditions, but only the directed faults (0,1,2,3)
+ // and the access violation fault may be restarted successfully by
+ // the hardware. Note that the software induced fault tag (1,2,3)
+ // faults cannot be properly restarted.
+ //
+ // An attempt to execute an EIS multiword instruction causes an
+ // illegal procedure fault.
+ //
+ // Attempted repetition with the rpt, rpd, or rpl instructions
+ // causes an illegal procedure fault.
+
+ cpu.cu.xde = 1;
+ cpu.cu.xdo = 1;
+// XXX NB. This used to be done in executeInstruction post-execution
+// processing; moving it here means that post-execution code cannot inspect IWB
+// to determine what the instruction or it flags were.
+ cpu.cu.IWB = cpu.Ypair[0];
+ cpu.cu.IRODD = cpu.Ypair[1];
+ return CONT_XEC;
+
+ case x0 (0001): // mme
+#ifdef TESTING
+ if (sim_deb_mme_cntdwn > 0)
+ sim_deb_mme_cntdwn --;
+#endif
+#ifdef ISOLTS
+ cpu.TPR.CA = GET_ADDR (IWB_IRODD);
+#endif
+ // Causes a fault that fetches and executes, in absolute mode, the
+ // instruction pair at main memory location C+4. The value of C is
+ // obtained from the FAULT VECTOR switches on the processor
+ // configuration panel.
+ doFault (FAULT_MME, fst_zero, "Master Mode Entry (mme)");
+
+ case x0 (0004): // mme2
+ // Causes a fault that fetches and executes, in absolute mode, the
+ // instruction pair at main memory location C+(52)8. The value of C
+ // is obtained from the FAULT VECTOR switches on the processor
+ // configuration panel.
+ doFault (FAULT_MME2, fst_zero, "Master Mode Entry 2 (mme2)");
+
+ case x0 (0005): // mme3
+ // Causes a fault that fetches and executes, in absolute mode, the
+ // instruction pair at main memory location C+(54)8. The value of C
+ // is obtained from the FAULT VECTOR switches on the processor
+ // configuration panel.
+ doFault (FAULT_MME3, fst_zero, "Master Mode Entry 3 (mme3)");
+
+ case x0 (0007): // mme4
+ // Causes a fault that fetches and executes, in absolute mode, the
+ // instruction pair at main memory location C+(56)8. The value of C
+ // is obtained from the FAULT VECTOR switches on the processor
+ // configuration panel.
+ doFault (FAULT_MME4, fst_zero, "Master Mode Entry 4 (mme4)");
+
+ case x0 (0011): // nop
+ break;
+
+ case x0 (0012): // puls1
+ break;
+
+ case x0 (0013): // puls2
+ break;
+
+ /// Repeat
+
+ case x0 (0560): // rpd
+ {
+ if ((cpu.PPR.IC & 1) == 0)
+ doFault (FAULT_IPR, fst_ill_proc, "rpd odd");
+ cpu.cu.delta = i->tag;
+ // a:AL39/rpd1
+ word1 c = (i->address >> 7) & 1;
+ if (c)
+ {
+ cpu.rX[0] = i->address; // Entire 18 bits
+ HDBGRegX (0);
+ }
+ cpu.cu.rd = 1;
+ cpu.cu.repeat_first = 1;
+ }
+ break;
+
+ case x0 (0500): // rpl
+ {
+ uint c = (i->address >> 7) & 1;
+ cpu.cu.delta = i->tag;
+ if (c)
+ {
+ cpu.rX[0] = i->address; // Entire 18 bits
+ HDBGRegX (0);
+ }
+ cpu.cu.rl = 1;
+ cpu.cu.repeat_first = 1;
+ }
+ break;
+
+ case x0 (0520): // rpt
+ {
+ uint c = (i->address >> 7) & 1;
+ cpu.cu.delta = i->tag;
+ if (c)
+ {
+ cpu.rX[0] = i->address; // Entire 18 bits
+ HDBGRegX (0);
+ }
+ cpu.cu.rpt = 1;
+ cpu.cu.repeat_first = 1;
+ }
+ break;
+
+ /// Ring Alarm Register
+
+ case x1 (0754): // sra
+ // 00...0 -> C(Y)0,32
+ // C(RALR) -> C(Y)33,35
+
+ CPTUR (cptUseRALR);
+ cpu.CY = (word36)cpu.rRALR;
+
+ break;
+
+ /// Store Base Address Register
+
+ case x0 (0550): // sbar
+ // C(BAR) -> C(Y) 0,17
+ CPTUR (cptUseBAR);
+ //SETHI (cpu.CY, (cpu.BAR.BASE << 9) | cpu.BAR.BOUND);
+ cpu.CY = ((((word36) cpu.BAR.BASE) << 9) | cpu.BAR.BOUND) << 18;
+ cpu.zone = 0777777000000;
+ cpu.useZone = true;
+#ifdef TEST_OLIN
+ cmpxchg ();
+#endif
+ break;
+
+
+ /// Translation
+
+ case x0 (0505): // bcd
+ // Shift C(A) left three positions
+ // | C(A) | / C(Y) -> 4-bit quotient
+ // C(A) - C(Y) * quotient -> remainder
+ // Shift C(Q) left six positions
+ // 4-bit quotient -> C(Q)32,35
+ // remainder -> C(A)
+
+ {
+ word36 tmp1 = cpu.rA & SIGN36; // A0
+ word36 tmp36 = (cpu.rA << 3) & DMASK;
+ word36 tmp36q = tmp36 / cpu.CY; // this may be more than 4 bits, keep it for remainder calculation
+ word36 tmp36r = 0;
+ if (!tmp1) {
+ tmp36r = tmp36 - tmp36q * cpu.CY;
+ } else {
+ // ISOLTS-745 05i: bcd when rA is negative.
+ // Note that this only gets called in the first round of the bcd
+ // conversion; the rA sign bit will get shifted out.
+ // Looking at the expected results, it appears that a 'borrow'
+ // is represented in a residue style notation -- an unborrow
+ // result is 0-9 (000 - 011), a borrowed digit as 6-15 (006-017)
+ tmp36q += 6;
+ tmp36r = tmp36 + tmp36q * cpu.CY;
+ }
+
+ cpu.rQ <<= 6; // Shift C(Q) left six positions
+ cpu.rQ &= DMASK;
+
+ //cpu.rQ &= (word36) ~017; // 4-bit quotient -> C(Q)32,35 lo6 bits already zeroed out
+ cpu.rQ |= (tmp36q & 017);
+ HDBGRegQ ();
+
+ cpu.rA = tmp36r & DMASK; // remainder -> C(A)
+ HDBGRegA ();
+
+ SC_I_ZERO (cpu.rA == 0); // If C(A) = 0, then ON;
+ // otherwise OFF
+ SC_I_NEG (tmp1); // If C(A)0 = 1 before execution,
+ // then ON; otherwise OFF
+ }
+ break;
+
+ case x0 (0774): // gtb
+ // C(A)0 -> C(A)0
+ // C(A)i XOR C(A)i-1 -> C(A)i for i = 1, 2, ..., 35
+ {
+ word36 tmp = cpu.rA & MASK36;
+ word36 mask = SIGN36;
+
+ for (int n=1;n<=35;n++) {
+ tmp ^= (tmp & mask) >> 1;
+ mask >>= 1;
+ }
+
+ cpu.rA = tmp;
+ HDBGRegA ();
+
+ SC_I_ZERO (cpu.rA == 0); // If C(A) = 0, then ON;
+ // otherwise OFF
+ SC_I_NEG (cpu.rA & SIGN36); // If C(A)0 = 1, then ON;
+ // otherwise OFF
+ }
+ break;
+
+ /// REGISTER LOAD
+
+ case x0 (0230): // lbar
+ // C(Y)0,17 -> C(BAR)
+ CPTUR (cptUseBAR);
+ // BAR.BASE is upper 9-bits (0-8)
+ cpu.BAR.BASE = (GETHI (cpu.CY) >> 9) & 0777;
+ // BAR.BOUND is next lower 9-bits (9-17)
+ cpu.BAR.BOUND = GETHI (cpu.CY) & 0777;
+ break;
+
+ /// PRIVILEGED INSTRUCTIONS
+
+ /// Privileged - Register Load
+
+ case x0 (0674): // lcpr
+ // DPS8M interpratation
+ switch (i->tag)
+ {
+ // Extract bits from 'from' under 'mask' shifted to where (where
+ // is dps8 '0 is the msbit.
+
+ case 02: // cache mode register
+ {
+ //cpu.CMR = cpu.CY;
+ // cpu.CMR.cache_dir_address = <ignored for lcpr>
+ // cpu.CMR.par_bit = <ignored for lcpr>
+ // cpu.CMR.lev_ful = <ignored for lcpr>
+
+ CPTUR (cptUseCMR);
+ // a:AL39/cmr2 If either cache enable bit c or d changes
+ // from disable state to enable state, the entire cache is
+ // cleared.
+ uint csh1_on = getbits36_1 (cpu.CY, 54 - 36);
+ uint csh2_on = getbits36_1 (cpu.CY, 55 - 36);
+ //bool clear = (cpu.CMR.csh1_on == 0 && csh1_on != 0) ||
+ //(cpu.CMR.csh1_on == 0 && csh1_on != 0);
+ cpu.CMR.csh1_on = (word1) csh1_on;
+ cpu.CMR.csh2_on = (word1) csh2_on;
+ //if (clear) // a:AL39/cmr2
+ //{
+ //}
+#ifdef L68
+ cpu.CMR.opnd_on = getbits36_1 (cpu.CY, 56 - 36);
+#endif
+ cpu.CMR.inst_on = getbits36_1 (cpu.CY, 57 - 36);
+ cpu.CMR.csh_reg = getbits36_1 (cpu.CY, 59 - 36);
+ if (cpu.CMR.csh_reg)
+ sim_warn ("LCPR set csh_reg\n");
+ // cpu.CMR.str_asd = <ignored for lcpr>
+ // cpu.CMR.col_ful = <ignored for lcpr>
+ // cpu.CMR.rro_AB = getbits36_1 (cpu.CY, 18);
+#ifdef DPS8M
+ cpu.CMR.bypass_cache = getbits36_1 (cpu.CY, 68 - 36);
+#endif
+ cpu.CMR.luf = getbits36_2 (cpu.CY, 70 - 36);
+ }
+ break;
+
+ case 04: // mode register
+ {
+#if 1
+ CPTUR (cptUseMR);
+ cpu.MR.r = cpu.CY;
+// XXX TEST/NORMAL switch is set to NORMAL
+ putbits36_1 (& cpu.MR.r, 32, 0);
+// SBZ
+ putbits36_2 (& cpu.MR.r, 33, 0);
+#ifdef L68
+ cpu.MR.FFV = getbits36_15 (cpu.CY, 0);
+ cpu.MR.OC_TRAP = getbits36_1 (cpu.CY, 16);
+ cpu.MR.ADR_TRAP = getbits36_1 (cpu.CY, 17);
+ cpu.MR.OPCODE = getbits36_9 (cpu.CY, 18);
+ cpu.MR.OPCODEX = getbits36_1 (cpu.CY, 27);
+#endif
+ cpu.MR.sdpap = getbits36_1 (cpu.CY, 20);
+ cpu.MR.separ = getbits36_1 (cpu.CY, 21);
+ cpu.MR.hrhlt = getbits36_1 (cpu.CY, 28);
+#ifdef DPS8M
+ cpu.MR.hrxfr = getbits36_1 (cpu.CY, 29);
+#endif
+ cpu.MR.ihr = getbits36_1 (cpu.CY, 30);
+ cpu.MR.ihrrs = getbits36_1 (cpu.CY, 31);
+ cpu.MR.emr = getbits36_1 (cpu.CY, 35);
+#ifdef DPS8M
+ cpu.MR.hexfp = getbits36_1 (cpu.CY, 33);
+#endif
+#else
+#ifdef L68
+ cpu.MR.FFV = getbits36_15 (cpu.CY, 0);
+ cpu.MR.isolts_tracks = getbits36_1 (cpu.CY, 15);
+ cpu.MR.OC_TRAP = getbits36_1 (cpu.CY, 16);
+ cpu.MR.ADR_TRAP = getbits36_1 (cpu.CY, 17);
+ cpu.MR.hropc = getbits36_1 (cpu.CY, 29);
+#if 1
+ //if (cpu.MR.OC_TRAP)
+ if (cpu.MR.OC_TRAP || cpu.MR.hropc)
+ {
+ cpu.MR.OPCODE = getbits36_10 (cpu.CY, 18);
+ }
+ else
+#endif
+ {
+ cpu.MR.cuolin = getbits36_1 (cpu.CY, 18);
+ cpu.MR.solin = getbits36_1 (cpu.CY, 19);
+ cpu.MR.sdpap = getbits36_1 (cpu.CY, 20);
+ cpu.MR.separ = getbits36_1 (cpu.CY, 21);
+// tm/vm are only set if the processor maintainence panel PROG switch is on
+#if 1
+ cpu.MR.tm = getbits36_2 (cpu.CY, 22);
+ cpu.MR.vm = getbits36_2 (cpu.CY, 24);
+ cpu.MR.isolts_tracks2 = getbits36_2 (cpu.CY, 26);
+#endif
+ }
+ cpu.MR.hrhlt = getbits36_1 (cpu.CY, 28);
+ // Captured above
+ //cpu.MR.hropc = getbits36_1 (cpu.CY, 29);
+ cpu.MR.ihr = getbits36_1 (cpu.CY, 30);
+ cpu.MR.ihrrs = getbits36_1 (cpu.CY, 31);
+ //cpu.MR.mrgctl = getbits36_1 (cpu.CY, 32);
+ cpu.MR.emr = getbits36_1 (cpu.CY, 35);
+#endif
+#ifdef DPS8M
+ cpu.MR.cuolin = getbits36_1 (cpu.CY, 18);
+ cpu.MR.solin = getbits36_1 (cpu.CY, 19);
+ cpu.MR.sdpap = getbits36_1 (cpu.CY, 20);
+ cpu.MR.separ = getbits36_1 (cpu.CY, 21);
+// tm/vm are only set if the processor maintainence panel PROG switch is on
+#if 1
+ cpu.MR.tm = getbits36_2 (cpu.CY, 22);
+ cpu.MR.vm = getbits36_2 (cpu.CY, 24);
+ cpu.MR.isolts_tracks2 = getbits36_2 (cpu.CY, 26);
+#endif
+ cpu.MR.hrhlt = getbits36_1 (cpu.CY, 28);
+ cpu.MR.hrxfr = getbits36_1 (cpu.CY, 29);
+ cpu.MR.ihr = getbits36_1 (cpu.CY, 30);
+ cpu.MR.ihrrs = getbits36_1 (cpu.CY, 31);
+ //cpu.MR.mrgctl = getbits36_1 (cpu.CY, 32);
+ cpu.MR.hexfp = getbits36_1 (cpu.CY, 33);
+ cpu.MR.emr = getbits36_1 (cpu.CY, 35);
+#endif
+#endif
+
+ // Stop HR Strobe on HR Counter Overflow. (Setting bit 28
+ // shall cause the HR counter to be reset to zero.)
+ // CAC: It is unclear if bit 28 is edge or level
+ // triggered; assuming level for simplicity.
+ if (cpu.MR.hrhlt)
+ {
+ for (uint hset = 0; hset < N_HIST_SETS; hset ++)
+ cpu.history_cyclic[hset] = 0;
+ }
+
+#if 0
+ if (cpu.MR.sdpap)
+ {
+ sim_warn ("LCPR set SDPAP\n");
+ }
+
+ if (cpu.MR.separ)
+ {
+ sim_warn ("LCPR set SEPAR\n");
+ }
+#endif
+ }
+ break;
+
+ case 03: // 0's -> history
+ {
+ for (uint i = 0; i < N_HIST_SETS; i ++)
+ add_history_force (i, 0, 0);
+// XXX ISOLTS pm700 test-01n
+// The test clears the history registers but with ihr & emr set, causing
+// the registers to fill with alternating 0's and lcpr instructions.
+// Set flag to prevent the LCPR from being recorded.
+ //cpu.MR.ihr = 0;
+ cpu.skip_cu_hist = true;
+
+ }
+ break;
+
+ case 07: // 1's -> history
+ {
+ for (uint i = 0; i < N_HIST_SETS; i ++)
+ add_history_force (i, MASK36, MASK36);
+// XXX ISOLTS pm700 test-01n
+// The test clears the history registers but with ihr & emr set, causing
+// the registers to fill with alternating 0's and lcpr instructions.
+// Set flag to prevent the LCPR from being recorded.
+ //cpu.MR.ihr = 0;
+ cpu.skip_cu_hist = true;
+ }
+ break;
+
+ default:
+ doFault (FAULT_IPR,
+ fst_ill_mod,
+ "lcpr tag invalid");
+
+ }
+ break;
+
+ case x0 (0232): // ldbr
+ do_ldbr (cpu.Ypair);
+ break;
+
+ case x0 (0637): // ldt
+ CPTUR (cptUseTR);
+ cpu.rTR = (cpu.CY >> 9) & MASK27;
+ cpu.rTRticks = 0;
+#if ISOLTS
+ cpu.shadowTR = cpu.TR0 = cpu.rTR;
+ cpu.rTRlsb = 0;
+#endif
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "ldt TR %d (%o)\n",
+ cpu.rTR, cpu.rTR);
+#ifdef LOOPTRC
+elapsedtime ();
+ sim_printf (" ldt %d PSR:IC %05o:%06o\r\n", cpu.rTR, cpu.PPR.PSR, cpu.PPR.IC);
+#endif
+ // Undocumented feature. return to bce has been observed to
+ // experience TRO while masked, setting the TR to -1, and
+ // experiencing an unexpected TRo interrupt when unmasking.
+ // Reset any pending TRO fault when the TR is loaded.
+ clearTROFault ();
+ break;
+
+ case x1 (0257): // lptp
+#ifdef DPS8M
+ break;
+#endif
+#ifdef L68
+ {
+ // For i = 0, 1, ..., 15
+ // m = C(PTWAM(i).USE)
+ // C(Y-block16+m)0,14 -> C(PTWAM(m).POINTER)
+ // C(Y-block16+m)15,26 -> C(PTWAM(m).PAGE)
+ // C(Y-block16+m)27 -> C(PTWAM(m).F)
+
+#ifdef WAM
+ for (uint i = 0; i < 16; i ++)
+ {
+ word4 m = cpu.PTWAM[i].USE;
+ cpu.PTWAM[m].POINTER = getbits36_15 (cpu.Yblock16[i], 0);
+ cpu.PTWAM[m].PAGENO = getbits36_12 (cpu.Yblock16[i], 15);
+ cpu.PTWAM[m].FE = getbits36_1 (cpu.Yblock16[i], 27);
+ }
+#endif
+ }
+ break;
+#endif
+
+ case x1 (0173): // lptr
+#ifdef DPS8M
+ break;
+#endif
+#ifdef L68
+ {
+ // For i = 0, 1, ..., 15
+ // m = C(PTWAM(i).USE)
+ // C(Y-block16+m)0,17 -> C(PTWAM(m).ADDR)
+ // C(Y-block16+m)29 -> C(PTWAM(m).M)
+#ifdef WAM
+ for (uint i = 0; i < 16; i ++)
+ {
+ word4 m = cpu.PTWAM[i].USE;
+ cpu.PTWAM[m].ADDR = getbits36_18 (cpu.Yblock16[i], 0);
+ cpu.PTWAM[m].M = getbits36_1 (cpu.Yblock16[i], 29);
+ }
+#endif
+ }
+ break;
+#endif
+
+ case x1 (0774): // lra
+ CPTUR (cptUseRALR);
+ cpu.rRALR = cpu.CY & MASK3;
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "RALR set to %o\n", cpu.rRALR);
+#ifdef LOOPTRC
+{
+void elapsedtime (void);
+elapsedtime ();
+ sim_printf (" RALR set to %o PSR:IC %05o:%06o\r\n", cpu.rRALR, cpu.PPR.PSR, cpu.PPR.IC);
+}
+#endif
+ break;
+
+ case x0 (0257): // lsdp
+#ifdef DPS8M
+ break;
+#endif
+#ifdef L68
+ {
+ // For i = 0, 1, ..., 15
+ // m = C(SDWAM(i).USE)
+ // C(Y-block16+m)0,14 -> C(SDWAM(m).POINTER)
+ // C(Y-block16+m)27 -> C(SDWAM(m).F) Note: typo in AL39, P(17) should be F(27)
+#ifdef WAM
+ for (uint i = 0; i < 16; i ++)
+ {
+ word4 m = cpu.SDWAM[i].USE;
+ cpu.SDWAM[m].POINTER = getbits36_15 (cpu.Yblock16[i], 0);
+ cpu.SDWAM[m].FE = getbits36_1 (cpu.Yblock16[i], 27);
+ }
+#endif
+ }
+ break;
+#endif
+
+ case x1 (0232): // lsdr
+#ifdef DPS8M
+ break;
+#endif
+#ifdef L68
+ {
+ // For i = 0, 1, ..., 15
+ // m = C(SDWAM(i).USE)
+ // C(Y-block32+2m)0,23 -> C(SDWAM(m).ADDR)
+ // C(Y-block32+2m)24,32 -> C(SDWAM(m).R1, R2, R3)
+ // C(Y-block32+2m)37,50 -> C(SDWAM(m).BOUND)
+ // C(Y-block32+2m)51,57 -> C(SDWAM(m).R, E, W, P, U, G, C) Note: typo in AL39, 52 should be 51
+ // C(Y-block32+2m)58,71 -> C(SDWAM(m).CL)
+#ifdef WAM
+ for (uint i = 0; i < 16; i ++)
+ {
+ word4 m = cpu.SDWAM[i].USE;
+ uint j = (uint)m * 2;
+ cpu.SDWAM[m].ADDR = getbits36_24 (cpu.Yblock32[j], 0);
+ cpu.SDWAM[m].R1 = getbits36_3 (cpu.Yblock32[j], 24);
+ cpu.SDWAM[m].R2 = getbits36_3 (cpu.Yblock32[j], 27);
+ cpu.SDWAM[m].R3 = getbits36_3 (cpu.Yblock32[j], 30);
+
+ cpu.SDWAM[m].BOUND = getbits36_14 (cpu.Yblock32[j + 1], 37 - 36);
+ cpu.SDWAM[m].R = getbits36_1 (cpu.Yblock32[j + 1], 51 - 36);
+ cpu.SDWAM[m].E = getbits36_1 (cpu.Yblock32[j + 1], 52 - 36);
+ cpu.SDWAM[m].W = getbits36_1 (cpu.Yblock32[j + 1], 53 - 36);
+ cpu.SDWAM[m].P = getbits36_1 (cpu.Yblock32[j + 1], 54 - 36);
+ cpu.SDWAM[m].U = getbits36_1 (cpu.Yblock32[j + 1], 55 - 36);
+ cpu.SDWAM[m].G = getbits36_1 (cpu.Yblock32[j + 1], 56 - 36);
+ cpu.SDWAM[m].C = getbits36_1 (cpu.Yblock32[j + 1], 57 - 36);
+ cpu.SDWAM[m].EB = getbits36_14 (cpu.Yblock32[j + 1], 58 - 36);
+ }
+#endif
+ }
+ break;
+#endif
+
+ case x0 (0613): // rcu
+ doRCU (); // never returns
+
+ /// Privileged - Register Store
+
+ case x0 (0452): // scpr
+ {
+ uint tag = (i->tag) & MASK6;
+ switch (tag)
+ {
+ case 000: // C(APU history register#1) -> C(Y-pair)
+ {
+ cpu.Ypair[0] =
+ cpu.history[APU_HIST_REG]
+ [cpu.history_cyclic[APU_HIST_REG]][0];
+ cpu.Ypair[1] =
+ cpu.history[APU_HIST_REG]
+ [cpu.history_cyclic[APU_HIST_REG]][1];
+ cpu.history_cyclic[APU_HIST_REG] =
+ (cpu.history_cyclic[APU_HIST_REG] + 1) % N_HIST_SIZE;
+ }
+ break;
+
+ case 001: // C(fault register) -> C(Y-pair)0,35
+ // 00...0 -> C(Y-pair)36,71
+ {
+ CPTUR (cptUseFR);
+ cpu.Ypair[0] = cpu.faultRegister[0];
+ cpu.Ypair[1] = cpu.faultRegister[1];
+ cpu.faultRegister[0] = 0;
+ cpu.faultRegister[1] = 0;
+ }
+ break;
+
+ case 006: // C(mode register) -> C(Y-pair)0,35
+ // C(cache mode register) -> C(Y-pair)36,72
+ {
+ CPTUR (cptUseMR);
+ cpu.Ypair[0] = cpu.MR.r;
+ putbits36_1 (& cpu.Ypair[0], 20, cpu.MR.sdpap);
+ putbits36_1 (& cpu.Ypair[0], 21, cpu.MR.separ);
+ putbits36_1 (& cpu.Ypair[0], 30, cpu.MR.ihr);
+#ifdef DPS8M
+ putbits36_1 (& cpu.Ypair[0], 33, cpu.MR.hexfp);
+#endif
+#if 0
+ cpu.Ypair[0] = 0;
+#ifdef L68
+ putbits36_15 (& cpu.Ypair[0], 0, cpu.MR.FFV);
+ putbits36_1 (& cpu.Ypair[0], 15, cpu.MR.isolts_tracks);
+ putbits36_1 (& cpu.Ypair[0], 16, cpu.MR.OC_TRAP);
+ putbits36_1 (& cpu.Ypair[0], 17, cpu.MR.ADR_TRAP);
+#if 1
+ if (cpu.MR.OC_TRAP || cpu.MR.hropc)
+ {
+ putbits36_10 (& cpu.Ypair[0], 18, cpu.MR.OPCODE);
+ }
+ else
+#endif
+ {
+ putbits36_1 (& cpu.Ypair[0], 18, cpu.MR.cuolin);
+ putbits36_1 (& cpu.Ypair[0], 19, cpu.MR.solin);
+ putbits36_1 (& cpu.Ypair[0], 20, cpu.MR.sdpap);
+ putbits36_1 (& cpu.Ypair[0], 21, cpu.MR.separ);
+// tm/vm are only set if the processor maintainence panel PROG switch is on
+#if 1
+ putbits36_2 (& cpu.Ypair[0], 22, cpu.MR.tm);
+ putbits36_2 (& cpu.Ypair[0], 24, cpu.MR.vm);
+#else
+ putbits36_2 (& cpu.Ypair[0], 22, 01llu);
+ putbits36_2 (& cpu.Ypair[0], 24, 01llu);
+#endif
+ }
+#endif
+#ifdef DPS8M
+ putbits36_1 (& cpu.Ypair[0], 18, cpu.MR.cuolin);
+ putbits36_1 (& cpu.Ypair[0], 19, cpu.MR.solin);
+ putbits36_1 (& cpu.Ypair[0], 20, cpu.MR.sdpap);
+ putbits36_1 (& cpu.Ypair[0], 21, cpu.MR.separ);
+#endif
+// tm/vm are only set if the processor maintainence panel PROG switch is on
+#if 1
+ putbits36_2 (& cpu.Ypair[0], 22, cpu.MR.tm);
+ putbits36_2 (& cpu.Ypair[0], 24, cpu.MR.vm);
+ putbits36_2 (& cpu.Ypair[0], 26, cpu.MR.isolts_tracks2);
+#else
+ putbits36_2 (& cpu.Ypair[0], 22, 01llu);
+ putbits36_2 (& cpu.Ypair[0], 24, 01llu);
+ putbits36_2 (& cpu.Ypair[0], 26, 03llu);
+#endif
+ putbits36_1 (& cpu.Ypair[0], 28, cpu.MR.hrhlt);
+#ifdef DPS8M
+ putbits36_1 (& cpu.Ypair[0], 29, cpu.MR.hrxfr);
+#endif
+#ifdef L68
+ putbits36_1 (& cpu.Ypair[0], 29, cpu.MR.hropc);
+#endif
+ putbits36_1 (& cpu.Ypair[0], 30, cpu.MR.ihr);
+ putbits36_1 (& cpu.Ypair[0], 31, cpu.MR.ihrrs);
+ //putbits36_1 (& cpu.Ypair[0], 32, cpu.MR.mrgctl);
+#ifdef DPS8M
+ putbits36_1 (& cpu.Ypair[0], 33, cpu.MR.hexfp);
+#endif
+ putbits36_1 (& cpu.Ypair[0], 35, cpu.MR.emr);
+#endif
+ CPTUR (cptUseCMR);
+ cpu.Ypair[1] = 0;
+ putbits36_15 (& cpu.Ypair[1], 36 - 36,
+ cpu.CMR.cache_dir_address);
+ putbits36_1 (& cpu.Ypair[1], 51 - 36, cpu.CMR.par_bit);
+ putbits36_1 (& cpu.Ypair[1], 52 - 36, cpu.CMR.lev_ful);
+ putbits36_1 (& cpu.Ypair[1], 54 - 36, cpu.CMR.csh1_on);
+ putbits36_1 (& cpu.Ypair[1], 55 - 36, cpu.CMR.csh2_on);
+#ifdef L68
+ putbits36_1 (& cpu.Ypair[1], 56 - 36, cpu.CMR.opnd_on);
+#endif
+ putbits36_1 (& cpu.Ypair[1], 57 - 36, cpu.CMR.inst_on);
+ putbits36_1 (& cpu.Ypair[1], 59 - 36, cpu.CMR.csh_reg);
+ putbits36_1 (& cpu.Ypair[1], 60 - 36, cpu.CMR.str_asd);
+ putbits36_1 (& cpu.Ypair[1], 61 - 36, cpu.CMR.col_ful);
+ putbits36_2 (& cpu.Ypair[1], 62 - 36, cpu.CMR.rro_AB);
+#ifdef DPS8M
+ putbits36_1 (& cpu.Ypair[1], 68 - 36,
+ cpu.CMR.bypass_cache);
+#endif
+ putbits36_2 (& cpu.Ypair[1], 70 - 36, cpu.CMR.luf);
+ }
+ break;
+
+ case 010: // C(APU history register#2) -> C(Y-pair)
+ {
+#ifdef DPS8M
+ cpu.Ypair[0] =
+ cpu.history[EAPU_HIST_REG]
+ [cpu.history_cyclic[EAPU_HIST_REG]][0];
+ cpu.Ypair[1] =
+ cpu.history[EAPU_HIST_REG]
+ [cpu.history_cyclic[EAPU_HIST_REG]][1];
+ cpu.history_cyclic[EAPU_HIST_REG] =
+ (cpu.history_cyclic[EAPU_HIST_REG] + 1) % N_HIST_SIZE;
+#endif
+#ifdef L68
+ cpu.Ypair[0] =
+ cpu.history[DU_HIST_REG]
+ [cpu.history_cyclic[DU_HIST_REG]][0];
+ cpu.Ypair[1] =
+ cpu.history[DU_HIST_REG]
+ [cpu.history_cyclic[DU_HIST_REG]][1];
+ cpu.history_cyclic[DU_HIST_REG] =
+ (cpu.history_cyclic[DU_HIST_REG] + 1) % N_HIST_SIZE;
+#endif
+ }
+ break;
+
+ case 020: // C(CU history register) -> C(Y-pair)
+ {
+ cpu.Ypair[0] =
+ cpu.history[CU_HIST_REG]
+ [cpu.history_cyclic[CU_HIST_REG]][0];
+ cpu.Ypair[1] =
+ cpu.history[CU_HIST_REG]
+ [cpu.history_cyclic[CU_HIST_REG]][1];
+ cpu.history_cyclic[CU_HIST_REG] =
+ (cpu.history_cyclic[CU_HIST_REG] + 1) % N_HIST_SIZE;
+ }
+ break;
+
+ case 040: // C(OU/DU history register) -> C(Y-pair)
+ {
+#ifdef DPS8M
+ cpu.Ypair[0] =
+ cpu.history[DU_OU_HIST_REG]
+ [cpu.history_cyclic[DU_OU_HIST_REG]][0];
+ cpu.Ypair[1] =
+ cpu.history[DU_OU_HIST_REG]
+ [cpu.history_cyclic[DU_OU_HIST_REG]][1];
+ cpu.history_cyclic[DU_OU_HIST_REG] =
+ (cpu.history_cyclic[DU_OU_HIST_REG] + 1) % N_HIST_SIZE;
+#endif
+#ifdef L68
+ cpu.Ypair[0] =
+ cpu.history[OU_HIST_REG]
+ [cpu.history_cyclic[OU_HIST_REG]][0];
+ cpu.Ypair[1] =
+ cpu.history[OU_HIST_REG]
+ [cpu.history_cyclic[OU_HIST_REG]][1];
+ cpu.history_cyclic[OU_HIST_REG] =
+ (cpu.history_cyclic[OU_HIST_REG] + 1) % N_HIST_SIZE;
+#endif
+ }
+ break;
+
+ default:
+ {
+ doFault (FAULT_IPR,
+ fst_ill_mod,
+ "SCPR Illegal register select value");
+ }
+ }
+ }
+ break;
+
+ case x0 (0657): // scu
+ // AL-39 defines the behaivor of SCU during fault/interrupt
+ // processing, but not otherwise.
+ // The T&D tape uses SCU during normal processing, and apparently
+ // expects the current CU state to be saved.
+
+ if (cpu.cycle == EXEC_cycle)
+ {
+ // T&D behavior
+
+ // An 'Add Delta' addressing mode will alter the TALLY bit;
+ // restore it.
+ //SC_I_TALLY (cpu.currentInstruction.stiTally == 0);
+
+ scu2words (cpu.Yblock8);
+ }
+ else
+ {
+ // AL-39 behavior
+ for (int j = 0; j < 8; j ++)
+ cpu.Yblock8[j] = cpu.scu_data[j];
+ }
+ break;
+
+ case x0 (0154): // sdbr
+ {
+ CPTUR (cptUseDSBR);
+ // C(DSBR.ADDR) -> C(Y-pair) 0,23
+ // 00...0 -> C(Y-pair) 24,36
+ cpu.Ypair[0] = ((word36) (cpu.DSBR.ADDR & PAMASK)) << (35 - 23);
+
+ // C(DSBR.BOUND) -> C(Y-pair) 37,50
+ // 0000 -> C(Y-pair) 51,54
+ // C(DSBR.U) -> C(Y-pair) 55
+ // 000 -> C(Y-pair) 56,59
+ // C(DSBR.STACK) -> C(Y-pair) 60,71
+ cpu.Ypair[1] = ((word36) (cpu.DSBR.BND & 037777)) << (71 - 50) |
+ ((word36) (cpu.DSBR.U & 1)) << (71 - 55) |
+ ((word36) (cpu.DSBR.STACK & 07777)) << (71 - 71);
+ }
+ break;
+
+ case x1 (0557): // sptp
+ {
+// XXX AL39 The associative memory is ignored (forced to "no match") during address
+// preparation.
+ // Level j is selected by C(TPR.CA)12,13
+#ifdef DPS8M
+ uint level = (cpu.TPR.CA >> 4) & 03;
+#endif
+#ifdef L68
+ uint level = 0;
+#endif
+#ifdef WAM
+ uint toffset = level * 16;
+#endif
+ for (uint j = 0; j < 16; j ++)
+ {
+ cpu.Yblock16[j] = 0;
+#ifdef WAM
+ putbits36_15 (& cpu.Yblock16[j], 0,
+ cpu.PTWAM[toffset + j].POINTER);
+#ifdef DPS8M
+ putbits36_12 (& cpu.Yblock16[j], 15,
+ cpu.PTWAM[toffset + j].PAGENO & 07760);
+
+ uint parity = 0;
+ if (cpu.PTWAM[toffset + j].FE)
+ {
+ // calculate parity
+ // 58009997-040 p.101,111
+ parity = ((uint) cpu.PTWAM[toffset + j].POINTER << 4) | (cpu.PTWAM[toffset + j].PAGENO >> 8);
+ parity = parity ^ (parity >>16);
+ parity = parity ^ (parity >> 8);
+ parity = parity ^ (parity >> 4);
+ parity = ~ (0x6996u >> (parity & 0xf));
+ }
+ putbits36_1 (& cpu.Yblock16[j], 23, (word1) (parity & 1));
+#endif
+#ifdef L68
+ putbits36_12 (& cpu.Yblock16[j], 15,
+ cpu.PTWAM[toffset + j].PAGENO);
+#endif
+ putbits36_1 (& cpu.Yblock16[j], 27,
+ cpu.PTWAM[toffset + j].FE);
+#ifdef DPS8M
+ putbits36_6 (& cpu.Yblock16[j], 30,
+ cpu.PTWAM[toffset + j].USE);
+#endif
+#ifdef L68
+ putbits36_4 (& cpu.Yblock16[j], 32,
+ cpu.PTWAM[toffset + j].USE);
+#endif
+
+#endif
+ }
+#ifndef WAM
+ if (level == 0)
+ {
+ putbits36 (& cpu.Yblock16[0], 0, 15,
+ cpu.PTW0.POINTER);
+#ifdef DPS8M
+ putbits36 (& cpu.Yblock16[0], 15, 12,
+ cpu.PTW0.PAGENO & 07760);
+#endif
+#ifdef L68
+ putbits36 (& cpu.Yblock16[0], 15, 12,
+ cpu.PTW0.PAGENO);
+#endif
+ putbits36 (& cpu.Yblock16[0], 27, 1,
+ cpu.PTW0.FE);
+#ifdef DPS8M
+ putbits36 (& cpu.Yblock16[0], 30, 6,
+ cpu.PTW0.USE);
+#endif
+#ifdef L68
+ putbits36 (& cpu.Yblock16[0], 32, 4,
+ cpu.PTW0.USE);
+#endif
+ }
+#endif
+ }
+ break;
+
+ case x1 (0154): // sptr
+ {
+// XXX The associative memory is ignored (forced to "no match") during address
+// preparation.
+
+ // Level j is selected by C(TPR.CA)12,13
+#ifdef DPS8M
+ uint level = (cpu.TPR.CA >> 4) & 03;
+#endif
+#ifdef L68
+ uint level = 0;
+#endif
+#ifdef WAM
+ uint toffset = level * 16;
+#endif
+ for (uint j = 0; j < 16; j ++)
+ {
+ cpu.Yblock16[j] = 0;
+#ifdef WAM
+#ifdef DPS8M
+ putbits36_18 (& cpu.Yblock16[j], 0,
+ cpu.PTWAM[toffset + j].ADDR & 0777760);
+#endif
+#ifdef L68
+ putbits36_18 (& cpu.Yblock16[j], 0,
+ cpu.PTWAM[toffset + j].ADDR);
+#endif
+ putbits36_1 (& cpu.Yblock16[j], 29,
+ cpu.PTWAM[toffset + j].M);
+#endif
+ }
+#ifndef WAM
+ if (level == 0)
+ {
+#ifdef DPS8M
+ putbits36 (& cpu.Yblock16[0], 0, 13, cpu.PTW0.ADDR & 0777760);
+#endif
+#ifdef L68
+ putbits36 (& cpu.Yblock16[0], 0, 13, cpu.PTW0.ADDR);
+#endif
+ putbits36_1 (& cpu.Yblock16[0], 29, cpu.PTW0.M);
+ }
+#endif
+ }
+ break;
+
+ case x0 (0557): // ssdp
+ {
+ // XXX AL39: The associative memory is ignored (forced to "no match")
+ // during address preparation.
+ // Level j is selected by C(TPR.CA)12,13
+#ifdef DPS8M
+ uint level = (cpu.TPR.CA >> 4) & 03;
+#endif
+#ifdef L68
+ uint level = 0;
+#endif
+#ifdef WAM
+ uint toffset = level * 16;
+#endif
+ for (uint j = 0; j < 16; j ++)
+ {
+ cpu.Yblock16[j] = 0;
+#ifdef WAM
+ putbits36_15 (& cpu.Yblock16[j], 0,
+ cpu.SDWAM[toffset + j].POINTER);
+ putbits36_1 (& cpu.Yblock16[j], 27,
+ cpu.SDWAM[toffset + j].FE);
+#ifdef DPS8M
+ uint parity = 0;
+ if (cpu.SDWAM[toffset + j].FE)
+ {
+ // calculate parity
+ // 58009997-040 p.112
+ parity = cpu.SDWAM[toffset + j].POINTER >> 4;
+ //parity = parity ^ (parity >>16);
+ parity = parity ^ (parity >> 8);
+ parity = parity ^ (parity >> 4);
+ parity = ~ (0x6996u >> (parity & 0xf));
+ }
+ putbits36_1 (& cpu.Yblock16[j], 15, (word1) (parity & 1));
+
+ putbits36_6 (& cpu.Yblock16[j], 30,
+ cpu.SDWAM[toffset + j].USE);
+#endif
+#ifdef L68
+ putbits36_4 (& cpu.Yblock16[j], 32,
+ cpu.SDWAM[toffset + j].USE);
+#endif
+#endif
+ }
+#ifndef WAM
+ if (level == 0)
+ {
+ putbits36 (& cpu.Yblock16[0], 0, 15,
+ cpu.SDW0.POINTER);
+ putbits36 (& cpu.Yblock16[0], 27, 1,
+ cpu.SDW0.FE);
+#ifdef DPS8M
+ putbits36 (& cpu.Yblock16[0], 30, 6,
+ cpu.SDW0.USE);
+#endif
+#ifdef L68
+ putbits36 (& cpu.Yblock16[0], 32, 4,
+ cpu.SDW0.USE);
+#endif
+ }
+#endif
+ }
+ break;
+
+ case x1 (0254): // ssdr
+ {
+// XXX AL39: The associative memory is ignored (forced to "no match") during
+// address preparation.
+
+ // Level j is selected by C(TPR.CA)11,12
+ // Note: not bits 12,13. This is due to operand being Yblock32
+#ifdef DPS8M
+ uint level = (cpu.TPR.CA >> 5) & 03;
+#endif
+#ifdef L68
+ uint level = 0;
+#endif
+#ifdef WAM
+ uint toffset = level * 16;
+#endif
+ for (uint j = 0; j < 16; j ++)
+ {
+ cpu.Yblock32[j * 2] = 0;
+#ifdef WAM
+ putbits36_24 (& cpu.Yblock32[j * 2], 0,
+ cpu.SDWAM[toffset + j].ADDR);
+ putbits36_3 (& cpu.Yblock32[j * 2], 24,
+ cpu.SDWAM[toffset + j].R1);
+ putbits36_3 (& cpu.Yblock32[j * 2], 27,
+ cpu.SDWAM[toffset + j].R2);
+ putbits36_3 (& cpu.Yblock32[j * 2], 30,
+ cpu.SDWAM[toffset + j].R3);
+#endif
+ cpu.Yblock32[j * 2 + 1] = 0;
+#ifdef WAM
+ putbits36_14 (& cpu.Yblock32[j * 2 + 1], 37 - 36,
+ cpu.SDWAM[toffset + j].BOUND);
+ putbits36_1 (& cpu.Yblock32[j * 2 + 1], 51 - 36,
+ cpu.SDWAM[toffset + j].R);
+ putbits36_1 (& cpu.Yblock32[j * 2 + 1], 52 - 36,
+ cpu.SDWAM[toffset + j].E);
+ putbits36_1 (& cpu.Yblock32[j * 2 + 1], 53 - 36,
+ cpu.SDWAM[toffset + j].W);
+ putbits36_1 (& cpu.Yblock32[j * 2 + 1], 54 - 36,
+ cpu.SDWAM[toffset + j].P);
+ putbits36_1 (& cpu.Yblock32[j * 2 + 1], 55 - 36,
+ cpu.SDWAM[toffset + j].U);
+ putbits36_1 (& cpu.Yblock32[j * 2 + 1], 56 - 36,
+ cpu.SDWAM[toffset + j].G);
+ putbits36_1 (& cpu.Yblock32[j * 2 + 1], 57 - 36,
+ cpu.SDWAM[toffset + j].C);
+ putbits36_14 (& cpu.Yblock32[j * 2 + 1], 58 - 36,
+ cpu.SDWAM[toffset + j].EB);
+#endif
+ }
+#ifndef WAM
+ if (level == 0)
+ {
+ putbits36 (& cpu.Yblock32[0], 0, 24,
+ cpu.SDW0.ADDR);
+ putbits36 (& cpu.Yblock32[0], 24, 3,
+ cpu.SDW0.R1);
+ putbits36 (& cpu.Yblock32[0], 27, 3,
+ cpu.SDW0.R2);
+ putbits36 (& cpu.Yblock32[0], 30, 3,
+ cpu.SDW0.R3);
+ putbits36 (& cpu.Yblock32[0], 37 - 36, 14,
+ cpu.SDW0.BOUND);
+ putbits36 (& cpu.Yblock32[1], 51 - 36, 1,
+ cpu.SDW0.R);
+ putbits36 (& cpu.Yblock32[1], 52 - 36, 1,
+ cpu.SDW0.E);
+ putbits36 (& cpu.Yblock32[1], 53 - 36, 1,
+ cpu.SDW0.W);
+ putbits36 (& cpu.Yblock32[1], 54 - 36, 1,
+ cpu.SDW0.P);
+ putbits36 (& cpu.Yblock32[1], 55 - 36, 1,
+ cpu.SDW0.U);
+ putbits36 (& cpu.Yblock32[1], 56 - 36, 1,
+ cpu.SDW0.G);
+ putbits36 (& cpu.Yblock32[1], 57 - 36, 1,
+ cpu.SDW0.C);
+ putbits36 (& cpu.Yblock32[1], 58 - 36, 14,
+ cpu.SDW0.EB);
+
+ }
+#endif
+ }
+ break;
+
+ /// Privileged - Clear Associative Memory
+
+ case x1 (0532): // camp
+ {
+ // C(TPR.CA) 16,17 control disabling or enabling the associative
+ // memory.
+ // This may be done to either or both halves.
+ // The full/empty bit of cache PTWAM register is set to zero and
+ // the LRU counters are initialized.
+#ifdef WAM
+ if (! cpu.switches.disable_wam)
+ { // disabled by simh, do nothing
+#ifdef DPS8M
+ if (cpu.cu.PT_ON) // only clear when enabled
+#endif
+ for (uint i = 0; i < N_WAM_ENTRIES; i ++)
+ {
+ cpu.PTWAM[i].FE = 0;
+#ifdef L68
+ cpu.PTWAM[i].USE = (word4) i;
+#endif
+#ifdef DPS8M
+ cpu.PTWAM[i].USE = 0;
+#endif
+ }
+
+// 58009997-040 A level of the associative memory is disabled if
+// C(TPR.CA) 16,17 = 01
+// 58009997-040 A level of the associative memory is enabled if
+// C(TPR.CA) 16,17 = 10
+// Level j is selected to be enabled/disable if
+// C(TPR.CA) 10+j = 1; j=1,2,3,4
+// All levels are selected to be enabled/disabled if
+// C(TPR.CA) 11,14 = 0
+// This is contrary to what AL39 says, so I'm not going to implement it. In
+// fact, I'm not even going to implement the halves.
+
+#ifdef DPS8M
+ if (cpu.TPR.CA != 0000002 && (cpu.TPR.CA & 3) != 0)
+ sim_warn ("CAMP ignores enable/disable %06o\n", cpu.TPR.CA);
+#endif
+ if ((cpu.TPR.CA & 3) == 02)
+ cpu.cu.PT_ON = 1;
+ else if ((cpu.TPR.CA & 3) == 01)
+ cpu.cu.PT_ON = 0;
+ }
+ else
+ {
+ cpu.PTW0.FE = 0;
+ cpu.PTW0.USE = 0;
+ }
+#else
+ cpu.PTW0.FE = 0;
+ cpu.PTW0.USE = 0;
+#endif
+ }
+ break;
+
+ case x0 (0532): // cams
+ {
+ // The full/empty bit of each SDWAM register is set to zero and the
+ // LRU counters are initialized. The remainder of the contents of
+ // the registers are unchanged. If the associative memory is
+ // disabled, F and LRU are unchanged.
+ // C(TPR.CA) 16,17 control disabling or enabling the associative
+ // memory.
+ // This may be done to either or both halves.
+#ifdef WAM
+ if (!cpu.switches.disable_wam)
+ { // disabled by simh, do nothing
+#ifdef DPS8M
+ if (cpu.cu.SD_ON) // only clear when enabled
+#endif
+ for (uint i = 0; i < N_WAM_ENTRIES; i ++)
+ {
+ cpu.SDWAM[i].FE = 0;
+#ifdef L68
+ cpu.SDWAM[i].USE = (word4) i;
+#endif
+#ifdef DPS8M
+ cpu.SDWAM[i].USE = 0;
+#endif
+ }
+// 58009997-040 A level of the associative memory is disabled if
+// C(TPR.CA) 16,17 = 01
+// 58009997-040 A level of the associative memory is enabled if
+// C(TPR.CA) 16,17 = 10
+// Level j is selected to be enabled/disable if
+// C(TPR.CA) 10+j = 1; j=1,2,3,4
+// All levels are selected to be enabled/disabled if
+// C(TPR.CA) 11,14 = 0
+// This is contrary to what AL39 says, so I'm not going to implement it. In
+// fact, I'm not even going to implement the halves.
+
+#ifdef DPS8M
+ if (cpu.TPR.CA != 0000006 && (cpu.TPR.CA & 3) != 0)
+ sim_warn ("CAMS ignores enable/disable %06o\n", cpu.TPR.CA);
+#endif
+ if ((cpu.TPR.CA & 3) == 02)
+ cpu.cu.SD_ON = 1;
+ else if ((cpu.TPR.CA & 3) == 01)
+ cpu.cu.SD_ON = 0;
+ }
+ else
+ {
+ cpu.SDW0.FE = 0;
+ cpu.SDW0.USE = 0;
+ }
+#else
+ cpu.SDW0.FE = 0;
+ cpu.SDW0.USE = 0;
+#endif
+ }
+ break;
+
+ /// Privileged - Configuration and Status
+
+// case x0 (0233): // rmcm
+// {
+// // C(TPR.CA)0,2 (C(TPR.CA)1,2 for the DPS 8M processor)
+// // specify which processor port (i.e., which system
+// // controller) is used.
+//#ifdef DPS8M
+// uint cpu_port_num = (cpu.TPR.CA >> 15) & 03;
+//#endif
+//#ifdef L68
+// uint cpu_port_num = (cpu.TPR.CA >> 15) & 07;
+//#endif
+// if (! get_scu_in_use (current_running_cpu_idx, cpu_port_num))
+// {
+// sim_warn ("rmcm to non-existent controller on "
+// "cpu %d port %d\n",
+// current_running_cpu_idx, cpu_port_num);
+// break;
+// }
+// uint scuUnitIdx = get_scu_idx (current_running_cpu_idx, cpu_port_num);
+// t_stat rc = scu_rmcm ((uint) scuUnitIdx,
+// current_running_cpu_idx,
+// & cpu.rA, & cpu.rQ);
+// HDBGRegA ();
+// HDBGRegQ ();
+// if (rc)
+// return rc;
+// SC_I_ZERO (cpu.rA == 0);
+// SC_I_NEG (cpu.rA & SIGN36);
+// }
+// break;
+//
+// case x0 (0413): // rscr
+// {
+// // For the rscr instruction, the first 2 (DPS8M) or 3 (L68) bits of
+// // the addr field of the instruction are used to specify which SCU.
+// // (2 bits for the DPS8M. (Expect for x6x and x7x below, where
+// // the selected SCU is the one holding the addressed memory).
+//
+// // According to DH02:
+// // XXXXXX0X SCU Mode Register (Level 66 only)
+// // XXXXXX1X Configuration switches
+// // XXXXXn2X Interrupt mask port n
+// // XXXXXX3X Interrupt cells
+// // XXXXXX4X Elapsed time clock
+// // XXXXXX5X Elapsed time clock
+// // XXXXXX6X Mode register
+// // XXXXXX7X Mode register
+//
+// // According to privileged_mode_ut,
+// // port*1024 + scr_input*8
+//
+// // privileged_mode_ut makes no reference to the special case
+// // of x6x and x7x.
+//
+//
+// // According to DH02, RSCR in Slave Mode does the CAF
+// // without BAR correction, and then forces the CA to 040,
+// // resulting in a Clock Read from the SCU on port 0.
+//
+// // According to AL93, RSCR in BAR mode is IPR.
+//
+//
+////
+//// Implementing privileged_mode_ut.alm algorithm
+////
+//
+// // Extract port number
+//#ifdef DPS8M
+// uint cpu_port_num = (cpu.TPR.CA >> 10) & 03;
+//#endif
+//#ifdef L68
+// uint cpu_port_num = (cpu.TPR.CA >> 10) & 07;
+//#endif
+//
+//
+// // Trace the cable from the port to find the SCU number
+// // connected to that port
+// if (! get_scu_in_use (current_running_cpu_idx, cpu_port_num))
+// {
+// // CPTUR (cptUseFR) -- will be set by doFault
+//
+// // Set IAn in Fault register
+// if (cpu_port_num == 0)
+// putbits36 (& cpu.faultRegister[0], 16, 4, 010);
+// else if (cpu_port_num == 1)
+// putbits36 (& cpu.faultRegister[0], 20, 4, 010);
+// else if (cpu_port_num == 2)
+// putbits36 (& cpu.faultRegister[0], 24, 4, 010);
+// else
+// putbits36 (& cpu.faultRegister[0], 28, 4, 010);
+//
+// doFault (FAULT_CMD, fst_cmd_ctl, "(rscr)");
+// }
+// uint scuUnitIdx = get_scu_idx (current_running_cpu_idx, cpu_port_num);
+//#ifdef PANEL
+// {
+// uint function = (cpu.iefpFinalAddress >> 3) & 07;
+// CPT (cpt13L, function);
+// }
+//#endif
+// t_stat rc = scu_rscr ((uint) scuUnitIdx, current_running_cpu_idx,
+// cpu.iefpFinalAddress & MASK15,
+// & cpu.rA, & cpu.rQ);
+// HDBGRegA ();
+// HDBGRegQ ();
+// if (rc)
+// return rc;
+// }
+// break;
+
+ case x0 (0231): // rsw
+ {
+#ifdef DPS8M
+ //if (i->tag == TD_DL)
+ word6 rTAG = GET_TAG (IWB_IRODD);
+ word6 Td = GET_TD (rTAG);
+ word6 Tm = GET_TM (rTAG);
+ if (Tm == TM_R && Td == TD_DL)
+ {
+
+// 58009997-040 MULTICS Differences Manual DPS 8-70M Aug83
+// disagress with Multics source, but probably a typo,
+// 0-13 CPU Model Number
+// 13-25 CPU Serial Number
+// 26-33 Date-Ship code (YYMMDD)
+// 34-40 CPU ID Field (reference RSW 2)
+// Byte 40: Bits 03 (Bits 32-35 of RSW 2 Field
+// Bit 4=1 Hex Option included
+// Bit 5=1 RSCR (Clock) is Slave Mode included
+// Bits 6-7 Reserved for later use.
+// 50: Operating System Use
+// 51-1777(8) To be defined.
+// NOTE: There is the possibility of disagreement between the
+// ID bits of RSW 2 and the ID bits of PROM locations
+// 35-40. This condition could result when alterable
+// configuration condition is contained in the PROM.
+// The user is adviced to ignore the PROM fields which
+// contain the processor fault vector base (GCOS III)
+// and the processor number and rely on the RSW 2 bits
+// for this purpose. Bits 14-16 of the RSW 2 should be
+// ignored and the bits represnting this information in
+// the PROM should be treated as valid.
+
+// CAC notes: I interpret the fields as
+// 0-12 CPU Model Number // 13 chars, typo
+// 13-25 CPU Serial Number // 13 chars
+// 26-33 Date-Ship code (YYMMDD) // 8 chars (enough for YYYYMMDD).
+// 34-40 CPU ID Field (reference RSW 2)
+// Byte 40: Bits 03 (Bits 32-35 of RSW 2 Field
+// Bit 4=1 Hex Option included
+// Bit 5=1 RSCR (Clock) is Slave Mode included
+// Bits 6-7 Reserved for later use.
+// 50: Operating System Use
+
+ unsigned char PROM[1024];
+ memset (PROM, 0, sizeof (PROM));
+ sprintf ((char *) PROM, "%13s%13d%8s",
+ "DPS8/70M Emul", // 0-12 CPU Model number
+ cpu.switches.serno, // 13-25 CPU Serial number
+ "20160304"); // 26-33 Ship date (YYMMDD)
+ word36 tmp = 0;
+ tmp |= (word36) ((cpu.switches.interlace[0] == 2 ? 1LL : 0LL)
+ << (35- 0));
+ tmp |= (word36) ((cpu.switches.interlace[1] == 2 ? 1LL : 0LL)
+ << (35- 1));
+ tmp |= (word36) ((cpu.switches.interlace[2] == 2 ? 1LL : 0LL)
+ << (35- 2));
+ tmp |= (word36) ((cpu.switches.interlace[3] == 2 ? 1LL : 0LL)
+ << (35- 3));
+ tmp |= (word36) ((01L) /* 0b01 DPS8M */
+ << (35- 5));
+ tmp |= (word36) ((cpu.switches.FLT_BASE & 0177LL)
+ << (35-12));
+ tmp |= (word36) ((01L) /* 0b1 ID_PROM installed */
+ << (35-13));
+ tmp |= (word36) ((00L) /* 0b0000 */
+ << (35-17));
+ //tmp |= (word36) ((0b111L)
+ //<< (35-20));
+ // According to rsw.incl.pl1, Multics ignores this bit.
+ tmp |= (word36) ((00L) // 0b0 BCD option off
+ << (35-18));
+ tmp |= (word36) ((01L) // 0b1 DPS option
+ << (35-19));
+ tmp |= (word36) ((cpu.switches.disable_cache ? 0 : 1) //8K cache
+ << (35-20));
+ tmp |= (word36) ((00L) // 0b00
+ << (35-22));
+ tmp |= (word36) ((01L) /* 0b1 DPS8M */
+ << (35-23));
+ tmp |= (word36) ((cpu.switches.proc_mode & 01LL)
+ << (35-24));
+ tmp |= (word36) ((00L) // 0b0
+ << (35-25)); // new product line (CPL/NPL)
+ tmp |= (word36) ((0L) // 0b000
+ << (35-28));
+ tmp |= (word36) ((cpu.switches.proc_speed & 017LL)
+ << (35-32));
+ tmp |= (word36) ((cpu.switches.cpu_num & 07LL)
+ << (35-35));
+ // 36: bits 00-07
+ PROM[36] = getbits36_8 (tmp, 0);
+ // 37: bits 08-15
+ PROM[37] = getbits36_8 (tmp, 8);
+ // 38: bits 16-23
+ PROM[38] = getbits36_8 (tmp, 16);
+ // 39: bits 24-31
+ PROM[39] = getbits36_8 (tmp, 24);
+ // 40: bits 32-35
+ // 40: bits 0-3: bits 32-35 of RSW 2 field
+ // (this is dps8m, so only 32 is always 0)
+ // 4: hex option
+ // 5: RSCR clock is slave
+ // 6-7: reserved
+ PROM[40] = ((unsigned char) ((tmp & 017) << 4))
+ // | 0100 // hex option
+ // | 0040 // clock is slave
+ ;
+
+ cpu.rA = PROM[cpu.TPR.CA & 1023];
+ break;
+ }
+#endif // DPS8M
+ uint select = cpu.TPR.CA & 0x7;
+ switch (select)
+ {
+ case 0: // data switches
+ cpu.rA = cpu.switches.data_switches;
+ break;
+
+ case 1: // configuration switches for ports A, B, C, D
+// y = 1:
+//
+// 0 0 0 1 1 2 2 3
+// 0 8 9 7 8 6 7 5
+// -------------------------------------------------------------------------
+// | PORT A | PORT B | PORT C | PORT D |
+// -------------------------------------------------------------------------
+// | ADR |j|k|l| MEM | ADR |j|k|l| MEM | ADR |j|k|l| MEM | ADR |j|k|l| MEM |
+// -------------------------------------------------------------------------
+//
+//
+// ADR: Address assignment switch setting for port
+// This defines the base address for the SCU
+// j: port enabled flag
+// k: system initialize enabled flag
+// l: interface enabled flag
+// MEM coded memory size
+// 000 32K 2^15
+// 001 64K 2^16
+// 010 128K 2^17
+// 011 256K 2^18
+// 100 512K 2^19
+// 101 1024K 2^20
+// 110 2048K 2^21
+// 111 4096K 2^22
+
+ cpu.rA = 0;
+ cpu.rA |= (word36) (cpu.switches.assignment [0] & 07LL)
+ << (35 - (2 + 0));
+ cpu.rA |= (word36) (cpu.switches.enable [0] & 01LL)
+ << (35 - (3 + 0));
+ cpu.rA |= (word36) (cpu.switches.init_enable [0] & 01LL)
+ << (35 - (4 + 0));
+ cpu.rA |= (word36) (cpu.switches.interlace [0] ? 1LL:0LL)
+ << (35 - (5 + 0));
+ cpu.rA |= (word36) (cpu.switches.store_size [0] & 07LL)
+ << (35 - (8 + 0));
+
+ cpu.rA |= (word36) (cpu.switches.assignment [1] & 07LL)
+ << (35 - (2 + 9));
+ cpu.rA |= (word36) (cpu.switches.enable [1] & 01LL)
+ << (35 - (3 + 9));
+ cpu.rA |= (word36) (cpu.switches.init_enable [1] & 01LL)
+ << (35 - (4 + 9));
+ cpu.rA |= (word36) (cpu.switches.interlace [1] ? 1LL:0LL)
+ << (35 - (5 + 9));
+ cpu.rA |= (word36) (cpu.switches.store_size [1] & 07LL)
+ << (35 - (8 + 9));
+
+ cpu.rA |= (word36) (cpu.switches.assignment [2] & 07LL)
+ << (35 - (2 + 18));
+ cpu.rA |= (word36) (cpu.switches.enable [2] & 01LL)
+ << (35 - (3 + 18));
+ cpu.rA |= (word36) (cpu.switches.init_enable [2] & 01LL)
+ << (35 - (4 + 18));
+ cpu.rA |= (word36) (cpu.switches.interlace [2] ? 1LL:0LL)
+ << (35 - (5 + 18));
+ cpu.rA |= (word36) (cpu.switches.store_size [2] & 07LL)
+ << (35 - (8 + 18));
+
+ cpu.rA |= (word36) (cpu.switches.assignment [3] & 07LL)
+ << (35 - (2 + 27));
+ cpu.rA |= (word36) (cpu.switches.enable [3] & 01LL)
+ << (35 - (3 + 27));
+ cpu.rA |= (word36) (cpu.switches.init_enable [3] & 01LL)
+ << (35 - (4 + 27));
+ cpu.rA |= (word36) (cpu.switches.interlace [3] ? 1LL:0LL)
+ << (35 - (5 + 27));
+ cpu.rA |= (word36) (cpu.switches.store_size [3] & 07LL)
+ << (35 - (8 + 27));
+ break;
+
+ case 2: // fault base and processor number switches
+// y = 2:
+//
+// 0 0 0 0 0 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 3 3 3
+// 0 3 4 5 6 2 3 4 7 8 9 0 1 2 3 4 5 6 8 9 2 3 5
+// --------------------------------------------------------------------------
+// |A|B|C|D| | | | | | | | | | | | | | |
+// --------- b | FLT BASE |c|0 0 0 0|d|e|f|0 0|g|h|i|0 0 0| SPEED | CPU |
+// |a|a|a|a| | | | | | | | | | | | | | |
+// --------------------------------------------------------------------------
+//
+
+// a: port A-D is 0: 4 word or 1: 2 word
+// b: processor type 0:L68 or DPS, 1: DPS8M, 2,3: reserved for future use
+// c: id prom 0: not installed, 1: installed
+// d: 1: bcd option installed (marketing designation)
+// e: 1: dps option installed (marketing designation)
+// f: 1: 8k cache installed
+// g: processor type designation: 0: dps8/xx, 1: dps8m/xx
+// h: gcos/vms switch position: 0:GCOS mode 1: virtual mode
+// i: current or new product line peripheral type: 0:CPL, 1:NPL
+// SPEED: 0000 = 8/70, 0100 = 8/52
+// CPU: Processor number
+// DPS 8M processors:
+// C(Port interlace, Ports A-D) -> C(A) 0,3
+// 01 -> C(A) 4,5
+// C(Fault base switches) -> C(A) 6,12
+// 1 -> C(A) 13
+// 0000 -> C(A) 14,17
+// 111 -> C(A) 18,20
+// 00 -> C(A) 21,22
+// 1 -> C(A) 23
+// C(Processor mode sw) -> C(A) 24
+// 1 -> C(A) 25
+// 000 -> C(A) 26,28
+// C(Processor speed) -> C (A) 29,32
+
+
+
+// C(Processor number switches) -> C(A) 33,35
+
+// According to bound_gcos_.1.s.archive/gcos_fault_processor_.pl1 (L68/DPS):
+//
+// /* Set the A register to reflect switch info. */
+// mc.regs.a =
+//
+// /* (A-reg bits) */
+// /* (0-3) Port address expansion option: */ (4)"0"b
+// /* (4-5) Reserved for future use: */ || (2)"0"b
+// /* (6-12) Processor fault base address switches: */ || (7)"0"b
+// /* (13-16) L66 peripheral connectability: */ || (4)"0"b
+// /* (17) Future use (must be zero): */ || (1)"1"b
+// /* (18) BCD option installed: */ || (1)"1"b
+// /* (19) DPS type processor: */ || (1)"0"b
+// /* (20) 8K cache option installed: */ || (1)"0"b
+// /* (21) Gear shift model processor: */ || (1)"0"b
+// /* (22) Power pach option installed: */ || (1)"0"b
+// /* (23) VMS-CU option installed - 66B' proc: */ || (1)"0"b
+// /* (24) VMS-VU option installed - 66B proc: */ || (1)"0"b
+// /* (25) Type processor (0) CPL, (1) DPSE-NPL: */ || (1)"0"b
+// /* (26) 6025, 6605 or 6610 type processor: */ || (1)"0"b
+// /* (27) 2K cache option installed: */ || (1)"0"b
+// /* (28) Extended memory option installed: */ || (1)"0"b
+// /* (29-30) cabinet (00) 8/70, (01) 8/52, (10) 862, (11) 846: */ || (2)"0"b
+// /* (31) EIS option installed: */ || (1)"1"b
+// /* (32) (1) slow memory access, (0) fast memory: */ || (1)"0"b
+// /* (33) (1) no instruction overlap, (0) overlap: */ || (1)"0"b
+// /* (34-35) Processor number: */ ||unspec (mc.cpu_type);
+
+ cpu.rA = 0;
+#ifdef DPS8M
+ cpu.rA |= (word36) ((cpu.switches.interlace[0] == 2 ?
+ 1LL : 0LL) << (35- 0));
+ cpu.rA |= (word36) ((cpu.switches.interlace[1] == 2 ?
+ 1LL : 0LL) << (35- 1));
+ cpu.rA |= (word36) ((cpu.switches.interlace[2] == 2 ?
+ 1LL : 0LL) << (35- 2));
+ cpu.rA |= (word36) ((cpu.switches.interlace[3] == 2 ?
+ 1LL : 0LL) << (35- 3));
+#endif
+
+#ifdef DPS8M
+ cpu.rA |= (word36) ((01L) /* 0b01 DPS8M */
+ << (35- 5));
+#endif
+#ifdef L68
+ cpu.rA |= (word36) ((00L) /* 0b00 L68/DPS */
+ << (35- 5));
+#endif
+ cpu.rA |= (word36) ((cpu.switches.FLT_BASE & 0177LL)
+ << (35-12));
+#ifdef DPS8M
+ cpu.rA |= (word36) ((01L) /* 0b1 ID_PROM installed */
+ << (35-13));
+#endif
+ cpu.rA |= (word36) ((0L) // 0b0000
+ << (35-17));
+ //cpu.rA |= (word36) ((0b111L)
+ //<< (35-20));
+ // According to rsw.incl.pl1, Multics ignores this bit.
+ cpu.rA |= (word36) ((00L) // 0b0 BCD option off
+ << (35-18));
+#ifdef DPS8M
+ cpu.rA |= (word36) ((01L) // 0b1 L68/DPS option: DPS
+ << (35-19));
+#endif
+#ifdef L68
+ cpu.rA |= (word36) ((00L) // 0b0 L68/DPS option: L68
+ << (35-19));
+#endif
+#ifdef DPS8M
+ // 8K cache
+ // 0b0: not installed
+ // 0b1: installed
+ cpu.rA |= (word36) ((cpu.switches.disable_cache ? 0 : 1)
+ << (35-20));
+ cpu.rA |= (word36) ((00L) // 0b00
+ << (35-22));
+ cpu.rA |= (word36) ((01L) /* 0b1 DPS8M */
+ << (35-23));
+ cpu.rA |= (word36) ((cpu.switches.proc_mode & 01LL)
+ << (35-24));
+ cpu.rA |= (word36) ((00L) // 0b0 new product line (CPL/NPL)
+ << (35-25));
+ cpu.rA |= (word36) ((00L) // 0b000
+ << (35-28));
+ cpu.rA |= (word36) ((cpu.switches.proc_speed & 017LL)
+ << (35-32));
+#endif
+#ifdef L68
+ cpu.rA |= (word36) ((00L) // 0b0 2K cache disabled
+ << (35-27));
+ cpu.rA |= (word36) ((00L) // 0b0 GCOS mode extended memory disabled
+ << (35-28));
+ cpu.rA |= (word36) ((016L) // 0b1110 CPU ID
+ << (35-32));
+#endif
+ cpu.rA |= (word36) ((cpu.switches.cpu_num & 07LL)
+ << (35-35));
+ break;
+
+ case 3: // configuration switches for ports E, F, G, H
+#ifdef DPS8M
+ cpu.rA = 0;
+ break;
+#endif
+#ifdef L68
+// y = 3:
+//
+// 0 0 0 1 1 2 2 3
+// 0 8 9 7 8 6 7 5
+// -------------------------------------------------------------------------
+// | PORT E | PORT F | PORT G | PORT H |
+// -------------------------------------------------------------------------
+// | ADR |j|k|l| MEM | ADR |j|k|l| MEM | ADR |j|k|l| MEM | ADR |j|k|l| MEM |
+// -------------------------------------------------------------------------
+//
+//
+// ADR: Address assignment switch setting for port
+// This defines the base address for the SCU
+// j: port enabled flag
+// k: system initialize enabled flag
+// l: interface enabled flag
+// MEM coded memory size
+// 000 32K 2^15
+// 001 64K 2^16
+// 010 128K 2^17
+// 011 256K 2^18
+// 100 512K 2^19
+// 101 1024K 2^20
+// 110 2048K 2^21
+// 111 4096K 2^22
+
+ cpu.rA = 0;
+ cpu.rA |= (word36) (cpu.switches.assignment [4] & 07LL)
+ << (35 - (2 + 0));
+ cpu.rA |= (word36) (cpu.switches.enable [4] & 01LL)
+ << (35 - (3 + 0));
+ cpu.rA |= (word36) (cpu.switches.init_enable [4] & 01LL)
+ << (35 - (4 + 0));
+ cpu.rA |= (word36) (cpu.switches.interlace [4] ? 1LL:0LL)
+ << (35 - (5 + 0));
+ cpu.rA |= (word36) (cpu.switches.store_size [4] & 07LL)
+ << (35 - (8 + 0));
+
+ cpu.rA |= (word36) (cpu.switches.assignment [5] & 07LL)
+ << (35 - (2 + 9));
+ cpu.rA |= (word36) (cpu.switches.enable [5] & 01LL)
+ << (35 - (3 + 9));
+ cpu.rA |= (word36) (cpu.switches.init_enable [5] & 01LL)
+ << (35 - (4 + 9));
+ cpu.rA |= (word36) (cpu.switches.interlace [5] ? 1LL:0LL)
+ << (35 - (5 + 9));
+ cpu.rA |= (word36) (cpu.switches.store_size [5] & 07LL)
+ << (35 - (8 + 9));
+
+ cpu.rA |= (word36) (cpu.switches.assignment [6] & 07LL)
+ << (35 - (2 + 18));
+ cpu.rA |= (word36) (cpu.switches.enable [6] & 01LL)
+ << (35 - (3 + 18));
+ cpu.rA |= (word36) (cpu.switches.init_enable [6] & 01LL)
+ << (35 - (4 + 18));
+ cpu.rA |= (word36) (cpu.switches.interlace [6] ? 1LL:0LL)
+ << (35 - (5 + 18));
+ cpu.rA |= (word36) (cpu.switches.store_size [6] & 07LL)
+ << (35 - (8 + 18));
+
+ cpu.rA |= (word36) (cpu.switches.assignment [7] & 07LL)
+ << (35 - (2 + 27));
+ cpu.rA |= (word36) (cpu.switches.enable [7] & 01LL)
+ << (35 - (3 + 27));
+ cpu.rA |= (word36) (cpu.switches.init_enable [7] & 01LL)
+ << (35 - (4 + 27));
+ cpu.rA |= (word36) (cpu.switches.interlace [7] ? 1LL:0LL)
+ << (35 - (5 + 27));
+ cpu.rA |= (word36) (cpu.switches.store_size [7] & 07LL)
+ << (35 - (8 + 27));
+ break;
+
+#endif
+
+ case 4:
+ // I suspect the this is a L68 only, but AL39 says both
+ // port interlace and half/full size
+ // The DPS doesn't seem to have the half/full size switches
+ // so we'll always report full, and the interlace bits were
+ // squeezed into RSW 2
+
+// 0 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3
+// 0 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 5
+// -------------------------------------------------------------------------
+// | | A | B | C | D | E | F | G | H | |
+// |0 0 0 0 0 0 0 0 0 0 0 0 0---------------------------------0 0 0 0 0 0 0|
+// | |f|g|f|g|f|g|f|g|f|g|f|g|f|g|f|g| |
+// -------------------------------------------------------------------------
+// 13 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 7
+
+ cpu.rA = 0;
+ cpu.rA |= (word36) (cpu.switches.interlace [0] == 2 ?
+ 1LL : 0LL) << (35-13);
+ cpu.rA |= (word36) (cpu.switches.interlace [1] == 2 ?
+ 1LL : 0LL) << (35-15);
+ cpu.rA |= (word36) (cpu.switches.interlace [2] == 2 ?
+ 1LL : 0LL) << (35-17);
+ cpu.rA |= (word36) (cpu.switches.interlace [3] == 2 ?
+ 1LL : 0LL) << (35-19);
+#ifdef L68
+ cpu.rA |= (word36) (cpu.switches.interlace [4] == 2 ?
+ 1LL : 0LL) << (35-21);
+ cpu.rA |= (word36) (cpu.switches.interlace [5] == 2 ?
+ 1LL : 0LL) << (35-23);
+ cpu.rA |= (word36) (cpu.switches.interlace [6] == 2 ?
+ 1LL : 0LL) << (35-25);
+ cpu.rA |= (word36) (cpu.switches.interlace [7] == 2 ?
+ 1LL : 0LL) << (35-27);
+#endif
+ break;
+
+ default:
+ // XXX Guessing values; also don't know if this is actually
+ // a fault
+ doFault (FAULT_IPR,
+ fst_ill_mod,
+ "Illegal register select value");
+ }
+ HDBGRegA ();
+ SC_I_ZERO (cpu.rA == 0);
+ SC_I_NEG (cpu.rA & SIGN36);
+ }
+ break;
+
+ /// Privileged - System Control
+
+// case x0 (0015): // cioc
+// {
+// // cioc The system controller addressed by Y (i.e., contains
+// // the word at Y) sends a connect signal to the port specified
+// // by C(Y) 33,35.
+//#ifdef SCUMEM
+// word24 offset;
+// int cpu_port_num = lookup_cpu_mem_map (cpu.iefpFinalAddress, & offset);
+//#else
+// int cpu_port_num = lookup_cpu_mem_map (cpu.iefpFinalAddress);
+//#endif
+// // If the there is no port to that memory location, fault
+// if (cpu_port_num < 0)
+// {
+// doFault (FAULT_ONC, fst_onc_nem, "(cioc)");
+// }
+// if (! get_scu_in_use (current_running_cpu_idx, cpu_port_num))
+// {
+// doFault (FAULT_ONC, fst_onc_nem, "(cioc)");
+// }
+// uint scuUnitIdx = get_scu_idx (current_running_cpu_idx, cpu_port_num);
+//
+//// expander word
+//// dcl 1 scs$reconfig_general_cow aligned external, /* Used during reconfig
+//// ops. */
+//// 2 pad bit (36) aligned,
+//// 2 cow, /* Connect operand word, in odd location. */
+//// 3 sub_mask bit (8) unaligned, /* Expander sub-port mask */
+//// 3 mbz1 bit (13) unaligned,
+//// 3 expander_command bit (3) unaligned, /* Expander command. */
+//// 3 mbz2 bit (9) unaligned,
+//// 3 controller_port fixed bin (3) unaligned unsigned;/* controller port for
+//// this CPU */
+//
+// word8 sub_mask = getbits36_8 (cpu.CY, 0);
+// word3 expander_command = getbits36_3 (cpu.CY, 21);
+// uint scu_port_num = (uint) getbits36_3 (cpu.CY, 33);
+// scu_cioc (current_running_cpu_idx, (uint) scuUnitIdx, scu_port_num,
+// expander_command, sub_mask);
+// }
+// break;
+//
+// case x0 (0553): // smcm
+// {
+// // C(TPR.CA)0,2 (C(TPR.CA)1,2 for the DPS 8M processor)
+// // specify which processor port (i.e., which system
+// // controller) is used.
+//#ifdef DPS8M
+// uint cpu_port_num = (cpu.TPR.CA >> 15) & 03;
+//#endif
+//#ifdef L68
+// uint cpu_port_num = (cpu.TPR.CA >> 15) & 07;
+//#endif
+// if (! get_scu_in_use (current_running_cpu_idx, cpu_port_num))
+// {
+// sim_warn ("smcm to non-existent controller on "
+// "cpu %d port %d\n",
+// current_running_cpu_idx, cpu_port_num);
+// break;
+// }
+// uint scuUnitIdx = get_scu_idx (current_running_cpu_idx, cpu_port_num);
+// t_stat rc = scu_smcm ((uint) scuUnitIdx,
+// current_running_cpu_idx, cpu.rA, cpu.rQ);
+// if (rc)
+// return rc;
+// }
+// break;
+//
+// case x0 (0451): // smic
+// {
+// // For the smic instruction, the first 2 or 3 bits of the addr
+// // field of the instruction are used to specify which SCU.
+// // 2 bits for the DPS8M.
+// //int scuUnitIdx = getbits36_2 (TPR.CA, 0);
+//
+// // C(TPR.CA)0,2 (C(TPR.CA)1,2 for the DPS 8M processor)
+// // specify which processor port (i.e., which system
+// // controller) is used.
+//#ifdef DPS8M
+// uint cpu_port_num = (cpu.TPR.CA >> 15) & 03;
+//#endif
+//#ifdef L68
+// uint cpu_port_num = (cpu.TPR.CA >> 15) & 07;
+//#endif
+// if (! get_scu_in_use (current_running_cpu_idx, cpu_port_num))
+// {
+//#ifdef DPS8M
+// return SCPE_OK;
+//#endif
+//#ifdef L68
+// // CPTUR (cptUseFR) -- will be set by doFault
+// if (cpu_port_num == 0)
+// putbits36_4 (& cpu.faultRegister[0], 16, 010);
+// else if (cpu_port_num == 1)
+// putbits36_4 (& cpu.faultRegister[0], 20, 010);
+// else if (cpu_port_num == 2)
+// putbits36_4 (& cpu.faultRegister[0], 24, 010);
+// else if (cpu_port_num == 3)
+// putbits36 (& cpu.faultRegister[0], 28, 4, 010);
+//// XXX What if the port is > 3?
+// doFault (FAULT_CMD, fst_cmd_ctl, "(smic)");
+//#endif
+// }
+// uint scuUnitIdx = get_scu_idx (current_running_cpu_idx, cpu_port_num);
+// t_stat rc = scu_smic ((uint) scuUnitIdx, current_running_cpu_idx,
+// cpu_port_num, cpu.rA);
+// if (rc)
+// return rc;
+// }
+// break;
+//
+// case x0 (0057): // sscr
+// {
+// //uint cpu_port_num = (cpu.TPR.CA >> 15) & 03;
+// // Looking at privileged_mode_ut.alm, shift 10 bits...
+//#ifdef DPS8M
+// uint cpu_port_num = (cpu.TPR.CA >> 10) & 03;
+//#endif
+//#ifdef L68
+// uint cpu_port_num = (cpu.TPR.CA >> 10) & 07;
+//#endif
+// if (! get_scu_in_use (current_running_cpu_idx, cpu_port_num))
+// {
+// // CPTUR (cptUseFR) -- will be set by doFault
+// if (cpu_port_num == 0)
+// putbits36_4 (& cpu.faultRegister[0], 16, 010);
+// else if (cpu_port_num == 1)
+// putbits36_4 (& cpu.faultRegister[0], 20, 010);
+// else if (cpu_port_num == 2)
+// putbits36_4 (& cpu.faultRegister[0], 24, 010);
+// else
+// putbits36 (& cpu.faultRegister[0], 28, 4, 010);
+// doFault (FAULT_CMD, fst_cmd_ctl, "(sscr)");
+// }
+// uint scuUnitIdx = get_scu_idx (current_running_cpu_idx, cpu_port_num);
+// t_stat rc = scu_sscr ((uint) scuUnitIdx, current_running_cpu_idx,
+// cpu_port_num, cpu.iefpFinalAddress & MASK15,
+// cpu.rA, cpu.rQ);
+//
+// if (rc)
+// return rc;
+// }
+// break;
+
+ // Privileged - Miscellaneous
+
+ case x0 (0212): // absa
+ {
+ word36 result;
+ int rc = doABSA (& result);
+ if (rc)
+ return rc;
+ cpu.rA = result;
+ HDBGRegA ();
+ SC_I_ZERO (cpu.rA == 0);
+ SC_I_NEG (cpu.rA & SIGN36);
+ }
+ break;
+
+// case x0 (0616): // dis
+//
+// if (! cpu.switches.dis_enable)
+// {
+// return STOP_STOP;
+// }
+//
+// // XXX This is subtle; g7Pending below won't see the queued
+// // g7Fault. I don't understand how the real hardware dealt
+// // with this, but this seems to work. (I would hazard a guess
+// // that DIS was doing a continuous FETCH/EXECUTE cycle
+// // ('if !interrupt goto .'))
+// advanceG7Faults ();
+//
+// if ((! cpu.switches.tro_enable) &&
+// (! sample_interrupts ()) &&
+// (sim_qcount () == 0)) // XXX If clk_svc is implemented it will
+// // break this logic
+// {
+// sim_printf ("DIS@0%06o with no interrupts pending and"
+// " no events in queue\n", cpu.PPR.IC);
+// sim_printf ("\nCycles = %"PRId64"\n", cpu.cycleCnt);
+// sim_printf ("\nInstructions = %"PRId64"\n", cpu.cycleCnt);
+// longjmp (cpu.jmpMain, JMP_STOP);
+// }
+//
+//// Multics/BCE halt
+// if (cpu.PPR.PSR == 0430 && cpu.PPR.IC == 012)
+// {
+// sim_printf ("BCE DIS causes CPU halt\n");
+// sim_debug (DBG_MSG, & cpu_dev, "BCE DIS causes CPU halt\n");
+//#ifdef LOCKLESS
+// bce_dis_called = true;
+//#endif // LOCKLESS
+// longjmp (cpu.jmpMain, JMP_STOP);
+// }
+//
+//#if 0
+//#ifdef LOCKLESS
+//// Changes to pxss.alm will move the address of the delete_me dis instuction
+//// That dis has a distintive bit pattern; use the segment and IWB instead
+//// of segment and IC.
+//
+//// pxss.list
+//// 005217 aa 000777 6162 07 4608 dis =o777,dl
+//
+// //if (cpu.PPR.PSR == 044 && cpu.PPR.IC == 0005217)
+// if (cpu.PPR.PSR == 044 && cpu.cu.IWB == 0000777616207)
+// {
+// sim_printf ("[%lld] pxss:delete_me DIS causes CPU halt\n", cpu.cycleCnt);
+// sim_debug (DBG_MSG, & cpu_dev, "pxss:delete_me DIS causes CPU halt\n");
+// longjmp (cpu.jmpMain, JMP_STOP);
+// //stopCPUThread ();
+// }
+//#endif
+//#endif
+//#ifdef ROUND_ROBIN
+// if (cpu.PPR.PSR == 034 && cpu.PPR.IC == 03535)
+// {
+// sim_printf ("[%lld] sys_trouble$die DIS causes CPU halt\n", cpu.cycleCnt);
+// sim_debug (DBG_MSG, & cpu_dev, "sys_trouble$die DIS causes CPU halt\n");
+// //longjmp (cpu.jmpMain, JMP_STOP);
+// cpu.isRunning = false;
+// }
+//#endif
+//#if 0
+// if (i->address == 0777)
+// {
+// sim_printf ("Multics DIS disables CPU: CA: 0x%x\n",cpu.TPR.CA);
+// sim_debug (DBG_MSG, & cpu_dev, "Multics DIS disables CPU\n");
+//#if 1
+// setCPURun (current_running_cpu_idx, false);
+//#else
+// longjmp (cpu.jmpMain, JMP_STOP);
+//#endif
+// }
+//#endif
+//
+// sim_debug (DBG_TRACEEXT, & cpu_dev, "entered DIS_cycle\n");
+// //sim_printf ("entered DIS_cycle\n");
+//
+// // No operation takes place, and the processor does not
+// // continue with the next instruction; it waits for a
+// // external interrupt signal.
+// // AND, according to pxss.alm, TRO
+//
+//// Bless NovaScale...
+//// DIS
+////
+//// NOTES:
+////
+//// 1. The inhibit bit in this instruction only affects the recognition
+//// of a Timer Runout (TROF) fault.
+////
+//// Inhibit ON delays the recognition of a TROF until the processor
+//// enters Slave mode.
+////
+//// Inhibit OFF allows the TROF to interrupt the DIS state.
+////
+//// 2. For all other faults and interrupts, the inhibit bit is ignored.
+////
+//// 3. The use of this instruction in the Slave or Master mode causes a
+//// Command fault.
+//
+// if (sample_interrupts ())
+// {
+// sim_debug (DBG_TRACEEXT, & cpu_dev, "DIS sees an interrupt\n");
+// cpu.interrupt_flag = true;
+// break;
+// }
+//// Implementing TRO according to AL39 for the DIS cause caues idle systems to
+//// hang in the DIS instruction. Revert back to the old behavior.
+//#if 1
+// if (GET_I (cpu.cu.IWB) ? bG7PendingNoTRO () : bG7Pending ())
+//#else
+// //if (GET_I (cpu.cu.IWB) ? bG7PendingNoTRO () : bG7Pending ())
+// // Don't check timer runout if in absolute mode, privledged, or
+// // interrupts inhibited.
+// bool noCheckTR = is_priv_mode () ||
+// GET_I (cpu.cu.IWB);
+// if (noCheckTR ? bG7PendingNoTRO () : bG7Pending ())
+//#endif
+// {
+// sim_debug (DBG_TRACEEXT, & cpu_dev, "DIS sees a TRO\n");
+// cpu.g7_flag = true;
+// break;
+// }
+// else
+// {
+// sim_debug (DBG_TRACEEXT, & cpu_dev, "DIS refetches\n");
+//#ifdef ROUND_ROBIN
+//#ifdef ISOLTS
+// if (current_running_cpu_idx)
+// {
+////sim_printf ("stopping CPU %c\n", current_running_cpu_idx + 'A');
+// cpu.isRunning = false;
+// }
+//#endif
+//#endif
+// return CONT_DIS;
+// }
+
+ /// POINTER REGISTER INSTRUCTIONS
+
+ /// PRIVILEGED INSTRUCTIONS
+
+ /// Privileged - Register Load
+
+ /// Privileged - Clear Associative Memory
+
+ /// EIS - Address Register Load
+
+ // aarn
+ case x1 (0560): // aar0
+ case x1 (0561): // aar1
+ case x1 (0562): // aar2
+ case x1 (0563): // aar3
+ case x1 (0564): // aar4
+ case x1 (0565): // aar5
+ case x1 (0566): // aar6
+ case x1 (0567): // aar7
+ {
+ // For n = 0, 1, ..., or 7 as determined by operation code
+ PNL (L68_ (DU_CYCLE_DDU_LDEA;))
+
+ if (getbits36_1 (cpu.CY, 23) != 0)
+ doFault (FAULT_IPR,
+ fst_ill_proc,
+ "aarn C(Y)23 != 0");
+
+ uint32 n = opcode10 & 07; // get
+ CPTUR (cptUsePRn + n);
+
+ // C(Y)0,17 -> C(ARn.WORDNO)
+ cpu.AR[n].WORDNO = GETHI (cpu.CY);
+
+ uint TA = getbits36_2 (cpu.CY, 21);
+ uint CN = getbits36_3 (cpu.CY, 18);
+
+ switch (TA)
+ {
+ case CTA4: // 2
+ // If C(Y)21,22 = 10 (TA code = 2), then
+ // C(Y)18,20 / 2 -> C(ARn.CHAR)
+ // 4 * (C(Y)18,20)mod2 + 1 -> C(ARn.BITNO)
+
+ // According to AL39, CN is translated:
+ // CN CHAR BIT
+ // 0 0 1
+ // 1 0 5
+ // 2 1 1
+ // 3 1 5
+ // 4 2 1
+ // 5 2 5
+ // 6 3 1
+ // 7 3 5
+ //SET_AR_CHAR_BITNO (n, CN/2, 4 * (CN % 2) + 1);
+
+ // According to ISOLTS ps805
+ // CN CHAR BIT
+ // 0 0 0
+ // 1 0 5
+ // 2 1 0
+ // 3 1 5
+ // 4 2 0
+ // 5 2 5
+ // 6 3 0
+ // 7 3 5
+ SET_AR_CHAR_BITNO (n, (word2) (CN/2), (CN % 2) ? 5 : 0);
+
+ break;
+
+ case CTA6: // 1
+ // If C(Y)21,22 = 01 (TA code = 1) and C(Y)18,20 = 110
+ // or 111 an illegal procedure fault occurs.
+ if (CN > 5)
+ {
+ cpu.AR[n].WORDNO = 0;
+ SET_AR_CHAR_BITNO (n, 0, 0);
+ doFault (FAULT_IPR, fst_ill_proc, "aarn TN > 5");
+ }
+
+ // If C(Y)21,22 = 01 (TA code = 1), then
+ // (6 * C(Y)18,20) / 9 -> C(ARn.CHAR)
+ // (6 * C(Y)18,20)mod9 -> C(ARn.BITNO)
+ SET_AR_CHAR_BITNO (n, (word2) ((6 * CN) / 9),
+ (6 * CN) % 9);
+ break;
+
+ case CTA9: // 0
+ // If C(Y)21,22 = 00 (TA code = 0), then
+ // C(Y)18,19 -> C(ARn.CHAR)
+ // 0000 -> C(ARn.BITNO)
+ // remember, 9-bit CN's are funky
+ SET_AR_CHAR_BITNO (n, (word2) (CN >> 1), 0);
+ break;
+
+ case CTAILL: // 3
+ // If C(Y)21,22 = 11 (TA code = 3) an illegal procedure
+ // fault occurs.
+ cpu.AR[n].WORDNO = 0;
+ SET_AR_CHAR_BITNO (n, 0, 0);
+ HDBGRegAR (n);
+ doFault (FAULT_IPR, fst_ill_proc, "aarn TA = 3");
+ }
+ HDBGRegAR (n);
+ }
+#ifdef TEST_OLIN
+ cmpxchg ();
+#endif
+ break;
+
+ // Load Address Register n
+ // larn
+ case x1 (0760): // lar0
+ case x1 (0761): // lar1
+ case x1 (0762): // lar2
+ case x1 (0763): // lar3
+ case x1 (0764): // lar4
+ case x1 (0765): // lar5
+ case x1 (0766): // lar6
+ case x1 (0767): // lar7
+ {
+ // For n = 0, 1, ..., or 7 as determined by operation code
+ // C(Y)0,23 -> C(ARn)
+ PNL (L68_ (DU_CYCLE_DDU_LDEA;))
+
+ uint32 n = opcode10 & 07; // get n
+ CPTUR (cptUsePRn + n);
+ cpu.AR[n].WORDNO = GETHI (cpu.CY);
+// AL-38 implies CHAR/BITNO, but ISOLTS requires PR.BITNO.
+ SET_AR_CHAR_BITNO (n, getbits36_2 (cpu.CY, 18),
+ getbits36_4 (cpu.CY, 20));
+ HDBGRegAR (n);
+ }
+ break;
+
+ // lareg - Load Address Registers
+
+ case x1 (0463): // lareg
+ PNL (L68_ (DU_CYCLE_DDU_LDEA;))
+
+ for (uint32 n = 0 ; n < 8 ; n += 1)
+ {
+ CPTUR (cptUsePRn + n);
+ word36 tmp36 = cpu.Yblock8[n];
+ cpu.AR[n].WORDNO = getbits36_18 (tmp36, 0);
+ SET_AR_CHAR_BITNO (n, getbits36_2 (tmp36, 18),
+ getbits36_4 (tmp36, 20));
+ HDBGRegAR (n);
+ }
+ break;
+
+ // lpl - Load Pointers and Lengths
+
+ case x1 (0467): // lpl
+ PNL (L68_ (DU_CYCLE_DDU_LDEA;))
+ words2du (cpu.Yblock8);
+ break;
+
+ // narn - (G'Kar?) Numeric Descriptor to Address Register n
+ // narn
+ case x1 (0660): // nar0
+ case x1 (0661): // nar1
+ case x1 (0662): // nar2
+ case x1 (0663): // nar3
+ case x1 (0664): // nar4
+ case x1 (0665): // nar5
+ case x1 (0666): // nar6 beware!!!! :-)
+ case x1 (0667): // nar7
+ {
+ // For n = 0, 1, ..., or 7 as determined by operation code
+ PNL (L68_ (DU_CYCLE_DDU_LDEA;))
+
+ uint32 n = opcode10 & 07; // get
+ CPTUR (cptUsePRn + n);
+
+ // C(Y)0,17 -> C(ARn.WORDNO)
+ cpu.AR[n].WORDNO = GETHI (cpu.CY);
+
+ uint TN = getbits36_1 (cpu.CY, 21); // C(Y) 21
+ uint CN = getbits36_3 (cpu.CY, 18); // C(Y) 18-20
+
+ switch(TN)
+ {
+ case CTN4: // 1
+ // If C(Y)21 = 1 (TN code = 1), then
+ // (C(Y)18,20) / 2 -> C(ARn.CHAR)
+ // 4 * (C(Y)18,20)mod2 + 1 -> C(ARn.BITNO)
+
+ // According to AL39, CN is translated:
+ // CN CHAR BIT
+ // 0 0 1
+ // 1 0 5
+ // 2 1 1
+ // 3 1 5
+ // 4 2 1
+ // 5 2 5
+ // 6 3 1
+ // 7 3 5
+ //SET_AR_CHAR_BITNO (n, CN/2, 4 * (CN % 2) + 1);
+
+ // According to ISOLTS ps805
+ // CN CHAR BIT
+ // 0 0 0
+ // 1 0 5
+ // 2 1 0
+ // 3 1 5
+ // 4 2 0
+ // 5 2 5
+ // 6 3 0
+ // 7 3 5
+ SET_AR_CHAR_BITNO (n, (word2) (CN/2), (CN % 2) ? 5 : 0);
+
+ break;
+
+ case CTN9: // 0
+ // If C(Y)21 = 0 (TN code = 0) and C(Y)20 = 1 an
+ // illegal procedure fault occurs.
+ if ((CN & 1) != 0)
+ doFault (FAULT_IPR, fst_ill_proc, "narn N9 and CN odd");
+ // The character number is in bits 18-19; recover it
+ CN >>= 1;
+ // If C(Y)21 = 0 (TN code = 0), then
+ // C(Y)18,20 -> C(ARn.CHAR)
+ // 0000 -> C(ARn.BITNO)
+ SET_AR_CHAR_BITNO (n, (word2) CN, 0);
+ break;
+ }
+ HDBGRegAR (n);
+ }
+ break;
+
+ /// EIS - Address Register Store
+
+ // aran Address Register n to Alphanumeric Descriptor
+
+ // aarn
+ case x1 (0540): // aar0
+ case x1 (0541): // aar1
+ case x1 (0542): // aar2
+ case x1 (0543): // aar3
+ case x1 (0544): // aar4
+ case x1 (0545): // aar5
+ case x1 (0546): // aar6
+ case x1 (0547): // aar7
+ {
+ // The alphanumeric descriptor is fetched from Y and C(Y)21,22
+ // (TA field) is examined to determine the data type described.
+ PNL (L68_ (DU_CYCLE_DDU_STEA;))
+
+ uint TA = getbits36_2 (cpu.CY, 21);
+
+ // If C(Y)21,22 = 11 (TA code = 3) or C(Y)23 = 1 (unused bit),
+ // an illegal procedure fault occurs.
+ if (TA == 03)
+ doFault (FAULT_IPR,
+ fst_ill_proc,
+ "ARAn tag == 3");
+ if (getbits36_1 (cpu.CY, 23) != 0)
+ doFault (FAULT_IPR,
+ fst_ill_proc,
+ "ARAn b23 == 1");
+
+ uint32 n = opcode10 & 07; // get
+ CPTUR (cptUsePRn + n);
+ // For n = 0, 1, ..., or 7 as determined by operation code
+
+ // C(ARn.WORDNO) -> C(Y)0,17
+ putbits36_18 (& cpu.CY, 0, cpu.AR[n].WORDNO & MASK18);
+
+ // If TA = 1 (6-bit data) or TA = 2 (4-bit data), C(ARn.CHAR)
+ // and C(ARn.BITNO) are translated to an equivalent character
+ // position that goes to C(Y)18,20.
+
+ int CN = 0;
+
+ switch (TA)
+ {
+ case CTA4: // 2
+ // If C(Y)21,22 = 10 (TA code = 2), then
+ // (9 * C(ARn.CHAR) + C(ARn.BITNO) - 1) / 4 -> C(Y)18,20
+ CN = (9 * GET_AR_CHAR (n) + GET_AR_BITNO (n) - 1) / 4;
+ putbits36_3 (& cpu.CY, 18, (word3) CN & MASK3);
+ break;
+
+ case CTA6: // 1
+ // If C(Y)21,22 = 01 (TA code = 1), then
+ // (9 * C(ARn.CHAR) + C(ARn.BITNO)) / 6 -> C(Y)18,20
+ CN = (9 * GET_AR_CHAR (n) + GET_AR_BITNO (n)) / 6;
+ putbits36_3 (& cpu.CY, 18, (word3) CN & MASK3);
+ break;
+
+ case CTA9: // 0
+ // If C(Y)21,22 = 00 (TA code = 0), then
+ // C(ARn.CHAR) -> C(Y)18,19
+ // 0 -> C(Y)20
+ putbits36_3 (& cpu.CY, 18,
+ (word3) ((GET_AR_CHAR (n) & MASK2) << 1));
+ break;
+ }
+ cpu.zone = 0777777700000;
+ cpu.useZone = true;
+ }
+ break;
+
+ // arnn Address Register n to Numeric Descriptor
+
+ // aarn
+ case x1 (0640): // aar0
+ case x1 (0641): // aar1
+ case x1 (0642): // aar2
+ case x1 (0643): // aar3
+ case x1 (0644): // aar4
+ case x1 (0645): // aar5
+ case x1 (0646): // aar6
+ case x1 (0647): // aar7
+ {
+ PNL (L68_ (DU_CYCLE_DDU_STEA;))
+ uint32 n = opcode10 & 07; // get register #
+ CPTUR (cptUsePRn + n);
+
+ // The Numeric descriptor is fetched from Y and C(Y)21,22 (TA
+ // field) is examined to determine the data type described.
+
+ uint TN = getbits36_1 (cpu.CY, 21); // C(Y) 21
+
+ // For n = 0, 1, ..., or 7 as determined by operation code
+ // C(ARn.WORDNO) -> C(Y)0,17
+ putbits36_18 (& cpu.CY, 0, cpu.AR[n].WORDNO & MASK18);
+
+ switch (TN)
+ {
+ case CTN4: // 1
+ {
+ // If C(Y)21 = 1 (TN code = 1) then
+ // (9 * C(ARn.CHAR) + C(ARn.BITNO) - 1) / 4 ->
+ // C(Y)18,20
+ word3 CN = (9 * GET_AR_CHAR (n) +
+ GET_AR_BITNO (n) - 1) / 4;
+ putbits36_3 (& cpu.CY, 18, CN & MASK3);
+ break;
+ }
+ case CTN9: // 0
+ // If C(Y)21 = 0 (TN code = 0), then
+ // C(ARn.CHAR) -> C(Y)18,19
+ // 0 -> C(Y)20
+ putbits36_3 (& cpu.CY, 18,
+ (word3) ((GET_AR_CHAR (n) & MASK2) << 1));
+ break;
+ }
+ cpu.zone = 0777777700000;
+ cpu.useZone = true;
+ }
+ break;
+
+ // sarn Store Address Register n
+
+ // sarn
+ case x1 (0740): // sar0
+ case x1 (0741): // sar1
+ case x1 (0742): // sar2
+ case x1 (0743): // sar3
+ case x1 (0744): // sar4
+ case x1 (0745): // sar5
+ case x1 (0746): // sar6
+ case x1 (0747): // sar7
+ //For n = 0, 1, ..., or 7 as determined by operation code
+ // C(ARn) -> C(Y)0,23
+ // C(Y)24,35 -> unchanged
+ {
+ PNL (L68_ (DU_CYCLE_DDU_STEA;))
+ uint32 n = opcode10 & 07; // get n
+ CPTUR (cptUsePRn + n);
+ putbits36 (& cpu.CY, 0, 18, cpu.PR[n].WORDNO);
+// AL-39 implies CHAR/BITNO, but ISOLTS test 805 requires BITNO
+ putbits36 (& cpu.CY, 18, 2, GET_AR_CHAR (n));
+ putbits36 (& cpu.CY, 20, 4, GET_AR_BITNO (n));
+ //putbits36 (& cpu.CY, 18, 6, GET_PR_BITNO (n));
+ cpu.zone = 0777777770000;
+ cpu.useZone = true;
+ }
+#ifdef TEST_OLIN
+ cmpxchg ();
+#endif
+ break;
+
+ // sareg Store Address Registers
+
+ case x1 (0443): // sareg
+ // a:AL39/ar1 According to ISOLTS ps805, the BITNO data is stored
+ // in BITNO format, not CHAR/BITNO.
+ PNL (L68_ (DU_CYCLE_DDU_STEA;))
+ memset (cpu.Yblock8, 0, sizeof (cpu.Yblock8));
+ for (uint32 n = 0 ; n < 8 ; n += 1)
+ {
+ CPTUR (cptUsePRn + n);
+ word36 arx = 0;
+ putbits36 (& arx, 0, 18, cpu.PR[n].WORDNO);
+ putbits36 (& arx, 18, 2, GET_AR_CHAR (n));
+ putbits36 (& arx, 20, 4, GET_AR_BITNO (n));
+ cpu.Yblock8[n] = arx;
+ }
+ break;
+
+ // spl Store Pointers and Lengths
+
+ case x1 (0447): // spl
+ PNL (L68_ (DU_CYCLE_DDU_STEA;))
+ du2words (cpu.Yblock8);
+ break;
+
+ /// EIS - Address Register Special Arithmetic
+
+ // a4bd Add 4-bit Displacement to Address Register 5
+
+ case x1 (0502): // a4bd
+ asxbd (4, false);
+ break;
+
+ // a6bd Add 6-bit Displacement to Address Register
+
+ case x1 (0501): // a6bd
+ asxbd (6, false);
+ break;
+
+ // a9bd Add 9-bit Displacement to Address Register
+
+ case x1 (0500): // a9bd
+ asxbd (9, false);
+ break;
+
+ // abd Add Bit Displacement to Address Register
+
+ case x1 (0503): // abd
+ asxbd (1, false);
+ break;
+
+ // awd Add Word Displacement to Address Register
+
+ case x1 (0507): // awd
+ asxbd (36, false);
+ break;
+
+ // s4bd Subtract 4-bit Displacement from Address Register
+
+ case x1 (0522): // s4bd
+ asxbd (4, true);
+ break;
+
+ // s6bd Subtract 6-bit Displacement from Address Register
+
+ case x1 (0521): // s6bd
+ asxbd (6, true);
+ break;
+
+ // s9bd Subtract 9-bit Displacement from Address Register
+
+ case x1 (0520): // s9bd
+ asxbd (9, true);
+ break;
+
+ // sbd Subtract Bit Displacement from Address Register
+
+ case x1 (0523): // sbd
+ asxbd (1, true);
+ break;
+
+ // swd Subtract Word Displacement from Address Register
+
+ case x1 (0527): // swd
+ asxbd (36, true);
+ break;
+
+ /// EIS = Alphanumeric Compare
+
+ case x1 (0106): // cmpc
+ cmpc ();
+ break;
+
+ case x1 (0120): // scd
+ scd ();
+ break;
+
+ case x1 (0121): // scdr
+ scdr ();
+ break;
+
+ case x1 (0124): // scm
+ scm ();
+ break;
+
+ case x1 (0125): // scmr
+ scmr ();
+ break;
+
+ case x1 (0164): // tct
+ tct ();
+ break;
+
+ case x1 (0165): // tctr
+ tctr ();
+ break;
+
+ /// EIS - Alphanumeric Move
+
+ case x1 (0100): // mlr
+ mlr ();
+ break;
+
+ case x1 (0101): // mrl
+ mrl ();
+ break;
+
+ case x1 (0020): // mve
+ mve ();
+ break;
+
+ case x1 (0160): // mvt
+ mvt ();
+ break;
+
+ /// EIS - Numeric Compare
+
+ case x1 (0303): // cmpn
+ cmpn ();
+ break;
+
+ /// EIS - Numeric Move
+
+ case x1 (0300): // mvn
+ mvn ();
+ break;
+
+ case x1 (0024): // mvne
+ mvne ();
+ break;
+
+ /// EIS - Bit String Combine
+
+ case x1 (0060): // csl
+ csl ();
+ break;
+
+ case x1 (0061): // csr
+ csr ();
+ break;
+
+ /// EIS - Bit String Compare
+
+ case x1 (0066): // cmpb
+ cmpb ();
+ break;
+
+ /// EIS - Bit String Set Indicators
+
+ case x1 (0064): // sztl
+ // The execution of this instruction is identical to the Combine
+ // Bit Strings Left (csl) instruction except that C(BOLR)m is
+ // not placed into C(Y-bit2)i-1.
+ sztl ();
+ break;
+
+ case x1 (0065): // sztr
+ // The execution of this instruction is identical to the Combine
+ // Bit Strings Left (csr) instruction except that C(BOLR)m is
+ // not placed into C(Y-bit2)i-1.
+ sztr ();
+ break;
+
+ /// EIS -- Data Conversion
+
+ case x1 (0301): // btd
+ btd ();
+ break;
+
+ case x1 (0305): // dtb
+ dtb ();
+ break;
+
+ /// EIS - Decimal Addition
+
+ case x1 (0202): // ad2d
+ ad2d ();
+ break;
+
+ case x1 (0222): // ad3d
+ ad3d ();
+ break;
+
+ /// EIS - Decimal Subtraction
+
+ case x1 (0203): // sb2d
+ sb2d ();
+ break;
+
+ case x1 (0223): // sb3d
+ sb3d ();
+ break;
+
+ /// EIS - Decimal Multiplication
+
+ case x1 (0206): // mp2d
+ mp2d ();
+ break;
+
+ case x1 (0226): // mp3d
+ mp3d ();
+ break;
+
+ /// EIS - Decimal Division
+
+ case x1 (0207): // dv2d
+ dv2d ();
+ break;
+
+ case x1 (0227): // dv3d
+ dv3d ();
+ break;
+
+#ifdef TESTING
+#if EMULATOR_ONLY
+
+ case x1 (0420): // emcall instruction Custom, for an emulator call for
+ // simh stuff ...
+ {
+ int ret = emCall ();
+ if (ret)
+ return ret;
+ break;
+ }
+#endif
+#endif
+
+ default:
+ if (cpu.switches.halt_on_unimp)
+ return STOP_STOP;
+ doFault (FAULT_IPR,
+ fst_ill_op,
+ "Illegal instruction");
+ }
+#ifdef L68
+ cpu.ou.STR_OP = (is_ou && (i->info->flags & (STORE_OPERAND | STORE_YPAIR))) ? 1 : 0;
+ cpu.ou.cycle |= ou_GOF;
+ if (cpu.MR_cache.emr && cpu.MR_cache.ihr && is_ou)
+ add_OU_history ();
+ if (cpu.MR_cache.emr && cpu.MR_cache.ihr && is_du)
+ add_DU_history ();
+#endif
+ return SCPE_OK;
+}
+
+
+
+#ifdef TESTING
+#include <ctype.h>
+
+#if EMULATOR_ONLY
+/**
+ * emulator call instruction. Do whatever address field sez' ....
+ */
+static int emCall (void)
+{
+ DCDstruct * i = & cpu.currentInstruction;
+ switch (i->address) // address field
+ {
+ case 1: // putc9 - put 9-bit char in AL to stdout
+ {
+ if (cpu.rA > 0xff) // don't want no 9-bit bytes here!
+ break;
+
+ char c = cpu.rA & 0x7f;
+ if (c) // ignore NULL chars.
+ sim_printf ("%c", c);
+ break;
+ }
+ case 0100: // putc9 - put 9-bit char in A(0) to stdout
+ {
+ char c = (cpu.rA >> 27) & 0x7f;
+ if (isascii (c)) // ignore NULL chars.
+ sim_printf ("%c", c);
+ else
+ sim_printf ("\\%03o", c);
+ break;
+ }
+ case 2: // putc6 - put 6-bit char in A to stdout
+ {
+ int c = GEBcdToASCII[cpu.rA & 077];
+ if (c != -1)
+ {
+ if (isascii (c)) // ignore NULL chars.
+ sim_printf ("%c", c);
+ else
+ sim_printf ("\\%3o", c);
+ }
+ break;
+ }
+ case 3: // putoct - put octal contents of A to stdout (split)
+ {
+ sim_printf ("%06o %06o", GETHI (cpu.rA), GETLO (cpu.rA));
+ break;
+ }
+ case 4: // putoctZ - put octal contents of A to stdout
+ // (zero-suppressed)
+ {
+ sim_printf ("%"PRIo64"", cpu.rA);
+ break;
+ }
+ case 5: // putdec - put decimal contents of A to stdout
+ {
+ t_int64 tmp = SIGNEXT36_64 (cpu.rA);
+ sim_printf ("%"PRId64"", tmp);
+ break;
+ }
+ case 6: // putEAQ - put float contents of C(EAQ) to stdout
+ {
+#ifndef __MINGW64__
+ long double eaq = EAQToIEEElongdouble ();
+ sim_printf ("%12.8Lg", eaq);
+#else
+ double eaq = EAQToIEEEdouble();
+ sim_printf("%12.8g", eaq);
+#endif
+ break;
+ }
+ case 7: // dump index registers
+ for (int i = 0 ; i < 8 ; i += 4)
+ sim_printf ("r[%d]=%06o r[%d]=%06o r[%d]=%06o r[%d]=%06o\n",
+ i+0, cpu.rX[i+0], i+1, cpu.rX[i+1], i+2, cpu.rX[i+2],
+ i+3, cpu.rX[i+3]);
+ break;
+
+ case 17: // dump pointer registers
+ for (int n = 0 ; n < 8 ; n++)
+ {
+ sim_printf ("PR[%d]/%s: SNR=%05o RNR=%o WORDNO=%06o "
+ "BITNO:%02o\n",
+ n, PRalias[n], cpu.PR[n].SNR, cpu.PR[n].RNR,
+ cpu.PR[n].WORDNO, GET_PR_BITNO (n));
+ }
+ break;
+ case 27: // dump registers A & Q
+ sim_printf ("A: %012"PRIo64" Q:%012"PRIo64"\n", cpu.rA, cpu.rQ);
+ break;
+
+ case 8: // crlf to console
+ sim_printf ("\n");
+ break;
+
+ case 13: // putoct - put octal contents of Q to stdout (split)
+ {
+ sim_printf ("%06o %06o", GETHI (cpu.rQ), GETLO (cpu.rQ));
+ break;
+ }
+ case 14: // putoctZ - put octal contents of Q to stdout
+ // (zero-suppressed)
+ {
+ sim_printf ("%"PRIo64"", cpu.rQ);
+ break;
+ }
+ case 15: // putdec - put decimal contents of Q to stdout
+ {
+ t_int64 tmp = SIGNEXT36_64 (cpu.rQ);
+ sim_printf ("%"PRId64"", tmp);
+ break;
+ }
+
+ case 16: // puts - A high points to by an aci string; print it.
+ // The string includes C-sytle escapes: \0 for end
+ // of string, \n for newline, \\ for a backslash
+ case 21: // puts: A contains a 24 bit address
+ {
+ const int maxlen = 256;
+ char buf[maxlen + 1];
+
+ word36 addr;
+ if (i->address == 16)
+ addr = cpu.rA >> 18;
+ else // 21
+ addr = cpu.rA >> 12;
+ word36 chunk = 0;
+ int i;
+ bool is_escape = false;
+ int cnt = 0;
+
+ for (i = 0; cnt < maxlen; i ++)
+ {
+ // fetch char
+ if (i % 4 == 0)
+ chunk = M[addr ++];
+ word36 wch = chunk >> (9 * 3);
+ chunk = (chunk << 9) & DMASK;
+ char ch = (char) (wch & 0x7f);
+
+ if (is_escape)
+ {
+ if (ch == '0')
+ ch = '\0';
+ else if (ch == 'n')
+ ch = '\n';
+ else
+ {
+ /* ch = ch */;
+ }
+ is_escape = false;
+ buf[cnt ++] = ch;
+ if (ch == '\0')
+ break;
+ }
+ else
+ {
+ if (ch == '\\')
+ is_escape = true;
+ else
+ {
+ buf[cnt ++] = ch;
+ if (ch == '\0')
+ break;
+ }
+ }
+ }
+ // Safety; if filled buffer before finding eos, put an eos
+ // in the extra space that was allocated
+ buf[maxlen] = '\0';
+ sim_printf ("%s", buf);
+ break;
+ }
+
+ // case 17 used above
+
+ case 18: // halt
+ return STOP_STOP;
+
+ case 19: // putdecaq - put decimal contents of AQ to stdout
+ {
+ int64_t t0 = SIGNEXT36_64 (cpu.rA);
+ __int128_t AQ = ((__int128_t) t0) << 36;
+ AQ |= (cpu.rQ & DMASK);
+ print_int128 (AQ, NULL);
+ break;
+ }
+
+ case 20: // Report fault
+ {
+ emCallReportFault ();
+ break;
+ }
+
+ // case 21 defined above
+
+ }
+ return 0;
+}
+#endif
+#endif // TESTING
+
+// CANFAULT
+static int doABSA (word36 * result)
+ {
+ word36 res;
+ sim_debug (DBG_APPENDING, & cpu_dev, "absa CA:%08o\n", cpu.TPR.CA);
+
+ //if (get_addr_mode () == ABSOLUTE_mode && ! cpu.isb29)
+ //if (get_addr_mode () == ABSOLUTE_mode && ! cpu.went_appending) // ISOLTS-860
+ if (get_addr_mode () == ABSOLUTE_mode && ! (cpu.cu.XSF || cpu.currentInstruction.b29)) // ISOLTS-860
+ {
+ * result = ((word36) (cpu.TPR.CA & MASK18)) << 12; // 24:12 format
+ return SCPE_OK;
+ }
+
+ // ABSA handles directed faults differently, so a special append cycle is
+ // needed.
+ // do_append_cycle also provides WAM support, which is required by
+ // ISOLTS-860 02
+ // res = (word36) do_append_cycle (cpu.TPR.CA & MASK18, ABSA_CYCLE, NULL,
+ // 0) << 12;
+ res = (word36) do_append_cycle (ABSA_CYCLE, NULL, 0) << 12;
+
+ * result = res;
+
+ return SCPE_OK;
+ }
+
+void doRCU (void)
+ {
+#ifdef LOOPTRC
+elapsedtime ();
+ sim_printf (" rcu to %05o:%06o PSR:IC %05o:%06o\r\n", (cpu.Yblock8[0]>>18)&MASK15, (cpu.Yblock8[4]>>18)&MASK18, cpu.PPR.PSR, cpu.PPR.IC);
+#endif
+
+ if_sim_debug (DBG_FAULT, & cpu_dev)
+ {
+ dump_words(cpu.Yblock8);
+ //for (int i = 0; i < 8; i ++)
+ // {
+ // sim_debug (DBG_FAULT, & cpu_dev, "RCU %d %012"PRIo64"\n", i,
+ // cpu.Yblock8[i]);
+ // }
+ }
+
+ words2scu (cpu.Yblock8);
+ decode_instruction (IWB_IRODD, & cpu.currentInstruction);
+
+// Restore addressing mode
+
+ word1 saveP = cpu.PPR.P; // ISOLTS-870 02m
+ if (TST_I_ABS == 0)
+ set_addr_mode (APPEND_mode);
+ else
+ set_addr_mode (ABSOLUTE_mode);
+ cpu.PPR.P = saveP;
+
+ if (getbits36_1 (cpu.Yblock8[1], 35) == 0) // cpu.cu.FLT_INT is interrupt, not fault
+ {
+ sim_debug (DBG_FAULT, & cpu_dev, "RCU interrupt return\n");
+ longjmp (cpu.jmpMain, JMP_REFETCH);
+ }
+
+ // Resync the append unit
+ fauxDoAppendCycle (INSTRUCTION_FETCH);
+
+// All of the faults list as having handlers have actually
+// been encountered in Multics operation and are believed
+// to be being handled correctly. The handlers in
+// parenthesis are speculative and untested.
+//
+// Unhandled:
+//
+// SDF Shutdown: Why would you RCU from a shutdown fault?
+// STR Store:
+// AL39 is contradictory or vague about store fault subfaults and store
+// faults in general. They are mentioned:
+// SPRPn: store fault (illegal pointer) (assuming STR:ISN)
+// SMCM: store fault (not control) -
+// SMIC: store fault (not control) > I believe that these should be
+// SSCR: store fault (not control) - command fault
+// TSS: STR:OOB
+// Bar mode out-of-bounds: STR:OOB
+// The SCU register doesn't define which bit is "store fault (not control)"
+// STR:ISN - illegal segment number
+// STR:NEA - nonexistent address
+// STR:OOB - bar mode out-of-bounds
+//
+// decimal octal
+// fault fault mnemonic name priority group handler
+// number address
+// 0 0 sdf Shutdown 27 7
+// 1 2 str Store 10 4 get_BAR_address, instruction execution
+// 2 4 mme Master mode entry 1 11 5 JMP_SYNC_FAULT_RETURN instruction execution
+// 3 6 f1 Fault tag 1 17 5 (JMP_REFETCH/JMP_RESTART) do_caf
+// 4 10 tro Timer runout 26 7 JMP_REFETCH FETCH_cycle
+// 5 12 cmd Command 9 4 JMP_REFETCH/JMP_RESTART instruction execution
+// 6 14 drl Derail 15 5 JMP_REFETCH/JMP_RESTART instruction execution
+// 7 16 luf Lockup 5 4 JMP_REFETCH do_caf, FETCH_cycle
+// 8 20 con Connect 25 7 JMP_REFETCH FETCH_cycle
+// 9 22 par Parity 8 4
+// 10 24 ipr Illegal procedure 16 5 doITSITP, do_caf, instruction execution
+// 11 26 onc Operation not complete 4 2 nem_check, instruction execution
+// 12 30 suf Startup 1 1
+// 13 32 ofl Overflow 7 3 JMP_REFETCH/JMP_RESTART instruction execution
+// 14 34 div Divide check 6 3 instruction execution
+// 15 36 exf Execute 2 1 JMP_REFETCH/JMP_RESTART FETCH_cycle
+// 16 40 df0 Directed fault 0 20 6 JMP_REFETCH/JMP_RESTART getSDW, do_append_cycle
+// 17 42 df1 Directed fault 1 21 6 JMP_REFETCH/JMP_RESTART getSDW, do_append_cycle
+// 18 44 df2 Directed fault 2 22 6 (JMP_REFETCH/JMP_RESTART) getSDW, do_append_cycle
+// 19 46 df3 Directed fault 3 23 6 JMP_REFETCH/JMP_RESTART getSDW, do_append_cycle
+// 20 50 acv Access violation 24 6 JMP_REFETCH/JMP_RESTART fetchDSPTW, modifyDSPTW, fetchNSDW, do_append_cycle, EXEC_cycle (ring alarm)
+// 21 52 mme2 Master mode entry 2 12 5 JMP_SYNC_FAULT_RETURN instruction execution
+// 22 54 mme3 Master mode entry 3 13 5 (JMP_SYNC_FAULT_RETURN) instruction execution
+// 23 56 mme4 Master mode entry 4 14 5 (JMP_SYNC_FAULT_RETURN) instruction execution
+// 24 60 f2 Fault tag 2 18 5 JMP_REFETCH/JMP_RESTART do_caf
+// 25 62 f3 Fault tag 3 19 5 JMP_REFETCH/JMP_RESTART do_caf
+// 26 64 Unassigned
+// 27 66 Unassigned
+// 28 70 Unassigned
+// 29 72 Unassigned
+// 30 74 Unassigned
+// 31 76 trb Trouble 3 2 FETCH_cycle, doRCU
+
+
+// Reworking logic
+
+#define rework
+#ifdef rework
+ if (cpu.cu.FIF) // fault occured during instruction fetch
+ {
+//if (cpu.cu.rfi) sim_printf ( "RCU FIF refetch return caught rfi\n");
+ // I am misusing this bit; on restart I want a way to tell the
+ // CPU state machine to restart the instruction, which is not
+ // how Multics uses it. I need to pick a different way to
+ // communicate; for now, turn it off on refetch so the state
+ // machine doesn't become confused.
+ cpu.cu.rfi = 0;
+ sim_debug (DBG_FAULT, & cpu_dev, "RCU FIF REFETCH return\n");
+ longjmp (cpu.jmpMain, JMP_REFETCH);
+ }
+
+// RFI means 'refetch this instruction'
+ if (cpu.cu.rfi)
+ {
+//sim_printf ( "RCU rfi refetch return\n");
+ sim_debug (DBG_FAULT, & cpu_dev, "RCU rfi refetch return\n");
+// Setting the to RESTART causes ISOLTS 776 to report unexpected
+// trouble faults.
+// Without clearing rfi, ISOLTS pm776-08i LUFs.
+ cpu.cu.rfi = 0;
+ longjmp (cpu.jmpMain, JMP_REFETCH);
+ }
+
+// The debug command uses MME2 to implement breakpoints, but it is not
+// clear what it does to the MC data to signal RFI behavior.
+
+ word5 fi_addr = getbits36_5 (cpu.Yblock8[1], 30);
+ if (fi_addr == FAULT_MME ||
+ fi_addr == FAULT_MME2 ||
+ fi_addr == FAULT_MME3 ||
+ fi_addr == FAULT_MME4 ||
+ fi_addr == FAULT_DRL)
+ //if (fi_addr == FAULT_MME2)
+ {
+//sim_printf ("MME2 restart\n");
+ sim_debug (DBG_FAULT, & cpu_dev, "RCU MME2 restart return\n");
+ cpu.cu.rfi = 0;
+ longjmp (cpu.jmpMain, JMP_RESTART);
+ }
+#else
+ if (cpu.cu.rfi || // S/W asked for the instruction to be started
+ cpu.cu.FIF) // fault occured during instruction fetch
+ {
+
+ // I am misusing this bit; on restart I want a way to tell the
+ // CPU state machine to restart the instruction, which is not
+ // how Multics uses it. I need to pick a different way to
+ // communicate; for now, turn it off on refetch so the state
+ // machine doesn't become confused.
+
+ cpu.cu.rfi = 0;
+ sim_debug (DBG_FAULT, & cpu_dev, "RCU rfi/FIF REFETCH return\n");
+ longjmp (cpu.jmpMain, JMP_REFETCH);
+ }
+
+// It seems obvious that MMEx should do a JMP_SYNC_FAULT_RETURN, but doing
+// a JMP_RESTART makes 'debug' work. (The same change to DRL does not make
+// 'gtss' work, tho.
+
+ if (fi_addr == FAULT_MME2)
+ {
+//sim_printf ("MME2 restart\n");
+ sim_debug (DBG_FAULT, & cpu_dev, "RCU MME2 restart return\n");
+ cpu.cu.rfi = 1;
+ longjmp (cpu.jmpMain, JMP_RESTART);
+ }
+#endif
+
+#if 0
+// I beleive this logic is correct (cf. ISOLTS pa870 test-02d TRA PR1|6 not
+// switching to append mode do to page fault clearing went_appending), but the
+// emulator's refetching of operand descriptors after page fault of EIS
+// instruction in absolute mode is breaking the logic.
+ // If restarting after a page fault, set went_appending...
+ if (fi_addr == FAULT_DF0 ||
+ fi_addr == FAULT_DF1 ||
+ fi_addr == FAULT_DF2 ||
+ fi_addr == FAULT_DF3 ||
+ fi_addr == FAULT_ACV ||
+ fi_addr == FAULT_F1 ||
+ fi_addr == FAULT_F2 ||
+ fi_addr == FAULT_F3)
+ {
+ set_went_appending ();
+ }
+#endif
+ // MME faults resume with the next instruction
+
+
+
+#ifdef rework
+ if (fi_addr == FAULT_DIV ||
+ fi_addr == FAULT_OFL ||
+ fi_addr == FAULT_IPR)
+ {
+ sim_debug (DBG_FAULT, & cpu_dev, "RCU sync fault return\n");
+ cpu.cu.rfi = 0;
+ longjmp (cpu.jmpMain, JMP_SYNC_FAULT_RETURN);
+ }
+#else
+ if (fi_addr == FAULT_MME ||
+ /* fi_addr == FAULT_MME2 || */
+ fi_addr == FAULT_MME3 ||
+ fi_addr == FAULT_MME4 ||
+ fi_addr == FAULT_DRL ||
+ fi_addr == FAULT_DIV ||
+ fi_addr == FAULT_OFL ||
+ fi_addr == FAULT_IPR)
+ {
+ sim_debug (DBG_FAULT, & cpu_dev, "RCU MMEx sync fault return\n");
+ cpu.cu.rfi = 0;
+ longjmp (cpu.jmpMain, JMP_SYNC_FAULT_RETURN);
+ }
+#endif
+
+
+
+
+
+
+ // LUF can happen during fetch or CAF. If fetch, handled above
+ if (fi_addr == FAULT_LUF)
+ {
+ cpu.cu.rfi = 1;
+ sim_debug (DBG_FAULT, & cpu_dev, "RCU LUF RESTART return\n");
+ longjmp (cpu.jmpMain, JMP_RESTART);
+ }
+
+ if (fi_addr == FAULT_DF0 ||
+ fi_addr == FAULT_DF1 ||
+ fi_addr == FAULT_DF2 ||
+ fi_addr == FAULT_DF3 ||
+ fi_addr == FAULT_ACV ||
+ fi_addr == FAULT_F1 ||
+ fi_addr == FAULT_F2 ||
+ fi_addr == FAULT_F3 ||
+ fi_addr == FAULT_CMD ||
+ fi_addr == FAULT_EXF)
+ {
+ // If the fault occurred during fetch, handled above.
+ cpu.cu.rfi = 1;
+ sim_debug (DBG_FAULT, & cpu_dev, "RCU ACV RESTART return\n");
+ longjmp (cpu.jmpMain, JMP_RESTART);
+ }
+ sim_printf ("doRCU dies with unhandled fault number %d\n", fi_addr);
+ doFault (FAULT_TRB,
+ (_fault_subtype) {.bits=fi_addr},
+ "doRCU dies with unhandled fault number");
+ }
+
+
--- /dev/null
+/*
+ Copyright (c) 2007-2013 Michael Mondy
+ Copyright 2012-2016 by Harry Reed
+ Copyright 2013-2016 by Charles Anthony
+
+ All rights reserved.
+
+ This software is made available under the terms of the
+ ICU License -- ICU 1.8.1 and later.
+ See the LICENSE file at the top-level directory of this distribution and
+ at https://sourceforge.net/p/dps8m/code/ci/master/tree/LICENSE
+ */
+
+void tidy_cu (void);
+void cu_safe_store(void);
+#ifdef MATRIX
+void initializeTheMatrix (void);
+void addToTheMatrix (uint32 opcode, bool opcodeX, bool a, word6 tag);
+t_stat display_the_matrix (int32 arg, const char * buf);
+#endif
+t_stat prepareComputedAddress (void); // new
+void cu_safe_restore(void);
+void fetchInstruction(word18 addr);
+t_stat executeInstruction (void);
+void doRCU (void) NO_RETURN;
+void traceInstruction (uint flag);
+bool tstOVFfault (void);
+bool chkOVF (void);
--- /dev/null
+/*
+ Copyright (c) 2007-2013 Michael Mondy
+ Copyright 2012-2016 by Harry Reed
+ Copyright 2013-2016 by Charles Anthony
+ Copyright 2016 by Michal Tomek
+
+ All rights reserved.
+
+ This software is made available under the terms of the
+ ICU License -- ICU 1.8.1 and later.
+ See the LICENSE file at the top-level directory of this distribution and
+ at https://sourceforge.net/p/dps8m/code/ci/master/tree/LICENSE
+ */
+
+/**
+ * \file dps8_math.c
+ * \project dps8
+ * \date 12/6/12
+ * \copyright Copyright (c) 2012 Harry Reed. All rights reserved.
+ * \brief stuff related to math routines for the dps8 simulator
+ * * floating-point routines
+ * * fixed-point routines (eventually)
+*/
+
+#include <stdio.h>
+#include <math.h>
+
+#include "dps8.h"
+#include "dps8_sys.h"
+#include "dps8_faults.h"
+//#include "dps8_scu.h"
+//#include "dps8_iom.h"
+//#include "dps8_cable.h"
+#include "dps8_cpu.h"
+#include "dps8_ins.h"
+#include "dps8_math.h"
+#include "dps8_utils.h"
+
+#define DBG_CTR cpu.cycleCnt
+
+#ifdef __CYGWIN__
+long double ldexpl(long double x, int n) {
+ return __builtin_ldexpl(x, n);
+}
+
+long double exp2l (long double e) {
+ return __builtin_exp2l(e);
+}
+#endif
+
+//! floating-point stuff ....
+//! quad to octal
+//char *Qtoo(__uint128_t n128);
+
+/*!
+ * convert floating point quantity in C(EAQ) to a IEEE long double ...
+ */
+long double EAQToIEEElongdouble(void)
+{
+ // mantissa
+ word72 Mant = convert_to_word72 (cpu.rA, cpu.rQ);
+#ifdef NEED_128
+
+ if (iszero_128 (Mant))
+ return 0;
+
+ bool S = isnonzero_128 (and_128 (Mant, SIGN72)); // sign of mantissa
+ if (S)
+ Mant = and_128 (negate_128 (Mant), MASK71); // 71 bits (not 72!)
+#else
+
+ if (Mant == 0)
+ return 0;
+
+ bool S = Mant & SIGN72; // sign of mantissa
+ if (S)
+ Mant = (-Mant) & MASK71; // 71 bits (not 72!)
+#endif
+ long double m = 0; // mantissa value;
+ int e = SIGNEXT8_int (cpu . rE & MASK8); // make signed
+
+#ifdef NEED_128
+ if (S && iszero_128 (Mant))// denormalized -1.0*2^e
+ return -exp2l(e);
+#else
+ if (S && Mant == 0) // denormalized -1.0*2^e
+ return -exp2l(e);
+#endif
+
+ long double v = 0.5;
+ for(int n = 70 ; n >= 0 ; n -= 1) // this also normalizes the mantissa
+ {
+#ifdef NEED_128
+ if (isnonzero_128 (and_128 (Mant, lshift_128 (construct_128 (0, 1), (unsigned int) n))))
+ {
+ m += v;
+ }
+#else
+ if (Mant & ((word72)1 << n))
+ {
+ m += v;
+ }
+#endif
+ v /= 2.0;
+ }
+
+ /*if (m == 0 && e == -128) // special case - normalized 0
+ return 0;
+ if (m == 0)
+ return (S ? -1 : 1) * exp2l(e); */
+
+ return (S ? -1 : 1) * ldexpl(m, e);
+}
+
+// MINGW doesn't have long double support, convert to IEEE double instead
+double EAQToIEEEdouble(void)
+{
+ // mantissa
+ word72 Mant = convert_to_word72 (cpu.rA, cpu.rQ);
+
+#ifdef NEED_128
+ if (iszero_128 (Mant))
+ return 0;
+
+ bool S = isnonzero_128 (and_128 (Mant, SIGN72)); // sign of mantissa
+ if (S)
+ Mant = and_128 (negate_128 (Mant), MASK71); // 71 bits (not 72!)
+#else
+ if (Mant == 0)
+ return 0;
+
+ bool S = Mant & SIGN72; // sign of mantissa
+ if (S)
+ Mant = (-Mant) & MASK71; // 71 bits (not 72!)
+#endif
+
+ double m = 0; // mantissa value
+ int e = SIGNEXT8_int (cpu . rE & MASK8); // make signed
+
+#ifdef NEED_128
+ if (S && iszero_128 (Mant))// denormalized -1.0*2^e
+ return -exp2(e);
+#else
+ if (S && Mant == 0) // denormalized -1.0*2^e
+ return -exp2(e);
+#endif
+ double v = 0.5;
+ for(int n = 70 ; n >= 0 ; n -= 1) // this also normalizes the mantissa
+ {
+#ifdef NEED_128
+ if (isnonzero_128 (and_128 (Mant, lshift_128 (construct_128 (0, 1), (unsigned int) n))))
+ {
+ m += v;
+ }
+#else
+ if (Mant & ((word72)1 << n))
+ {
+ m += v;
+ }
+#endif
+ v /= 2.0;
+ }
+
+ return (S ? -1 : 1) * ldexp(m, e);
+}
+
+#ifndef QUIET_UNUSED
+/*!
+ * return normalized dps8 representation of IEEE double f0 ...
+ */
+float72 IEEElongdoubleToFloat72(long double f0)
+{
+ if (f0 == 0)
+ return (float72)((float72)0400000000000LL << 36);
+
+ bool sign = f0 < 0 ? true : false;
+ long double f = fabsl(f0);
+
+ int exp;
+ long double mant = frexpl(f, &exp);
+ //sim_printf (sign=%d f0=%Lf mant=%Lf exp=%d\n", sign, f0, mant, exp);
+
+ word72 result = 0;
+
+ // now let's examine the mantissa and assign bits as necessary...
+
+ if (sign && mant == 0.5)
+ {
+ //result = bitfieldInsert72(result, 1, 63, 1);
+ putbits72 (& result, 71-62, 1, 1);
+ exp -= 1;
+ mant -= 0.5;
+ }
+
+ long double bitval = 0.5; ///< start at 1/2 and go down .....
+ for(int n = 62 ; n >= 0 && mant > 0; n -= 1)
+ {
+ if (mant >= bitval)
+ {
+ //result = bitfieldInsert72(result, 1, n, 1);
+ putbits72 (& result, 71-n, 1, 1);
+ mant -= bitval;
+ //sim_printf ("Inserting a bit @ %d %012"PRIo64" %012"PRIo64"\n", n , (word36)((result >> 36) & DMASK), (word36)(result & DMASK));
+ }
+ bitval /= 2.0;
+ }
+ //sim_printf ("n=%d mant=%f\n", n, mant);
+
+ //sim_printf ("result=%012"PRIo64" %012"PRIo64"\n", (word36)((result >> 36) & DMASK), (word36)(result & DMASK));
+
+ // if f is < 0 then take 2-comp of result ...
+ if (sign)
+ {
+ result = -result & (((word72)1 << 64) - 1);
+ //sim_printf ("-result=%012"PRIo64" %012"PRIo64"\n", (word36)((result >> 36) & DMASK), (word36)(result & DMASK));
+ }
+ //! insert exponent ...
+ int e = (int)exp;
+ //result = bitfieldInsert72(result, e & 0377, 64, 8); ///< & 0777777777777LL;
+ putbits72 (& result, 71-64, 8, e & 0377);
+
+ // XXX TODO test for exp under/overflow ...
+
+ return result;
+}
+#endif
+
+
+#ifndef QUIET_UNUSED
+static long double MYfrexpl(long double x, int *exp)
+{
+ long double exponents[20], *next;
+ int exponent, bit;
+
+ /* Check for zero, nan and infinity. */
+ if (x != x || x + x == x )
+ {
+ *exp = 0;
+ return x;
+ }
+
+ if (x < 0)
+ return -MYfrexpl(-x, exp);
+
+ exponent = 0;
+ if (x > 1.0)
+ {
+ for (next = exponents, exponents[0] = 2.0L, bit = 1;
+ *next <= x + x;
+ bit <<= 1, next[1] = next[0] * next[0], next++);
+
+ for (; next >= exponents; bit >>= 1, next--)
+ if (x + x >= *next)
+ {
+ x /= *next;
+ exponent |= bit;
+ }
+
+ }
+
+ else if (x < 0.5)
+ {
+ for (next = exponents, exponents[0] = 0.5L, bit = 1;
+ *next > x;
+ bit <<= 1, next[1] = next[0] * next[0], next++);
+
+ for (; next >= exponents; bit >>= 1, next--)
+ if (x < *next)
+ {
+ x /= *next;
+ exponent |= bit;
+ }
+
+ exponent = -exponent;
+ }
+
+ *exp = exponent;
+ return x;
+}
+#endif
+
+#ifndef QUIET_UNUSED
+/*!
+ * Store EAQ with normalized dps8 representation of IEEE double f0 ...
+ */
+void IEEElongdoubleToEAQ(long double f0)
+{
+ if (f0 == 0)
+ {
+ cpu . rA = 0;
+ HDBGRegA ();
+ cpu . rQ = 0;
+ cpu . rE = 0200U; /*-128*/
+ return;
+ }
+
+ bool sign = f0 < 0 ? true : false;
+ long double f = fabsl(f0);
+
+ int exp;
+ long double mant = MYfrexpl(f, &exp);
+
+ word72 result = 0;
+
+ // now let's examine the mantissa and assign bits as necessary...
+ if (sign && mant == 0.5)
+ {
+ //result = bitfieldInsert72(result, 1, 63, 1);
+ result = putbits72 (& result, 71-63, 1, 1);
+ exp -= 1;
+ mant -= 0.5;
+ }
+
+ long double bitval = 0.5; ///< start at 1/2 and go down .....
+ for(int n = 70 ; n >= 0 && mant > 0; n -= 1)
+ {
+ if (mant >= bitval)
+ {
+ //result = bitfieldInsert72(result, 1, n, 1);
+ putbits72 (& result 71-n, 1, 1);
+ mant -= bitval;
+ //sim_printf ("Inserting a bit @ %d %012"PRIo64" %012"PRIo64"\n", n , (word36)((result >> 36) & DMASK), (word36)(result & DMASK));
+ }
+ bitval /= 2.0;
+ }
+
+ // if f is < 0 then take 2-comp of result ...
+ if (sign)
+ result = -result & (((word72)1 << 72) - 1);
+
+ cpu . rE = exp & MASK8;
+ cpu . rA = (result >> 36) & MASK36;
+ HDBGRegA ();
+ cpu . rQ = result & MASK36;
+}
+#endif
+
+//#ifndef QUIET_UNUSED
+#ifdef ISOLTS
+/*!
+ * return IEEE double version dps8 single-precision number ...
+ */
+static double float36ToIEEEdouble(word36 f36)
+{
+ unsigned char E; ///< exponent
+ uint64 Mant; ///< mantissa
+ E = (f36 >> 28) & 0xff;
+ Mant = f36 & 01777777777LL;
+ if (Mant == 0)
+ return 0;
+
+ bool S = Mant & 01000000000LL; ///< sign of mantissa
+ if (S)
+ Mant = (-Mant) & 0777777777; // 27 bits (not 28!)
+
+ double m = 0; ///< mantissa value;
+ int e = (char)E; ///< make signed
+
+ if (S && Mant == 0) // denormalized -1.0*2^e
+ return -exp2(e);
+
+ double v = 0.5;
+ for(int n = 26 ; n >= 0 ; n -= 1) // this also normalizes the mantissa
+ {
+ if (Mant & ((uint64)1 << n))
+ {
+ m += v;
+ } //else
+ v /= 2.0;
+ }
+
+ return (S ? -1 : 1) * ldexp(m, e);
+}
+//#endif
+#endif
+
+#ifndef QUIET_UNUSED
+/*!
+ * return normalized dps8 representation of IEEE double f0 ...
+ */
+float36 IEEEdoubleTofloat36(double f0)
+{
+ if (f0 == 0)
+ return 0400000000000LL;
+
+ double f = f0;
+ bool sign = f0 < 0 ? true : false;
+ if (sign)
+ f = fabs(f0);
+
+ int exp;
+ double mant = frexp(f, &exp);
+
+ //sim_printf (sign=%d f0=%f mant=%f exp=%d\n", sign, f0, mant, exp);
+
+ word36 result = 0;
+
+ // now let's examine the mantissa and assign bits as necessary...
+
+ double bitval = 0.5; ///< start at 1/2 and go down .....
+ for(int n = 26 ; n >= 0 && mant > 0; n -= 1)
+ {
+ if (mant >= bitval)
+ {
+ //result = bitfieldInsert36(result, 1, n, 1);
+ setbits36_1 (& result, 35-n, 1);
+ mant -= bitval;
+ //sim_printf ("Inserting a bit @ %d result=%012"PRIo64"\n", n, result);
+ }
+ bitval /= 2.0;
+ }
+ //sim_printf ("result=%012"PRIo64"\n", result);
+
+ // if f is < 0 then take 2-comp of result ...
+ if (sign)
+ {
+ result = -result & 001777777777LL;
+ //sim_printf ("-result=%012"PRIo64"\n", result);
+ }
+ // insert exponent ...
+ int e = (int)exp;
+ //result = bitfieldInsert36(result, e, 28, 8) & 0777777777777LL;
+ putbits36_8 (& result, 0, e);
+
+ // XXX TODO test for exp under/overflow ...
+
+ return result;
+}
+#endif
+
+/*
+ * single-precision arithmetic routines ...
+ */
+
+
+#ifdef HEX_MODE
+//#define HEX_SIGN (SIGN72 | BIT71 | BIT70 | BIT69 | BIT68)
+//#define HEX_MSB ( BIT71 | BIT70 | BIT69 | BIT68)
+#ifdef NEED_128
+#define HEX_SIGN construct_128 (0xF0, 0)
+#define HEX_MSB consturct_128 (0x70, 0)
+#define HEX_NORM construct_128 (0x78, 0)
+#else
+#define HEX_SIGN (SIGN72 | BIT71 | BIT70 | BIT69)
+#define HEX_MSB ( BIT71 | BIT70 | BIT69)
+#define HEX_NORM ( BIT71 | BIT70 | BIT69 | BIT68)
+#endif
+
+static inline bool isHex (void)
+ {
+ return (!!cpu.MR.hexfp) && (!!TST_I_HEX);
+ }
+#endif
+
+/*!
+ * unnormalized floating single-precision add
+ */
+void ufa (bool sub)
+{
+ // C(EAQ) + C(Y) → C(EAQ)
+ // The ufa instruction is executed as follows:
+ //
+ // The mantissas are aligned by shifting the mantissa of the operand having
+ // the algebraically smaller exponent to the right the number of places
+ // equal to the absolute value of the difference in the two exponents. Bits
+ // shifted beyond the bit position equivalent to AQ71 are lost.
+ //
+ // The algebraically larger exponent replaces C(E). The sum of the
+ // mantissas replaces C(AQ).
+ //
+ // If an overflow occurs during addition, then;
+ // * C(AQ) are shifted one place to the right.
+ // * C(AQ)0 is inverted to restore the sign.
+ // * C(E) is increased by one.
+
+ CPTUR (cptUseE);
+#ifdef HEX_MODE
+ uint shift_amt = isHex() ? 4 : 1;
+ //uint shift_msk = isHex() ? 017 : 1;
+ //word72 sign_msk = isHex() ? HEX_SIGN : SIGN72;
+ //word72 sign_msb = isHex() ? HEX_MSB : BIT71;
+#endif
+ word72 m1 = convert_to_word72 (cpu.rA, cpu.rQ);
+#ifdef NEED_128
+ // 28-bit mantissa (incl sign)
+ word72 m2 = lshift_128 (construct_128 (0, (uint64_t) getbits36_28 (cpu.CY, 8)), 44u); // 28-bit mantissa (incl sign)
+#else
+ // 28-bit mantissa (incl sign)
+ word72 m2 = ((word72) getbits36_28 (cpu.CY, 8)) << 44; // 28-bit mantissa (incl sign)
+#endif
+
+ int e1 = SIGNEXT8_int (cpu . rE & MASK8);
+ int e2 = SIGNEXT8_int (getbits36_8 (cpu.CY, 0));
+
+ // RJ78: The two's complement of the subtrahend is first taken and the
+ // smaller value is then right-shifted to equalize it (i.e. ufa).
+
+ int m2zero = 0;
+ if (sub) {
+ // ISOLTS-735 08i asserts no carry for (-1.0*2^96)-(-1.0*2^2) but 08g
+ // asserts carry for -0.5079365*2^78-0.0
+ // I assume zero subtrahend is handled in a special way.
+
+#ifdef NEED_128
+ if (iszero_128 (m2))
+ m2zero = 1;
+ if (iseq_128 (m2, SIGN72)) { // -1.0 -> 0.5, increase exponent, ISOLTS-735 08i,j
+#ifdef HEX_MODE
+ m2 = rshift_128 (m2, shift_amt);
+ e2 += 1;
+#else // ! HEX_MODE
+ m2 = rshift_128 (m2, 1);
+ e2 += 1;
+#endif // ! HEX_MODE
+ } else
+ m2 = and_128 (negate_128 (m2), MASK72);
+ }
+#else // ! NEED_128
+ if (m2 == 0)
+ m2zero = 1;
+ if (m2 == SIGN72) { // -1.0 -> 0.5, increase exponent, ISOLTS-735 08i,j
+#ifdef HEX_MODE
+ m2 >>= shift_amt;
+ e2 += 1;
+#else // ! HEX_MODE
+ m2 >>= 1;
+ e2 += 1;
+#endif // ! HEX_MODE
+ } else
+ m2 = (-m2) & MASK72;
+ }
+
+#endif // ! NEED_128
+
+ int e3 = -1;
+
+ // which exponent is smaller?
+#ifdef L68
+ cpu.ou.cycle |= ou_GOE;
+#endif
+ int shift_count = -1;
+ word1 allones = 1;
+ word1 notallzeros = 0;
+ //word1 last = 0;
+ if (e1 == e2)
+ {
+ shift_count = 0;
+ e3 = e1;
+ }
+ else if (e1 < e2)
+ {
+#ifdef HEX_MODE
+ shift_count = abs(e2 - e1) * (int) shift_amt;
+#else
+ shift_count = abs(e2 - e1);
+#endif
+#ifdef NEED_128
+ bool sign = isnonzero_128 (and_128 (m1, SIGN72)); // mantissa negative?
+ for(int n = 0 ; n < shift_count ; n += 1)
+ {
+ //last = m1.l & 1;
+ allones &= m1.l & 1;
+ notallzeros |= m1.l & 1;
+ m1 = rshift_128 (m1, 1);
+ if (sign)
+ m1 = or_128 (m1, SIGN72);
+ }
+#ifdef HEX_MODE
+ if (iseq_128 (m1, MASK72) && notallzeros == 1 && shift_count * (int) shift_amt > 71)
+ m1 = construct_128 (0, 0);
+#else
+ if (iseq_128 (m1, MASK72) && notallzeros == 1 && shift_count > 71)
+ m1 = construct_128 (0, 0);
+#endif
+ m1 = and_128 (m1, MASK72);
+ e3 = e2;
+#else
+ bool sign = m1 & SIGN72; // mantissa negative?
+ for(int n = 0 ; n < shift_count ; n += 1)
+ {
+ //last = m1 & 1;
+ allones &= m1 & 1;
+ notallzeros |= m1 & 1;
+ m1 >>= 1;
+ if (sign)
+ m1 |= SIGN72;
+ }
+
+#ifdef HEX_MODE
+ if (m1 == MASK72 && notallzeros == 1 && shift_count * (int) shift_amt > 71)
+ m1 = 0;
+#else
+ if (m1 == MASK72 && notallzeros == 1 && shift_count > 71)
+ m1 = 0;
+#endif
+ m1 &= MASK72;
+ e3 = e2;
+#endif // NEED_128
+ }
+ else
+ {
+ // e2 < e1;
+#ifdef HEX_MODE
+ shift_count = abs(e1 - e2) * (int) shift_amt;
+#else
+ shift_count = abs(e1 - e2);
+#endif
+#ifdef NEED_128
+ bool sign = isnonzero_128 (and_128 (m2, SIGN72)); // mantissa negative?
+ for(int n = 0 ; n < shift_count ; n += 1)
+ {
+ //last = m2.l & 1;
+ allones &= m2.l & 1;
+ notallzeros |= m2.l & 1;
+ m2 = rshift_128 (m2, 1);
+ if (sign)
+ m2 = or_128 (m2, SIGN72);
+ }
+#ifdef HEX_MODE
+ if (iseq_128 (m2, MASK72) && notallzeros == 1 && shift_count * (int) shift_amt > 71)
+ m2 = construct_128 (0, 0);
+#else
+ if (iseq_128 (m2, MASK72) && notallzeros == 1 && shift_count > 71)
+ m2 = construct_128 (0, 0);
+#endif
+ m2 = and_128 (m2, MASK72);
+ e3 = e1;
+#else
+ bool sign = m2 & SIGN72; // mantissa negative?
+ for(int n = 0 ; n < shift_count ; n += 1)
+ {
+ //last = m2 & 1;
+ allones &= m2 & 1;
+ notallzeros |= m2 & 1;
+ m2 >>= 1;
+ if (sign)
+ m2 |= SIGN72;
+ }
+#ifdef HEX_MODE
+ if (m2 == MASK72 && notallzeros == 1 && shift_count * (int) shift_amt > 71)
+ m2 = 0;
+#else
+ if (m2 == MASK72 && notallzeros == 1 && shift_count > 71)
+ m2 = 0;
+#endif
+ m2 &= MASK72;
+ e3 = e1;
+#endif // NEED_128
+ }
+
+ bool ovf;
+ word72 m3;
+ m3 = Add72b (m1, m2, 0, I_CARRY, & cpu.cu.IR, & ovf);
+ // ISOLTS-735 08g
+ // if 2's complement carried, OR it in now.
+ if (m2zero)
+ SET_I_CARRY;
+
+ if (ovf)
+ {
+#ifdef NEED_128
+#ifdef HEX_MODE
+// word72 signbit = m3 & sign_msk;
+// m3 >>= shift_amt;
+// m3 = (m3 & MASK71) | signbit;
+// m3 ^= SIGN72; // C(AQ)0 is inverted to restore the sign
+// e3 += 1;
+ word72 s = and_128 (m3, SIGN72); // save the sign bit
+ if (isHex ())
+ {
+ m3 = rshift_128 (m3, shift_amt); // renormalize the mantissa
+ if (isnonzero_128 (s))
+ // Sign is set, number should be positive; clear the sign bit and the 3 MSBs
+ m3 = and_128 (m3, MASK68);
+ else
+ // Sign is clr, number should be negative; set the sign bit and the 3 MSBs
+ m3 = or_128 (m3, HEX_SIGN);
+ }
+ else
+ {
+ word72 signbit = and_128 (m3, SIGN72);
+ m3 = rshift_128 (m3, 1);
+ m3 = and_128 (m3, MASK71);
+ m3 = or_128 (m3, signbit);
+ m3 = xor_128 (m3, SIGN72); // C(AQ)0 is inverted to restore the sign
+ }
+ e3 += 1;
+#else
+ word72 signbit = and_128 (m3, SIGN72);
+ m3 = rshift_128 (m3, 1);
+ m3 = and_128 (m3, MASK71);
+ m3 = or_128 (m3, signbit);
+ m3 = xor_128 (m3, SIGN72); // C(AQ)0 is inverted to restore the sign
+ e3 += 1;
+#endif
+#else // NEED_128
+#ifdef HEX_MODE
+// word72 signbit = m3 & sign_msk;
+// m3 >>= shift_amt;
+// m3 = (m3 & MASK71) | signbit;
+// m3 ^= SIGN72; // C(AQ)0 is inverted to restore the sign
+// e3 += 1;
+ word72 s = m3 & SIGN72; // save the sign bit
+ if (isHex ())
+ {
+ m3 >>= shift_amt; // renormalize the mantissa
+ if (s)
+ // Sign is set, number should be positive; clear the sign bit and the 3 MSBs
+ m3 &= MASK68;
+ else
+ // Sign is clr, number should be negative; set the sign bit and the 3 MSBs
+ m3 |= HEX_SIGN;
+ }
+ else
+ {
+ word72 signbit = m3 & SIGN72;
+ m3 >>= 1;
+ m3 = (m3 & MASK71) | signbit;
+ m3 ^= SIGN72; // C(AQ)0 is inverted to restore the sign
+ }
+ e3 += 1;
+#else
+ word72 signbit = m3 & SIGN72;
+ m3 >>= 1;
+ m3 = (m3 & MASK71) | signbit;
+ m3 ^= SIGN72; // C(AQ)0 is inverted to restore the sign
+ e3 += 1;
+#endif
+#endif // NEED_128
+ }
+
+ convert_to_word36 (m3, & cpu.rA, & cpu.rQ);
+ HDBGRegA ();
+ cpu . rE = e3 & 0377;
+
+ SC_I_NEG (cpu.rA & SIGN36); // Do this here instead of in Add72b because
+ // of ovf handling above
+ if (cpu.rA == 0 && cpu.rQ == 0)
+ {
+ SET_I_ZERO;
+ cpu . rE = 0200U; /*-128*/
+ }
+ else
+ {
+ CLR_I_ZERO;
+ }
+
+ // EOFL: If exponent is greater than +127, then ON
+ if (e3 > 127)
+ {
+ SET_I_EOFL;
+ if (tstOVFfault ())
+ dlyDoFault (FAULT_OFL, fst_zero, "ufa exp overflow fault");
+ }
+
+ // EUFL: If exponent is less than -128, then ON
+ if(e3 < -128)
+ {
+ SET_I_EUFL;
+ if (tstOVFfault ())
+ dlyDoFault (FAULT_OFL, fst_zero, "ufa exp underflow fault");
+ }
+}
+
+
+/*
+ * floating normalize ...
+ */
+
+void fno (word8 * E, word36 * A, word36 * Q)
+{
+ // The fno instruction normalizes the number in C(EAQ) if C(AQ) ≠ 0 and the
+ // overflow indicator is OFF.
+ //
+ // A normalized floating number is defined as one whose mantissa lies in
+ // the interval [0.5,1.0) such that
+ // 0.5<= |C(AQ)| <1.0 which, in turn, requires that C(AQ)0 ≠ C(AQ)1.
+ //
+ // If the overflow indicator is ON, then C(AQ) is shifted one place to the
+ // right, C(AQ)0 is inverted to reconstitute the actual sign, and the
+ // overflow indicator is set OFF. This action makes the fno instruction
+ // useful in correcting overflows that occur with fixed point numbers.
+ //
+ // Normalization is performed by shifting C(AQ)1,71 one place to the left
+ // and reducing C(E) by 1, repeatedly, until the conditions for C(AQ)0 and
+ // C(AQ)1 are met. Bits shifted out of AQ1 are lost.
+ //
+ // If C(AQ) = 0, then C(E) is set to -128 and the zero indicator is set ON.
+
+#ifdef L68
+ cpu.ou.cycle |= ou_GON;
+#endif
+#ifdef HEX_MODE
+ uint shift_amt = isHex() ? 4 : 1;
+ //uint shift_msk = isHex() ? 017 : 1;
+ //word72 sign_msk = isHex() ? HEX_SIGN : SIGN72;
+ //word72 sign_msb = isHex() ? HEX_NORM : BIT71;
+#endif
+ *A &= DMASK;
+ *Q &= DMASK;
+ float72 m = convert_to_word72 (* A, * Q);
+ if (TST_I_OFLOW)
+ {
+ CLR_I_OFLOW;
+#ifdef NEED_128
+ word72 s = and_128 (m, SIGN72); // save the sign bit
+#ifdef HEX_MODE
+ if (isHex ())
+ {
+ m = rshift_128 (m, shift_amt); // renormalize the mantissa
+ if (isnonzero_128 (s)) // sign of mantissa
+ // Sign is set, number should be positive; clear the sign bit and the 3 MSBs
+ m = and_128 (m, MASK68);
+ else
+ // Sign is clr, number should be negative; set the sign bit and the 3 MSBs
+ m = or_128 (m, HEX_SIGN);
+ }
+ else
+ {
+ m = rshift_128 (m, 1); // renormalize the mantissa
+ m = or_128 (m, SIGN72); // set the sign bit
+ m = xor_128 (m, s); // if the was 0, leave it 1; if it was 1, make it 0
+ }
+#else
+ m = rshift_128 (m, 1); // renormalize the mantissa
+ m = or_128 (m, SIGN72); // set the sign bit
+ m = xor_128 (m, s); // if the was 0, leave it 1; if it was 1, make it 0
+#endif
+
+ // Zero: If C(AQ) = floating point 0, then ON; otherwise OFF
+ if (iszero_128 (m))
+ {
+ *E = 0200U; /*-128*/
+ SET_I_ZERO;
+ }
+ else
+ {
+ CLR_I_ZERO;
+ if (*E == 127)
+ {
+ SET_I_EOFL;
+ if (tstOVFfault ())
+ dlyDoFault (FAULT_OFL, fst_zero, "fno exp overflow fault");
+ }
+ (*E) ++;
+ *E &= MASK8;
+ }
+#else
+ word72 s = m & SIGN72; // save the sign bit
+#ifdef HEX_MODE
+ if (isHex ())
+ {
+ m >>= shift_amt; // renormalize the mantissa
+ if (s)
+ // Sign is set, number should be positive; clear the sign bit and the 3 MSBs
+ m &= MASK68;
+ else
+ // Sign is clr, number should be negative; set the sign bit and the 3 MSBs
+ m |= HEX_SIGN;
+ }
+ else
+ {
+ m >>= 1; // renormalize the mantissa
+ m |= SIGN72; // set the sign bit
+ m ^= s; // if the was 0, leave it 1; if it was 1, make it 0
+ }
+#else
+ m >>= 1; // renormalize the mantissa
+ m |= SIGN72; // set the sign bit
+ m ^= s; // if the was 0, leave it 1; if it was 1, make it 0
+#endif
+
+ // Zero: If C(AQ) = floating point 0, then ON; otherwise OFF
+ if (m == 0)
+ {
+ *E = 0200U; /*-128*/
+ SET_I_ZERO;
+ }
+ else
+ {
+ CLR_I_ZERO;
+ if (*E == 127)
+ {
+ SET_I_EOFL;
+ if (tstOVFfault ())
+ dlyDoFault (FAULT_OFL, fst_zero, "fno exp overflow fault");
+ }
+ (*E) ++;
+ *E &= MASK8;
+ }
+#endif // NEED_128
+ convert_to_word36 (m, A, Q);
+ SC_I_NEG ((*A) & SIGN36);
+
+ return;
+ }
+
+ // only normalize C(EAQ) if C(AQ) ≠ 0 and the overflow indicator is OFF
+#ifdef NEED_128
+ if (iszero_128 (m)) /// C(AQ) == 0.
+#else
+ if (m == 0) // C(AQ) == 0.
+#endif
+ {
+ //*A = (m >> 36) & MASK36;
+ //*Q = m & MASK36;
+ *E = 0200U; /*-128*/
+ SET_I_ZERO;
+ CLR_I_NEG;
+ return;
+ }
+
+ int e = SIGNEXT8_int ((*E) & MASK8);
+#ifdef NEED_128
+ bool s = isnonzero_128 (and_128 (m, SIGN72));
+#else
+ bool s = (m & SIGN72) != (word72)0; ///< save sign bit
+#endif
+
+#ifdef HEX_MODE
+// Normalized in Hex Mode: If sign is 0, bits 1-4 != 0; if sign is 1,
+// bits 1-4 != 017.
+ if (isHex ())
+ {
+ if (s)
+ {
+ // Negative
+ // Until bits 1-4 != 014
+ // Termination guarantee: Zeros are being shifted into the right
+ // end, so the loop will terminate when the first shifted
+ // zero enters bits 1-4.
+#ifdef NEED_128
+ while (iseq_128 (and_128 (m, HEX_NORM), HEX_NORM))
+ {
+//if (m == 0) // XXX: necessary??
+// break;
+ m = lshift_128 (m, 4);
+ e -= 1;
+ }
+ m = and_128 (m, MASK71);
+ m = or_128 (m, SIGN72);
+#else
+ while ((m & HEX_NORM) == HEX_NORM)
+ {
+//if (m == 0) // XXX: necessary??
+// break;
+ m <<= 4;
+ e -= 1;
+ }
+ m &= MASK71;
+ m |= SIGN72;
+#endif
+ }
+ else
+ {
+#ifdef NEED_128
+ // Positive
+ // Until bits 1-4 != 0
+ // Termination guarantee: m is known to be non-zero; a non-zero
+ // bit will eventually be shifted into bits 1-4.
+ while (iszero_128 (and_128 (m, HEX_NORM)))
+ {
+ m = lshift_128 (m, 4);
+ e -= 1;
+ }
+ m = and_128 (m, MASK71);
+#else
+ // Positive
+ // Until bits 1-4 != 0
+ // Termination guarantee: m is known to be non-zero; a non-zero
+ // bit will eventually be shifted into bits 1-4.
+ while ((m & HEX_NORM) == 0)
+ {
+ m <<= 4;
+ e -= 1;
+ }
+ m &= MASK71;
+#endif
+ }
+ }
+ else
+ {
+#ifdef NEED_128
+ while (s == isnonzero_128 (and_128 (m, BIT71))) // until C(AQ)0 != C(AQ)1?
+ {
+ m = lshift_128 (m, 1);
+ e -= 1;
+ //if (m == 0) // XXX: necessary??
+ // break;
+ }
+
+ m = and_128 (m, MASK71);
+
+ if (s)
+ m = or_128 (m, SIGN72);
+#else
+ while (s == !! (m & BIT71)) // until C(AQ)0 != C(AQ)1?
+ {
+ m <<= 1;
+ e -= 1;
+ //if (m == 0) // XXX: necessary??
+ // break;
+ }
+
+ m &= MASK71;
+
+ if (s)
+ m |= SIGN72;
+#endif
+ }
+#else
+#ifdef NEED_128
+ while (s == isnonzero_128 (and_128 (m, BIT71))) // until C(AQ)0 != C(AQ)1?
+ {
+ m = lshift_128 (m, 1);
+ e -= 1;
+ //if (m == 0) // XXX: necessary??
+ // break;
+ }
+
+ m = and_128 (m, MASK71);
+
+ if (s)
+ m = or_128 (m, SIGN72);
+#else
+ while (s == !! (m & BIT71)) // until C(AQ)0 != C(AQ)1?
+ {
+ m <<= 1;
+ e -= 1;
+ //if (m == 0) // XXX: necessary??
+ // break;
+ }
+
+ m &= MASK71;
+
+ if (s)
+ m |= SIGN72;
+#endif
+#endif
+
+ if (e < -128)
+ {
+ SET_I_EUFL;
+ if (tstOVFfault ())
+ dlyDoFault (FAULT_OFL, fst_zero, "fno exp underflow fault");
+ }
+
+ *E = (word8) e & MASK8;
+ convert_to_word36 (m, A, Q);
+
+ // EAQ is normalized, so if A is 0, so is Q, and the check can be elided
+ if (*A == 0) // set to normalized 0
+ *E = 0200U; /*-128*/
+
+ // Zero: If C(AQ) = floating point 0, then ON; otherwise OFF
+ SC_I_ZERO (*A == 0 && *Q == 0);
+
+ // Neg: If C(AQ)0 = 1, then ON; otherwise OFF
+ SC_I_NEG ((*A) & SIGN36);
+}
+
+#if 0
+// XXX eventually replace fno() with fnoEAQ()
+void fnoEAQ(word8 *E, word36 *A, word36 *Q)
+{
+ //! The fno instruction normalizes the number in C(EAQ) if C(AQ) ≠ 0 and the overflow indicator is OFF.
+ //! A normalized floating number is defined as one whose mantissa lies in the interval [0.5,1.0) such that
+ //! 0.5<= |C(AQ)| <1.0 which, in turn, requires that C(AQ)0 ≠ C(AQ)1.
+ //! If the overflow indicator is ON, then C(AQ) is shifted one place to the right, C(AQ)0 is inverted to reconstitute the actual sign, and the overflow indicator is set OFF. This action makes the fno instruction useful in correcting overflows that occur with fixed point numbers.
+ //! Normalization is performed by shifting C(AQ)1,71 one place to the left and reducing C(E) by 1, repeatedly, until the conditions for C(AQ)0 and C(AQ)1 are met. Bits shifted out of AQ1 are lost.
+ //! If C(AQ) = 0, then C(E) is set to -128 and the zero indicator is set ON.
+
+ float72 m = ((word72)*A << 36) | (word72)*Q;
+ if (TST_I_OFLOW)
+ {
+ m >>= 1;
+ m &= MASK72;
+
+ m ^= ((word72)1 << 71);
+
+ CLR_I_OFLOW;
+
+ // Zero: If C(AQ) = floating point 0, then ON; otherwise OFF
+ //SC_I_ZERO (*E == -128 && m == 0);
+ //SC_I_ZERO (*E == 0200U /*-128*/ && m == 0);
+ if (m == 0)
+ {
+ *E = -128;
+ SET_I_ZERO;
+ }
+ // Neg:
+ CLR_I_NEG;
+ return; // XXX: ???
+ }
+
+ // only normalize C(EAQ) if C(AQ) ≠ 0 and the overflow indicator is OFF
+ if (m == 0) // C(AQ) == 0.
+ {
+ *A = (m >> 36) & MASK36;
+ *Q = m & MASK36;
+ *E = 0200U; /*-128*/
+
+ // Zero: If C(AQ) = floating point 0, then ON; otherwise OFF
+ //SC_I_ZERO(*E == -128 && m == 0);
+ //SC_I_ZERO(*E == 0200U /*-128*/ && m == 0);
+ SET_I_ZERO;
+ // Neg:
+ CLR_I_NEG;
+
+ return;
+ }
+ int8 e = (int8)*E;
+
+
+ bool s = m & SIGN72; ///< save sign bit
+ while ((bool)(m & SIGN72) == (bool)(m & (SIGN72 >> 1))) // until C(AQ)0 ≠ C(AQ)1?
+ {
+ m <<= 1;
+ m &= MASK72;
+
+ if (s)
+ m |= SIGN72;
+
+ if ((e - 1) < -128)
+ SET_I_EUFL;
+ else // XXX: my interpretation
+ e -= 1;
+
+ if (m == 0) // XXX: necessary??
+ {
+ *E = (word8)-128;
+ break;
+ }
+ }
+
+ *E = e & 0377;
+ *A = (m >> 36) & MASK36;
+ *Q = m & MASK36;
+
+ // EAQ is normalized, so if A is 0, so is Q, and the check can be elided
+ if (*A == 0) // set to normalized 0
+ *E = (word8)-128;
+
+ // Zero: If C(AQ) = floating point 0, then ON; otherwise OFF
+ SC_I_ZERO (*A == 0);
+
+ // Neg: If C(AQ)0 = 1, then ON; otherwise OFF
+ SC_I_NEG (*A & SIGN36);
+
+}
+#endif
+
+/*
+ * floating negate ...
+ */
+void fneg (void)
+ {
+ // This instruction changes the number in C(EAQ) to its normalized negative
+ // (if C(AQ) ≠ 0). The operation is executed by first forming the twos
+ // complement of C(AQ), and then normalizing C(EAQ).
+ //
+ // Even if originally C(EAQ) were normalized, an exponent overflow can
+ // still occur, namely when C(E) = +127 and C(AQ) = 100...0 which is the
+ // twos complement approximation for the decimal value -1.0.
+
+#if 0
+ float72 m = ((word72)cpu . rA << 36) | (word72)cpu . rQ;
+
+ if (m == 0) // (if C(AQ) ≠ 0)
+ {
+ SET_I_ZERO; // it's zero
+ CLR_I_NEG; // it ain't negative
+ return; //XXX: ????
+ }
+
+ int8 e = (int8)cpu . rE;
+
+ word72 mc = 0;
+ if (m == FLOAT72SIGN)
+ {
+ mc = m >> 1;
+ e += 1;
+ } else
+ mc = ~m + 1; // take 2-comp of mantissa
+
+
+ //mc &= FLOAT72MASK;
+ mc &= ((word72)1 << 72) - 1;
+ /*
+ * When signs are the *same* we have an overflow
+ * (Treat as in addition when we get an overflow)
+ */
+ //bool ov = ((m & FLOAT72SIGN) == (mc & FLOAT72SIGN)); // the "weird" number!
+ bool ov = ((m & ((word72)1 << 71)) == (mc & ((word72)1 << 71))); // the "weird" number!
+// if (ov)
+// bool ov = ((m & 01000000000LL) == (mc & 01000000000LL)); // the "weird" number.
+ if (ov && m != 0)
+ {
+ mc >>= 1;
+ //mc &= FLOAT72MASK;
+ mc &= ((word72)1 << 72) - 1;
+
+ if ((e + 1) > 127)
+ SET_I_EOFL;
+ else // XXX: this is my interpretation
+ e += 1;
+ }
+
+ cpu . rE = e & 0377;
+ cpu . rA = (mc >> 36) & MASK36;
+ cpu . rQ = mc & MASK36;
+#else
+ CPTUR (cptUseE);
+ // Form the mantissa from AQ
+
+ word72 m = convert_to_word72 (cpu.rA, cpu.rQ);
+
+ // If the mantissa is 4000...00 (least negative value, it is negable in
+ // two's complement arithmetic. Divide it by 2, losing a bit of precision,
+ // and increment the exponent.
+#ifdef NEED_128
+ if (iseq_128 (m, SIGN72))
+#else
+ if (m == SIGN72)
+#endif
+ {
+ // Negation of 400..0 / 2 is 200..0; we can get there shifting; we know
+ // that a zero will be shifted into the sign bit becuase fo the masking
+ // in 'm='.
+#ifdef NEED_128
+ m = rshift_128 (m, 1);
+#else
+ m >>= 1;
+#endif
+ // Increment the exp, checking for overflow.
+ if (cpu . rE == 127)
+ {
+ SET_I_EOFL;
+ if (tstOVFfault ())
+ dlyDoFault (FAULT_OFL, fst_zero, "fneg exp overflow fault");
+ }
+ cpu . rE ++;
+ cpu . rE &= MASK8;
+ }
+ else
+ {
+ // Do the negation
+#ifdef NEED_128
+ m = negate_128 (m);
+#else
+ m = -m;
+#endif
+ }
+ convert_to_word36 (m, & cpu.rA, & cpu.rQ);
+#endif
+ fno (& cpu.rE, & cpu.rA, & cpu.rQ); // normalize
+ HDBGRegA ();
+}
+
+/*!
+ * Unnormalized Floating Multiply ...
+ */
+void ufm (void)
+{
+ // The ufm instruction is executed as follows:
+ // C(E) + C(Y)0,7 → C(E)
+ // ( C(AQ) × C(Y)8,35 )0,71 → C(AQ)
+
+ // Zero: If C(AQ) = 0, then ON; otherwise OFF
+ // Neg: If C(AQ)0 = 1, then ON; otherwise OFF
+ // Exp Ovr: If exponent is greater than +127, then ON
+ // Exp Undr: If exponent is less than -128, then ON
+
+ CPTUR (cptUseE);
+ word72 m1 = convert_to_word72 (cpu.rA, cpu.rQ);
+ int e1 = SIGNEXT8_int (cpu . rE & MASK8);
+
+#ifdef NEED_128
+ word72 m2 = lshift_128 (construct_128 (0, (uint64_t) getbits36_28 (cpu.CY, 8)), 44u); // 28-bit mantissa (incl sign)
+#else
+ word72 m2 = ((word72) getbits36_28 (cpu.CY, 8)) << 44; ///< 28-bit mantissa (incl sign)
+#endif
+ int e2 = SIGNEXT8_int (getbits36_8 (cpu.CY, 0));
+
+#ifdef NEED_128
+ if (iszero_128 (m1) || iszero_128 (m2))
+#else
+ if (m1 == 0 || m2 == 0)
+#endif
+ {
+ SET_I_ZERO;
+ CLR_I_NEG;
+
+ cpu . rE = 0200U; /*-128*/
+ cpu . rA = 0;
+ HDBGRegA ();
+ cpu . rQ = 0;
+
+ return; // normalized 0
+ }
+
+ int e3 = e1 + e2;
+
+ if (e3 > 127)
+ {
+ SET_I_EOFL;
+ if (tstOVFfault ())
+ dlyDoFault (FAULT_OFL, fst_zero, "ufm exp overflow fault");
+ }
+ if (e3 < -128)
+ {
+ SET_I_EUFL;
+ if (tstOVFfault ())
+ dlyDoFault (FAULT_OFL, fst_zero, "ufm exp underflow fault");
+ }
+
+ // RJ78: This multiplication is executed in the following way:
+ // C(E) + C(Y)(0-7) -> C(E)
+ // C(AQ) * C(Y)(8-35) results in a 98-bit product plus sign,
+ // the leading 71 bits plus sign of which -> C(AQ).
+
+ // shift the CY mantissa to get 98 bits precision
+#ifdef NEED_128
+ int128 t = SIGNEXT72_128(m2);
+ uint128 ut = rshift_128 (cast_128 (t), 44);
+ int128 m3 = multiply_s128 (SIGNEXT72_128(m1), cast_s128 (ut));
+//sim_debug (DBG_TRACEEXT, & cpu_dev, "m3 %016"PRIx64"%016"PRIx64"\n", m3.h, m3.l);
+#else
+ int128 m3 = (SIGNEXT72_128(m1) * (SIGNEXT72_128(m2) >> 44));
+//sim_debug (DBG_TRACEEXT, & cpu_dev, "m3 %016"PRIx64"%016"PRIx64"\n", (uint64_t) (m3>>64), (uint64_t) m3);
+#endif
+ // realign to 72bits
+#ifdef NEED_128
+ word72 m3a = and_128 (rshift_128 (cast_128 (m3), 98u - 71u), MASK72);
+//sim_debug (DBG_TRACEEXT, & cpu_dev, "m3a %016"PRIx64"%016"PRIx64"\n", m3a.h, m3a.l);
+#else
+ word72 m3a = ((word72) m3 >> (98-71)) & MASK72;
+//sim_debug (DBG_TRACEEXT, & cpu_dev, "m3a %016"PRIx64"%016"PRIx64"\n", (uint64_t) (m3a>>64), (uint64_t) m3a);
+#endif
+
+ // A normalization is performed only in the case of both factor mantissas being 100...0
+ // which is the twos complement approximation to the decimal value -1.0.
+#ifdef NEED_128
+ if (iseq_128 (m1, SIGN72) && iseq_128 (m2, SIGN72))
+#else
+ if ((m1 == SIGN72) && (m2 == SIGN72))
+#endif
+ {
+ if (e3 == 127)
+ {
+ SET_I_EOFL;
+ if (tstOVFfault ())
+ dlyDoFault (FAULT_OFL, fst_zero, "ufm exp overflow fault");
+ }
+#ifdef NEED_128
+ m3a = rshift_128 (m3a, 1);
+#else
+ m3a >>= 1;
+#endif
+ e3 += 1;
+ }
+
+ convert_to_word36 (m3a, & cpu.rA, & cpu.rQ);
+ HDBGRegA ();
+ cpu . rE = (word8) e3 & MASK8;
+sim_debug (DBG_TRACEEXT, & cpu_dev, "fmp A %012"PRIo64" Q %012"PRIo64" E %03o\n", cpu.rA, cpu.rQ, cpu.rE);
+ SC_I_NEG (cpu.rA & SIGN36);
+
+ if (cpu.rA == 0 && cpu.rQ == 0)
+ {
+ SET_I_ZERO;
+ cpu . rE = 0200U; /*-128*/
+ }
+ else
+ {
+ CLR_I_ZERO;
+ }
+}
+
+/*!
+ * floating divide ...
+ */
+// CANFAULT
+static void fdvX(bool bInvert)
+{
+ //! C(EAQ) / C (Y) → C(EA)
+ //! C(Y) / C(EAQ) → C(EA) (Inverted)
+
+ //! 00...0 → C(Q)
+
+ //! The fdv instruction is executed as follows:
+ //! The dividend mantissa C(AQ) is shifted right and the dividend exponent
+ //! C(E) increased accordingly until | C(AQ)0,27 | < | C(Y)8,35 |
+ //! C(E) - C(Y)0,7 → C(E)
+ //! C(AQ) / C(Y)8,35 → C(A)
+ //! 00...0 → C(Q)
+
+ CPTUR (cptUseE);
+#ifdef HEX_MODE
+ uint shift_amt = isHex() ? 4 : 1;
+#endif
+ word72 m1;
+ int e1;
+
+ word72 m2;
+ int e2;
+
+ bool roundovf = 0;
+
+ if (!bInvert)
+ {
+ m1 = convert_to_word72 (cpu.rA, cpu.rQ);
+ e1 = SIGNEXT8_int (cpu . rE & MASK8);
+
+#ifdef NEED_128
+ m2 = lshift_128 (construct_128 (0, (uint64_t) getbits36_28 (cpu.CY, 8)), 44u); // 28-bit mantissa (incl sign)
+#else
+ m2 = ((word72) getbits36_28 (cpu.CY, 8)) << 44; ///< 28-bit mantissa (incl sign)
+#endif
+ e2 = SIGNEXT8_int (getbits36_8 (cpu.CY, 0));
+
+ } else { // invert
+
+ m2 = convert_to_word72 (cpu.rA, cpu.rQ);
+ e2 = SIGNEXT8_int (cpu . rE & MASK8);
+
+ // round divisor per RJ78
+ // If AQ(28-71) is not equal to 0 and A(0) = 0, then 1 is added to AQ(27).
+ // 0 -> AQ(28-71) unconditionally. AQ(0-27) is then used as the divisor mantissa.
+#ifdef NEED_128
+ if ((iszero_128 (and_128 (m2, SIGN72))) &&
+ (isnonzero_128 (and_128 (m2, construct_128 (0, 0377777777777777LL))))) {
+ m2 = add_128 (m2, construct_128 (0, 0400000000000000LL));
+ // I surmise that the divisor is taken as unsigned 28 bits in this case
+ roundovf = 1;
+ }
+ m2 = and_128 (m2, lshift_128 (construct_128 (0, 0777777777400), 36));
+
+ m1 = lshift_128 (construct_128 (0, getbits36_28 (cpu.CY, 8)), 44); ///< 28-bit mantissa (incl sign)
+ e1 = SIGNEXT8_int (getbits36_8 (cpu.CY, 0));
+#else
+ if (!(m2 & SIGN72) && m2 & 0377777777777777LL) {
+ m2 += 0400000000000000LL;
+ // I surmise that the divisor is taken as unsigned 28 bits in this case
+ roundovf = 1;
+ }
+ m2 &= (word72)0777777777400 << 36;
+
+ m1 = ((word72) getbits36_28 (cpu.CY, 8)) << 44; ///< 28-bit mantissa (incl sign)
+ e1 = SIGNEXT8_int (getbits36_8 (cpu.CY, 0));
+#endif
+ }
+
+#ifdef NEED_128
+ if (iszero_128 (m1))
+#else
+ if (m1 == 0)
+#endif
+ {
+ SET_I_ZERO;
+ CLR_I_NEG;
+
+ cpu . rE = 0200U; /*-128*/
+ cpu . rA = 0;
+ HDBGRegA ();
+ cpu . rQ = 0;
+
+ return; // normalized 0
+ }
+
+ // make everything positive, but save sign info for later....
+ int sign = 1;
+#ifdef NEED_128
+ if (isnonzero_128 (and_128 (m1, SIGN72)))
+#else
+ if (m1 & SIGN72)
+#endif
+ {
+ SET_I_NEG; // in case of divide fault
+#ifdef NEED_128
+ if (iseq_128 (m1, SIGN72))
+ {
+#ifdef HEX_MODE
+ m1 = rshift_128 (m1, shift_amt);
+#else
+ m1 = rshift_128 (m1, 1);
+#endif
+ e1 += 1;
+ } else
+ m1 = and_128 (negate_128 (m1), MASK72);
+#else
+ if (m1 == SIGN72)
+ {
+#ifdef HEX_MODE
+ m1 >>= shift_amt;
+#else
+ m1 >>= 1;
+#endif
+ e1 += 1;
+ } else
+ m1 = (~m1 + 1) & MASK72;
+#endif
+ sign = -sign;
+ } else {
+ CLR_I_NEG; // in case of divide fault
+ }
+
+#ifdef NEED_128
+ if ((isnonzero_128 (and_128 (m2, SIGN72))) && !roundovf)
+ {
+ if (iseq_128 (m2, SIGN72))
+ {
+#ifdef HEX_MODE
+ m2 = rshift_128 (m2, shift_amt);
+#else
+ m2 = rshift_128 (m2, 1);
+#endif
+ e2 += 1;
+ } else
+ m2 = and_128 (negate_128 (m2), MASK72);
+ sign = -sign;
+ }
+#else
+ if ((m2 & SIGN72) && !roundovf)
+ {
+ if (m2 == SIGN72)
+ {
+#ifdef HEX_MODE
+ m2 >>= shift_amt;
+#else
+ m2 >>= 1;
+#endif
+ e2 += 1;
+ } else
+ m2 = (~m2 + 1) & MASK72;
+ sign = -sign;
+ }
+#endif
+
+#ifdef NEED_128
+ if (iszero_128 (m2))
+#else
+ if (m2 == 0)
+#endif
+ {
+ // NB: If C(Y)8,35 ==0 then the alignment loop will never exit! That's why it been moved before the alignment
+
+ SET_I_ZERO;
+ // NEG already set
+
+ // FDV: If the divisor mantissa C(Y)8,35 is zero after alignment (HWR: why after?), the division does
+ // not take place. Instead, a divide check fault occurs, C(AQ) contains the dividend magnitude, and
+ // the negative indicator reflects the dividend sign.
+ // FDI: If the divisor mantissa C(AQ) is zero, the division does not take place.
+ // Instead, a divide check fault occurs and all the registers remain unchanged.
+ if (!bInvert) {
+ convert_to_word36 (m1, & cpu.rA, & cpu.rQ);
+ HDBGRegA ();
+ }
+
+ doFault(FAULT_DIV, fst_zero, "FDV: divide check fault");
+ }
+
+#ifdef NEED_128
+ while (isge_128 (m1, m2)) // DH02 (equivalent but perhaps clearer description):
+ // dividend exponent C(E) increased accordingly until | C(AQ)0,71 | < | C(Y)8,35 with zero fill |
+ // We have already taken the absolute value so just shift it
+ {
+#ifdef HEX_MODE
+ m1 = rshift_128 (m1, shift_amt);
+#else
+ m1 = rshift_128 (m1, 1);
+#endif
+ e1 += 1;
+ }
+#else
+ while (m1 >= m2) // DH02 (equivalent but perhaps clearer description):
+ // dividend exponent C(E) increased accordingly until | C(AQ)0,71 | < | C(Y)8,35 with zero fill |
+ // We have already taken the absolute value so just shift it
+ {
+#ifdef HEX_MODE
+ m1 >>= shift_amt;
+#else
+ m1 >>= 1;
+#endif
+ e1 += 1;
+ }
+#endif
+
+ int e3 = e1 - e2;
+
+ if (e3 > 127)
+ {
+ SET_I_EOFL;
+ if (tstOVFfault ())
+ dlyDoFault (FAULT_OFL, fst_zero, "fdvX exp overflow fault");
+ }
+ if (e3 < -128)
+ {
+ SET_I_EUFL;
+ if (tstOVFfault ())
+ dlyDoFault (FAULT_OFL, fst_zero, "fdvX exp underflow fault");
+ }
+
+ // We need 35 bits quotient + sign. Divisor is at most 28 bits.
+ // Do a 63(28+35) by 35 fractional divide
+ // lo 44bits are always zero
+#ifdef NEED_128
+ uint32_t divisor = rshift_128 (m2, 44).l & MASK28;
+ word72 m3;
+ if (divisor > MASK16)
+ m3 = divide_128_32 (rshift_128 (m1, (44u-35u)), divisor, NULL);
+ else
+ m3 = divide_128_16 (rshift_128 (m1, (44u-35u)), (uint16_t) divisor, NULL);
+#else
+ word72 m3 = (m1 >> (44-35)) / (m2 >> 44);
+#endif
+
+#ifdef NEED_128
+ m3 = lshift_128 (m3, 36);
+ if (sign == -1)
+ m3 = and_128 (negate_128 (m3), MASK72);
+#else
+ m3 <<= 36; // convert back to float
+ if (sign == -1)
+ m3 = (~m3 + 1) & MASK72;
+#endif
+
+ cpu . rE = (word8) e3 & MASK8;
+#ifdef NEED_128
+ cpu.rA = rshift_128 (m3, 36u).l & MASK36;
+#else
+ cpu . rA = (m3 >> 36) & MASK36;
+#endif
+ HDBGRegA ();
+ cpu . rQ = 0;
+
+ SC_I_ZERO (cpu . rA == 0);
+ SC_I_NEG (cpu . rA & SIGN36);
+
+ if (cpu . rA == 0) // set to normalized 0
+ cpu . rE = 0200U; /*-128*/
+}
+
+void fdv (void)
+{
+ fdvX(false); // no inversion
+}
+void fdi (void)
+{
+ fdvX(true);
+}
+
+/*!
+ * single precision floating round ...
+ */
+void frd (void)
+ {
+ // If C(AQ) != 0, the frd instruction performs a true round to a precision
+ // of 28 bits and a normalization on C(EAQ).
+ // A true round is a rounding operation such that the sum of the result of
+ // applying the operation to two numbers of equal magnitude but opposite
+ // sign is exactly zero.
+
+ // The frd instruction is executed as follows:
+ // C(AQ) + (11...1)29,71 -> C(AQ)
+ // If C(AQ)0 = 0, then a carry is added at AQ71
+ // If overflow occurs, C(AQ) is shifted one place to the right and C(E) is
+ // increased by 1.
+ // If overflow does not occur, C(EAQ) is normalized.
+ // If C(AQ) = 0, C(E) is set to -128 and the zero indicator is set ON.
+
+ // I believe AL39 is incorrect; bits 28-71 should be set to 0, not 29-71.
+ // DH02-01 & Bull 9000 is correct.
+
+ // test case 15.5
+ // rE rA rQ
+ // 014174000000 00000110 000111110000000000000000000000000000 000000000000000000000000000000000000
+ // + 1111111 111111111111111111111111111111111111
+ // = 00000110 000111110000000000000000000001111111 111111111111111111111111111111111111
+ // If C(AQ)0 = 0, then a carry is added at AQ71
+ // = 00000110 000111110000000000000000000010000000 000000000000000000000000000000000000
+ // 0 → C(AQ)29,71
+ // 00000110 000111110000000000000000000010000000 000000000000000000000000000000000000
+ // after normalization .....
+ // 010760000002 00000100 011111000000000000000000001000000000 000000000000000000000000000000000000
+ // I think this is wrong
+
+ // 0 -> C(AQ)28,71
+ // 00000110 000111110000000000000000000000000000 000000000000000000000000000000000000
+ // after normalization .....
+ // 010760000000 00000100 011111000000000000000000000000000000 000000000000000000000000000000000000
+ // which I think is correct
+
+ //
+ // GE CPB1004F, DH02-01 (DPS8/88) & Bull DPS9000 assembly ... have this ...
+
+ // The rounding operation is performed in the following way.
+ // - a) A constant (all 1s) is added to bits 29-71 of the mantissa.
+ // - b) If the number being rounded is positive, a carry is inserted into
+ // the least significant bit position of the adder.
+ // - c) If the number being rounded is negative, the carry is not inserted.
+ // - d) Bits 28-71 of C(AQ) are replaced by zeros.
+ // If the mantissa overflows upon rounding, it is shifted right one place
+ // and a corresponding correction is made to the exponent.
+ // If the mantissa does not overflow and is nonzero upon rounding,
+ // normalization is performed.
+
+ // If the resultant mantissa is all zeros, the exponent is forced to -128
+ // and the zero indicator is set.
+ // If the exponent resulting from the operation is greater than +127, the
+ // exponent Overflow indicator is set.
+ // If the exponent resulting from the operation is less than -128, the
+ // exponent Underflow indicator is set.
+ // The definition of normalization is located under the description of the FNO instruction.
+
+ // So, Either AL39 is wrong or the DPS8m did it wrong. (Which was fixed in
+ // later models.) I'll assume AL39 is wrong.
+
+ CPTUR (cptUseE);
+#ifdef L68
+ cpu.ou.cycle |= ou_GOS;
+#endif
+
+ word72 m = convert_to_word72 (cpu.rA, cpu.rQ);
+#ifdef NEED_128
+ if (iszero_128 (m))
+#else
+ if (m == 0)
+#endif
+ {
+ cpu.rE = 0200U; /*-128*/
+ SET_I_ZERO;
+ CLR_I_NEG;
+ return;
+ }
+
+
+#if 1 // according to RJ78
+ // C(AQ) + (11...1)29,71 → C(AQ)
+ bool ovf;
+ word18 flags1 = 0;
+ //word18 flags2 = 0;
+ word1 carry = 0;
+ // If C(AQ)0 = 0, then a carry is added at AQ71
+#ifdef NEED_128
+ if (iszero_128 (and_128 (m, SIGN72)))
+#else
+ if ((m & SIGN72) == 0)
+#endif
+ {
+ carry = 1;
+ }
+#ifdef NEED_128
+ m = Add72b (m, construct_128 (0, 0177777777777777LL), carry, I_OFLOW, & flags1, & ovf);
+#else
+ m = Add72b (m, 0177777777777777LL, carry, I_OFLOW, & flags1, & ovf);
+#endif
+#endif
+
+#if 0 // according to AL39
+ // C(AQ) + (11...1)29,71 → C(AQ)
+ bool ovf;
+ word18 flags1 = 0;
+ word18 flags2 = 0;
+ m = Add72b (m, 0177777777777777LL, 0, I_OFLOW, & flags1, & ovf);
+
+ // If C(AQ)0 = 0, then a carry is added at AQ71
+ if ((m & SIGN72) == 0)
+ {
+ m = Add72b (m, 1, 0, I_OFLOW, & flags2, & ovf);
+ }
+#endif
+
+ // 0 -> C(AQ)28,71 (per. RJ78)
+#ifdef NEED_128
+ m = and_128 (m, lshift_128 (construct_128 (0, 0777777777400), 36));
+#else
+ m &= ((word72)0777777777400 << 36);
+#endif
+
+ // If overflow occurs, C(AQ) is shifted one place to the right and C(E) is
+ // increased by 1.
+ // If overflow does not occur, C(EAQ) is normalized.
+ // All of this is done by fno, we just need to save the overflow flag
+
+ bool savedovf = TST_I_OFLOW;
+ SC_I_OFLOW(ovf);
+ convert_to_word36 (m, & cpu.rA, & cpu.rQ);
+
+ fno (& cpu.rE, & cpu.rA, & cpu.rQ);
+ HDBGRegA ();
+ SC_I_OFLOW(savedovf);
+ }
+
+void fstr (word36 *Y)
+ {
+ // The fstr instruction performs a true round and normalization on C(EAQ)
+ // as it is stored. The definition of true round is located under the
+ // description of the frd instruction. The definition of normalization is
+ // located under the description of the fno instruction. Attempted
+ // repetition with the rpl instruction causes an illegal procedure fault.
+
+#ifdef L68
+ cpu.ou.cycle |= ou_GOS;
+#endif
+ word36 A = cpu . rA, Q = cpu . rQ;
+ word8 E = cpu . rE;
+ //A &= DMASK;
+ //Q &= DMASK;
+ //E &= (int) MASK8;
+
+ float72 m = convert_to_word72 (A, Q);
+#ifdef NEED_128
+ if (iszero_128 (m))
+#else
+ if (m == 0)
+#endif
+ {
+ E = (word8)-128;
+ SET_I_ZERO;
+ CLR_I_NEG;
+ *Y = 0;
+ putbits36_8 (Y, 0, (word8) E & MASK8);
+ return;
+ }
+
+ // C(AQ) + (11...1)29,71 → C(AQ)
+ bool ovf;
+ word18 flags1 = 0;
+ word1 carry = 0;
+ // If C(AQ)0 = 0, then a carry is added at AQ71
+#ifdef NEED_128
+ if (iszero_128 (and_128 (m, SIGN72)))
+#else
+ if ((m & SIGN72) == 0)
+#endif
+ {
+ carry = 1;
+ }
+#ifdef NEED_128
+ m = Add72b (m, construct_128 (0, 0177777777777777LL), carry, I_OFLOW, & flags1, & ovf);
+#else
+ m = Add72b (m, 0177777777777777LL, carry, I_OFLOW, & flags1, & ovf);
+#endif
+
+ // 0 -> C(AQ)28,71 (per. RJ78)
+#ifdef NEED_128
+ m = and_128 (m, lshift_128 (construct_128 (0, 0777777777400), 36));
+#else
+ m &= ((word72)0777777777400 << 36);
+#endif
+
+ // If overflow occurs, C(AQ) is shifted one place to the right and C(E) is
+ // increased by 1.
+ // If overflow does not occur, C(EAQ) is normalized.
+ // All of this is done by fno, we just need to save the overflow flag
+
+ bool savedovf = TST_I_OFLOW;
+ SC_I_OFLOW(ovf);
+ convert_to_word36 (m, & A, & Q);
+ fno (& E, & A, & Q);
+ SC_I_OFLOW(savedovf);
+
+ * Y = setbits36_8 (A >> 8, 0, (word8) E);
+ }
+
+/*!
+ * single precision Floating Compare ...
+ */
+void fcmp(void)
+ {
+ // C(E) :: C(Y)0,7
+ // C(AQ)0,27 :: C(Y)8,35
+
+ // Zero: If C(EAQ) = C(Y), then ON; otherwise OFF
+ // Neg: If C(EAQ) < C(Y), then ON; otherwise OFF
+
+ // Notes: The fcmp instruction is executed as follows:
+ // The mantissas are aligned by shifting the mantissa of the operand with
+ // the algebraically smaller exponent to the right the number of places
+ // equal to the difference in the two exponents.
+ // The aligned mantissas are compared and the indicators set accordingly.
+
+ CPTUR (cptUseE);
+#ifdef NEED_128
+ word72 m1= lshift_128 (construct_128 (0, cpu.rA & 0777777777400), 36);
+#else
+ word72 m1 = ((word72)cpu.rA & 0777777777400LL) << 36;
+#endif
+ int e1 = SIGNEXT8_int (cpu.rE & MASK8);
+
+ // 28-bit mantissa (incl sign)
+#ifdef NEED_128
+ word72 m2 = lshift_128 (construct_128 (0, getbits36_28 (cpu.CY, 8)), 44);
+#else
+ word72 m2 = ((word72) getbits36_28 (cpu.CY, 8)) << 44;
+#endif
+ int e2 = SIGNEXT8_int (getbits36_8 (cpu.CY, 0));
+
+ //which exponent is smaller???
+
+#ifdef L68
+ cpu.ou.cycle = ou_GOE;
+#endif
+#ifdef HEX_MODE
+ uint shift_amt = isHex() ? 4 : 1;
+#endif
+ int shift_count = -1;
+ word1 notallzeros = 0;
+
+ if (e1 == e2)
+ {
+ shift_count = 0;
+ }
+ else if (e1 < e2)
+ {
+#ifdef L68
+ cpu.ou.cycle = ou_GOA;
+#endif
+#ifdef HEX_MODE
+ shift_count = abs(e2 - e1) * (int) shift_amt;
+#else
+ shift_count = abs(e2 - e1);
+#endif
+ // mantissa negative?
+#ifdef NEED_128
+ bool s = isnonzero_128 (and_128 (m1, SIGN72));
+#else
+ bool s = (m1 & SIGN72) != (word72)0;
+#endif
+ for(int n = 0; n < shift_count; n += 1)
+ {
+#ifdef NEED_128
+ notallzeros |= m1.l & 1;
+ m1 = rshift_128 (m1, 1);
+#else
+ notallzeros |= m1 & 1;
+ m1 >>= 1;
+#endif
+ if (s)
+#ifdef NEED_128
+ m1 = or_128 (m1, SIGN72);
+#else
+ m1 |= SIGN72;
+#endif
+ }
+#ifdef NEED_128
+#ifdef HEX_MODE
+ if (iseq_128 (m1, MASK72) && notallzeros == 1 && shift_count * (int) shift_amt > 71)
+ m1 = construct_128 (0, 0);
+#else
+ if (iseq_128 (m1, MASK72) && notallzeros == 1 && shift_count > 71)
+ m1 = construct_128 (0, 0);
+#endif
+ m1 = and_128 (m1, MASK72);
+#else // NEED_128
+#ifdef HEX_MODE
+ if (m1 == MASK72 && notallzeros == 1 && shift_count * (int) shift_amt > 71)
+ m1 = 0;
+#else
+ if (m1 == MASK72 && notallzeros == 1 && shift_count > 71)
+ m1 = 0;
+#endif
+ m1 &= MASK72;
+#endif // NEED_128
+ }
+ else
+ {
+ // e2 < e1;
+#ifdef L68
+ cpu.ou.cycle = ou_GOA;
+#endif
+#ifdef HEX_MODE
+ shift_count = abs(e1 - e2) * (int) shift_amt;
+#else
+ shift_count = abs(e1 - e2);
+#endif
+ // mantissa negative?
+#ifdef NEED_128
+ bool s = isnonzero_128 (and_128 (m2, SIGN72));
+#else
+ bool s = (m2 & SIGN72) != (word72)0;
+#endif
+ for(int n = 0 ; n < shift_count ; n += 1)
+ {
+#ifdef NEED_128
+ notallzeros |= m2.l & 1;
+ m2 = rshift_128 (m2, 1);
+#else
+ notallzeros |= m2 & 1;
+ m2 >>= 1;
+#endif
+ if (s)
+#ifdef NEED_128
+ m2 = or_128 (m2, SIGN72);
+#else
+ m2 |= SIGN72;
+#endif
+ }
+#ifdef NEED_128
+#ifdef HEX_MODE
+ if (iseq_128 (m2, MASK72) && notallzeros == 1 && shift_count * (int) shift_amt > 71)
+ m2 = construct_128 (0, 0);
+#else
+ if (iseq_128 (m2, MASK72) && notallzeros == 1 && shift_count > 71)
+ m2 = construct_128 (0, 0);
+#endif
+ m2 = and_128 (m2, MASK72);
+ //e3 = e1;
+#else
+#ifdef HEX_MODE
+ if (m2 == MASK72 && notallzeros == 1 && shift_count * (int) shift_amt > 71)
+ m2 = 0;
+#else
+ if (m2 == MASK72 && notallzeros == 1 && shift_count > 71)
+ m2 = 0;
+#endif
+ m2 &= MASK72;
+ //e3 = e1;
+#endif
+ }
+
+ // need to do algebraic comparisons of mantissae
+#ifdef NEED_128
+ SC_I_ZERO (iseq_128 (m1, m2));
+ SC_I_NEG (islt_s128 (SIGNEXT72_128(m1), SIGNEXT72_128(m2)));
+#else
+ SC_I_ZERO (m1 == m2);
+ SC_I_NEG ((int128)SIGNEXT72_128(m1) < (int128)SIGNEXT72_128(m2));
+#endif
+ }
+
+/*!
+ * single precision Floating Compare magnitude ...
+ */
+void fcmg ()
+ {
+ // C(E) :: C(Y)0,7
+ // | C(AQ)0,27 | :: | C(Y)8,35 |
+ // * Zero: If | C(EAQ)| = | C(Y) |, then ON; otherwise OFF
+ // * Neg : If | C(EAQ)| < | C(Y) |, then ON; otherwise OFF
+
+ // Notes: The fcmp instruction is executed as follows:
+ // The mantissas are aligned by shifting the mantissa of the operand with
+ // the algebraically smaller exponent to the right the number of places
+ // equal to the difference in the two exponents.
+ // The aligned mantissas are compared and the indicators set accordingly.
+
+ // The fcmg instruction is identical to the fcmp instruction except that the
+ // magnitudes of the mantissas are compared instead of the algebraic values.
+
+ // ISOLTS-736 01u asserts that |0.0*2^64|<|1.0| in 28bit precision
+ // this implies that all shifts are 72 bits long
+ // RJ78 also comments: If the number of shifts equals or exceeds 72, the
+ // number with the lower exponent is defined as zero.
+
+ CPTUR (cptUseE);
+#ifdef L68
+ cpu.ou.cycle = ou_GOS;
+#endif
+#ifdef HEX_MODE
+ uint shift_amt = isHex() ? 4 : 1;
+#endif
+#if 1
+ // C(AQ)0,27
+#ifdef NEED_128
+ word72 m1= lshift_128 (construct_128 (0, cpu.rA & 0777777777400), 36);
+#else
+ word72 m1 = ((word72)cpu.rA & 0777777777400LL) << 36;
+#endif
+ int e1 = SIGNEXT8_int (cpu.rE & MASK8);
+
+ // C(Y)0,7
+ // 28-bit mantissa (incl sign)
+#ifdef NEED_128
+ word72 m2 = lshift_128 (construct_128 (0, getbits36_28 (cpu.CY, 8)), 44);
+#else
+ word72 m2 = ((word72) getbits36_28 (cpu.CY, 8)) << 44;
+#endif
+ int e2 = SIGNEXT8_int (getbits36_8 (cpu.CY, 0));
+
+ //int e3 = -1;
+
+ //which exponent is smaller???
+
+#ifdef L68
+ cpu.ou.cycle = ou_GOE;
+#endif
+ int shift_count = -1;
+ word1 notallzeros = 0;
+
+ if (e1 == e2)
+ {
+ shift_count = 0;
+ }
+ else if (e1 < e2)
+ {
+#ifdef L68
+ cpu.ou.cycle = ou_GOA;
+#endif
+#ifdef HEX_MODE
+ shift_count = abs(e2 - e1) * (int) shift_amt;
+#else
+ shift_count = abs(e2 - e1);
+#endif
+#ifdef NEED_128
+ bool s = isnonzero_128 (and_128 (m1, SIGN72));
+#else
+ bool s = (m1 & SIGN72) != (word72)0; ///< save sign bit
+#endif
+ for(int n = 0 ; n < shift_count ; n += 1)
+ {
+#ifdef NEED_128
+ notallzeros |= m1.l & 1;
+ m1 = rshift_128 (m1, 1);
+#else
+ notallzeros |= m1 & 1;
+ m1 >>= 1;
+#endif
+ if (s)
+#ifdef NEED_128
+ m1 = or_128 (m1, SIGN72);
+#else
+ m1 |= SIGN72;
+#endif
+ }
+
+#ifdef NEED_128
+#ifdef HEX_MODE
+ if (iseq_128 (m1, MASK72) && notallzeros == 1 && shift_count * (int) shift_amt > 71)
+ m1 = construct_128 (0, 0);
+#else
+ if (iseq_128 (m1, MASK72) && notallzeros == 1 && shift_count > 71)
+ m1 = construct_128 (0, 0);
+#endif
+ m1 = and_128 (m1, MASK72);
+#else
+#ifdef HEX_MODE
+ if (m1 == MASK72 && notallzeros == 1 && shift_count * (int) shift_amt > 71)
+ m1 = 0;
+#else
+ if (m1 == MASK72 && notallzeros == 1 && shift_count > 71)
+ m1 = 0;
+#endif
+ m1 &= MASK72;
+#endif
+ //e3 = e2;
+ }
+ else
+ {
+ // e2 < e1;
+#ifdef L68
+ cpu.ou.cycle = ou_GOA;
+#endif
+#ifdef HEX_MODE
+ shift_count = abs(e1 - e2) * (int) shift_amt;
+#else
+ shift_count = abs(e1 - e2);
+#endif
+#ifdef NEED_128
+ bool s = isnonzero_128 (and_128 (m2, SIGN72));
+#else
+ bool s = (m2 & SIGN72) != (word72)0; ///< save sign bit
+#endif
+ for(int n = 0 ; n < shift_count ; n += 1)
+ {
+#ifdef NEED_128
+ notallzeros |= m2.l & 1;
+ m2 = rshift_128 (m2, 1);
+#else
+ notallzeros |= m2 & 1;
+ m2 >>= 1;
+#endif
+ if (s)
+#ifdef NEED_128
+ m2 = or_128 (m2, SIGN72);
+#else
+ m2 |= SIGN72;
+#endif
+ }
+#ifdef NEED_128
+#ifdef HEX_MODE
+ if (iseq_128 (m2, MASK72) && notallzeros == 1 && shift_count * (int) shift_amt > 71)
+ m2 = construct_128 (0, 0);
+#else
+ if (iseq_128 (m2, MASK72) && notallzeros == 1 && shift_count > 71)
+ m2 = construct_128 (0, 0);
+#endif
+ m2 = and_128 (m2, MASK72);
+#else
+#ifdef HEX_MODE
+ if (m2 == MASK72 && notallzeros == 1 && shift_count * (int) shift_amt > 71)
+ m2 = 0;
+#else
+ if (m2 == MASK72 && notallzeros == 1 && shift_count > 71)
+ m2 = 0;
+#endif
+ m2 &= MASK72;
+#endif
+ //e3 = e1;
+ }
+
+#ifdef NEED_128
+ SC_I_ZERO (iseq_128 (m1, m2));
+#else
+ SC_I_ZERO (m1 == m2);
+#endif
+#ifdef NEED_128
+ int128 sm1 = SIGNEXT72_128 (m1);
+ if (islt_s128 (sm1, construct_s128 (0, 0)))
+ sm1 = negate_s128 (sm1);
+ int128 sm2 = SIGNEXT72_128 (m2);
+ if (islt_s128 (sm2, construct_s128 (0, 0)))
+ sm2 = negate_s128 (sm2);
+ SC_I_NEG (islt_s128 (sm1, sm2));
+#else
+ int128 sm1 = SIGNEXT72_128 (m1);
+ if (sm1 < 0)
+ sm1 = - sm1;
+ int128 sm2 = SIGNEXT72_128 (m2);
+ if (sm2 < 0)
+ sm2 = - sm2;
+ SC_I_NEG (sm1 < sm2);
+#endif
+#else
+ int e1 = SIGNEXT8_int (cpu . rE & MASK8);
+ int e2 = SIGNEXT8_int (getbits36_8 (cpu.CY, 0));
+ word36 m1 = cpu . rA & 0777777777400LL;
+ word36 m2 = ((word36) getbits36_28 (cpu.CY, 8)) << 8; ///< 28-bit mantissa (incl sign)
+ if (m1 & SIGN36)
+ m1 = ((~m1) + 1) & MASK36;
+ if (m2 & SIGN36)
+ m2 = ((~m2) + 1) & MASK36;
+ bool m1waszero = m1 == 0;
+ bool m2waszero = m2 == 0;
+
+ if (e1 < e2)
+ {
+ int shift_count = abs(e2 - e1);
+ bool s = m1 & SIGN36; // mantissa negative?
+ for(int n = 0 ; n < shift_count ; n += 1)
+ {
+ m1 >>= 1;
+ if (s)
+ m1 |= SIGN36;
+ }
+
+ m1 &= MASK36;
+ e1 = e2;
+ }
+ else if (e2 < e1)
+ {
+ int shift_count = abs(e1 - e2);
+ bool s = m2 & SIGN36; // mantissa negative?
+ for(int n = 0 ; n < shift_count ; n += 1)
+ {
+ m2 >>= 1;
+ if (s)
+ m2 |= SIGN36;
+ }
+ m2 &= MASK36;
+ e2 = e1;
+ }
+
+ if (m1 < m2)
+ {
+ SET_I_NEG;
+ CLR_I_ZERO;
+ return;
+ }
+ else if (m1 > m2)
+ {
+ CLR_I_NEG;
+ CLR_I_ZERO;
+ return;
+ }
+// ISOLTS ps736 test-01u
+ if (m1 == 0 && ! m2waszero)
+ SET_I_NEG;
+ else
+ CLR_I_NEG;
+ SET_I_ZERO;
+#endif
+ }
+
+/*
+ * double-precision arithmetic routines ....
+ */
+
+#if 0
+//! extract mantissa + exponent from a YPair ....
+static void YPairToExpMant(word36 Ypair[], word72 *mant, int *exp)
+{
+ //*mant = (word72)bitfieldExtract36(Ypair[0], 0, 28) << 44; // 28-bit mantissa (incl sign)
+ *mant = ((word72) getbits36_28 (Ypair[0], 8)) << 44; // 28-bit mantissa (incl sign)
+ *mant |= (((word72) Ypair[1]) & DMASK) << 8;
+ //*exp = SIGNEXT8_int (bitfieldExtract36(Ypair[0], 28, 8) & 0377U); // 8-bit signed integer (incl sign)
+ *exp = SIGNEXT8_int (getbits36_8 (Ypair[0], 0) & 0377U); // 8-bit signed integer (incl sign)
+}
+
+//! combine mantissa + exponent intoa YPair ....
+static void ExpMantToYpair(word72 mant, int exp, word36 *yPair)
+{
+ yPair[0] = ((word36)exp & 0377) << 28;
+ yPair[0] |= (mant >> 44) & 01777777777LL;
+ yPair[1] = (mant >> 8) & 0777777777777LL; //400LL;
+}
+#endif
+
+
+/*!
+ * unnormalized floating double-precision add
+ */
+void dufa (bool subtract)
+ {
+ // Except for the precision of the mantissa of the operand from main
+ // memory, the dufa instruction is identical to the ufa instruction.
+
+ // C(EAQ) + C(Y) → C(EAQ)
+ // The ufa instruction is executed as follows:
+ // The mantissas are aligned by shifting the mantissa of the operand having
+ // the algebraically smaller exponent to the right the number of places
+ // equal to the absolute value of the difference in the two exponents. Bits
+ // shifted beyond the bit position equivalent to AQ71 are lost.
+ // The algebraically larger exponent replaces C(E). The sum of the
+ // mantissas replaces C(AQ).
+ // If an overflow occurs during addition, then;
+ // * C(AQ) are shifted one place to the right.
+ // * C(AQ)0 is inverted to restore the sign.
+ // * C(E) is increased by one.
+
+ // The dufs instruction is identical to the dufa instruction with the
+ // exception that the twos complement of the mantissa of the operand from
+ // main memory (op2) is used.
+
+ CPTUR (cptUseE);
+#ifdef L68
+ cpu.ou.cycle |= ou_GOS;
+#endif
+#ifdef HEX_MODE
+ uint shift_amt = isHex() ? 4 : 1;
+#endif
+
+ word72 m1 = convert_to_word72 (cpu.rA, cpu.rQ);
+ int e1 = SIGNEXT8_int (cpu . rE & MASK8);
+
+ // 64-bit mantissa (incl sign)
+#ifdef NEED_128
+ word72 m2 = lshift_128 (construct_128 (0, (uint64_t) getbits36_28 (cpu.Ypair[0], 8)), 44u); // 28-bit mantissa (incl sign)
+ //m2 = or_128 (m2, construct_128 (0, cpu.Ypair[1] << 8));
+ m2 = or_128 (m2, lshift_128 (construct_128 (0, cpu.Ypair[1]), 8u));
+#else
+ word72 m2 = ((word72) getbits36_28 (cpu.Ypair[0], 8)) << 44;
+ m2 |= (word72) cpu.Ypair[1] << 8;
+#endif
+
+ int e2 = SIGNEXT8_int (getbits36_8 (cpu.Ypair[0], 0));
+
+ // see ufs
+ int m2zero = 0;
+ if (subtract) {
+
+#ifdef NEED_128
+ if (iszero_128 (m2))
+ m2zero = 1;
+ if (iseq_128 (m2, SIGN72)) {
+#ifdef HEX_MODE
+ m2 = rshift_128 (m2, shift_amt);
+ e2 += 1;
+#else
+ m2 = rshift_128 (m2, 1);
+ e2 += 1;
+#endif
+ } else
+ m2 = and_128 (negate_128 (m2), MASK72);
+#else
+ if (m2 == 0)
+ m2zero = 1;
+ if (m2 == SIGN72) {
+#ifdef HEX_MODE
+ m2 >>= shift_amt;
+ e2 += 1;
+#else
+ m2 >>= 1;
+ e2 += 1;
+#endif
+ } else
+ m2 = (-m2) & MASK72;
+#endif
+ }
+
+ int e3 = -1;
+
+ // which exponent is smaller?
+
+#ifdef L68
+ cpu.ou.cycle |= ou_GOE;
+#endif
+ int shift_count = -1;
+ word1 notallzeros = 0;
+
+ if (e1 == e2)
+ {
+ shift_count = 0;
+ e3 = e1;
+ }
+ else if (e1 < e2)
+ {
+#ifdef L68
+ cpu.ou.cycle |= ou_GOA;
+#endif
+#ifdef HEX_MODE
+ shift_count = abs(e2 - e1) * (int) shift_amt;
+#else
+ shift_count = abs(e2 - e1);
+#endif
+ // mantissa negative?
+#ifdef NEED_128
+ bool s = isnonzero_128 (and_128 (m1, SIGN72));
+#else
+ bool s = (m1 & SIGN72) != (word72)0;
+#endif
+ for(int n = 0 ; n < shift_count ; n += 1)
+ {
+#ifdef NEED_128
+ notallzeros |= m1.l & 1;
+ m1 = rshift_128 (m1, 1);
+#else
+ notallzeros |= m1 & 1;
+ m1 >>= 1;
+#endif
+ if (s)
+#ifdef NEED_128
+ m1 = or_128 (m1, SIGN72);
+#else
+ m1 |= SIGN72;
+#endif
+ }
+#ifdef NEED_128
+#ifdef HEX_MODE
+ if (iseq_128 (m1, MASK72) && notallzeros == 1 && shift_count * (int) shift_amt > 71)
+ m1 = construct_128 (0, 0);
+#else
+ if (iseq_128 (m1, MASK72) && notallzeros == 1 && shift_count > 71)
+ m1 = construct_128 (0, 0);
+#endif
+ m1 = and_128 (m1, MASK72);
+#else
+#ifdef HEX_MODE
+ if (m1 == MASK72 && notallzeros == 1 && shift_count * (int) shift_amt > 71)
+ m1 = 0;
+#else
+ if (m1 == MASK72 && notallzeros == 1 && shift_count > 71)
+ m1 = 0;
+#endif
+ m1 &= MASK72;
+#endif
+ e3 = e2;
+ }
+ else
+ {
+ // e2 < e1;
+#ifdef L68
+ cpu.ou.cycle |= ou_GOA;
+#endif
+#ifdef HEX_MODE
+ shift_count = abs(e1 - e2) * (int) shift_amt;
+#else
+ shift_count = abs(e1 - e2);
+#endif
+#ifdef NEED_128
+ bool s = isnonzero_128 (and_128 (m2, SIGN72));
+#else
+ bool s = (m2 & SIGN72) != (word72)0; ///< save sign bit
+#endif
+ for(int n = 0 ; n < shift_count ; n += 1)
+ {
+#ifdef NEED_128
+ notallzeros |= m2.l & 1;
+ m2 = rshift_128 (m2, 1);
+#else
+ notallzeros |= m2 & 1;
+ m2 >>= 1;
+#endif
+ if (s)
+#ifdef NEED_128
+ m2 = or_128 (m2, SIGN72);
+#else
+ m2 |= SIGN72;
+#endif
+ }
+#ifdef NEED_128
+#ifdef HEX_MODE
+ if (iseq_128 (m2, MASK72) && notallzeros == 1 && shift_count * (int) shift_amt > 71)
+ m2 = construct_128 (0, 0);
+#else
+ if (iseq_128 (m2, MASK72) && notallzeros == 1 && shift_count > 71)
+ m2 = construct_128 (0, 0);
+#endif
+ m2 = and_128 (m2, MASK72);
+#else
+#ifdef HEX_MODE
+ if (m2 == MASK72 && notallzeros == 1 && shift_count * (int) shift_amt > 71)
+ m2 = 0;
+#else
+ if (m2 == MASK72 && notallzeros == 1 && shift_count > 71)
+ m2 = 0;
+#endif
+ m2 &= MASK72;
+#endif
+ e3 = e1;
+ }
+ //sim_printf ("shift_count = %d\n", shift_count);
+
+ bool ovf;
+ word72 m3 = Add72b (m1, m2, 0, I_CARRY, & cpu.cu.IR, & ovf);
+ if (m2zero)
+ SET_I_CARRY;
+
+ if (ovf)
+ {
+#ifdef HEX_MODE
+// word72 signbit = m3 & sign_msk;
+// m3 >>= shift_amt;
+// m3 = (m3 & MASK71) | signbit;
+// m3 ^= SIGN72; // C(AQ)0 is inverted to restore the sign
+// e3 += 1;
+ // save the sign bit
+#ifdef NEED_128
+ bool s = isnonzero_128 (and_128 (m3, SIGN72));
+#else
+ bool s = (m3 & SIGN72) != (word72)0;
+#endif
+ if (isHex ())
+ {
+#ifdef NEED_128
+ m3 = rshift_128 (m3, shift_amt); // renormalize the mantissa
+ if (s)
+ // Sign is set, number should be positive; clear the sign bit and the 3 MSBs
+ m3 = and_128 (m3, MASK68);
+ else
+ // Sign is clr, number should be negative; set the sign bit and the 3 MSBs
+ m3 = or_128 (m3, HEX_SIGN);
+#else
+ m3 >>= shift_amt; // renormalize the mantissa
+ if (s)
+ // Sign is set, number should be positive; clear the sign bit and the 3 MSBs
+ m3 &= MASK68;
+ else
+ // Sign is clr, number should be negative; set the sign bit and the 3 MSBs
+ m3 |= HEX_SIGN;
+#endif
+ }
+ else
+ {
+#ifdef NEED_128
+ word72 signbit = and_128 (m3, SIGN72);
+ m3 = rshift_128 (m3, 1); // renormalize the mantissa
+ m3 = or_128 (and_128 (m3, MASK71), signbit);
+ m3 = xor_128 (m3, SIGN72); // C(AQ)0 is inverted to restore the sign
+#else
+ word72 signbit = m3 & SIGN72;
+ m3 >>= 1;
+ m3 = (m3 & MASK71) | signbit;
+ m3 ^= SIGN72; // C(AQ)0 is inverted to restore the sign
+#endif
+ }
+ e3 += 1;
+#else
+ word72 signbit = m3 & SIGN72;
+ m3 >>= 1;
+ m3 = (m3 & MASK71) | signbit;
+ m3 ^= SIGN72; // C(AQ)0 is inverted to restore the sign
+ e3 += 1;
+#endif
+ }
+
+ convert_to_word36 (m3, & cpu.rA, & cpu.rQ);
+ HDBGRegA ();
+ cpu.rE = e3 & 0377;
+
+ SC_I_NEG (cpu.rA & SIGN36); // Do this here instead of in Add72b because
+ // of ovf handling above
+ if (cpu.rA == 0 && cpu.rQ == 0)
+ {
+ SET_I_ZERO;
+ cpu . rE = 0200U; /*-128*/
+ }
+ else
+ {
+ CLR_I_ZERO;
+ }
+
+ // EOFL: If exponent is greater than +127, then ON
+ if (e3 > 127)
+ {
+ SET_I_EOFL;
+ if (tstOVFfault ())
+ dlyDoFault (FAULT_OFL, fst_zero, "dufa exp overflow fault");
+ }
+
+ // EUFL: If exponent is less than -128, then ON
+ if (e3 < -128)
+ {
+ SET_I_EUFL;
+ if (tstOVFfault ())
+ dlyDoFault (FAULT_OFL, fst_zero, "dufa exp underflow fault");
+ }
+ }
+
+#if 0
+/*!
+ * unnormalized floating double-precision subtract
+ */
+void dufs (void)
+{
+ // Except for the precision of the mantissa of the operand from main memory,
+ // the dufs instruction is identical with the ufs instruction.
+
+ // The ufs instruction is identical to the ufa instruction with the
+ // exception that the twos complement of the mantissa of the operand from
+ // main memory (op2) is used.
+
+ // They're probably a few gotcha's here but we'll see.
+ // Yup ... when mantissa 1 000 000 .... 000 we can't do 2'c comp.
+
+ float72 m2 = 0;
+ int e2 = -128;
+
+ YPairToExpMant(cpu.Ypair, &m2, &e2);
+
+ word72 m2c = ~m2 + 1; ///< & 01777777777LL; ///< take 2-comp of mantissa
+ m2c &= MASK72;
+
+ /*!
+ * When signs are the *same* after complement we have an overflow
+ * (Treat as in addition when we get an overflow)
+ */
+ bool ov = ((m2 & SIGN72) == (m2c & SIGN72)); ///< the "weird" number.
+
+ if (ov && m2 != 0)
+ {
+ m2c >>= 1;
+ m2c &= MASK72;
+
+ if (e2 == 127)
+ {
+ SET_I_EOFL;
+ if (tstOVFfault ())
+ doFault (FAULT_OFL, fst_zero, "dufs exp overflow fault");
+ }
+ e2 += 1;
+ }
+
+ if (m2c == 0)
+ {
+// cpu.Ypair[0] = 0400000000000LL;
+// cpu.Ypair[1] = 0;
+ ExpMantToYpair(0, -128, cpu.Ypair);
+ }
+ else
+ {
+// cpu.Ypair[0] = ((word36)e2 & 0377) << 28;
+// cpu.Ypair[0] |= (m2c >> 44) & 01777777777LL;
+// cpu.Ypair[1] = (m2c >> 8) & 0777777777400LL;
+ ExpMantToYpair(m2c, e2, cpu.Ypair);
+ }
+
+ dufa ();
+
+}
+#endif
+
+/*!
+ * double-precision Unnormalized Floating Multiply ...
+ */
+void dufm (void)
+{
+ // Except for the precision of the mantissa of the operand from main memory,
+ //the dufm instruction is identical to the ufm instruction.
+
+ // The ufm instruction is executed as follows:
+ // C(E) + C(Y)0,7 → C(E)
+ // ( C(AQ) × C(Y)8,35 )0,71 → C(AQ)
+
+ // * Zero: If C(AQ) = 0, then ON; otherwise OFF
+ // * Neg: If C(AQ)0 = 1, then ON; otherwise OFF
+ // * Exp Ovr: If exponent is greater than +127, then ON
+ // * Exp Undr: If exponent is less than -128, then ON
+
+ CPTUR (cptUseE);
+#ifdef L68
+ cpu.ou.cycle |= ou_GOS;
+#endif
+ word72 m1 = convert_to_word72 (cpu.rA, cpu.rQ);
+ int e1 = SIGNEXT8_int (cpu . rE & MASK8);
+
+#ifndef NEED_128
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "dufm e1 %d %03o m1 %012"PRIo64" %012"PRIo64"\n", e1, e1, (word36) (m1 >> 36) & MASK36, (word36) m1 & MASK36);
+#endif
+ // 64-bit mantissa (incl sign)
+#ifdef NEED_128
+ word72 m2 = lshift_128 (construct_128 (0, (uint64_t) getbits36_28 (cpu.Ypair[0], 8)), 44u); // 28-bit mantissa (incl sign)
+ //m2 = or_128 (m2, construct_128 (0, cpu.Ypair[1] << 8));
+ m2 = or_128 (m2, lshift_128 (construct_128 (0, cpu.Ypair[1]), 8u));
+#else
+ word72 m2 = ((word72) getbits36_28 (cpu.Ypair[0], 8)) << 44;
+ m2 |= (word72) cpu.Ypair[1] << 8;
+#endif
+
+ // 8-bit signed integer (incl sign)
+ int e2 = SIGNEXT8_int (getbits36_8 (cpu.Ypair[0], 0));
+
+#ifndef NEED_128
+ sim_debug (DBG_TRACEEXT, & cpu_dev,
+ "dufm e2 %d %03o m2 %012"PRIo64" %012"PRIo64"\n", e2, e2, (word36) (m2 >> 36) & MASK36, (word36) m2 & MASK36);
+#endif
+
+
+#ifdef NEED_128
+ if (iszero_128 (m1) || iszero_128 (m2))
+#else
+ if (m1 == 0 || m2 == 0)
+#endif
+ {
+ SET_I_ZERO;
+ CLR_I_NEG;
+
+ cpu . rE = 0200U; /*-128*/
+ cpu . rA = 0;
+ HDBGRegA ();
+ cpu . rQ = 0;
+
+ return; // normalized 0
+ }
+
+ int e3 = e1 + e2;
+
+ if (e3 > 127)
+ {
+ SET_I_EOFL;
+ if (tstOVFfault ())
+ dlyDoFault (FAULT_OFL, fst_zero, "dufm exp overflow fault");
+ }
+ if (e3 < -128)
+ {
+ SET_I_EUFL;
+ if (tstOVFfault ())
+ dlyDoFault (FAULT_OFL, fst_zero, "dufm exp underflow fault");
+ }
+
+ // RJ78: This multiplication is executed in the following way:
+ // C(E) + C(Y)(0-7) -> C(E)
+ // C(AQ) * C(Y-pair)(8-71) results in a 134-bit product plus sign. This sign plus the
+ // leading 71 bits are loaded into the AQ.
+
+ // do a 128x64 signed multiplication
+#if 0
+ // absolute value and unsigned multiplication
+ // does not work
+ uint128 m1a = m1;
+ uint128 m2a = m2;
+ int sign = 1;
+
+ if (m1 & SIGN72)
+ {
+ m1a = (-m1) & MASK72;
+ sign = -sign;
+ }
+ if (m2 & SIGN72)
+ {
+ m2a = (-m2) & MASK72;
+ sign = -sign;
+ }
+
+ // shift the CY mantissa
+ m2a >>= 8;
+
+ uint128 m3l = (m1a & (((uint128)1<<64)-1)) * m2a; // lo partial product
+ uint128 m3h = (m1a >> 64) * m2a; // hi partial product
+
+ // realign to 72bits XXX this is wrong, arithmetic shift is required
+ m3l >>= 63; // 134-71
+ m3h <<= 1;
+ word72 m3a = ((word72) (m3h+m3l)) & MASK72;
+ if (sign < 0)
+ m3a = (-m3a) & MASK72;
+#endif
+
+#if 1
+ // fast signed multiplication algorithm without 2's complements
+ // passes ISOLTS-745 08
+
+#ifdef NEED_128
+ // shift the CY mantissa
+ int128 m2s = rshift_s128 (SIGNEXT72_128(m2), 8);
+
+ // do a 128x64 signed multiplication
+ int128 m1l = and_s128 (cast_s128 (m1), construct_128 (0, MASK64));
+ int128 m1h = rshift_s128 (SIGNEXT72_128(m1), 64);
+ int128 m3h = multiply_s128 (m1h, m2s); // hi partial product
+ int128 m3l = multiply_s128 (m1l, m2s); // lo partial product
+
+ // realign to 72bits
+ m3l = rshift_s128 (m3l, 63);
+ m3h = lshift_s128 (m3h, 1); // m3h is hi by 64, align it for addition. The result is 135 bits so this cannot overflow.
+ word72 m3a = and_128 (add_128 (cast_128 (m3h), cast_128 (m3l)), MASK72);
+#else
+ // shift the CY mantissa
+ int128 m2s = SIGNEXT72_128(m2) >> 8;
+
+ // do a 128x64 signed multiplication
+ int128 m1l = m1 & (((uint128)1<<64)-1);
+ int128 m1h = SIGNEXT72_128(m1) >> 64;
+ int128 m3h = m1h * m2s; // hi partial product
+ int128 m3l = m1l * m2s; // lo partial product
+
+ // realign to 72bits
+ m3l >>= 63;
+ m3h <<= 1; // m3h is hi by 64, align it for addition. The result is 135 bits so this cannot overflow.
+ word72 m3a = ((word72) (m3h+m3l)) & MASK72;
+#endif
+#endif
+
+ // A normalization is performed only in the case of both factor mantissas being 100...0
+ // which is the twos complement approximation to the decimal value -1.0.
+#ifdef NEED_128
+ if (iseq_128 (m1, SIGN72) && iseq_128 (m2, SIGN72))
+#else
+ if ((m1 == SIGN72) && (m2 == SIGN72))
+#endif
+ {
+ if (e3 == 127)
+ {
+ SET_I_EOFL;
+ if (tstOVFfault ())
+ dlyDoFault (FAULT_OFL, fst_zero, "dufm exp overflow fault");
+ }
+#ifdef NEED_128
+ m3a = rshift_128 (m3a, 1);
+#else
+ m3a >>= 1;
+#endif
+ e3 += 1;
+ }
+
+ convert_to_word36 (m3a, & cpu.rA, & cpu.rQ);
+ HDBGRegA ();
+ cpu . rE = (word8) e3 & MASK8;
+
+ SC_I_NEG (cpu.rA & SIGN36);
+
+ if (cpu.rA == 0 && cpu.rQ == 0)
+ {
+ SET_I_ZERO;
+ cpu . rE = 0200U; /*-128*/
+ }
+ else
+ {
+ CLR_I_ZERO;
+ }
+}
+
+/*!
+ * floating divide ...
+ */
+// CANFAULT
+static void dfdvX (bool bInvert)
+ {
+ // C(EAQ) / C (Y) → C(EA)
+ // C(Y) / C(EAQ) → C(EA) (Inverted)
+
+ // 00...0 → C(Q)
+
+ // The dfdv instruction is executed as follows:
+ // The dividend mantissa C(AQ) is shifted right and the dividend exponent
+ // C(E) increased accordingly until
+ // | C(AQ)0,63 | < | C(Y-pair)8,71 |
+ // C(E) - C(Y-pair)0,7 → C(E)
+ // C(AQ) / C(Y-pair)8,71 → C(AQ)0,63 00...0 → C(Q)64,71
+
+ CPTUR (cptUseE);
+#ifdef L68
+ cpu.ou.cycle |= ou_GOS;
+#endif
+#ifdef HEX_MODE
+ uint shift_amt = isHex() ? 4 : 1;
+#endif
+ word72 m1;
+ int e1;
+
+ word72 m2;
+ int e2;
+
+ bool roundovf = 0;
+
+ if (!bInvert)
+ {
+ m1 = convert_to_word72 (cpu.rA, cpu.rQ);
+ e1 = SIGNEXT8_int (cpu . rE & MASK8);
+
+ // 64-bit mantissa (incl sign)
+#ifdef NEED_128
+ m2 = lshift_128 (construct_128 (0, (uint64_t) getbits36_28 (cpu.Ypair[0], 8)), 44u); // 28-bit mantissa (incl sign)
+ //m2 = or_128 (m2, construct_128 (0, cpu.Ypair[1] << 8));
+ m2 = or_128 (m2, lshift_128 (construct_128 (0, cpu.Ypair[1]), 8u));
+#else
+ m2 = ((word72) getbits36_28 (cpu.Ypair[0], 8)) << 44;
+ m2 |= (word72) cpu.Ypair[1] << 8;
+#endif
+
+ e2 = SIGNEXT8_int (getbits36_8 (cpu.Ypair[0], 0));
+ }
+ else
+ { // invert
+ m2 = convert_to_word72 (cpu.rA, cpu.rQ);
+ e2 = SIGNEXT8_int (cpu . rE & MASK8);
+
+ // round divisor per RJ78
+ // If AQ(64-71) is not = 0 and A(0) = 0, a 1 is added to AQ(63). Zero is moved to
+ // AQ(64-71), unconditionally. AQ(0-63) is then used as the divisor mantissa.
+ // ISOLTS-745 10b
+#ifdef NEED_128
+ if ((iszero_128 (and_128 (m2, SIGN72))) && m2.l & 0377)
+#else
+ if (!(m2 & SIGN72) && m2 & 0377)
+#endif
+ {
+#ifdef NEED_128
+ m2 = add_128 (m2, construct_128 (0, 0400));
+#else
+ m2 += 0400;
+#endif
+ // ISOLTS-745 10e asserts that an overflowing addition of 400 to 377777777777 7777777774xx does not shift the quotient (nor divisor)
+ // I surmise that the divisor is taken as unsigned 64 bits in this case
+ roundovf = 1;
+ }
+#ifdef NEED_128
+ putbits72 (& m2, 64, 8, construct_128 (0, 0));
+#else
+ putbits72 (& m2, 64, 8, 0);
+#endif
+
+ // 64-bit mantissa (incl sign)
+#ifdef NEED_128
+ m1 = lshift_128 (construct_128 (0, (uint64_t) getbits36_28 (cpu.Ypair[0], 8)), 44u); // 28-bit mantissa (incl sign)
+ //m1 = or_128 (m1, construct_128 (0, cpu.Ypair[1] << 8));
+ m1 = or_128 (m1, lshift_128 (construct_128 (0, cpu.Ypair[1]), 8u));
+#else
+ m1 = ((word72) getbits36_28 (cpu.Ypair[0], 8)) << 44;
+ m1 |= (word72) cpu.Ypair[1] << 8;
+#endif
+
+ e1 = SIGNEXT8_int (getbits36_8 (cpu.Ypair[0], 0));
+ }
+
+#ifdef NEED_128
+ if (iszero_128 (m1))
+#else
+ if (m1 == 0)
+#endif
+ {
+ SET_I_ZERO;
+ CLR_I_NEG;
+
+ cpu.rE = 0200U; /*-128*/
+ cpu.rA = 0;
+ HDBGRegA ();
+ cpu.rQ = 0;
+
+ return; // normalized 0
+ }
+
+ // make everything positive, but save sign info for later....
+ int sign = 1;
+#ifdef NEED_128
+ if (isnonzero_128 (and_128 (m1, SIGN72)))
+ {
+ SET_I_NEG; // in case of divide fault
+ if (iseq_128 (m1, SIGN72))
+ {
+#ifdef HEX_MODE
+ m1 = rshift_128 (m1, shift_amt);
+#else
+ m1 = rshift_128 (m1, 1);
+#endif
+ e1 += 1;
+ } else
+ m1 = and_128 (negate_128 (m1), MASK72);
+ sign = -sign;
+ } else {
+ CLR_I_NEG; // in case of divide fault
+ }
+
+ if ((isnonzero_128 (and_128 (m2, SIGN72))) && !roundovf)
+ {
+ if (iseq_128 (m2, SIGN72))
+ {
+#ifdef HEX_MODE
+ m2 = rshift_128 (m2, shift_amt);
+#else
+ m2 = rshift_128 (m2, 1);
+#endif
+ e2 += 1;
+ } else
+ m2 = and_128 (negate_128 (m2), MASK72);
+ sign = -sign;
+ }
+#else
+ if (m1 & SIGN72)
+ {
+ SET_I_NEG; // in case of divide fault
+ if (m1 == SIGN72)
+ {
+#ifdef HEX_MODE
+ m1 >>= shift_amt;
+#else
+ m1 >>= 1;
+#endif
+ e1 += 1;
+ } else
+ m1 = (~m1 + 1) & MASK72;
+ sign = -sign;
+ } else {
+ CLR_I_NEG; // in case of divide fault
+ }
+
+ if ((m2 & SIGN72) && !roundovf)
+ {
+ if (m2 == SIGN72)
+ {
+#ifdef HEX_MODE
+ m2 >>= shift_amt;
+#else
+ m2 >>= 1;
+#endif
+ e2 += 1;
+ } else
+ m2 = (~m2 + 1) & MASK72;
+ sign = -sign;
+ }
+#endif
+
+#ifdef NEED_128
+ if (iszero_128 (m2))
+#else
+ if (m2 == 0)
+#endif
+ {
+ // NB: If C(Y-pair)8,71 == 0 then the alignment loop will never exit! That's why it been moved before the alignment
+
+ SET_I_ZERO;
+ // NEG already set
+
+ // FDV: If the divisor mantissa C(Y-pair)8,71 is zero after alignment (HWR: why after?), the division does
+ // not take place. Instead, a divide check fault occurs, C(AQ) contains the dividend magnitude, and
+ // the negative indicator reflects the dividend sign.
+ // FDI: If the divisor mantissa C(AQ) is zero, the division does not take place.
+ // Instead, a divide check fault occurs and all the registers remain unchanged.
+ if (!bInvert) {
+ convert_to_word36 (m1, & cpu.rA, & cpu.rQ);
+ HDBGRegA ();
+ }
+ doFault (FAULT_DIV, fst_zero, "DFDV: divide check fault");
+ }
+
+#ifdef L68
+ cpu.ou.cycle |= ou_GOA;
+#endif
+#ifdef NEED_128
+ while (isge_128 (m1, m2))
+ {
+#ifdef HEX_MODE
+ m1 = rshift_128 (m1, shift_amt);
+#else
+ m1 = rshift_128 (m1, 1);
+#endif
+ e1 += 1;
+ }
+#else
+ while (m1 >= m2)
+ {
+#ifdef HEX_MODE
+ m1 >>= shift_amt;
+#else
+ m1 >>= 1;
+#endif
+ e1 += 1;
+ }
+#endif
+ int e3 = e1 - e2;
+ if (e3 > 127)
+ {
+ SET_I_EOFL;
+ if (tstOVFfault ())
+ dlyDoFault (FAULT_OFL, fst_zero, "dfdvX exp overflow fault");
+ }
+ if (e3 < -128)
+ {
+ SET_I_EUFL;
+ if (tstOVFfault ())
+ dlyDoFault (FAULT_OFL, fst_zero, "dfdvX exp underflow fault");
+ }
+
+#ifdef L68
+ cpu.ou.cycle |= ou_GD1;
+#endif
+
+ // We need 63 bits quotient + sign. Divisor is at most 64 bits.
+ // Do a 127 by 64 fractional divide
+ // lo 8bits are always zero
+#ifdef NEED_128
+ word72 m3 = divide_128 (lshift_128 (m1, 63-8), rshift_128 (m2, 8), NULL);
+#else
+ word72 m3 = ((uint128)m1 << (63-8)) / ((uint128)m2 >> 8);
+#endif
+#ifdef L68
+ cpu.ou.cycle |= ou_GD2;
+#endif
+
+#ifdef NEED_128
+ m3 = lshift_128 (m3, 8); // convert back to float
+ if (sign == -1)
+ m3 = and_128 (negate_128 (m3), MASK72);
+#else
+ m3 <<= 8; // convert back to float
+ if (sign == -1)
+ m3 = (~m3 + 1) & MASK72;
+#endif
+
+ convert_to_word36 (m3, & cpu.rA, & cpu.rQ);
+ HDBGRegA ();
+ cpu.rE = (word8) e3 & MASK8;
+
+ SC_I_ZERO (cpu.rA == 0 && cpu . rQ == 0);
+ SC_I_NEG (cpu.rA & SIGN36);
+
+ if (cpu.rA == 0 && cpu.rQ == 0) // set to normalized 0
+ cpu.rE = 0200U; /*-128*/
+ }
+
+// CANFAULT
+void dfdv (void)
+ {
+ dfdvX (false); // no inversion
+ }
+
+// CANFAULT
+void dfdi (void)
+ {
+ dfdvX (true);
+ }
+
+
+//#define DVF_HWR
+//#define DVF_FRACTIONAL
+#define DVF_CAC
+// CANFAULT
+void dvf (void)
+{
+//#ifdef DBGCAC
+//sim_printf ("DVF %"PRId64" %06o:%06o\n", cpu.cycleCnt, PPR.PSR, PPR.IC);
+//#endif
+ // C(AQ) / (Y)
+ // fractional quotient → C(A)
+ // fractional remainder → C(Q)
+
+ // A 71-bit fractional dividend (including sign) is divided by a 36-bit
+ // fractional divisor yielding a 36-bit fractional quotient (including
+ // sign) and a 36-bit fractional remainder (including sign). C(AQ)71 is
+ // ignored; bit position 35 of the remainder corresponds to bit position
+ // 70 of the dividend. The remainder sign is equal to the dividend sign
+ // unless the remainder is zero.
+ //
+ // If | dividend | >= | divisor | or if the divisor = 0, division does
+ // not take place. Instead, a divide check fault occurs, C(AQ) contains
+ // the dividend magnitude in absolute, and the negative indicator
+ // reflects the dividend sign.
+
+// HWR code
+//HWR--#ifdef DVF_HWR
+//HWR-- // m1 divedend
+//HWR-- // m2 divisor
+//HWR--
+//HWR-- word72 m1 = SIGNEXT36_72((cpu . rA << 36) | (cpu . rQ & 0777777777776LLU));
+//HWR-- word72 m2 = SIGNEXT36_72(cpu.CY);
+//HWR--
+//HWR--//sim_debug (DBG_CAC, & cpu_dev, "[%"PRId64"]\n", cpu.cycleCnt);
+//HWR--//sim_debug (DBG_CAC, & cpu_dev, "m1 "); print_int128 (m1); sim_printf ("\n");
+//HWR--//sim_debug (DBG_CAC, & cpu_dev, "-----------------\n");
+//HWR--//sim_debug (DBG_CAC, & cpu_dev, "m2 "); print_int128 (m2); sim_printf ("\n");
+//HWR--
+//HWR-- if (m2 == 0)
+//HWR-- {
+//HWR-- // XXX check flags
+//HWR-- SET_I_ZERO;
+//HWR-- SC_I_NEG (cpu . rA & SIGN36);
+//HWR--
+//HWR-- cpu . rA = 0;
+//HWR-- cpu . rQ = 0;
+//HWR--
+//HWR-- return;
+//HWR-- }
+//HWR--
+//HWR-- // make everything positive, but save sign info for later....
+//HWR-- int sign = 1;
+//HWR-- int dividendSign = 1;
+//HWR-- if (m1 & SIGN72)
+//HWR-- {
+//HWR-- m1 = (~m1 + 1);
+//HWR-- sign = -sign;
+//HWR-- dividendSign = -1;
+//HWR-- }
+//HWR--
+//HWR-- if (m2 & SIGN72)
+//HWR-- {
+//HWR-- m2 = (~m2 + 1);
+//HWR-- sign = -sign;
+//HWR-- }
+//HWR--
+//HWR-- if (m1 >= m2 || m2 == 0)
+//HWR-- {
+//HWR-- //cpu . rA = m1;
+//HWR-- cpu . rA = (m1 >> 36) & MASK36;
+//HWR-- cpu . rQ = m1 & 0777777777776LLU;
+//HWR--
+//HWR-- SET_I_ZERO;
+//HWR-- SC_I_NEG (cpu . rA & SIGN36);
+//HWR--
+//HWR-- doFault(FAULT_DIV, fst_zero, "DVF: divide check fault");
+//HWR-- }
+//HWR--
+//HWR-- uint128 dividend = (uint128)m1 << 63;
+//HWR-- uint128 divisor = (uint128)m2;
+//HWR--
+//HWR-- //uint128 m3 = ((uint128)m1 << 63) / (uint128)m2;
+//HWR-- //uint128 m3r = ((uint128)m1 << 63) % (uint128)m2;
+//HWR-- int128 m3 = (int128)(dividend / divisor);
+//HWR-- int128 m3r = (int128)(dividend % divisor);
+//HWR--
+//HWR-- if (sign == -1)
+//HWR-- m3 = -m3; //(~m3 + 1);
+//HWR--
+//HWR-- if (dividendSign == -1) // The remainder sign is equal to the dividend sign unless the remainder is zero.
+//HWR-- m3r = -m3r; //(~m3r + 1);
+//HWR--
+//HWR-- cpu . rA = (m3 >> 64) & MASK36;
+//HWR-- cpu . rQ = m3r & MASK36; //01777777777LL;
+//HWR--#endif
+
+// canonial code
+#ifdef DVF_FRACTIONAL
+// http://www.ece.ucsb.edu/~parhami/pres_folder/f31-book-arith-pres-pt4.pdf
+// slide 10: sequential algorithim
+
+ // dividend format
+ // 0 1 70 71
+ // s dividend x
+ // C(AQ)
+
+#ifdef L68
+ cpu.ou.cycle |= ou_GD1;
+#endif
+ int sign = 1;
+ bool dividendNegative = (getbits36_1 (cpu . rA, 0) != 0);
+ bool divisorNegative = (getbits36_1 (cpu.CY, 0) != 0);
+
+ // Get the 70 bits of the dividend (72 bits less the sign bit and the
+ // ignored bit 71.
+
+ // dividend format: . d(0) ... d(69)
+
+ uint128 zFrac = ((uint128) (cpu . rA & MASK35) << 35) | ((cpu . rQ >> 1) & MASK35);
+ if (dividendNegative)
+ {
+ zFrac = ~zFrac + 1;
+ sign = - sign;
+ }
+ zFrac &= MASK70;
+//sim_printf ("zFrac "); print_int128 (zFrac); sim_printf ("\n");
+
+ // Get the 35 bits of the divisor (36 bits less the sign bit)
+
+ // divisor format: . d(0) .... d(34) 0 0 0 .... 0
+
+#if 1
+ // divisor goes in the high half
+ uint128 dFrac = (cpu.CY & MASK35) << 35;
+ if (divisorNegative)
+ {
+ dFrac = ~dFrac + 1;
+ sign = - sign;
+ }
+ dFrac &= MASK35 << 35;
+#else
+ // divisor goes in the low half
+ uint128 dFrac = cpu.CY & MASK35;
+ if (divisorNegative)
+ {
+ dFrac = ~dFrac + 1;
+ sign = - sign;
+ }
+ dFrac &= MASK35;
+#endif
+
+ if (dFrac == 0)
+ {
+sim_printf ("DVFa A %012"PRIo64" Q %012"PRIo64" Y %012"PRIo64"\n", cpu.rA, cpu.rQ, cpu.CY);
+// case 1: 400000000000 000000000000 000000000000 --> 400000000000 000000000000
+// dFrac 000000000000 000000000000
+
+ //cpu . rA = (zFrac >> 35) & MASK35;
+ //cpu . rQ = (zFrac & MASK35) << 1;
+
+ //SC_I_ZERO (dFrac == 0);
+ //SC_I_NEG (cpu . rA & SIGN36);
+ SC_I_ZERO (cpu.CY == 0);
+ SC_I_NEG (cpu.rA & SIGN36);
+ doFault(FAULT_DIV, fst_zero, "DVF: divide check fault");
+ }
+
+#ifdef L68
+ cpu.ou.cycle |= ou_GD2;
+#endif
+ uint128 sn = zFrac;
+ word36 quot = 0;
+ for (uint i = 0; i < 35; i ++)
+ {
+ // 71 bit number
+ uint128 s2n = sn << 1;
+ if (s2n > dFrac)
+ {
+ s2n -= dFrac;
+ quot |= (1llu << (34 - i));
+ }
+ sn = s2n;
+ }
+ word36 remainder = sn;
+
+ if (sign == -1)
+ quot = ~quot + 1;
+
+ if (dividendNegative)
+ remainder = ~remainder + 1;
+
+#ifdef L68
+ cpu.ou.cycle |= ou_GD2;
+#endif
+ // I am surmising that the "If | dividend | >= | divisor |" is an
+ // overflow prediction; implement it by checking that the calculated
+ // quotient will fit in 35 bits.
+
+ if (quot & ~MASK35)
+ {
+ SC_I_ZERO (cpu.rA == 0);
+ SC_I_NEG (cpu.rA & SIGN36);
+
+ doFault(FAULT_DIV, fst_zero, "DVF: divide check fault");
+ }
+ cpu . rA = quot & MASK36;
+ HDBGRegA ();
+ cpu . rQ = remainder & MASK36;
+
+#endif
+
+// MM code
+#ifdef DVF_CAC
+
+#if 0
+ if (cpu.CY == 0)
+ {
+ // XXX check flags
+ SET_I_ZERO;
+ SC_I_NEG (cpu . rA & SIGN36);
+
+ cpu . rA = 0;
+ cpu . rQ = 0;
+
+ return;
+ }
+#endif
+
+
+ // dividend format
+ // 0 1 70 71
+ // s dividend x
+ // C(AQ)
+
+#ifdef L68
+ cpu.ou.cycle |= ou_GD1;
+#endif
+ int sign = 1;
+ bool dividendNegative = (getbits36_1 (cpu . rA, 0) != 0);
+ bool divisorNegative = (getbits36_1 (cpu.CY, 0) != 0);
+
+ // Get the 70 bits of the dividend (72 bits less the sign bit and the
+ // ignored bit 71.
+
+ // dividend format: . d(0) ... d(69)
+
+#ifdef NEED_128
+ uint128 zFrac = lshift_128 (construct_128 (0, cpu.rA & MASK35), 35);
+ zFrac = or_128 (zFrac, construct_128 (0, (cpu.rQ >> 1) & MASK35));
+//sim_debug (DBG_TRACEEXT, & cpu_dev, "zfrac %016"PRIx64" %016"PRIx64"\n", zFrac.h, zFrac.l);
+#else
+ uint128 zFrac = ((uint128) (cpu . rA & MASK35) << 35) | ((cpu . rQ >> 1) & MASK35);
+//sim_debug (DBG_TRACEEXT, & cpu_dev, "zfrac %016"PRIx64" %016"PRIx64"\n", (uint64) (zFrac>>64), (uint64) zFrac);
+#endif
+ //zFrac <<= 1; -- Makes Multics unbootable.
+
+ if (dividendNegative)
+ {
+#ifdef NEED_128
+ zFrac = negate_128 (zFrac);
+#else
+ zFrac = ~zFrac + 1;
+#endif
+ sign = - sign;
+ }
+#ifdef NEED_128
+ zFrac = and_128 (zFrac, MASK70);
+//sim_debug (DBG_TRACEEXT, & cpu_dev, "zfrac %016"PRIx64" %016"PRIx64"\n", zFrac.h, zFrac.l);
+#else
+ zFrac &= MASK70;
+//sim_debug (DBG_TRACEEXT, & cpu_dev, "zfrac %016"PRIx64" %016"PRIx64"\n", (uint64) (zFrac>>64), (uint64) zFrac);
+#endif
+
+ //char buf [128] = "";
+ //print_int128 (zFrac, buf);
+ //sim_debug (DBG_CAC, & cpu_dev, "zFrac %s\n", buf);
+
+ // Get the 35 bits of the divisor (36 bits less the sign bit)
+
+ // divisor format: . d(0) .... d(34) 0 0 0 .... 0
+
+ // divisor goes in the low half
+ uint128 dFrac = convert_to_word72 (0, cpu.CY & MASK35);
+#ifdef NEED_128
+//sim_debug (DBG_TRACEEXT, & cpu_dev, "dfrac %016"PRIx64" %016"PRIx64"\n", dFrac.h, dFrac.l);
+#else
+//sim_debug (DBG_TRACEEXT, & cpu_dev, "dfrac %016"PRIx64" %016"PRIx64"\n", (uint64) (dFrac>>64), (uint64) dFrac);
+#endif
+ if (divisorNegative)
+ {
+#ifdef NEED_128
+ dFrac = negate_128 (dFrac);
+#else
+ dFrac = ~dFrac + 1;
+#endif
+ sign = - sign;
+ }
+#ifdef NEED_128
+ dFrac = and_128 (dFrac, construct_128 (0, MASK35));
+//sim_debug (DBG_TRACEEXT, & cpu_dev, "dfrac %016"PRIx64" %016"PRIx64"\n", dFrac.h, dFrac.l);
+#else
+ dFrac &= MASK35;
+//sim_debug (DBG_TRACEEXT, & cpu_dev, "dfrac %016"PRIx64" %016"PRIx64"\n", (uint64) (dFrac>>64), (uint64) dFrac);
+#endif
+
+ //char buf2 [128] = "";
+ //print_int128 (dFrac, buf2);
+ //sim_debug (DBG_CAC, & cpu_dev, "dFrac %s\n", buf2);
+
+ //if (dFrac == 0 || zFrac >= dFrac)
+ //if (dFrac == 0 || zFrac >= dFrac << 35)
+#ifdef NEED_128
+ if (iszero_128 (dFrac))
+#else
+ if (dFrac == 0)
+#endif
+ {
+// case 1: 400000000000 000000000000 000000000000 --> 400000000000 000000000000
+// dFrac 000000000000 000000000000
+
+ //cpu . rA = (zFrac >> 35) & MASK35;
+ //cpu . rQ = (word36) ((zFrac & MASK35) << 1);
+// ISOLTS 730 expects the right to be zero and the sign
+// bit to be untouched.
+ cpu.rQ = cpu.rQ & (MASK35 << 1);
+
+ //SC_I_ZERO (dFrac == 0);
+ //SC_I_NEG (cpu . rA & SIGN36);
+ SC_I_ZERO (cpu.CY == 0);
+ SC_I_NEG (cpu.rA & SIGN36);
+ dlyDoFault(FAULT_DIV, fst_zero, "DVF: divide check fault");
+ return;
+ }
+
+#ifdef L68
+ cpu.ou.cycle |= ou_GD2;
+#endif
+#ifdef NEED_128
+ uint128 remainder;
+ uint128 quot = divide_128 (zFrac, dFrac, & remainder);
+//sim_debug (DBG_TRACEEXT, & cpu_dev, "remainder %016"PRIx64" %016"PRIx64"\n", remainder.h, remainder.l);
+//sim_debug (DBG_TRACEEXT, & cpu_dev, "quot %016"PRIx64" %016"PRIx64"\n", quot.h, quot.l);
+#else
+ uint128 quot = zFrac / dFrac;
+ uint128 remainder = zFrac % dFrac;
+//sim_debug (DBG_TRACEEXT, & cpu_dev, "remainder %016"PRIx64" %016"PRIx64"\n", (uint64) (remainder>>64), (uint64) remainder);
+//sim_debug (DBG_TRACEEXT, & cpu_dev, "quot %016"PRIx64" %016"PRIx64"\n", (uint64) (quot>>64), (uint64) quot);
+#endif
+
+ // I am surmising that the "If | dividend | >= | divisor |" is an
+ // overflow prediction; implement it by checking that the calculated
+ // quotient will fit in 35 bits.
+
+#ifdef NEED_128
+ if (isnonzero_128 (and_128 (quot, construct_128 (MASK36, ~MASK35))))
+#else
+ if (quot & ~MASK35)
+#endif
+ {
+//
+// this got:
+// s/b 373737373737 373737373740 200200
+// was 373737373740 373737373740 000200
+// ~~
+#if 1
+ bool Aneg = (cpu.rA & SIGN36) != 0; // blood type
+ bool AQzero = cpu.rA == 0 && cpu.rQ == 0;
+ if (cpu.rA & SIGN36)
+ {
+ cpu.rA = (~cpu.rA) & MASK36;
+ cpu.rQ = (~cpu.rQ) & MASK36;
+ cpu.rQ += 1;
+ if (cpu.rQ & BIT37) // overflow?
+ {
+ cpu.rQ &= MASK36;
+ cpu.rA = (cpu.rA + 1) & MASK36;
+ }
+ }
+#else
+ if (cpu.rA & SIGN36)
+ {
+ cpu.rA = (cpu.rA + 1) & MASK36;
+ cpu.rQ = (cpu.rQ + 1) & MASK36;
+ }
+#endif
+ HDBGRegA ();
+ //cpu . rA = (zFrac >> 35) & MASK35;
+ //cpu . rQ = (word36) ((zFrac & MASK35) << 1);
+// ISOLTS 730 expects the right to be zero and the sign
+// bit to be untouched.
+ cpu.rQ = cpu.rQ & (MASK35 << 1);
+
+ //SC_I_ZERO (dFrac == 0);
+ //SC_I_NEG (cpu . rA & SIGN36);
+ SC_I_ZERO (AQzero);
+ SC_I_NEG (Aneg);
+
+ dlyDoFault(FAULT_DIV, fst_zero, "DVF: divide check fault");
+ return;
+ }
+ //char buf3 [128] = "";
+ //print_int128 (remainder, buf3);
+ //sim_debug (DBG_CAC, & cpu_dev, "remainder %s\n", buf3);
+
+ if (sign == -1)
+#ifdef NEED_128
+ quot = negate_128 (quot);
+#else
+ quot = ~quot + 1;
+#endif
+
+ if (dividendNegative)
+#ifdef NEED_128
+ remainder = negate_128 (remainder);
+#else
+ remainder = ~remainder + 1;
+#endif
+
+#ifdef NEED_128
+ cpu.rA = quot.l & MASK36;
+ cpu.rQ = remainder.l & MASK36;
+#else
+ cpu . rA = quot & MASK36;
+ cpu . rQ = remainder & MASK36;
+#endif
+ HDBGRegA ();
+
+#endif
+
+//sim_debug (DBG_CAC, & cpu_dev, "Quotient %"PRId64" (%"PRIo64")\n", cpu . rA, cpu . rA);
+//sim_debug (DBG_CAC, & cpu_dev, "Remainder %"PRId64"\n", cpu . rQ);
+ SC_I_ZERO (cpu . rA == 0 && cpu . rQ == 0);
+ SC_I_NEG (cpu . rA & SIGN36);
+}
+
+
+/*!
+ * double precision floating round ...
+ */
+void dfrd (void)
+ {
+ // The dfrd instruction is identical to the frd instruction except that the
+ // rounding constant used is (11...1)65,71 instead of (11...1)29,71.
+
+ // If C(AQ) != 0, the frd instruction performs a true round to a precision
+ // of 64 bits and a normalization on C(EAQ).
+ // A true round is a rounding operation such that the sum of the result of
+ // applying the operation to two numbers of equal magnitude but opposite
+ // sign is exactly zero.
+
+ // The frd instruction is executed as follows:
+ // C(AQ) + (11...1)65,71 -> C(AQ)
+ // * If C(AQ)0 = 0, then a carry is added at AQ71
+ // * If overflow occurs, C(AQ) is shifted one place to the right and C(E)
+ // is increased by 1.
+ // * If overflow does not occur, C(EAQ) is normalized.
+ // * If C(AQ) = 0, C(E) is set to -128 and the zero indicator is set ON.
+
+ CPTUR (cptUseE);
+ float72 m = convert_to_word72 (cpu.rA, cpu.rQ);
+#ifdef NEED_128
+ if (iszero_128 (m))
+#else
+ if (m == 0)
+#endif
+ {
+ cpu.rE = 0200U; /*-128*/
+ SET_I_ZERO;
+ CLR_I_NEG;
+
+ return;
+ }
+
+ // C(AQ) + (11...1)65,71 -> C(AQ)
+ bool ovf;
+ word18 flags1 = 0;
+ word1 carry = 0;
+ // If C(AQ)0 = 0, then a carry is added at AQ71
+#ifdef NEED_128
+ if (iszero_128 (and_128 (m, SIGN72)))
+#else
+ if ((m & SIGN72) == 0)
+#endif
+ {
+ carry = 1;
+ }
+#ifdef NEED_128
+ m = Add72b (m, construct_128 (0, 0177), carry, I_OFLOW, & flags1, & ovf);
+#else
+ m = Add72b (m, 0177, carry, I_OFLOW, & flags1, & ovf);
+#endif
+
+ // 0 -> C(AQ)64,71
+#ifdef NEED_128
+ putbits72 (& m, 64, 8, construct_128 (0, 0)); // 64-71 => 0 per DH02
+#else
+ putbits72 (& m, 64, 8, 0); // 64-71 => 0 per DH02
+#endif
+
+ // If overflow occurs, C(AQ) is shifted one place to the right and C(E) is
+ // increased by 1.
+ // If overflow does not occur, C(EAQ) is normalized.
+ // All of this is done by fno, we just need to save the overflow flag
+
+ bool savedovf = TST_I_OFLOW;
+ SC_I_OFLOW(ovf);
+ convert_to_word36 (m, & cpu.rA, & cpu.rQ);
+
+ fno (& cpu.rE, & cpu.rA, & cpu.rQ);
+ HDBGRegA ();
+ SC_I_OFLOW(savedovf);
+ }
+
+void dfstr (word36 *Ypair)
+{
+ //! The dfstr instruction performs a double-precision true round and normalization on C(EAQ) as it is stored.
+ //! The definition of true round is located under the description of the frd instruction.
+ //! The definition of normalization is located under the description of the fno instruction.
+ //! Except for the precision of the stored result, the dfstr instruction is identical to the fstr instruction.
+
+ //! The dfrd instruction is identical to the frd instruction except that the rounding constant used is (11...1)65,71 instead of (11...1)29,71.
+
+ //! If C(AQ) ≠ 0, the frd instruction performs a true round to a precision of 28 bits and a normalization on C(EAQ).
+ //! A true round is a rounding operation such that the sum of the result of applying the operation to two numbers of equal magnitude but opposite sign is exactly zero.
+
+ //! The frd instruction is executed as follows:
+ //! C(AQ) + (11...1)29,71 → C(AQ)
+ //! * If C(AQ)0 = 0, then a carry is added at AQ71
+ //! * If overflow occurs, C(AQ) is shifted one place to the right and C(E) is increased by 1.
+ //! * If overflow does not occur, C(EAQ) is normalized.
+ //! * If C(AQ) = 0, C(E) is set to -128 and the zero indicator is set ON.
+
+ //! I believe AL39 is incorrect; bits 64-71 should be set to 0, not 65-71. DH02-01 & Bull 9000 is correct.
+
+ CPTUR (cptUseE);
+ word36 A = cpu . rA, Q = cpu . rQ;
+ word8 E = cpu . rE;
+ //A &= DMASK;
+ //Q &= DMASK;
+
+ float72 m = convert_to_word72 (A, Q);
+#ifdef NEED_128
+ if (iszero_128 (m))
+#else
+ if (m == 0)
+#endif
+ {
+ E = (word8)-128;
+ SET_I_ZERO;
+ CLR_I_NEG;
+
+ Ypair[0] = ((word36) E & MASK8) << 28;
+ Ypair[1] = 0;
+
+ return;
+ }
+
+
+ // C(AQ) + (11...1)65,71 → C(AQ)
+ bool ovf;
+ word18 flags1 = 0;
+ word1 carry = 0;
+ // If C(AQ)0 = 0, then a carry is added at AQ71
+#ifdef NEED_128
+ if (iszero_128 (and_128 (m, SIGN72)))
+#else
+ if ((m & SIGN72) == 0)
+#endif
+ {
+ carry = 1;
+ }
+#ifdef NEED_128
+ m = Add72b (m, construct_128 (0, 0177), carry, I_OFLOW, & flags1, & ovf);
+#else
+ m = Add72b (m, 0177, carry, I_OFLOW, & flags1, & ovf);
+#endif
+
+ // 0 -> C(AQ)65,71 (per. RJ78)
+#ifdef NEED_128
+ putbits72 (& m, 64, 8, construct_128 (0, 0)); // 64-71 => 0 per DH02
+#else
+ putbits72 (& m, 64, 8, 0); // 64-71 => 0 per DH02
+
+#endif
+
+ // If overflow occurs, C(AQ) is shifted one place to the right and C(E) is
+ // increased by 1.
+ // If overflow does not occur, C(EAQ) is normalized.
+ // All of this is done by fno, we just need to save the overflow flag
+
+ bool savedovf = TST_I_OFLOW;
+ SC_I_OFLOW(ovf);
+ convert_to_word36 (m, & A, & Q);
+
+ fno (& E, & A, & Q);
+ SC_I_OFLOW(savedovf);
+
+ Ypair[0] = (((word36)E & MASK8) << 28) | ((A & 0777777777400LL) >> 8);
+ Ypair[1] = ((A & 0377) << 28) | ((Q & 0777777777400LL) >> 8);
+}
+
+/*!
+ * double precision Floating Compare ...
+ */
+void dfcmp (void)
+{
+#if 0
+ //! C(E) :: C(Y-pair)0,7
+ //! C(AQ)0,63 :: C(Y-pair)8,71
+
+ //! Zero: If C(EAQ) = C(Y), then ON; otherwise OFF
+ //! Neg: If C(EAQ) < C(Y), then ON; otherwise OFF
+
+ //! The dfcmp instruction is identical to the fcmp instruction except for the precision of the mantissas actually compared.
+
+ //! Notes: The fcmp instruction is executed as follows:
+ //! The mantissas are aligned by shifting the mantissa of the operand with the algebraically smaller exponent to the right the number of places equal to the difference in the two exponents.
+ //! The aligned mantissas are compared and the indicators set accordingly.
+
+ int64 m1 = (int64) ((cpu . rA << 28) | ((cpu . rQ & 0777777777400LL) >> 8)); ///< only keep the 1st 64-bits :(
+ int e1 = SIGNEXT8_int (cpu . rE & MASK8);
+
+ //int64 m2 = (int64) (bitfieldExtract36(cpu.Ypair[0], 0, 28) << 36); ///< 64-bit mantissa (incl sign)
+ int64 m2 = ((int64) (getbits36_28 (cpu.Ypair[0], 8)) << 36); // 64-bit mantissa (incl sign)
+ m2 |= cpu.Ypair[1];
+ // CAC int64 m2 = (int64) (bitfieldExtract36(cpu.Ypair[0], 0, 28) << 44); ///< 64-bit mantissa (incl sign)
+ // CAC m2 |= cpu.Ypair[1] << 8;
+
+ //int8 e2 = (int8) (bitfieldExtract36(cpu.Ypair[0], 28, 8) & 0377U); ///< 8-bit signed integer (incl sign)
+ int e2 = SIGNEXT8_int (getbits36_8 (cpu.Ypair[0], 0));
+
+ //which exponent is smaller???
+
+ int shift_count = -1;
+
+ if (e1 == e2)
+ {
+ shift_count = 0;
+ }
+ else if (e1 < e2)
+ {
+ shift_count = abs(e2 - e1);
+ m1 >>= shift_count;
+ }
+ else
+ {
+ shift_count = abs(e1 - e2);
+ m2 >>= shift_count;
+ }
+
+ // need to do algebraic comparisons of mantissae
+ SC_I_ZERO (m1 == m2);
+ SC_I_NEG (m1 < m2);
+#else
+ // C(E) :: C(Y)0,7
+ // C(AQ)0,63 :: C(Y-pair)8,71
+ // * Zero: If | C(EAQ)| = | C(Y-pair) |, then ON; otherwise OFF
+ // * Neg : If | C(EAQ)| < | C(Y-pair) |, then ON; otherwise OFF
+
+ // The dfcmp instruction is identical to the fcmp instruction except for
+ // the precision of the mantissas actually compared.
+
+ // Notes: The fcmp instruction is executed as follows:
+ // The mantissas are aligned by shifting the mantissa of the operand with
+ // the algebraically smaller exponent to the right the number of places
+ // equal to the difference in the two exponents.
+ // The aligned mantissas are compared and the indicators set accordingly.
+
+ // C(AQ)0,63
+ CPTUR (cptUseE);
+#ifdef HEX_MODE
+ uint shift_amt = isHex() ? 4 : 1;
+#endif
+ word72 m1 = convert_to_word72 (cpu.rA, cpu.rQ & 0777777777400LL);
+ int e1 = SIGNEXT8_int (cpu . rE & MASK8);
+
+ // C(Y-pair)8,71
+#ifdef NEED_128
+ word72 m2 = lshift_128 (construct_128 (0, getbits36_28 (cpu.Ypair[0], 8)), (36 + 8));
+ //m2 = or_128 (m2, construct_128 (0, cpu.Ypair[1] << 8));
+ m2 = or_128 (m2, lshift_128 (construct_128 (0, cpu.Ypair[1]), 8u));
+#else
+ word72 m2 = (word72) getbits36_28 (cpu.Ypair[0], 8) << (36 + 8);
+ m2 |= cpu.Ypair[1] << 8;
+#endif
+ int e2 = SIGNEXT8_int (getbits36_8 (cpu.Ypair[0], 0));
+
+ //int e3 = -1;
+
+ //which exponent is smaller???
+
+ int shift_count = -1;
+ word1 notallzeros = 0;
+
+#ifdef NEED_128
+ if (e1 == e2)
+ {
+ shift_count = 0;
+ //e3 = e1;
+ }
+ else if (e1 < e2)
+ {
+#ifdef HEX_MODE
+ shift_count = abs(e2 - e1) * (int) shift_amt;
+#else
+ shift_count = abs(e2 - e1);
+#endif
+ bool s = isnonzero_128 (and_128 (m1, SIGN72)); ///< mantissa negative?
+ for(int n = 0 ; n < shift_count ; n += 1)
+ {
+ notallzeros |= m1.l & 1;
+ m1 = rshift_128 (m1, 1);
+ if (s)
+ m1 = or_128 (m1, SIGN72);
+ }
+
+#ifdef HEX_MODE
+ if (iseq_128 (m1, MASK72) && notallzeros == 1 && shift_count * (int) shift_amt > 71)
+ m1 = construct_128 (0, 0);
+#else
+ if (iseq_128 (m1, MASK72) && notallzeros == 1 && shift_count > 71)
+ m1 = construct_128 (0, 0);
+#endif
+ m1 = and_128 (m1, MASK72);
+ //e3 = e2;
+ }
+ else
+ {
+ // e2 < e1;
+#ifdef HEX_MODE
+ shift_count = abs(e1 - e2) * (int) shift_amt;
+#else
+ shift_count = abs(e1 - e2);
+#endif
+ bool s = isnonzero_128 (and_128 (m2, SIGN72)); ///< mantissa negative?
+ for(int n = 0 ; n < shift_count ; n += 1)
+ {
+ notallzeros |= m2.l & 1;
+ m2 = rshift_128 (m2, 1);
+ if (s)
+ m2 = or_128 (m2, SIGN72);
+ }
+#ifdef HEX_MODE
+ if (iseq_128 (m2, MASK72) && notallzeros == 1 && shift_count * (int) shift_amt > 71)
+ m2 = construct_128 (0, 0);
+#else
+ if (iseq_128 (m2, MASK72) && notallzeros == 1 && shift_count > 71)
+ m2 = construct_128 (0, 0);
+#endif
+ m2 = and_128 (m2, MASK72);
+ //e3 = e1;
+ }
+
+ SC_I_ZERO (iseq_128 (m1, m2));
+ int128 sm1 = SIGNEXT72_128 (m1);
+ int128 sm2 = SIGNEXT72_128 (m2);
+ SC_I_NEG (islt_s128 (sm1, sm2));
+#else // NEED_128
+ if (e1 == e2)
+ {
+ shift_count = 0;
+ //e3 = e1;
+ }
+ else if (e1 < e2)
+ {
+#ifdef HEX_MODE
+ shift_count = abs(e2 - e1) * (int) shift_amt;
+#else
+ shift_count = abs(e2 - e1);
+#endif
+ bool s = m1 & SIGN72; ///< mantissa negative?
+ for(int n = 0 ; n < shift_count ; n += 1)
+ {
+ notallzeros |= m1 & 1;
+ m1 >>= 1;
+ if (s)
+ m1 |= SIGN72;
+ }
+
+#ifdef HEX_MODE
+ if (m1 == MASK72 && notallzeros == 1 && shift_count * (int) shift_amt > 71)
+ m1 = 0;
+#else
+ if (m1 == MASK72 && notallzeros == 1 && shift_count > 71)
+ m1 = 0;
+#endif
+ m1 &= MASK72;
+ //e3 = e2;
+ }
+ else
+ {
+ // e2 < e1;
+#ifdef HEX_MODE
+ shift_count = abs(e1 - e2) * (int) shift_amt;
+#else
+ shift_count = abs(e1 - e2);
+#endif
+ bool s = m2 & SIGN72; ///< mantissa negative?
+ for(int n = 0 ; n < shift_count ; n += 1)
+ {
+ notallzeros |= m2 & 1;
+ m2 >>= 1;
+ if (s)
+ m2 |= SIGN72;
+ }
+#ifdef HEX_MODE
+ if (m2 == MASK72 && notallzeros == 1 && shift_count * (int) shift_amt > 71)
+ m2 = 0;
+#else
+ if (m2 == MASK72 && notallzeros == 1 && shift_count > 71)
+ m2 = 0;
+#endif
+ m2 &= MASK72;
+ //e3 = e1;
+ }
+
+ SC_I_ZERO (m1 == m2);
+ int128 sm1 = SIGNEXT72_128 (m1);
+ int128 sm2 = SIGNEXT72_128 (m2);
+ SC_I_NEG (sm1 < sm2);
+#endif // NEED_128
+#endif
+}
+
+/*!
+ * double precision Floating Compare magnitude ...
+ */
+void dfcmg (void)
+ {
+ // C(E) :: C(Y)0,7
+ // | C(AQ)0,27 | :: | C(Y)8,35 |
+ // * Zero: If | C(EAQ)| = | C(Y) |, then ON; otherwise OFF
+ // * Neg : If | C(EAQ)| < | C(Y) |, then ON; otherwise OFF
+
+ // Notes: The fcmp instruction is executed as follows:
+ // The mantissas are aligned by shifting the mantissa of the operand with
+ // the algebraically smaller exponent to the right the number of places
+ // equal to the difference in the two exponents.
+ // The aligned mantissas are compared and the indicators set accordingly.
+
+ // The dfcmg instruction is identical to the dfcmp instruction except that
+ // the magnitudes of the mantissas are compared instead of the algebraic
+ // values.
+
+ CPTUR (cptUseE);
+#ifdef HEX_MODE
+ uint shift_amt = isHex() ? 4 : 1;
+#endif
+ // C(AQ)0,63
+ word72 m1 = convert_to_word72 (cpu.rA & MASK36, cpu.rQ & 0777777777400LL);
+ int e1 = SIGNEXT8_int (cpu.rE & MASK8);
+
+ // C(Y-pair)8,71
+#ifdef NEED_128
+ //word72 m2 = construct_128 ((uint64_t) getbits36_28 (cpu.Ypair[0], 8) << 8, 0); // 28-bit mantissa (incl sign)
+ word72 m2 = lshift_128 (construct_128 (0, getbits36_28 (cpu.Ypair[0], 8)), (36 + 8));
+ //m2 = or_128 (m2, construct_128 (0, cpu.Ypair[1] << 8));
+ m2 = or_128 (m2, lshift_128 (construct_128 (0, cpu.Ypair[1]), 8u));
+#else
+ word72 m2 = (word72) getbits36_28 (cpu.Ypair[0], 8) << (36 + 8);
+ m2 |= cpu.Ypair[1] << 8;
+#endif
+ int e2 = SIGNEXT8_int (getbits36_8 (cpu.Ypair[0], 0));
+
+ //int e3 = -1;
+
+ //which exponent is smaller???
+#ifdef L68
+ cpu.ou.cycle = ou_GOE;
+#endif
+ int shift_count = -1;
+ word1 notallzeros = 0;
+
+ if (e1 == e2)
+ {
+ shift_count = 0;
+ //e3 = e1;
+ }
+ else if (e1 < e2)
+ {
+#ifdef L68
+ cpu.ou.cycle = ou_GOA;
+#endif
+#ifdef HEX_MODE
+ shift_count = abs(e2 - e1) * (int) shift_amt;
+#else
+ shift_count = abs(e2 - e1);
+#endif
+#ifdef NEED_128
+ bool s = isnonzero_128 (and_128 (m1, SIGN72)); ///< mantissa negative?
+ for( int n = 0; n < shift_count; n += 1)
+ {
+ notallzeros |= m1.l & 1;
+ m1 = rshift_128 (m1, 1);
+ if (s)
+ m1 = or_128 (m1, SIGN72);
+ }
+#ifdef HEX_MODE
+ if (iseq_128 (m1, MASK72) && notallzeros == 1 && shift_count * (int) shift_amt > 71)
+ m1 = construct_128 (0, 0);
+#else
+ if (iseq_128 (m1, MASK72) && notallzeros == 1 && shift_count > 71)
+ m1 = construct_128 (0, 0);
+#endif
+ m1 = and_128 (m1, MASK72);
+ //e3 = e2;
+#else
+ bool s = m1 & SIGN72; ///< mantissa negative?
+ for( int n = 0; n < shift_count; n += 1)
+ {
+ notallzeros |= m1 & 1;
+ m1 >>= 1;
+ if (s)
+ m1 |= SIGN72;
+ }
+#ifdef HEX_MODE
+ if (m1 == MASK72 && notallzeros == 1 && shift_count * (int) shift_amt > 71)
+ m1 = 0;
+#else
+ if (m1 == MASK72 && notallzeros == 1 && shift_count > 71)
+ m1 = 0;
+#endif
+ m1 &= MASK72;
+ //e3 = e2;
+#endif
+ }
+ else
+ {
+ // e2 < e1;
+#ifdef HEX_MODE
+ shift_count = abs(e1 - e2) * (int) shift_amt;
+#else
+ shift_count = abs(e1 - e2);
+#endif
+#ifdef NEED_128
+ bool s = isnonzero_128 (and_128 (m2, SIGN72)); ///< mantissa negative?
+ for(int n = 0; n < shift_count; n += 1)
+ {
+ notallzeros |= m2.l & 1;
+ m2 = rshift_128 (m2, 1);
+ if (s)
+ m2 = or_128 (m2, SIGN72);
+ }
+#ifdef HEX_MODE
+ if (iseq_128 (m2, MASK72) && notallzeros == 1 && shift_count * (int) shift_amt > 71)
+ m2 = construct_128 (0, 0);
+#else
+ if (iseq_128 (m2, MASK72) && notallzeros == 1 && shift_count > 71)
+ m2 = construct_128 (0, 0);
+#endif
+ m2 = and_128 (m2, MASK72);
+ //e3 = e1;
+#else
+ bool s = m2 & SIGN72; ///< mantissa negative?
+ for(int n = 0; n < shift_count; n += 1)
+ {
+ notallzeros |= m2 & 1;
+ m2 >>= 1;
+ if (s)
+ m2 |= SIGN72;
+ }
+#ifdef HEX_MODE
+ if (m2 == MASK72 && notallzeros == 1 && shift_count * (int) shift_amt > 71)
+ m2 = 0;
+#else
+ if (m2 == MASK72 && notallzeros == 1 && shift_count > 71)
+ m2 = 0;
+#endif
+ m2 &= MASK72;
+ //e3 = e1;
+#endif
+ }
+
+#ifdef NEED_128
+ SC_I_ZERO (iseq_128 (m1, m2));
+ int128 sm1 = SIGNEXT72_128 (m1);
+ if (sm1.h < 0)
+ sm1 = negate_s128 (sm1);
+ int128 sm2 = SIGNEXT72_128 (m2);
+ if (sm2.h < 0)
+ sm2 = negate_s128 (sm2);
+
+ SC_I_NEG (islt_s128 (sm1, sm2));
+#else
+ SC_I_ZERO (m1 == m2);
+ int128 sm1 = SIGNEXT72_128 (m1);
+ if (sm1 < 0)
+ sm1 = - sm1;
+ int128 sm2 = SIGNEXT72_128 (m2);
+ if (sm2 < 0)
+ sm2 = - sm2;
+
+ SC_I_NEG (sm1 < sm2);
+#endif
+ }
--- /dev/null
+/*
+ Copyright (c) 2007-2013 Michael Mondy
+ Copyright 2012-2016 by Harry Reed
+ Copyright 2013-2016 by Charles Anthony
+
+ All rights reserved.
+
+ This software is made available under the terms of the
+ ICU License -- ICU 1.8.1 and later.
+ See the LICENSE file at the top-level directory of this distribution and
+ at https://sourceforge.net/p/dps8m/code/ci/master/tree/LICENSE
+ */
+
+long double EAQToIEEElongdouble(void);
+double EAQToIEEEdouble(void);
+#ifndef QUIET_UNUSED
+float72 IEEElongdoubleToFloat72(long double f);
+void IEEElongdoubleToEAQ(long double f0);
+double float36ToIEEEdouble(float36 f36);
+float36 IEEEdoubleTofloat36(double f);
+#endif
+void ufa (bool sub);
+void ufs (void);
+void fno (word8 * E, word36 * A, word36 * Q);
+
+void fneg (void);
+void ufm (void);
+void fdv (void);
+void fdi (void);
+void frd (void);
+void fcmp(void);
+void fcmg(void);
+
+//void dufa (void);
+//void dufs (void);
+void dufa (bool subtraact);
+void dufm (void);
+void dfdv (void);
+void dfdi (void);
+void dfrd (void);
+void dfcmp (void);
+void dfcmg (void);
+
+void dvf (void);
+
+void dfstr (word36 *Ypair);
+void fstr(word36 *CY);
+
+
--- /dev/null
+/*
+ Copyright 2016 by Jean-Michel Merliot
+
+ All rights reserved.
+
+ This software is made available under the terms of the
+ ICU License -- ICU 1.8.1 and later.
+ See the LICENSE file at the top-level directory of this distribution and
+ at https://sourceforge.net/p/dps8m/code/ci/master/tree/LICENSE
+ */
+
+#ifndef DPS8_MATH128
+#define DPS8_MATH128
+
+#ifdef TEST_128
+// gcc -m32 -DTEST_128 -DNEED_128 dps8_math128.c
+#include <stdbool.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <stdio.h>
+typedef struct { uint64_t h; uint64_t l; } uint128;
+typedef struct { int64_t h; uint64_t l; } int128;
+
+typedef uint128 word72;
+typedef int128 word72s;
+typedef uint128 word73;
+typedef uint128 word74;
+
+#define construct_128(h, l) ((uint128_t) { (h), (l) })
+#define construct_s128(h, l) ((int128) { (h), (l) })
+
+#define MASK63 0x7FFFFFFFFFFFFFFF
+#define MASK64 0xFFFFFFFFFFFFFFFF
+#define SIGN64 ((uint64_t)1U << 63)
+#else
+
+#include "dps8.h"
+#endif
+
+#include "dps8_math128.h"
+
+#ifdef NEED_128
+
+bool iszero_128 (uint128 w)
+ {
+ if (w.h || w.l)
+ return false;
+ return true;
+ }
+
+bool isnonzero_128 (uint128 w)
+ {
+ if (w.h || w.l)
+ return true;
+ return false;
+ }
+
+bool iseq_128 (uint128 a, uint128 b)
+ {
+ return a.h == b.h && a.l == b.l;
+ }
+
+bool isgt_128 (uint128 a, uint128 b)
+ {
+ if (a.h > b.h) return true;
+ if (a.h < b.h) return false;
+ if (a.l > b.l) return true;
+ return false;
+ }
+
+bool islt_128 (uint128 a, uint128 b)
+ {
+ if (a.h < b.h) return true;
+ if (a.h > b.h) return false;
+ if (a.l < b.l) return true;
+ return false;
+ }
+
+bool isge_128 (uint128 a, uint128 b)
+ {
+ if (a.h > b.h) return true;
+ if (a.h < b.h) return false;
+ if (a.l >= b.l) return true;
+ return false;
+ }
+
+bool islt_s128 (int128 a, int128 b)
+ {
+ if (a.h < b.h) return true;
+ if (a.h > b.h) return false;
+ if (a.l < b.l) return true;
+ return false;
+ }
+
+bool isgt_s128 (int128 a, int128 b)
+ {
+ if (a.h > b.h) return true;
+ if (a.h < b.h) return false;
+ if (a.l > b.l) return true;
+ return false;
+ }
+
+uint128 and_128 (uint128 a, uint128 b)
+ {
+ return (uint128) {a.h & b.h, a.l & b.l};
+ }
+
+int128 and_s128 (int128 a, uint128 b)
+ {
+ return (int128) {a.h & (int64_t)b.h, a.l & b.l};
+ }
+
+uint128 or_128 (uint128 a, uint128 b)
+ {
+ return (uint128) {a.h | b.h, a.l | b.l};
+ }
+
+uint128 xor_128 (uint128 a, uint128 b)
+ {
+ return (uint128) {a.h ^ b.h, a.l ^ b.l};
+ }
+
+uint128 complement_128 (uint128 a)
+ {
+ return (uint128) {~ a.h, ~ a.l};
+ }
+
+uint128 add_128 (uint128 a, uint128 b)
+ {
+// To do carry detection from low to high, bust the low into 1 bit/63
+// bit fields; add the 63 bit fields checking for carry in the "sign"
+// bit; add the 1 bit fields plus that carry
+
+ uint64_t al63 = a.l & MASK63; // low 63 bits of a
+ uint64_t bl63 = b.l & MASK63; // low 63 bits of b
+ uint64_t l63 = al63 + bl63; // low 63 bits of a + b, with carry into bit 64
+ uint64_t c63 = l63 & SIGN64; // the carry out of low 63 a + b
+ l63 &= MASK63; // lose the carry bit
+
+ unsigned int al64 = (a.l >> 63) & 1; // bit 64 of a
+ unsigned int bl64 = (b.l >> 63) & 1; // bit 64 of b
+ unsigned int cl64 = (c63 >> 63) & 1; // the carry out of bit 63
+ uint64_t l64 = al64 + bl64 + cl64; // bit 64 a + b + carry in
+ unsigned int c64 = l64 > 1 ? 1 : 0; // carry out of bit 64
+ l64 = (l64 & 1) << 63; // put bit 64 in the right place
+ l64 |= l63; // put low 63 bits in
+ uint64_t h64 = a.h + b.h + c64; // compute the high
+ return construct_128 (h64, l64);
+ }
+
+uint128 subtract_128 (uint128 a, uint128 b)
+ {
+//printf ("sub a %016llx %016llx\n", a.h, a.l);
+//printf ("sub b %016llx %016llx\n", b.h, b.l);
+ bool borrow = !! (b.l > a.l);
+ uint128 res = construct_128 (a.h - b.h, a.l - b.l);
+ if (borrow)
+ res.h --;
+//printf ("sub res %016llx %016llx\n", res.h, res.l);
+ return res;
+ }
+
+uint128 negate_128 (uint128 a)
+ {
+ return add_128 (complement_128 (a), construct_128 (0, 1));
+ }
+
+int128 negate_s128 (int128 a)
+ {
+ uint128 t = add_128 (complement_128 (cast_128 (a)), construct_128 (0, 1));
+ return cast_s128 (t);
+ }
+
+uint128 lshift_128 (uint128 a, unsigned int n)
+ {
+ if (n < 64)
+ {
+ uint64_t nmask = (uint64_t) ((~(MASK64 << n)));
+//printf ("nmask %016llx\r\n", nmask);
+ // capture the high bits of the low half
+ uint64_t keep = (a.l >> (64 - n)) & nmask;
+//printf ("keep %016llx\r\n", keep);
+ // shift the low half
+ uint64_t l = a.l << n;
+//printf ("l %016llx\r\n", l);
+ // shift the high half
+ uint64_t h = a.h << n;
+//printf ("h %016llx\r\n", h);
+ // put the bits from the low into the high
+ h |= keep;
+//printf ("h|keep %016llx\r\n", h);
+ return construct_128 (h, l);
+ }
+ uint64_t h = a.l << (n - 64);
+ return construct_128 (h, 0);
+ }
+
+int128 lshift_s128 (int128 a, unsigned int n)
+ {
+ uint128 t = lshift_128 (cast_128 (a), n);
+ return cast_s128 (t);
+ }
+
+uint128 rshift_128 (uint128 a, unsigned int n)
+ {
+#if 0
+ uint64_t sign = a.h & SIGN64;
+ if (n < 64)
+ {
+ uint64_t nmask = (uint64_t) ((~(-1 << n)));
+ // capture the low bits of the high half
+ uint64_t keep = (a.h & nmask) << (64 - n);
+ // shift the low half
+ uint64_t l = a.l >> n;
+ // shifting zeros in from on high
+ l &= (uint64_t) (-(1 << (64 - n)));
+ // put in the bits from the high half
+ l |= keep;
+ // shift the high half
+ uint64_t h = a.h >> n;
+ if (sign)
+ // extending the signbit
+ l |= nmask << (64 - n);
+ else
+ // shifting zeros from on high
+ l &= (uint64_t) (-(1 << (64 - n)));
+ return construct_128 (h, l);
+ }
+ if (n == 64)
+ {
+ if (sign)
+ return construct_128 (MASK64, a.h);
+ return construct_128 (0, a.h);
+ }
+ uint64_t nmask = (uint64_t) ((1LLU << (n - 65)) - 1);
+printf ("nmask %016llx\n", nmask);
+ uint64_t l = a.h >> (n - 64);
+printf ("l %016llx\n", l);
+ uint64_t h = sign ? MASK64 : 0;
+printf ("h %016llx\n", h);
+ if (sign)
+ {
+ // extending the signbit
+ l |= nmask << (64 - n);
+ }
+ else
+ {
+ // shifting zeros from on high
+ l &= (uint64_t) (~(-1 << (64 - n)));
+ }
+printf ("l2 %016llx\n", l);
+#endif
+
+ uint64_t h = a.h;
+ uint64_t l = a.l;
+ uint64_t sign = a.h & SIGN64;
+ while (n)
+ {
+ uint64_t b = (h & 1) ? SIGN64 : 0;
+ h >>= 1;
+ h |= sign;
+ l >>= 1;
+ l &= MASK63;
+ l |= b;
+ n --;
+ }
+ return construct_128 (h, l);
+ }
+
+int128 rshift_s128 (int128 a, unsigned int n)
+ {
+ uint128 t = rshift_128 (cast_128 (a), n);
+ return cast_s128 (t);
+ }
+
+
+// http://www.icodeguru.com/Embedded/Hacker's-Delight/
+
+static void mulmn (uint32_t w[], uint32_t u[],
+ uint32_t v[], int m, int n)
+ {
+//for (int i = m - 1; i >= 0; i --) printf ("%08x", u [i]);
+//printf (" ");
+//for (int i = n - 1; i >= 0; i --) printf ("%08x", v [i]);
+//printf ("\n");
+ uint64_t k, t;
+ int i, j;
+
+ for (i = 0; i < m; i++)
+ w[i] = 0;
+
+ for (j = 0; j < n; j++)
+ {
+ k = 0;
+ for (i = 0; i < m; i++)
+ {
+ t = (uint64_t) u[i] * (uint64_t) v[j] + (uint64_t) w[i + j] + k;
+//printf ("%d %d %016llx\n",i, j, t);
+ w[i + j] = (uint32_t) t; // (I.e., t & 0xFFFF).
+ k = t >> 32;
+ }
+ w[j + m] = (uint32_t) k;
+ }
+ }
+
+static void mulmns (uint32_t w[], uint32_t u[],
+ uint32_t v[], int m, int n)
+ {
+ mulmn (w, u, v, m, n);
+
+ // Now w[] has the unsigned product. Correct by
+ // subtracting v*2**16m if u < 0, and
+ // subtracting u*2**16n if v < 0.
+
+ if ((int32_t)u[m - 1] < 0)
+ {
+ int b = 0; // Initialize borrow.
+ for (int j = 0; j < n; j++)
+ {
+ int t = (int) w[j + m] - (int) v[j] - b;
+ w[j + m] = (uint32_t) t;
+ b = t >> 31;
+ }
+ }
+ if ((int32_t)v[n - 1] < 0)
+ {
+ int b = 0;
+ for (int i = 0; i < m; i++)
+ {
+ int t = (int) w[i + n] - (int) u[i] - b;
+ w[i + n] = (uint32_t) t;
+ b = t >> 31;
+ }
+ }
+ return;
+ }
+
+static int32_t nlz (unsigned x)
+ {
+ unsigned y;
+ int n;
+
+ n = 32;
+ y = x >>16; if (y != 0) {n = n -16;x = y;}
+ y = x >> 8; if (y != 0) {n = n - 8;x = y;}
+ y = x >> 4; if (y != 0) {n = n - 4;x = y;}
+ y = x >> 2; if (y != 0) {n = n - 2;x = y;}
+ y = x >> 1; if (y != 0) return n - 2;
+ return n - (int) x;
+ }
+
+int divmnu (uint16_t q[], uint16_t r[],
+ const uint16_t u[], const uint16_t v[],
+ int m, int n)
+ {
+
+ const uint32_t b = 65536; // Number base (16 bits).
+ //uint16_t *un, *vn; // Normalized form of u, v.
+ unsigned qhat; // Estimated quotient digit.
+ unsigned rhat; // A remainder.
+ unsigned p; // Product of two digits.
+ int s, i, j, t, k;
+
+ if (m < n || n <= 0 || v[n-1] == 0)
+ return 1; // Return if invalid param.
+
+ // Take care of the case of a single-digit span
+ if (n == 1)
+ {
+ k = 0;
+ for (j = m - 1; j >= 0; j--)
+ {
+ q[j] = (uint16_t) (((unsigned int) k*b + u[j])/v[0]); // divisor here.
+ k = (int) (((unsigned int) k*b + u[j]) - q[j]*v[0]);
+ }
+ if (r != NULL) r[0] = (uint16_t) k;
+ return 0;
+ }
+
+ // Normalize by shifting v left just enough so that
+ // its high-order bit is on, and shift u left the
+ // same amount. We may have to append a high-order
+ // digit on the dividend; we do that unconditionally.
+
+ s = nlz (v[n-1]) - 16; // 0 <= s <= 16.
+ //vn = (uint16_t *) alloca (2*n);
+ uint16_t vn [n];
+ for (i = n - 1; i > 0; i--)
+ vn[i] = (uint16_t) (v[i] << s) | (uint16_t) (v[i-1] >> (16-s));
+ vn[0] = (uint16_t) (v[0] << s);
+
+ //un = (uint16_t *)alloca(2*(m + 1));
+ uint16_t un [m+1];
+ un[m] = u[m-1] >> (16-s);
+ for (i = m - 1; i > 0; i--)
+ un[i] = (uint16_t) (u[i] << s) | (uint16_t) (u[i-1] >> (16-s));
+ un[0] = (uint16_t) (u[0] << s);
+ for (j = m - n; j >= 0; j--)
+ { // Main loop.
+ // Compute estimate qhat of q[j].
+ qhat = (un[j+n]*b + un[j+n-1])/vn[n-1];
+ rhat = (un[j+n]*b + un[j+n-1]) - qhat*vn[n-1];
+again:
+ if (qhat >= b || qhat*vn[n-2] > b*rhat + un[j+n-2])
+ {
+ qhat = qhat - 1;
+ rhat = rhat + vn[n-1];
+ if (rhat < b) goto again;
+ }
+
+ // Multiply and subtract.
+ k = 0;
+ for (i = 0; i < n; i++)
+ {
+ p = qhat*vn[i];
+ t = (int32_t) un[i+j] - k - (int32_t) (p & 0xFFFF);
+ un[i+j] = (uint16_t) t;
+ k = (int) (p >> 16) - (t >> 16);
+ }
+ t = un[j+n] - k;
+ un[j+n] = (uint16_t) t;
+
+ q[j] = (uint16_t) qhat; // Store quotient digit.
+ if (t < 0)
+ { // If we subtracted too
+ q[j] = q[j] - 1; // much, add back.
+ k = 0;
+ for (i = 0; i < n; i++)
+ {
+ t = un[i+j] + vn[i] + k;
+ un[i+j] = (uint16_t) t;
+ k = t >> 16;
+ }
+ un[j+n] = (uint16_t) (un[j+n] + k);
+ }
+ } // End j.
+ // If the caller wants the remainder, unnormalize
+ // it and pass it back.
+ if (r != NULL)
+ {
+ for (i = 0; i < n; i++)
+ r[i] = (uint16_t) (un[i] >> s) | (uint16_t) (un[i+1] << (16-s));
+ }
+ return 0;
+ }
+
+uint128 multiply_128 (uint128 a, uint128 b)
+ {
+//printf ("%016llx%016llx %016llx%016llx\n", a.h,a.l,b.h,b.l);
+ const int l = 4;
+ uint32_t w[l+l], u[l], v[l];
+
+ u[3] = (uint32_t) (a.h >> 32);
+ u[2] = (uint32_t) a.h;
+ u[1] = (uint32_t) (a.l >> 32);
+ u[0] = (uint32_t) a.l;
+ v[3] = (uint32_t) (b.h >> 32);
+ v[2] = (uint32_t) b.h;
+ v[1] = (uint32_t) (b.l >> 32);
+ v[0] = (uint32_t) b.l;
+ mulmn (w, u, v, l, l);
+ return construct_128 (
+ (((uint64_t) w[3]) << 32) | w[2],
+ (((uint64_t) w[1]) << 32) | w[0]);
+ }
+
+int128 multiply_s128 (int128 a, int128 b)
+ {
+//printf ("%016llx%016llx %016llx%016llx\n", a.h,a.l,b.h,b.l);
+ const int l = 4;
+ uint32_t w[l+l], u[l], v[l];
+
+ u[3] = (uint32_t) (a.h >> 32);
+ u[2] = (uint32_t) a.h;
+ u[1] = (uint32_t) (a.l >> 32);
+ u[0] = (uint32_t) a.l;
+ v[3] = (uint32_t) (b.h >> 32);
+ v[2] = (uint32_t) b.h;
+ v[1] = (uint32_t) (b.l >> 32);
+ v[0] = (uint32_t) b.l;
+ mulmns (w, u, v, l, l);
+ return construct_s128 (
+ (((int64_t) w[3]) << 32) | w[2],
+ (((uint64_t) w[1]) << 32) | w[0]);
+ }
+
+// Note: divisor is < 2^16
+uint128 divide_128 (uint128 a, uint128 b, uint128 * remp)
+ {
+ const int m = 8;
+ const int n = 8;
+ uint16_t q[m], u[m], v[n];
+ u[0] = (uint16_t) a.l;
+ u[1] = (uint16_t) (a.l >> 16);
+ u[2] = (uint16_t) (a.l >> 32);
+ u[3] = (uint16_t) (a.l >> 48);
+ u[4] = (uint16_t) a.h;
+ u[5] = (uint16_t) (a.h >> 16);
+ u[6] = (uint16_t) (a.h >> 32);
+ u[7] = (uint16_t) (a.h >> 48);
+
+ v[0] = (uint16_t) b.l;
+ v[1] = (uint16_t) (b.l >> 16);
+ v[2] = (uint16_t) (b.l >> 32);
+ v[3] = (uint16_t) (b.l >> 48);
+ v[4] = (uint16_t) b.h;
+ v[5] = (uint16_t) (b.h >> 16);
+ v[6] = (uint16_t) (b.h >> 32);
+ v[7] = (uint16_t) (b.h >> 48);
+
+ int normlen;
+ for (normlen = 8; normlen > 0; normlen --)
+ if (v [normlen - 1])
+ break;
+ uint16_t r [8] = { 8 * 0 };
+ divmnu (q, remp ? r : NULL, u, v, m, normlen);
+ if (remp)
+ {
+ * remp = construct_128 (
+ (((uint64_t) r [7]) << 48) |
+ (((uint64_t) r [6]) << 32) |
+ (((uint64_t) r [5]) << 16) |
+ (((uint64_t) r [4]) << 0),
+ (((uint64_t) r [3]) << 48) |
+ (((uint64_t) r [2]) << 32) |
+ (((uint64_t) r [1]) << 16) |
+ (((uint64_t) r [0]) << 0));
+ }
+ return construct_128 (
+ (((uint64_t) q [7]) << 48) |
+ (((uint64_t) q [6]) << 32) |
+ (((uint64_t) q [5]) << 16) |
+ (((uint64_t) q [4]) << 0),
+ (((uint64_t) q [3]) << 48) |
+ (((uint64_t) q [2]) << 32) |
+ (((uint64_t) q [1]) << 16) |
+ (((uint64_t) q [0]) << 0));
+ }
+
+// Note: divisor is < 2^16
+uint128 divide_128_16 (uint128 a, uint16_t b, uint16_t * remp)
+ {
+ const int m = 8;
+ const int n = 1;
+ uint16_t q[m], u[m], v[n];
+ u[0] = (uint16_t) a.l;
+ u[1] = (uint16_t) (a.l >> 16);
+ u[2] = (uint16_t) (a.l >> 32);
+ u[3] = (uint16_t) (a.l >> 48);
+ u[4] = (uint16_t) a.h;
+ u[5] = (uint16_t) (a.h >> 16);
+ u[6] = (uint16_t) (a.h >> 32);
+ u[7] = (uint16_t) (a.h >> 48);
+
+ v[0] = (uint16_t) b;
+ divmnu (q, remp, u, v, m, n);
+ return construct_128 (
+ (((uint64_t) q [7]) << 48) |
+ (((uint64_t) q [6]) << 32) |
+ (((uint64_t) q [5]) << 16) |
+ (((uint64_t) q [4]) << 0),
+ (((uint64_t) q [3]) << 48) |
+ (((uint64_t) q [2]) << 32) |
+ (((uint64_t) q [1]) << 16) |
+ (((uint64_t) q [0]) << 0));
+ }
+
+//divide_128_32 (
+//00000000000000010000000000000000, 00010000) returned
+//00000000000000000001000000000000, 00000000
+//divide_128_32 (ffffffffffffffffffffffffffffffff, 00010000) returned f771ffffffffffffffffffffffffffff, 0000ffff
+
+// Note: divisor is < 2^32 and >= 2^16; i.e. high 16 bits *must* be none zero
+uint128 divide_128_32 (uint128 a, uint32_t b, uint32_t * remp)
+ {
+ const int m = 8;
+ const int n = 2;
+ uint16_t q[m], u[m], v[n], r[2];
+ u[0] = (uint16_t) a.l;
+ u[1] = (uint16_t) (a.l >> 16);
+ u[2] = (uint16_t) (a.l >> 32);
+ u[3] = (uint16_t) (a.l >> 48);
+ u[4] = (uint16_t) a.h;
+ u[5] = (uint16_t) (a.h >> 16);
+ u[6] = (uint16_t) (a.h >> 32);
+ u[7] = (uint16_t) (a.h >> 48);
+
+ v[0] = (uint16_t) b;
+ v[1] = (uint16_t) (b >> 16);
+
+ divmnu (q, remp ? r : NULL, u, v, m, n);
+ if (remp)
+ * remp = r [0] | (uint32_t) r[1] << 16;
+
+ return construct_128 (
+ (((uint64_t) q [7]) << 48) |
+ (((uint64_t) q [6]) << 32) |
+ (((uint64_t) q [5]) << 16) |
+ (((uint64_t) q [4]) << 0),
+ (((uint64_t) q [3]) << 48) |
+ (((uint64_t) q [2]) << 32) |
+ (((uint64_t) q [1]) << 16) |
+ (((uint64_t) q [0]) << 0));
+ }
+
+#ifdef TEST_128
+
+static void tisz (uint64_t h, uint64_t l, bool expect)
+ {
+ bool r = iszero_128 (construct_128 (h, l));
+ if (r != expect)
+ printf ("iszero_128 (%llu, %llu) returned %u\n", h, l, r);
+ return;
+ }
+
+static void tand (uint64_t ah, uint64_t al, uint64_t bh, uint64_t bl,
+ uint64_t rh, uint64_t rl)
+ {
+ uint128 a = construct_128 (ah, al);
+ uint128 b = construct_128 (bh, bl);
+ uint128 r = and_128 (a, b);
+ if (r.h != rh || r.l != rl)
+ printf ("and_128 (%016llx%016llx, %016llx%016llx) returned %016llx%016llx\n",
+ ah, al, bh, bl, r.h, r.l);
+ }
+
+static void tor (uint64_t ah, uint64_t al, uint64_t bh, uint64_t bl,
+ uint64_t rh, uint64_t rl)
+ {
+ uint128 a = construct_128 (ah, al);
+ uint128 b = construct_128 (bh, bl);
+ uint128 r = or_128 (a, b);
+ if (r.h != rh || r.l != rl)
+ printf ("or_128 (%016llx%016llx, %016llx%016llx) returned %016llx%016llx\n",
+ ah, al, bh, bl, r.h, r.l);
+ }
+
+static void tcomp (uint64_t ah, uint64_t al,
+ uint64_t rh, uint64_t rl)
+ {
+ uint128 a = construct_128 (ah, al);
+ uint128 r = complement_128 (a);
+ if (r.h != rh || r.l != rl)
+ printf ("complement_128 (%016llx%016llx) returned %016llx%016llx\n",
+ ah, al, r.h, r.l);
+ }
+
+static void tadd (uint64_t ah, uint64_t al, uint64_t bh, uint64_t bl,
+ uint64_t rh, uint64_t rl)
+ {
+ uint128 a = construct_128 (ah, al);
+ uint128 b = construct_128 (bh, bl);
+ uint128 r = add_128 (a, b);
+ if (r.h != rh || r.l != rl)
+ printf ("add_128 (%016llx%016llx, %016llx%016llx) returned %016llx%016llx\n",
+ ah, al, bh, bl, r.h, r.l);
+ }
+
+static void tsub (uint64_t ah, uint64_t al, uint64_t bh, uint64_t bl,
+ uint64_t rh, uint64_t rl)
+ {
+ uint128 a = construct_128 (ah, al);
+ uint128 b = construct_128 (bh, bl);
+ uint128 r = subtract_128 (a, b);
+ if (r.h != rh || r.l != rl)
+ printf ("subtract_128 (%016llx%016llx, %016llx%016llx) returned %016llx%016llx\n",
+ ah, al, bh, bl, r.h, r.l);
+ }
+
+static void tneg (uint64_t ah, uint64_t al,
+ uint64_t rh, uint64_t rl)
+ {
+ uint128 a = construct_128 (ah, al);
+ uint128 r = negate_128 (a);
+ if (r.h != rh || r.l != rl)
+ printf ("negate_128 (%016llx%016llx) returned %016llx%016llx\n",
+ ah, al, r.h, r.l);
+ }
+
+static void tgt (uint64_t ah, uint64_t al, uint64_t bh, uint64_t bl,
+ bool expect)
+ {
+ uint128 a = construct_128 (ah, al);
+ uint128 b = construct_128 (bh, bl);
+ bool r = isgt_128 (a, b);
+ if (r != expect)
+ printf ("gt_128 (%016llx%016llx, %016llx%016llx) returned %u\n",
+ ah, al, bh, bl, r);
+ }
+
+static void tls (uint64_t ah, uint64_t al, unsigned int n,
+ uint64_t rh, uint64_t rl)
+ {
+ uint128 a = construct_128 (ah, al);
+ uint128 r = lshift_128 (a, n);
+ if (r.h != rh || r.l != rl)
+ printf ("lshift_128 (%016llx%016llx, %u) returned %016llx%016llx\n",
+ ah, al, n, r.h, r.l);
+ }
+
+static void trs (uint64_t ah, uint64_t al, unsigned int n,
+ uint64_t rh, uint64_t rl)
+ {
+ uint128 a = construct_128 (ah, al);
+ uint128 r = rshift_128 (a, n);
+ if (r.h != rh || r.l != rl)
+ printf ("rshift_128 (%016llx%016llx, %u) returned %016llx%016llx\n",
+ ah, al, n, r.h, r.l);
+ }
+
+static void tmul (uint64_t ah, uint64_t al, uint64_t bh, uint64_t bl,
+ uint64_t rh, uint64_t rl)
+ {
+ uint128 a = construct_128 (ah, al);
+ uint128 b = construct_128 (bh, bl);
+ uint128 r = multiply_128 (a, b);
+ if (r.h != rh || r.l != rl)
+ printf ("multiply_128 (%016llx%016llx, %016llx%016llx) returned %016llx%016llx\n",
+ ah, al, bh, bl, r.h, r.l);
+ }
+
+static void tsmul (int64_t ah, uint64_t al, int64_t bh, uint64_t bl,
+ int64_t rh, uint64_t rl)
+ {
+ int128 a = construct_s128 (ah, al);
+ int128 b = construct_s128 (bh, bl);
+ int128 r = multiply_s128 (a, b);
+ if (r.h != rh || r.l != rl)
+ printf ("multiply_s128 (%016llx%016llx, %016llx%016llx) returned %016llx%016llx\n",
+ ah, al, bh, bl, r.h, r.l);
+ }
+
+static void tdiv16 (uint64_t ah, uint64_t al, uint16_t b,
+ uint64_t resh, uint64_t resl,
+ uint16_t remainder)
+ {
+ uint128 a = construct_128 (ah, al);
+ uint16_t rem;
+ uint128 res = divide_128_16 (a, b, & rem);
+ if (res.h != resh || res.l != resl || rem != remainder)
+ printf ("divide_128_16 (%016llx%016llx, %04x) returned %016llx%016llx, %04x\n",
+ ah, al, b, res.h, res.l, rem);
+ }
+
+static void tdiv32 (uint64_t ah, uint64_t al, uint32_t b,
+ uint64_t resh, uint64_t resl,
+ uint32_t remainder)
+ {
+ uint128 a = construct_128 (ah, al);
+ uint32_t rem;
+ uint128 res = divide_128_32 (a, b, & rem);
+ if (res.h != resh || res.l != resl || rem != remainder)
+ printf ("divide_128_32 (%016llx%016llx, %08x) returned %016llx%016llx, %08x\n",
+ ah, al, b, res.h, res.l, rem);
+ }
+
+int main (int argc, char * argv [])
+ {
+
+ //uint128 x = construct_128 (0, 0);
+ //int128 y;
+ //y = * (int128 *) & x;
+
+ tisz (0, 0, true);
+ tisz (1, 0, false);
+ tisz (0, 1, false);
+ tisz (1, 1, false);
+ tisz (SIGN64, 0, false);
+ tisz (0, SIGN64, false);
+ tisz (SIGN64, SIGN64, false);
+
+ tand (0, 0, 0, 0, 0, 0);
+ tand (MASK64, MASK64, 0, 0, 0, 0);
+ tand (0, 0, MASK64, MASK64, 0, 0);
+ tand (MASK64, MASK64, MASK64, MASK64, MASK64, MASK64);
+
+ tor (0, 0, 0, 0, 0, 0);
+ tor (MASK64, MASK64, 0, 0, MASK64, MASK64);
+ tor (0, 0, MASK64, MASK64, MASK64, MASK64);
+ tor (MASK64, MASK64, MASK64, MASK64, MASK64, MASK64);
+
+ tcomp (MASK64, MASK64, 0, 0);
+ tcomp (0, 0, MASK64, MASK64);
+ tcomp (0, 1, MASK64, MASK64 - 1);
+
+ tadd (0, 0, 0, 0, 0, 0);
+ tadd (0, 0, 0, 1, 0, 1);
+ tadd (0, 1, 0, 0, 0, 1);
+ tadd (0, 1, 0, 1, 0, 2);
+ tadd (0, 1, 0, MASK64, 1, 0);
+ tadd (0, 1, MASK64, MASK64, 0, 0);
+
+ tsub (0, 0, 0, 0, 0, 0);
+ tsub (0, 1, 0, 1, 0, 0);
+ tsub (MASK64, MASK64, MASK64, MASK64, 0, 0);
+ tsub (MASK64, MASK64, 0, 0, MASK64, MASK64);
+ tsub (0, 0, 0, 1, MASK64, MASK64);
+
+
+ tneg (0, 0, 0, 0);
+ tneg (0, 1, MASK64, MASK64);
+ tneg (MASK64, MASK64, 0, 1);
+
+ tgt (0, 0, 0, 0, false);
+ tgt (0, 0, 0, 1, false);
+ tgt (0, 1, 0, 0, true);
+ tgt (MASK64, MASK64, MASK64, MASK64, false);
+ tgt (0, 0, MASK64, MASK64, false);
+ tgt (MASK64, MASK64, 0, 0, true);
+
+ tls (0, 0, 0, 0, 0);
+ tls (MASK64, MASK64, 0, MASK64, MASK64);
+ tls (0, 1, 127, SIGN64, 0);
+ tls (0, MASK64, 64, MASK64, 0);
+ tls (0, MASK64, 1, 1, MASK64 - 1);
+ tls (0, 1, 64, 1, 0);
+ tls (0, 1, 63, 0, SIGN64);
+ tls (1, 0, 63, SIGN64, 0);
+
+ trs (0, 0, 0, 0, 0);
+ trs (MASK64, MASK64, 0, MASK64, MASK64);
+ trs (SIGN64, 0, 127, MASK64, MASK64);
+ trs (MASK64, 0, 64, MASK64, MASK64);
+ trs (MASK64, 0, 1, MASK64, SIGN64);
+ trs (1, 0, 64, 0, 1);
+ trs (1, 0, 1, 0, SIGN64);
+ trs (SIGN64, 0, 63, MASK64, 0);
+ trs (SIGN64, 0, 64, MASK64, SIGN64);
+
+ tmul (0, 0, 0, 0, 0, 0);
+ tmul (MASK64, MASK64, 0, 0, 0, 0);
+ tmul (0, 0, MASK64, MASK64, 0, 0);
+ tmul (0, 1, 0, 1, 0, 1);
+ tmul (0, 1, 0, 10, 0, 10);
+ tmul (0, 10, 0, 10, 0, 100);
+ tmul (0, 100, 0, 10, 0, 1000);
+ tmul (0, MASK64, 0, 2, 1, MASK64-1);
+//printf ("%016llx\n", (uint64_t) -1ll * (uint64_t) -1ll);
+//printf ("%016llx\n", (int64_t) -1ll * (int64_t) -1ll);
+ tmul (MASK64, MASK64, MASK64, MASK64, 0, 1);
+
+ tsmul (0, 1, MASK64, MASK64, MASK64, MASK64);
+ tsmul (MASK64, MASK64, MASK64, MASK64, 0, 1);
+
+
+ tdiv16 (0, 1, 1, 0, 1, 0);
+ tdiv16 (0, 10, 2, 0, 5, 0);
+ tdiv16 (MASK64, MASK64, 16, MASK64>>4, MASK64, 15);
+ tdiv16 (0, 3, 2, 0, 1, 1);
+
+ tdiv32 (1, 0, 1 << 16, 0, 1ll << 48, 0);
+ tdiv32 (MASK64, MASK64, 1 << 16, MASK64 >> 16, MASK64, 0xffff);
+ return 0;
+ }
+#endif
+
+#else
+
+#if __SIZEOF_LONG__ < 8 && ! defined (__MINGW64__)
+
+#include "dps8_math128.h"
+
+void __udivmodti3(UTItype div, UTItype dvd,UTItype *result,UTItype *remain);
+UTItype __udivti3(UTItype div, UTItype dvd);
+UTItype __udivti3(UTItype div, UTItype dvd);
+
+UTItype __umodti3(UTItype div, UTItype dvd);
+
+UTItype __udivti3(UTItype div, UTItype dvd)
+{
+ UTItype result,remain;
+
+ __udivmodti3(div,dvd,&result,&remain);
+
+ return result;
+}
+
+void __udivmodti3(UTItype div, UTItype dvd,UTItype *result,UTItype *remain)
+{
+ UTItype z1 = dvd;
+ UTItype z2 = (UTItype)1;
+
+ *result = (UTItype)0;
+ *remain = div;
+
+ if( z1 == (UTItype)0)
+ 1/0;
+
+ while( z1 < *remain )
+ {
+ z1 <<= 1 ;
+ z2 <<= 1;
+ }
+
+ do
+ {
+ if( *remain >= z1 )
+ {
+ *remain -= z1;
+ *result += z2;
+ }
+ z1 >>= 1;
+ z2 >>= 1;
+ } while( z2 );
+
+}
+
+TItype __divti3(TItype div, TItype dvd)
+{
+ int sign=1;
+
+ if (div < (TItype)0)
+ {
+ sign = -1;
+ div = -div;
+ }
+
+ if (dvd < (TItype)0)
+ {
+ sign = -sign;
+ dvd = -dvd;
+ }
+
+ if (sign > 0)
+ return (TItype)__udivti3(div,dvd);
+ else
+ return -((TItype)__udivti3(div,dvd));
+}
+
+TItype __modti3(TItype div, TItype dvd)
+{
+ int sign=1;
+
+ if (div < (TItype)0)
+ {
+ sign = -1;
+ div = -div;
+ }
+
+ if (dvd < (TItype)0)
+ {
+ sign = -sign;
+ dvd = -dvd;
+ }
+
+ if (sign > 0)
+ return (TItype)__umodti3(div,dvd);
+ else
+ return ((TItype)0-(TItype)__umodti3(div,dvd));
+}
+
+UTItype __umodti3(UTItype div, UTItype dvd)
+{
+ UTItype result,remain;
+
+ __udivmodti3(div,dvd,&result,&remain);
+
+ return remain;
+}
+
+TItype __multi3 (TItype u, TItype v)
+{
+ TItype result = (TItype)0;
+ int sign = 1;
+
+ if(u<0)
+ {
+ sign = -1;
+ u = -u;
+ }
+
+ while (u!=(TItype)0)
+ {
+ if( u&(TItype)1 )
+ result += v;
+ u>>=1;
+ v<<=1;
+ }
+
+ if ( sign < 0 )
+ return -result;
+ else
+ return result;
+
+}
+#endif
+#endif
+
+#endif
--- /dev/null
+/*
+ Copyright 2016 by Jean-Michel Merliot
+
+ All rights reserved.
+
+ This software is made available under the terms of the
+ ICU License -- ICU 1.8.1 and later.
+ See the LICENSE file at the top-level directory of this distribution and
+ at https://sourceforge.net/p/dps8m/code/ci/master/tree/LICENSE
+ *//*
+Defines 128 bits Integer for 32 bits platform
+*/
+
+#ifdef NEED_128
+
+//#define cast_128(x) (* (uint128 *) & (x))
+//#define cast_s128(x) (* (int128 *) & (x))
+#define cast_128(x) construct_128 ((uint64_t) (x).h, (x).l)
+#define cast_s128(x) construct_s128 ((int64_t) (x).h, (x).l)
+
+bool iszero_128 (uint128 w);
+bool isnonzero_128 (uint128 w);
+bool iseq_128 (uint128 a, uint128 b);
+bool isgt_128 (uint128 a, uint128 b);
+bool islt_128 (uint128 a, uint128 b);
+bool isge_128 (uint128 a, uint128 b);
+bool islt_s128 (int128 a, int128 b);
+bool isgt_s128 (int128 a, int128 b);
+uint128 and_128 (uint128 a, uint128 b);
+int128 and_s128 (int128 a, uint128 b);
+uint128 or_128 (uint128 a, uint128 b);
+uint128 xor_128 (uint128 a, uint128 b);
+uint128 add_128 (uint128 a, uint128 b);
+uint128 subtract_128 (uint128 a, uint128 b);
+uint128 multiply_128 (uint128 a, uint128 b);
+int128 multiply_s128 (int128 a, int128 b);
+uint128 divide_128_16 (uint128 a, uint16_t b, uint16_t * rem);
+uint128 divide_128_32 (uint128 a, uint32_t b, uint32_t * rem);
+uint128 divide_128 (uint128 a, uint128 b, uint128 * rem);
+uint128 complement_128 (uint128 a);
+uint128 negate_128 (uint128 a);
+int128 negate_s128 (int128 a);
+uint128 lshift_128 (uint128 a, unsigned int n);
+int128 lshift_s128 (int128 a, unsigned int n);
+uint128 rshift_128 (uint128 a, unsigned int n);
+int128 rshift_s128 (int128 a, unsigned int n);
+#else
+
+/* if (sizeof(long) < 8), I expect we're on a 32 bit system */
+
+#if __SIZEOF_LONG__ < 8 && ! defined (__MINGW64__)
+
+typedef int TItype __attribute__ ((mode (TI)));
+typedef unsigned int UTItype __attribute__ ((mode (TI)));
+
+typedef TItype __int128_t ;
+typedef UTItype __uint128_t ;
+#endif
+#endif
--- /dev/null
+/*
+ Copyright (c) 2007-2013 Michael Mondy
+ Copyright 2012-2016 by Harry Reed
+ Copyright 2013-2016 by Charles Anthony
+
+ All rights reserved.
+
+ This software is made available under the terms of the
+ ICU License -- ICU 1.8.1 and later.
+ See the LICENSE file at the top-level directory of this distribution and
+ at https://sourceforge.net/p/dps8m/code/ci/master/tree/LICENSE
+ */
+
+/**
+ * \file dps8_opcodetable.c
+ * \project dps8
+*/
+
+//#include <string.h>
+
+#include "dps8.h"
+#include "dps8_opcodetable.h"
+
+#define _EIS_ NO_TAG | NO_XED | NO_RPT | IGN_B29
+
+struct opcode_s opcodes10[02000] = {
+// NonEIS
+ /* 000 */
+ {NULL, 0, 0, 0, 0},
+ {"mme", NO_RPT, 0, 0, 0},
+ {"drl", NO_RPT, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {"mme2", NO_BAR | NO_RPT, 0, 0, 0},
+ {"mme3", NO_BAR | NO_RPT, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {"mme4", NO_BAR | NO_RPT, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {"nop", PREPARE_CA | NO_RPT, 0, 0, ru_none},
+ {"puls1", PREPARE_CA | NO_RPT, 0, 0, 0},
+ {"puls2", PREPARE_CA | NO_RPT, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {"cioc", READ_OPERAND | PRIV_INS | NO_RPT, NO_DDCSS, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {"adlx0", READ_OPERAND | NO_RPT, NO_CSS, 0, ru_A | ru_X0},
+ {"adlx1", READ_OPERAND, NO_CSS, 0, ru_A | ru_X1},
+ {"adlx2", READ_OPERAND, NO_CSS, 0, ru_A | ru_X2},
+ {"adlx3", READ_OPERAND, NO_CSS, 0, ru_A | ru_X3},
+ {"adlx4", READ_OPERAND, NO_CSS, 0, ru_A | ru_X4},
+ {"adlx5", READ_OPERAND, NO_CSS, 0, ru_A | ru_X5},
+ {"adlx6", READ_OPERAND, NO_CSS, 0, ru_A | ru_X6},
+ {"adlx7", READ_OPERAND, NO_CSS, 0, ru_A | ru_X7},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {"ldqc", RMW, NO_DDCSS, 0, ru_Q},
+ {"adl", READ_OPERAND, NO_CSS, 0, ru_AQ},
+ {"ldac", RMW, NO_DDCSS, 0, ru_A},
+ {"adla", READ_OPERAND, 0, 0, ru_A},
+ {"adlq", READ_OPERAND, 0, 0, ru_Q},
+ {"adlaq", READ_YPAIR, NO_DDCSS, 0, ru_AQ},
+ {"asx0", RMW | NO_RPT | NO_RPL, NO_DDCSS, 0, ru_X0},
+ {"asx1", RMW | NO_RPL, NO_DDCSS, 0, ru_X1},
+ {"asx2", RMW | NO_RPL, NO_DDCSS, 0, ru_X2},
+ {"asx3", RMW | NO_RPL, NO_DDCSS, 0, ru_X3},
+ {"asx4", RMW | NO_RPL, NO_DDCSS, 0, ru_X4},
+ {"asx5", RMW | NO_RPL, NO_DDCSS, 0, ru_X5},
+ {"asx6", RMW | NO_RPL, NO_DDCSS, 0, ru_X6},
+ {"asx7", RMW | NO_RPL, NO_DDCSS, 0, ru_X7},
+ {"adwp0", READ_OPERAND | NO_BAR | NO_RPT, NO_DLCSS, 0, 0},
+ {"adwp1", READ_OPERAND | NO_BAR | NO_RPT, NO_DLCSS, 0, 0},
+ {"adwp2", READ_OPERAND | NO_BAR | NO_RPT, NO_DLCSS, 0, 0},
+ {"adwp3", READ_OPERAND | NO_BAR | NO_RPT, NO_DLCSS, 0, 0},
+ {"aos", RMW | NO_RPL, NO_DDCSS, 0, ru_none},
+ {"asa", RMW | NO_RPL, NO_DDCSS, 0, ru_A},
+ {"asq", RMW | NO_RPL, NO_DDCSS, 0, ru_Q},
+ {"sscr", PREPARE_CA | PRIV_INS, NO_DDCSS, 0, ru_AQ},
+ {"adx0", READ_OPERAND | NO_RPT, NO_CSS, 0, ru_X0},
+ {"adx1", READ_OPERAND, NO_CSS, 0, ru_X1},
+ {"adx2", READ_OPERAND, NO_CSS, 0, ru_X2},
+ {"adx3", READ_OPERAND, NO_CSS, 0, ru_X3},
+ {"adx4", READ_OPERAND, NO_CSS, 0, ru_X4},
+ {"adx5", READ_OPERAND, NO_CSS, 0, ru_X5},
+ {"adx6", READ_OPERAND, NO_CSS, 0, ru_X6},
+ {"adx7", READ_OPERAND, NO_CSS, 0, ru_X7},
+ {NULL, 0, 0, 0, 0},
+ {"awca", READ_OPERAND, 0, 0, ru_A},
+ {"awcq", READ_OPERAND, 0, 0, ru_Q},
+ {"lreg", READ_YBLOCK8 | NO_RPT, NO_DDCSS, 0, MASK10},
+ {NULL, 0, 0, 0, 0},
+ {"ada", READ_OPERAND, 0, 0, ru_A},
+ {"adq", READ_OPERAND, 0, 0, ru_Q},
+ {"adaq", READ_YPAIR, NO_DDCSS, 0, ru_AQ},
+
+ /* 100 */
+ {"cmpx0", READ_OPERAND | NO_RPT, NO_CSS, 0, ru_X0},
+ {"cmpx1", READ_OPERAND, NO_CSS, 0, ru_X1},
+ {"cmpx2", READ_OPERAND, NO_CSS, 0, ru_X2},
+ {"cmpx3", READ_OPERAND, NO_CSS, 0, ru_X3},
+ {"cmpx4", READ_OPERAND, NO_CSS, 0, ru_X4},
+ {"cmpx5", READ_OPERAND, NO_CSS, 0, ru_X5},
+ {"cmpx6", READ_OPERAND, NO_CSS, 0, ru_X6},
+ {"cmpx7", READ_OPERAND, NO_CSS, 0, ru_X7},
+ {NULL, 0, 0, 0, 0},
+ {"cwl", READ_OPERAND, 0, 0, ru_AQ},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {"cmpa", READ_OPERAND, 0, 0, ru_A},
+ {"cmpq", READ_OPERAND, 0, 0, ru_Q},
+ {"cmpaq", READ_YPAIR, NO_DDCSS, 0, ru_AQ},
+ {"sblx0", READ_OPERAND | NO_RPT, NO_CSS, 0, ru_X0},
+ {"sblx1", READ_OPERAND, NO_CSS, 0, ru_X1},
+ {"sblx2", READ_OPERAND, NO_CSS, 0, ru_X2},
+ {"sblx3", READ_OPERAND, NO_CSS, 0, ru_X3},
+ {"sblx4", READ_OPERAND, NO_CSS, 0, ru_X4},
+ {"sblx5", READ_OPERAND, NO_CSS, 0, ru_X5},
+ {"sblx6", READ_OPERAND, NO_CSS, 0, ru_X6},
+ {"sblx7", READ_OPERAND, NO_CSS, 0, ru_X7},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {"sbla", READ_OPERAND, 0, 0, ru_A},
+ {"sblq", READ_OPERAND, 0, 0, ru_Q},
+ {"sblaq", READ_YPAIR, NO_DDCSS, 0, ru_AQ},
+ {"ssx0", RMW | NO_RPT | NO_RPL, NO_DDCSS, 0, ru_X0},
+ {"ssx1", RMW | NO_RPL, NO_DDCSS, 0, ru_X1},
+ {"ssx2", RMW | NO_RPL, NO_DDCSS, 0, ru_X2},
+ {"ssx3", RMW | NO_RPL, NO_DDCSS, 0, ru_X3},
+ {"ssx4", RMW | NO_RPL, NO_DDCSS, 0, ru_X4},
+ {"ssx5", RMW | NO_RPL, NO_DDCSS, 0, ru_X5},
+ {"ssx6", RMW | NO_RPL, NO_DDCSS, 0, ru_X6},
+ {"ssx7", RMW | NO_RPL, NO_DDCSS, 0, ru_X7},
+ {"adwp4", READ_OPERAND | NO_BAR | NO_RPT, NO_DLCSS, 0, 0},
+ {"adwp5", READ_OPERAND | NO_BAR | NO_RPT, NO_DLCSS, 0, 0},
+ {"adwp6", READ_OPERAND | NO_BAR | NO_RPT, NO_DLCSS, 0, 0},
+ {"adwp7", READ_OPERAND | NO_BAR | NO_RPT, NO_DLCSS, 0, 0},
+ {"sdbr", STORE_YPAIR | PRIV_INS | NO_RPT, NO_DDCSS, 0, 0},
+ {"ssa", RMW | NO_RPL, NO_DDCSS, 0, ru_A},
+ {"ssq", RMW | NO_RPL, NO_DDCSS, 0, ru_Q},
+ {NULL, 0, 0, 0, 0},
+ {"sbx0", READ_OPERAND | NO_RPT, NO_CSS, 0, ru_X0},
+ {"sbx1", READ_OPERAND, NO_CSS, 0, ru_X1},
+ {"sbx2", READ_OPERAND, NO_CSS, 0, ru_X2},
+ {"sbx3", READ_OPERAND, NO_CSS, 0, ru_X3},
+ {"sbx4", READ_OPERAND, NO_CSS, 0, ru_X4},
+ {"sbx5", READ_OPERAND, NO_CSS, 0, ru_X5},
+ {"sbx6", READ_OPERAND, NO_CSS, 0, ru_X6},
+ {"sbx7", READ_OPERAND, NO_CSS, 0, ru_X7},
+ {NULL, 0, 0, 0, 0},
+ {"swca", READ_OPERAND, 0, 0, ru_A},
+ {"swcq", READ_OPERAND, 0, 0, ru_Q},
+ {"lpri", READ_YBLOCK16 | NO_BAR | NO_RPT, NO_DDCSS, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {"sba", READ_OPERAND, 0, 0, ru_A},
+ {"sbq", READ_OPERAND, 0, 0, ru_Q},
+ {"sbaq", READ_YPAIR, NO_DDCSS, 0, ru_AQ},
+
+ /* 200 */
+ {"cnax0", READ_OPERAND | NO_RPT, NO_CSS, 0, ru_X0},
+ {"cnax1", READ_OPERAND, NO_CSS, 0, ru_X1},
+ {"cnax2", READ_OPERAND, NO_CSS, 0, ru_X2},
+ {"cnax3", READ_OPERAND, NO_CSS, 0, ru_X3},
+ {"cnax4", READ_OPERAND, NO_CSS, 0, ru_X4},
+ {"cnax5", READ_OPERAND, NO_CSS, 0, ru_X5},
+ {"cnax6", READ_OPERAND, NO_CSS, 0, ru_X6},
+ {"cnax7", READ_OPERAND, NO_CSS, 0, ru_X7},
+ {NULL, 0, 0, 0, 0},
+ {"cmk", READ_OPERAND, 0, 0, ru_AQ},
+ // XXX AL-39 seems wrong w.r.t absa; it makes no sense as privileged.
+ {"absa", PREPARE_CA | PRIV_INS | NO_RPT, NO_DDCSS, 0, ru_A},
+ {"epaq", PREPARE_CA | NO_BAR | NO_RPT, NO_DDCSS, 0, ru_AQ},
+ {"sznc", RMW, NO_DDCSS, 0, ru_none},
+ {"cnaa", READ_OPERAND, 0, 0, ru_A},
+ {"cnaq", READ_OPERAND, 0, 0, ru_Q},
+ {"cnaaq", READ_YPAIR, NO_DDCSS, 0, ru_AQ},
+ {"ldx0", READ_OPERAND | NO_RPT | NO_RPL, NO_CSS, 0, ru_X0},
+ {"ldx1", READ_OPERAND, NO_CSS, 0, ru_X1},
+ {"ldx2", READ_OPERAND, NO_CSS, 0, ru_X2},
+ {"ldx3", READ_OPERAND, NO_CSS, 0, ru_X3},
+ {"ldx4", READ_OPERAND, NO_CSS, 0, ru_X4},
+ {"ldx5", READ_OPERAND, NO_CSS, 0, ru_X5},
+ {"ldx6", READ_OPERAND, NO_CSS, 0, ru_X6},
+ {"ldx7", READ_OPERAND, NO_CSS, 0, ru_X7},
+ {"lbar", READ_OPERAND | NO_RPT | NO_BAR, NO_CSS, 0, 0},
+ {"rsw", PREPARE_CA | PRIV_INS | NO_RPT, 0, 0, ru_A},
+ {"ldbr", READ_YPAIR | PRIV_INS | NO_RPT, NO_DDCSS, 0, 0},
+ {"rmcm", PRIV_INS, NO_DDCSS, 0, ru_AQ},
+ {"szn", READ_OPERAND, 0, 0, ru_none},
+ {"lda", READ_OPERAND, 0, 0, ru_A},
+ {"ldq", READ_OPERAND, 0, 0, ru_Q},
+ {"ldaq", READ_YPAIR, NO_DDCSS, 0, ru_AQ},
+ {"orsx0", RMW | NO_RPT | NO_RPL, NO_DDCSS, 0, ru_X0},
+ {"orsx1", RMW | NO_RPL, NO_DDCSS, 0, ru_X1},
+ {"orsx2", RMW | NO_RPL, NO_DDCSS, 0, ru_X2},
+ {"orsx3", RMW | NO_RPL, NO_DDCSS, 0, ru_X3},
+ {"orsx4", RMW | NO_RPL, NO_DDCSS, 0, ru_X4},
+ {"orsx5", RMW | NO_RPL, NO_DDCSS, 0, ru_X5},
+ {"orsx6", RMW | NO_RPL, NO_DDCSS, 0, ru_X6},
+ {"orsx7", RMW | NO_RPL, NO_DDCSS, 0, ru_X7},
+ {"spri0", STORE_YPAIR | NO_BAR | NO_RPT, NO_DDCSS, 0, 0},
+ {"spbp1", STORE_YPAIR | NO_BAR | NO_RPT, NO_DDCSS, 0, 0},
+ {"spri2", STORE_YPAIR | NO_BAR | NO_RPT, NO_DDCSS, 0, 0},
+ {"spbp3", STORE_YPAIR | NO_BAR | NO_RPT, NO_DDCSS, 0, 0},
+ {"spri", STORE_YBLOCK16 | NO_BAR | NO_RPT, NO_DDCSS, 0, 0},
+ {"orsa", RMW | NO_RPL, NO_DDCSS, 0, ru_A},
+ {"orsq", RMW | NO_RPL, NO_DDCSS, 0, ru_Q},
+ {"lsdp", READ_YBLOCK16 | PRIV_INS | NO_RPT, NO_DDCSS, 0, 0}, // not available on a dps8m
+ {"orx0", READ_OPERAND | NO_RPT, NO_CSS, 0, ru_X0},
+ {"orx1", READ_OPERAND, NO_CSS, 0, ru_X1},
+ {"orx2", READ_OPERAND, NO_CSS, 0, ru_X2},
+ {"orx3", READ_OPERAND, NO_CSS, 0, ru_X3},
+ {"orx4", READ_OPERAND, NO_CSS, 0, ru_X4},
+ {"orx5", READ_OPERAND, NO_CSS, 0, ru_X5},
+ {"orx6", READ_OPERAND, NO_CSS, 0, ru_X6},
+ {"orx7", READ_OPERAND, NO_CSS, 0, ru_X7},
+ {"tsp0", TRANSFER_INS | TSPN_INS | NO_RPT | NO_BAR, NO_DDCSS, 0, 0},
+ {"tsp1", TRANSFER_INS | TSPN_INS | NO_RPT | NO_BAR, NO_DDCSS, 0, 0},
+ {"tsp2", TRANSFER_INS | TSPN_INS | NO_RPT | NO_BAR, NO_DDCSS, 0, 0},
+ {"tsp3", TRANSFER_INS | TSPN_INS | NO_RPT | NO_BAR, NO_DDCSS, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {"ora", READ_OPERAND, 0, 0, ru_A},
+ {"orq", READ_OPERAND, 0, 0, ru_Q},
+ {"oraq", READ_YPAIR, NO_DDCSS, 0, ru_AQ},
+
+ /* 300 */
+ {"canx0", READ_OPERAND | NO_RPT, NO_CSS, 0, ru_X0},
+ {"canx1", READ_OPERAND, NO_CSS, 0, ru_X1},
+ {"canx2", READ_OPERAND, NO_CSS, 0, ru_X2},
+ {"canx3", READ_OPERAND, NO_CSS, 0, ru_X3},
+ {"canx4", READ_OPERAND, NO_CSS, 0, ru_X4},
+ {"canx5", READ_OPERAND, NO_CSS, 0, ru_X5},
+ {"canx6", READ_OPERAND, NO_CSS, 0, ru_X6},
+ {"canx7", READ_OPERAND, NO_CSS, 0, ru_X7},
+ {"eawp0", PREPARE_CA | NO_BAR | NO_RPT, NO_DDCSS, 0, 0},
+ {"easp0", PREPARE_CA | NO_BAR | NO_RPT, NO_DDCSS, 0, 0},
+ {"eawp2", PREPARE_CA | NO_BAR | NO_RPT, NO_DDCSS, 0, 0},
+ {"easp2", PREPARE_CA | NO_BAR | NO_RPT, NO_DDCSS, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {"cana", READ_OPERAND, 0, 0, ru_A},
+ {"canq", READ_OPERAND, 0, 0, ru_Q},
+ {"canaq", READ_YPAIR, NO_DDCSS, 0, ru_AQ},
+ {"lcx0", READ_OPERAND | NO_RPT | NO_RPL, NO_CSS, 0, ru_X0},
+ {"lcx1", READ_OPERAND, NO_CSS, 0, ru_X1},
+ {"lcx2", READ_OPERAND, NO_CSS, 0, ru_X2},
+ {"lcx3", READ_OPERAND, NO_CSS, 0, ru_X3},
+ {"lcx4", READ_OPERAND, NO_CSS, 0, ru_X4},
+ {"lcx5", READ_OPERAND, NO_CSS, 0, ru_X5},
+ {"lcx6", READ_OPERAND, NO_CSS, 0, ru_X6},
+ {"lcx7", READ_OPERAND, NO_CSS, 0, ru_X7},
+ {"eawp4", PREPARE_CA | NO_BAR | NO_RPT, NO_DDCSS, 0, 0},
+ {"easp4", PREPARE_CA | NO_BAR | NO_RPT, NO_DDCSS, 0, 0},
+ {"eawp6", PREPARE_CA | NO_BAR | NO_RPT, NO_DDCSS, 0, 0},
+ {"easp6", PREPARE_CA | NO_BAR | NO_RPT, NO_DDCSS, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {"lca", READ_OPERAND, 0, 0, ru_A},
+ {"lcq", READ_OPERAND, 0, 0, ru_Q},
+ {"lcaq", READ_YPAIR, NO_DDCSS, 0, ru_AQ},
+ {"ansx0", RMW | NO_RPT | NO_RPL, NO_DDCSS, 0, ru_X0},
+ {"ansx1", RMW | NO_RPL, NO_DDCSS, 0, ru_X1},
+ {"ansx2", RMW | NO_RPL, NO_DDCSS, 0, ru_X2},
+ {"ansx3", RMW | NO_RPL, NO_DDCSS, 0, ru_X3},
+ {"ansx4", RMW | NO_RPL, NO_DDCSS, 0, ru_X4},
+ {"ansx5", RMW | NO_RPL, NO_DDCSS, 0, ru_X5},
+ {"ansx6", RMW | NO_RPL, NO_DDCSS, 0, ru_X6},
+ {"ansx7", RMW | NO_RPL, NO_DDCSS, 0, ru_X7},
+ {"epp0", PREPARE_CA | NO_BAR | NO_RPT, NO_DDCSS, 0, 0},
+ {"epbp1", PREPARE_CA | NO_BAR | NO_RPT, NO_DDCSS, 0, 0},
+ {"epp2", PREPARE_CA | NO_BAR | NO_RPT, NO_DDCSS, 0, 0},
+ {"epbp3", PREPARE_CA | NO_BAR | NO_RPT, NO_DDCSS, 0, 0},
+ {"stac", RMW | NO_RPL | NO_BAR, NO_DDCSS, 0, ru_A},
+ {"ansa", RMW | NO_RPL, NO_DDCSS, 0, ru_A},
+ {"ansq", RMW | NO_RPL, NO_DDCSS, 0, ru_Q},
+ {"stcd", STORE_YPAIR | NO_RPT | NO_BAR, NO_DDCSS, 0, 0},
+ {"anx0", READ_OPERAND | NO_RPT, NO_CSS, 0, ru_X0},
+ {"anx1", READ_OPERAND, NO_CSS, 0, ru_X1},
+ {"anx2", READ_OPERAND, NO_CSS, 0, ru_X2},
+ {"anx3", READ_OPERAND, NO_CSS, 0, ru_X3},
+ {"anx4", READ_OPERAND, NO_CSS, 0, ru_X4},
+ {"anx5", READ_OPERAND, NO_CSS, 0, ru_X5},
+ {"anx6", READ_OPERAND, NO_CSS, 0, ru_X6},
+ {"anx7", READ_OPERAND, NO_CSS, 0, ru_X7},
+ {"epp4", PREPARE_CA | NO_BAR | NO_RPT, NO_DDCSS, 0, 0},
+ {"epbp5", PREPARE_CA | NO_BAR | NO_RPT, NO_DDCSS, 0, 0},
+ {"epp6", PREPARE_CA | NO_BAR | NO_RPT, NO_DDCSS, 0, 0},
+ {"epbp7", PREPARE_CA | NO_BAR | NO_RPT, NO_DDCSS, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {"ana", READ_OPERAND, 0, 0, ru_A},
+ {"anq", READ_OPERAND, 0, 0, ru_Q},
+ {"anaq", READ_YPAIR, NO_DDCSS, 0, ru_AQ},
+
+ /* 400 */
+ {NULL, 0, 0, 0, 0},
+ {"mpf", READ_OPERAND, NO_CSS, 0, ru_AQ},
+ {"mpy", READ_OPERAND, NO_CSS, 0, ru_AQ},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {"cmg", READ_OPERAND, 0, 0, ru_A},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {"lde", READ_OPERAND, NO_CSS, 0, ru_none},
+ {NULL, 0, 0, 0, 0},
+ {"rscr", PREPARE_CA | PRIV_INS | NO_RPL, NO_DDCSS, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {"ade", READ_OPERAND, NO_CSS, 0, ru_none},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {"ufm", READ_OPERAND, NO_CSS, 0, ru_AQ},
+ {NULL, 0, 0, 0, 0},
+ {"dufm", READ_YPAIR, NO_DDCSS, 0, ru_AQ},
+ {NULL, 0, 0, 0, 0},
+ {"fcmg", READ_OPERAND, NO_CSS, 0, ru_AQ},
+ {NULL, 0, 0, 0, 0},
+ {"dfcmg", READ_YPAIR, NO_DDCSS, 0, ru_AQ},
+ {"fszn", READ_OPERAND, NO_CSS, 0, ru_none},
+ {"fld", READ_OPERAND, NO_CSS, 0, ru_AQ},
+ {NULL, 0, 0, 0, 0},
+ {"dfld", READ_YPAIR, NO_DDCSS, 0, ru_AQ},
+ {NULL, 0, 0, 0, 0},
+ {"ufa", READ_OPERAND, NO_CSS, 0, ru_AQ},
+ {NULL, 0, 0, 0, 0},
+ {"dufa", READ_YPAIR, NO_DDCSS, 0, ru_AQ},
+ {"sxl0", STORE_OPERAND | NO_RPT | NO_RPL, NO_DDCSS, 0, ru_X0},
+ {"sxl1", STORE_OPERAND | NO_RPL, NO_DDCSS, 0, ru_X1},
+ {"sxl2", STORE_OPERAND | NO_RPL, NO_DDCSS, 0, ru_X2},
+ {"sxl3", STORE_OPERAND | NO_RPL, NO_DDCSS, 0, ru_X3},
+ {"sxl4", STORE_OPERAND | NO_RPL, NO_DDCSS, 0, ru_X4},
+ {"sxl5", STORE_OPERAND | NO_RPL, NO_DDCSS, 0, ru_X5},
+ {"sxl6", STORE_OPERAND | NO_RPL, NO_DDCSS, 0, ru_X6},
+ {"sxl7", STORE_OPERAND | NO_RPL, NO_DDCSS, 0, ru_X7},
+ {"stz", STORE_OPERAND | NO_RPL, NO_DUDL, 0, ru_none},
+ {"smic", PREPARE_CA | PRIV_INS, NO_DDCSS, 0, 0},
+ {"scpr", STORE_YPAIR | NO_TAG | PRIV_INS | NO_RPT, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {"stt", STORE_OPERAND | NO_RPT, NO_DDCSS, 0, 0},
+ {"fst", STORE_OPERAND | NO_RPL, NO_DDCSS, 0, ru_A},
+ {"ste", STORE_OPERAND | NO_RPL, NO_DDCSS, 0, ru_none},
+ {"dfst", STORE_YPAIR | NO_RPL, NO_DDCSS, 0, ru_AQ},
+ {NULL, 0, 0, 0, 0},
+ {"fmp", READ_OPERAND, NO_CSS, 0, ru_AQ},
+ {NULL, 0, 0, 0, 0},
+ {"dfmp", READ_YPAIR, NO_DDCSS, 0, ru_AQ},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {"fstr", STORE_OPERAND | NO_RPL, NO_DDCSS, 0, ru_AQ},
+ {"frd", NO_RPL, 0, 0, ru_AQ},
+ {"dfstr", STORE_YPAIR | NO_RPL, NO_DDCSS, 0, ru_AQ},
+ {"dfrd", NO_RPL, 0, 0, ru_AQ},
+ {NULL, 0, 0, 0, 0},
+ {"fad", READ_OPERAND, NO_CSS, 0, ru_AQ},
+ {NULL, 0, 0, 0, 0},
+ {"dfad", READ_YPAIR, NO_DDCSS, 0, ru_AQ},
+
+ /* 500 */
+ {"rpl", NO_TAG | NO_RPT, 0, 0, ru_X0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {"bcd", READ_OPERAND | NO_RPL, NO_CSS, 0, ru_AQ},
+ {"div", READ_OPERAND, 0, 0, ru_AQ},
+ {"dvf", READ_OPERAND, 0, 0, ru_AQ},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {"fneg", NO_RPL, 0, 0, ru_AQ},
+ {NULL, 0, 0, 0, 0},
+ {"fcmp", READ_OPERAND, NO_CSS, 0, ru_AQ},
+ {NULL, 0, 0, 0, 0},
+ {"dfcmp", READ_YPAIR, NO_DDCSS, 0, ru_AQ},
+ {"rpt", NO_TAG | NO_RPT, 0, 0, ru_X0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {"fdi", READ_OPERAND, NO_CSS, 0, ru_AQ},
+ {NULL, 0, 0, 0, 0},
+ {"dfdi", READ_YPAIR, NO_DDCSS, 0, ru_AQ},
+ {NULL, 0, 0, 0, 0},
+ {"neg", NO_RPL, 0, 0, ru_A},
+ {"cams", PREPARE_CA | PRIV_INS | NO_RPT, NO_DDCSS, 0, 0},
+ {"negl", NO_RPL, 0, 0, ru_AQ},
+ {NULL, 0, 0, 0, 0},
+ {"ufs", READ_OPERAND, NO_CSS, 0, ru_AQ},
+ {NULL, 0, 0, 0, 0},
+ {"dufs", READ_YPAIR, NO_DDCSS, 0, ru_AQ},
+ {"sprp0", STORE_OPERAND | NO_BAR | NO_RPT, NO_DDCSS, 0, 0},
+ {"sprp1", STORE_OPERAND | NO_BAR | NO_RPT, NO_DDCSS, 0, 0},
+ {"sprp2", STORE_OPERAND | NO_BAR | NO_RPT, NO_DDCSS, 0, 0},
+ {"sprp3", STORE_OPERAND | NO_BAR | NO_RPT, NO_DDCSS, 0, 0},
+ {"sprp4", STORE_OPERAND | NO_BAR | NO_RPT, NO_DDCSS, 0, 0},
+ {"sprp5", STORE_OPERAND | NO_BAR | NO_RPT, NO_DDCSS, 0, 0},
+ {"sprp6", STORE_OPERAND | NO_BAR | NO_RPT, NO_DDCSS, 0, 0},
+ {"sprp7", STORE_OPERAND | NO_BAR | NO_RPT, NO_DDCSS, 0, 0},
+ {"sbar", STORE_OPERAND | NO_RPT, NO_DDCSS, 0, 0},
+ {"stba", STORE_OPERAND | NO_TAG | NO_RPT, 0, 0, ru_A},
+ {"stbq", STORE_OPERAND | NO_TAG | NO_RPT, 0, 0, ru_Q},
+ {"smcm", PREPARE_CA | PRIV_INS | NO_RPL, NO_DDCSS, 0, 0},
+ {"stc1", STORE_OPERAND | NO_RPT, NO_DDCSS, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {"ssdp", STORE_YBLOCK16 | PRIV_INS | NO_RPT, NO_DDCSS, 0, 0},
+ {"rpd", NO_TAG | NO_RPT, 0, 0, ru_X0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {"fdv", READ_OPERAND, NO_CSS, 0, ru_AQ},
+ {NULL, 0, 0, 0, 0},
+ {"dfdv", READ_YPAIR, NO_DDCSS, 0, ru_AQ},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {"fno", NO_RPL, 0, 0, ru_AQ},
+ {NULL, 0, 0, 0, 0},
+ {"fsb", READ_OPERAND, NO_CSS, 0, ru_AQ},
+ {NULL, 0, 0, 0, 0},
+ {"dfsb", READ_YPAIR, NO_DDCSS, 0, ru_AQ},
+ /* 600 */
+ {"tze", TRANSFER_INS | NO_RPT, NO_DDCSS, 0, ru_none},
+ {"tnz", TRANSFER_INS | NO_RPT, NO_DDCSS, 0, ru_none},
+ {"tnc", TRANSFER_INS | NO_RPT, NO_DDCSS, 0, ru_none},
+ {"trc", TRANSFER_INS | NO_RPT, NO_DDCSS, 0, ru_none},
+ {"tmi", TRANSFER_INS | NO_RPT, NO_DDCSS, 0, ru_none},
+ {"tpl", TRANSFER_INS | NO_RPT, NO_DDCSS, 0, ru_none},
+ {NULL, 0, 0, 0, 0},
+ {"ttf", TRANSFER_INS | NO_RPT, NO_DDCSS, 0, ru_none},
+ {"rtcd", NO_RPT | NO_BAR, NO_DDCSS, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {"rcu", READ_YBLOCK8 | PRIV_INS | NO_RPT, NO_DDCSS, 0, 0},
+ {"teo", TRANSFER_INS | NO_RPT, NO_DDCSS, 0, ru_none},
+ {"teu", TRANSFER_INS | NO_RPT, NO_DDCSS, 0, ru_none},
+ {"dis", PRIV_INS | NO_RPT, 0, 0, 0},
+ {"tov", TRANSFER_INS | NO_RPT, NO_DDCSS, 0, ru_none},
+ {"eax0", PREPARE_CA | NO_RPT | NO_RPL, NO_DUDL, 0, ru_X0},
+ {"eax1", PREPARE_CA | NO_RPL, NO_DUDL, 0, ru_X1},
+ {"eax2", PREPARE_CA | NO_RPL, NO_DUDL, 0, ru_X2},
+ {"eax3", PREPARE_CA | NO_RPL, NO_DUDL, 0, ru_X3},
+ {"eax4", PREPARE_CA | NO_RPL, NO_DUDL, 0, ru_X4},
+ {"eax5", PREPARE_CA | NO_RPL, NO_DUDL, 0, ru_X5},
+ {"eax6", PREPARE_CA | NO_RPL, NO_DUDL, 0, ru_X6},
+ {"eax7", PREPARE_CA | NO_RPL, NO_DUDL, 0, ru_X7},
+ {"ret", NO_RPT, NO_DDCSS, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {"rccl", PREPARE_CA | NO_BAR | NO_RPT, NO_DDCSS, 0, 0},
+ {"ldi", READ_OPERAND | NO_RPT, NO_CSS, 0, ru_none},
+ {"eaa", PREPARE_CA | NO_RPL, NO_DUDL, 0, ru_A},
+ {"eaq", PREPARE_CA | NO_RPL, NO_DUDL, 0, ru_Q},
+ {"ldt", READ_OPERAND | PRIV_INS | NO_RPT, NO_CSS, 0, 0},
+ {"ersx0", RMW | NO_RPT | NO_RPL, NO_DDCSS, 0, ru_X0},
+ {"ersx1", RMW | NO_RPL, NO_DDCSS, 0, ru_X1},
+ {"ersx2", RMW | NO_RPL, NO_DDCSS, 0, ru_X2},
+ {"ersx3", RMW | NO_RPL, NO_DDCSS, 0, ru_X3},
+ {"ersx4", RMW | NO_RPL, NO_DDCSS, 0, ru_X4},
+ {"ersx5", RMW | NO_RPL, NO_DDCSS, 0, ru_X5},
+ {"ersx6", RMW | NO_RPL, NO_DDCSS, 0, ru_X6},
+ {"ersx7", RMW | NO_RPL, NO_DDCSS, 0, ru_X7},
+ {"spri4", STORE_YPAIR | NO_BAR | NO_RPT, NO_DDCSS, 0, 0},
+ {"spbp5", STORE_YPAIR | NO_BAR | NO_RPT, NO_DDCSS, 0, 0},
+ {"spri6", STORE_YPAIR | NO_BAR | NO_RPT, NO_DDCSS, 0, 0},
+ {"spbp7", STORE_YPAIR | NO_BAR | NO_RPT, NO_DDCSS, 0, 0},
+ {"stacq", RMW | NO_RPL | NO_BAR, NO_DDCSS, 0, ru_AQ},
+ {"ersa", RMW | NO_RPL, NO_DDCSS, 0, ru_A},
+ {"ersq", RMW | NO_RPL, NO_DDCSS, 0, ru_Q},
+ {"scu", STORE_YBLOCK8 | PRIV_INS | NO_RPT, NO_DDCSS, 0, 0},
+ {"erx0", READ_OPERAND | NO_RPT, NO_CSS, 0, ru_X0},
+ {"erx1", READ_OPERAND, NO_CSS, 0, ru_X1},
+ {"erx2", READ_OPERAND, NO_CSS, 0, ru_X2},
+ {"erx3", READ_OPERAND, NO_CSS, 0, ru_X3},
+ {"erx4", READ_OPERAND, NO_CSS, 0, ru_X4},
+ {"erx5", READ_OPERAND, NO_CSS, 0, ru_X5},
+ {"erx6", READ_OPERAND, NO_CSS, 0, ru_X6},
+ {"erx7", READ_OPERAND, NO_CSS, 0, ru_X7},
+ {"tsp4", TRANSFER_INS | TSPN_INS | NO_RPT | NO_BAR, NO_DDCSS, 0, 0},
+ {"tsp5", TRANSFER_INS | TSPN_INS | NO_RPT | NO_BAR, NO_DDCSS, 0, 0},
+ {"tsp6", TRANSFER_INS | TSPN_INS | NO_RPT | NO_BAR, NO_DDCSS, 0, 0},
+ {"tsp7", TRANSFER_INS | TSPN_INS | NO_RPT | NO_BAR, NO_DDCSS, 0, 0},
+ {"lcpr", READ_OPERAND | NO_TAG | PRIV_INS | NO_RPT, 0, 0, 0},
+ {"era", READ_OPERAND, 0, 0, ru_A},
+ {"erq", READ_OPERAND, 0, 0, ru_Q},
+ {"eraq", READ_YPAIR, NO_DDCSS, 0, ru_AQ},
+
+ /* 700 */
+ {"tsx0", TRANSFER_INS | NO_RPT, NO_DDCSS, 0, ru_X0},
+ {"tsx1", TRANSFER_INS | NO_RPT, NO_DDCSS, 0, ru_X1},
+ {"tsx2", TRANSFER_INS | NO_RPT, NO_DDCSS, 0, ru_X2},
+ {"tsx3", TRANSFER_INS | NO_RPT, NO_DDCSS, 0, ru_X3},
+ {"tsx4", TRANSFER_INS | NO_RPT, NO_DDCSS, 0, ru_X4},
+ {"tsx5", TRANSFER_INS | NO_RPT, NO_DDCSS, 0, ru_X5},
+ {"tsx6", TRANSFER_INS | NO_RPT, NO_DDCSS, 0, ru_X6},
+ {"tsx7", TRANSFER_INS | NO_RPT, NO_DDCSS, 0, ru_X7},
+ {"tra", TRANSFER_INS | NO_RPT, NO_DDCSS, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ // CALL6 must fetch the destination instruction to force do_append_cycle
+ // to do all of the ring checks and processing.
+ //{"call6", PREPARE_CA | TRANSFER_INS | CALL6_INS | NO_RPT, NO_DDCSS, 0},
+ {"call6", TRANSFER_INS | CALL6_INS | NO_RPT, NO_DDCSS, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {"tss", TRANSFER_INS | NO_RPT, NO_DDCSS, 0, 0},
+ {"xec", READ_OPERAND | NO_RPT, NO_DDCSS, 0, 0},
+ {"xed", READ_YPAIR | NO_RPT, NO_DDCSS, 0, 0}, // ????
+ {"lxl0", READ_OPERAND | NO_RPT | NO_RPL, NO_CSS, 0, ru_X0},
+ {"lxl1", READ_OPERAND, NO_CSS, 0, ru_X1},
+ {"lxl2", READ_OPERAND, NO_CSS, 0, ru_X2},
+ {"lxl3", READ_OPERAND, NO_CSS, 0, ru_X3},
+ {"lxl4", READ_OPERAND, NO_CSS, 0, ru_X4},
+ {"lxl5", READ_OPERAND, NO_CSS, 0, ru_X5},
+ {"lxl6", READ_OPERAND, NO_CSS, 0, ru_X6},
+ {"lxl7", READ_OPERAND, NO_CSS, 0, ru_X7},
+ {NULL, 0, 0, 0, 0},
+ {"ars", PREPARE_CA | NO_RPL, NO_DDCSS, 0, ru_A},
+ {"qrs", PREPARE_CA | NO_RPL, NO_DDCSS, 0, ru_Q},
+ {"lrs", PREPARE_CA | NO_RPL, NO_DDCSS, 0, ru_AQ},
+ {NULL, 0, 0, 0, 0},
+ {"als", PREPARE_CA | NO_RPL, NO_DDCSS, 0, ru_A},
+ {"qls", PREPARE_CA | NO_RPL, NO_DDCSS, 0, ru_Q},
+ {"lls", PREPARE_CA | NO_RPL, NO_DDCSS, 0, ru_AQ},
+ {"stx0", STORE_OPERAND | NO_RPT | NO_RPL, NO_DDCSS, 0, ru_X0},
+ {"stx1", STORE_OPERAND | NO_RPL, NO_DDCSS, 0, ru_X1},
+ {"stx2", STORE_OPERAND | NO_RPL, NO_DDCSS, 0, ru_X2},
+ {"stx3", STORE_OPERAND | NO_RPL, NO_DDCSS, 0, ru_X3},
+ {"stx4", STORE_OPERAND | NO_RPL, NO_DDCSS, 0, ru_X4},
+ {"stx5", STORE_OPERAND | NO_RPL, NO_DDCSS, 0, ru_X5},
+ {"stx6", STORE_OPERAND | NO_RPL, NO_DDCSS, 0, ru_X6},
+ {"stx7", STORE_OPERAND | NO_RPL, NO_DDCSS, 0, ru_X7},
+ {"stc2", STORE_OPERAND | NO_RPT, NO_DDCSS, 0, 0},
+ {"stca", STORE_OPERAND | NO_TAG | NO_RPT, 0, 0, ru_A},
+ {"stcq", STORE_OPERAND | NO_TAG | NO_RPT, 0, 0, ru_Q},
+ {"sreg", STORE_YBLOCK8 | NO_RPT, NO_DDCSS, 0, MASK10},
+ {"sti", STORE_OPERAND | NO_RPT, NO_DDCSS, 0, 0},
+ {"sta", STORE_OPERAND | NO_RPL, NO_DUDL, 0, ru_A},
+ {"stq", STORE_OPERAND | NO_RPL, NO_DUDL, 0, ru_Q},
+ {"staq", STORE_YPAIR | NO_RPL, NO_DDCSS, 0, ru_AQ},
+ {"lprp0", READ_OPERAND | NO_BAR | NO_RPT, NO_DDCSS, 0, 0},
+ {"lprp1", READ_OPERAND | NO_BAR | NO_RPT, NO_DDCSS, 0, 0},
+ {"lprp2", READ_OPERAND | NO_BAR | NO_RPT, NO_DDCSS, 0, 0},
+ {"lprp3", READ_OPERAND | NO_BAR | NO_RPT, NO_DDCSS, 0, 0},
+ {"lprp4", READ_OPERAND | NO_BAR | NO_RPT, NO_DDCSS, 0, 0},
+ {"lprp5", READ_OPERAND | NO_BAR | NO_RPT, NO_DDCSS, 0, 0},
+ {"lprp6", READ_OPERAND | NO_BAR | NO_RPT, NO_DDCSS, 0, 0},
+ {"lprp7", READ_OPERAND | NO_BAR | NO_RPT, NO_DDCSS, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {"arl", PREPARE_CA | NO_RPL, NO_DDCSS, 0, ru_A},
+ {"qrl", PREPARE_CA | NO_RPL, NO_DDCSS, 0, ru_Q},
+ {"lrl", PREPARE_CA | NO_RPL, NO_DDCSS, 0, ru_AQ},
+ {"gtb", PREPARE_CA | NO_RPL, 0, 0, ru_A},
+ {"alr", PREPARE_CA | NO_RPL, NO_DDCSS, 0, ru_A},
+ {"qlr", PREPARE_CA | NO_RPL, NO_DDCSS, 0, ru_Q},
+ {"llr", PREPARE_CA | NO_RPL, NO_DDCSS, 0, ru_AQ},
+// EIS
+ /* 000 - 017 */
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ /* 020 - 037 */
+ {"mve", NO_TAG | NO_XED | NO_RPT | IGN_B29, 0, 3, is_DU},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {"mvne", NO_TAG | NO_XED | NO_RPT | IGN_B29, 0, 3, is_DU},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ /* 040 - 057 */
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ /* 060 - 077 */
+ {"csl", NO_TAG | NO_XED | NO_RPT | IGN_B29, 0, 2, is_DU},
+ {"csr", NO_TAG | NO_XED | NO_RPT | IGN_B29, 0, 2, is_DU},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {"sztl", NO_TAG | NO_XED | NO_RPT | IGN_B29, 0, 2, is_DU},
+ {"sztr", NO_TAG | NO_XED | NO_RPT | IGN_B29, 0, 2, is_DU},
+ {"cmpb", NO_TAG | NO_XED | NO_RPT | IGN_B29, 0, 2, is_DU},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ /* 100 - 117 */
+ {"mlr", NO_TAG | NO_XED | NO_RPT | IGN_B29, 0, 2, is_DU},
+ {"mrl", NO_TAG | NO_XED | NO_RPT | IGN_B29, 0, 2, is_DU},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {"cmpc", _EIS_ | EOP1_ALPHA | EOP2_ALPHA, 0, 2, is_DU},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ /* 120 - 137 */
+ {"scd", NO_TAG | NO_XED | NO_RPT | IGN_B29, 0, 3, is_DU},
+ {"scdr", NO_TAG | NO_XED | NO_RPT | IGN_B29, 0, 3, is_DU},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {"scm", NO_TAG | NO_XED | NO_RPT | IGN_B29, 0, 3, is_DU},
+ {"scmr", NO_TAG | NO_XED | NO_RPT | IGN_B29, 0, 3, is_DU},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ /* 140 - 157 */
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {"sptr", STORE_YBLOCK16 | PRIV_INS | NO_RPT, NO_DDCSS, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ /* 167 - 177 */
+ {"mvt", NO_TAG | NO_XED | NO_RPT | IGN_B29, 0, 3, is_DU},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {"tct", NO_TAG | NO_XED | NO_RPT | IGN_B29, 0, 3, is_DU},
+ {"tctr", NO_TAG | NO_XED | NO_RPT | IGN_B29, 0, 3, is_DU},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {"lptr", READ_YBLOCK16 | PRIV_INS | NO_RPT, NO_DDCSS, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ /* 200 - 217 */
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {"ad2d", NO_TAG | NO_XED | NO_RPT | IGN_B29, 0, 2, is_DU},
+ {"sb2d", NO_TAG | NO_XED | NO_RPT | IGN_B29, 0, 2, is_DU},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {"mp2d", NO_TAG | NO_XED | NO_RPT | IGN_B29, 0, 2, is_DU},
+ {"dv2d", NO_TAG | NO_XED | NO_RPT | IGN_B29, 0, 2, is_DU},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ /* 220 - 237 */
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {"ad3d", NO_TAG | NO_XED | NO_RPT | IGN_B29, 0, 3, is_DU},
+ {"sb3d", NO_TAG | NO_XED | NO_RPT | IGN_B29, 0, 3, is_DU},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {"mp3d", NO_TAG | NO_XED | NO_RPT | IGN_B29, 0, 3, is_DU},
+ {"dv3d", NO_TAG | NO_XED | NO_RPT | IGN_B29, 0, 3, is_DU},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {"lsdr", READ_YBLOCK32 | PRIV_INS | NO_RPT, NO_DDCSS, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ /* 240 - 257 */
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {"spbp0", STORE_YPAIR | NO_BAR | NO_RPT, NO_DDCSS, 0, 0},
+ {"spri1", STORE_YPAIR | NO_BAR | NO_RPT, NO_DDCSS, 0, 0},
+ {"spbp2", STORE_YPAIR | NO_BAR | NO_RPT, NO_DDCSS, 0, 0},
+ {"spri3", STORE_YPAIR | NO_BAR | NO_RPT, NO_DDCSS, 0, 0},
+ {"ssdr", STORE_YBLOCK32 | PRIV_INS | NO_RPT, NO_DDCSS, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {"lptp", READ_YBLOCK16 | PRIV_INS | NO_RPT, NO_DDCSS, 0, 0},
+ /* 260 - 277 */
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ /* 300 - 317 */
+ {"mvn", NO_TAG | NO_XED | NO_RPT | IGN_B29, 0, 2, is_DU},
+ {"btd", NO_TAG | NO_XED | NO_RPT | IGN_B29, 0, 2, is_DU},
+ {NULL, 0, 0, 0, 0},
+ {"cmpn", NO_TAG | NO_XED | NO_RPT | IGN_B29, 0, 2, is_DU},
+ {NULL, 0, 0, 0, 0},
+ {"dtb", NO_TAG | NO_XED | NO_RPT | IGN_B29, 0, 2, is_DU},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {"easp1", PREPARE_CA | NO_BAR | NO_RPT, NO_DDCSS, 0, 0},
+ {"eawp1", PREPARE_CA | NO_BAR | NO_RPT, NO_DDCSS, 0, 0},
+ {"easp3", PREPARE_CA | NO_BAR | NO_RPT, NO_DDCSS, 0, 0},
+ {"eawp3", PREPARE_CA | NO_BAR | NO_RPT, NO_DDCSS, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ /* 320 - 337 */
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {"easp5", PREPARE_CA | NO_BAR | NO_RPT, NO_DDCSS, 0, 0},
+ {"eawp5", PREPARE_CA | NO_BAR | NO_RPT, NO_DDCSS, 0, 0},
+ {"easp7", PREPARE_CA | NO_BAR | NO_RPT, NO_DDCSS, 0, 0},
+ {"eawp7", PREPARE_CA | NO_BAR | NO_RPT, NO_DDCSS, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ /* 340 - 357 */
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {"epbp0", PREPARE_CA | NO_BAR | NO_RPT, NO_DDCSS, 0, 0},
+ {"epp1", PREPARE_CA | NO_BAR | NO_RPT, NO_DDCSS, 0, 0},
+ {"epbp2", PREPARE_CA | NO_BAR | NO_RPT, NO_DDCSS, 0, 0},
+ {"epp3", PREPARE_CA | NO_BAR | NO_RPT, NO_DDCSS, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ /* 360 - 377 */
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {"epbp4", PREPARE_CA | NO_BAR | NO_RPT, NO_DDCSS, 0, 0},
+ {"epp5", PREPARE_CA | NO_BAR | NO_RPT, NO_DDCSS, 0, 0},
+ {"epbp6", PREPARE_CA | NO_BAR | NO_RPT, NO_DDCSS, 0, 0},
+ {"epp7", PREPARE_CA | NO_BAR | NO_RPT, NO_DDCSS, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ /* 400 - 417 */
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+#if EMULATOR_ONLY
+ /* 420 - 437 */
+ {"emcall", IGN_B29, 0, 0, 0}, // 420 we add a emulator call instruction for SIMH use ONLY! (opcode 0420(1))
+ {"fxe", 0, 0, 0, 0}, // 421 fxe fault handler
+
+#ifdef DEPRECIATED
+ // with the lack of a stack it makes it very cumbersome to write decent code for reentrant subroutines.
+ // Yes, I *might* be able to use the DI, ID tally modifications, but you need to setup a indirect stack word to reference -
+ // and you couldn't easily do stack offsets, w/o another indirect word, etc.
+
+ // I spoze' that if I added macro's to the assembler I probably could do this utilizing the native ISA,
+ // but that *ain't* happening any time soon.
+
+ // So, let's add some instructions.
+
+ {"callx", READ_OPERAND | TRANSFER_INS, 0, 0, 0},// 422 and in the spirit of AIM-072...
+ {"exit", TRANSFER_INS, 0, 0, 0}, // 423
+
+ // how 'bout push/pop a/q
+ {"pusha", 0, 0, 0, 0}, // 424 push A onto stack via Xn
+ {"popa", 0, 0, 0, 0}, // 425 pop word from stack via Xn into A
+ {"pushq", 0, 0, 0, 0}, // 426 push Q onto stack via Xn
+ {"popq", 0, 0, 0, 0}, // 427 pop word from stack via Xn into Q
+#else // !DEPRECIATED
+ {NULL, 0, 0, 0, 0}, // 422
+ {NULL, 0, 0, 0, 0}, // 423
+ {NULL, 0, 0, 0, 0}, // 424
+ {NULL, 0, 0, 0, 0}, // 425
+ {NULL, 0, 0, 0, 0}, // 426
+ {NULL, 0, 0, 0, 0}, // 427
+#endif
+
+ {NULL, 0, 0, 0, 0}, // 430
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0}, // 437
+
+
+#else
+ /* 420 - 437 */
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+#endif
+ /* 440 - 457 */
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {"sareg", STORE_YBLOCK8 | NO_RPT, NO_DDCSS, 0, is_DU},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {"spl", STORE_YBLOCK8 | NO_RPT, NO_DDCSS, 0, is_DU},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ /* 460 - 477 */
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {"lareg", READ_YBLOCK8 | NO_RPT, NO_DDCSS, 0, is_DU},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {"lpl", READ_YBLOCK8 | NO_RPT, NO_DDCSS, 0, is_DU},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ /* 500 - 517 */
+ {"a9bd", IGN_B29 | NO_RPT, ONLY_AU_QU_AL_QL_XN, 0, is_9},
+ {"a6bd", IGN_B29 | NO_RPT, ONLY_AU_QU_AL_QL_XN, 0, is_6},
+ {"a4bd", IGN_B29 | NO_RPT, ONLY_AU_QU_AL_QL_XN, 0, is_4},
+ {"abd", IGN_B29 | NO_RPT, ONLY_AU_QU_AL_QL_XN, 0, is_1},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {"awd", IGN_B29 | NO_RPT, ONLY_AU_QU_AL_QL_XN, 0, is_WRD},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ /* 520 - 537 */
+ {"s9bd", IGN_B29 | NO_RPT, ONLY_AU_QU_AL_QL_XN, 0, is_9},
+ {"s6bd", IGN_B29 | NO_RPT, ONLY_AU_QU_AL_QL_XN, 0, is_6},
+ {"s4bd", IGN_B29 | NO_RPT, ONLY_AU_QU_AL_QL_XN, 0, is_4},
+ {"sbd", IGN_B29 | NO_RPT, ONLY_AU_QU_AL_QL_XN, 0, is_1},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {"swd", IGN_B29 | NO_RPT, ONLY_AU_QU_AL_QL_XN, 0, is_WRD},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {"camp", PREPARE_CA | PRIV_INS | NO_RPT, NO_DDCSS, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ /* 540 - 557 */
+ {"ara0", RMW | NO_RPT, NO_DDCSS, 0, is_DU},
+ {"ara1", RMW | NO_RPT, NO_DDCSS, 0, is_DU},
+ {"ara2", RMW | NO_RPT, NO_DDCSS, 0, is_DU},
+ {"ara3", RMW | NO_RPT, NO_DDCSS, 0, is_DU},
+ {"ara4", RMW | NO_RPT, NO_DDCSS, 0, is_DU},
+ {"ara5", RMW | NO_RPT, NO_DDCSS, 0, is_DU},
+ {"ara6", RMW | NO_RPT, NO_DDCSS, 0, is_DU},
+ {"ara7", RMW | NO_RPT, NO_DDCSS, 0, is_DU},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {"sptp", STORE_YBLOCK16 | PRIV_INS | NO_RPT, NO_DDCSS, 0, 0},
+ /* 560 - 577 */
+ {"aar0", READ_OPERAND | NO_RPT, NO_DDCSS, 0, is_DU},
+ {"aar1", READ_OPERAND | NO_RPT, NO_DDCSS, 0, is_DU},
+ {"aar2", READ_OPERAND | NO_RPT, NO_DDCSS, 0, is_DU},
+ {"aar3", READ_OPERAND | NO_RPT, NO_DDCSS, 0, is_DU},
+ {"aar4", READ_OPERAND | NO_RPT, NO_DDCSS, 0, is_DU},
+ {"aar5", READ_OPERAND | NO_RPT, NO_DDCSS, 0, is_DU},
+ {"aar6", READ_OPERAND | NO_RPT, NO_DDCSS, 0, is_DU},
+ {"aar7", READ_OPERAND | NO_RPT, NO_DDCSS, 0, is_DU},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ /* 600 - 617 */
+ {"trtn", TRANSFER_INS | NO_RPT, NO_DDCSS, 0, 0},
+ {"trtf", TRANSFER_INS | NO_RPT, NO_DDCSS, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {"tmoz", TRANSFER_INS | NO_RPT, NO_DDCSS, 0, 0},
+ {"tpnz", TRANSFER_INS | NO_RPT, NO_DDCSS, 0, 0},
+ {"ttn", TRANSFER_INS | NO_RPT, NO_DDCSS, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ /* 620 - 637 */
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ /* 640 - 657 */
+ {"arn0", STORE_OPERAND | NO_RPT, NO_DDCSS, 0, is_DU},
+ {"arn1", STORE_OPERAND | NO_RPT, NO_DDCSS, 0, is_DU},
+ {"arn2", STORE_OPERAND | NO_RPT, NO_DDCSS, 0, is_DU},
+ {"arn3", STORE_OPERAND | NO_RPT, NO_DDCSS, 0, is_DU},
+ {"arn4", STORE_OPERAND | NO_RPT, NO_DDCSS, 0, is_DU},
+ {"arn5", STORE_OPERAND | NO_RPT, NO_DDCSS, 0, is_DU},
+ {"arn6", STORE_OPERAND | NO_RPT, NO_DDCSS, 0, is_DU},
+ {"arn7", STORE_OPERAND | NO_RPT, NO_DDCSS, 0, is_DU},
+ {"spbp4", STORE_YPAIR | NO_BAR | NO_RPT, NO_DDCSS, 0, 0},
+ {"spri5", STORE_YPAIR | NO_BAR | NO_RPT, NO_DDCSS, 0, 0},
+ {"spbp6", STORE_YPAIR | NO_BAR | NO_RPT, NO_DDCSS, 0, 0},
+ {"spri7", STORE_YPAIR | NO_BAR | NO_RPT, NO_DDCSS, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ /* 660 - 677 */
+ {"nar0", READ_OPERAND | NO_RPT, NO_DDCSS, 0, is_DU},
+ {"nar1", READ_OPERAND | NO_RPT, NO_DDCSS, 0, is_DU},
+ {"nar2", READ_OPERAND | NO_RPT, NO_DDCSS, 0, is_DU},
+ {"nar3", READ_OPERAND | NO_RPT, NO_DDCSS, 0, is_DU},
+ {"nar4", READ_OPERAND | NO_RPT, NO_DDCSS, 0, is_DU},
+ {"nar5", READ_OPERAND | NO_RPT, NO_DDCSS, 0, is_DU},
+ {"nar6", READ_OPERAND | NO_RPT, NO_DDCSS, 0, is_DU},
+ {"nar7", READ_OPERAND | NO_RPT, NO_DDCSS, 0, is_DU},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ /* 700 - 717 */
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ /* 720 - 737 */
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ /* 740 - 757 */
+ {"sar0", STORE_OPERAND | NO_RPT, NO_DDCSS, 0, is_DU},
+ {"sar1", STORE_OPERAND | NO_RPT, NO_DDCSS, 0, is_DU},
+ {"sar2", STORE_OPERAND | NO_RPT, NO_DDCSS, 0, is_DU},
+ {"sar3", STORE_OPERAND | NO_RPT, NO_DDCSS, 0, is_DU},
+ {"sar4", STORE_OPERAND | NO_RPT, NO_DDCSS, 0, is_DU},
+ {"sar5", STORE_OPERAND | NO_RPT, NO_DDCSS, 0, is_DU},
+ {"sar6", STORE_OPERAND | NO_RPT, NO_DDCSS, 0, is_DU},
+ {"sar7", STORE_OPERAND | NO_RPT, NO_DDCSS, 0, is_DU},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {"sra", STORE_OPERAND | NO_BAR | NO_RPT, NO_DDCSS, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ /* 760 - 777 */
+ {"lar0", READ_OPERAND | NO_RPT, NO_DDCSS, 0, is_DU},
+ {"lar1", READ_OPERAND | NO_RPT, NO_DDCSS, 0, is_DU},
+ {"lar2", READ_OPERAND | NO_RPT, NO_DDCSS, 0, is_DU},
+ {"lar3", READ_OPERAND | NO_RPT, NO_DDCSS, 0, is_DU},
+ {"lar4", READ_OPERAND | NO_RPT, NO_DDCSS, 0, is_DU},
+ {"lar5", READ_OPERAND | NO_RPT, NO_DDCSS, 0, is_DU},
+ {"lar6", READ_OPERAND | NO_RPT, NO_DDCSS, 0, is_DU},
+ {"lar7", READ_OPERAND | NO_RPT, NO_DDCSS, 0, is_DU},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {"lra", READ_OPERAND | PRIV_INS | NO_RPT, NO_DDCSS, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0},
+ {NULL, 0, 0, 0, 0}
+};
+
+
+struct adrMods extMods[0100] = { ///< address modifiers w/ extended info
+ /* R */
+ {"", 0, 0},
+ {"au", 1, 0},
+ {"qu", 2, 0},
+ {"du", 3, 0},
+ {"ic", 4, 0},
+ {"al", 5, 0},
+ {"ql", 6, 0},
+ {"dl", 7, 0},
+ {"0", 8, 0},
+ {"1", 9, 0},
+ {"2", 10, 0},
+ {"3", 11, 0},
+ {"4", 12, 0},
+ {"5", 13, 0},
+ {"6", 14, 0},
+ {"7", 15, 0},
+
+ /* RI */
+ {"n*", 16, 0},
+ {"au*", 17, 0},
+ {"qu*", 18, 0},
+ {NULL, 19, 0},
+ {"ic*", 20, 0},
+ {"al*", 21, 0},
+ {"ql*", 22, 0},
+ {NULL, 23, 0},
+ {"0*", 24, 0},
+ {"1*", 25, 0},
+ {"2*", 26, 0},
+ {"3*", 27, 0},
+ {"4*", 28, 0},
+ {"5*", 29, 0},
+ {"6*", 30, 0},
+ {"7*", 31, 0},
+
+ /* IT */
+ {"f1", 32, 0},
+ {"itp", 33, 0},
+ {NULL, 34, 0},
+ {"its", 35, 0},
+ {"sd", 36, 0},
+ {"scr", 37, 0},
+ {"f2", 38, 0},
+ {"f3", 39, 0},
+ {"ci", 40, 0},
+ {"i", 41, 0},
+ {"sc", 42, 0},
+ {"ad", 43, 0},
+ {"di", 44, 0},
+ {"dic", 45, 0},
+ {"id", 46, 0},
+ {"idc", 47, 0},
+
+ /* IR */
+ {"*n", 48, 0},
+ {"*au", 49, 0},
+ {"*qu", 50, 0},
+ {"*du", 51, 0},
+ {"*ic", 52, 0},
+ {"*al", 53, 0},
+ {"*ql", 54, 0},
+ {"*dl", 55, 0},
+ {"*0", 56, 0},
+ {"*1", 57, 0},
+ {"*2", 58, 0},
+ {"*3", 59, 0},
+ {"*4", 60, 0},
+ {"*5", 61, 0},
+ {"*6", 62, 0},
+ {"*7", 63, 0},
+};
+
+char GEBcdToASCII[64] = ///< from pg 271 CPB1004F_GE635pgmRef_Jul69
+{
+/* 000 - 007 */ '0', '1', '2', '3', '4', '5', '6', '7',
+/* 010 - 017 */ '8', '9', '[', '#', '@', ':', '>', '?',
+/* 020 - 027 */ ' ', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
+/* 030 - 037 */ 'H', 'I', '&', '.', ']', '(', '<', '\\',
+/* 040 - 047 */ '^', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
+/* 050 - 057 */ 'Q', 'R', '-', '$', '*', ')', ';', '\'',
+/* 060 - 067 */ '+', '/', 'S', 'T', 'U', 'V', 'W', 'X',
+/* 070 - 077 */ 'Y', 'Z', '_', ',', '%', '=', '"', '!'
+};
+
+#ifndef QUIET_UNUSED
+char ASCIIToGEBcd[128] =
+{
+/* 000 - 007 */ -1, -1, -1, -1, -1, -1, -1, -1,
+/* 010 - 017 */ -1, -1, -1, -1, -1, -1, -1, -1,
+/* 020 - 027 */ -1, -1, -1, -1, -1, -1, -1, -1,
+/* 030 - 037 */ -1, -1, -1, -1, -1, -1, -1, -1,
+/* 040 - 047 */ 16, 63, 62, 11, 43, 60, 26, 47,
+/* 050 - 057 */ 29, 45, 44, 48, 59, 42, 27, 49,
+/* 060 - 067 */ 0, 1, 2, 3, 4, 5, 6, 7,
+/* 070 - 077 */ 8, 9, 13, 46, 30, 61, 14, 15,
+/* 100 - 107 */ 12, 17, 18, 19, 20, 21, 22, 23,
+/* 110 - 117 */ 24, 25, 33, 34, 35, 36, 37, 38,
+/* 120 - 127 */ 39, 40, 41, 50, 51, 52, 53, 54,
+/* 130 - 137 */ 55, 56, 57, 10, 31, 28, 32, 58,
+/* 140 - 147 */ -1, 17, 18, 19, 20, 21, 22, 23,
+/* 150 - 157 */ 24, 25, 33, 34, 35, 36, 37, 38,
+/* 160 - 167 */ 39, 40, 41, 50, 51, 52, 53, 54,
+/* 170 - 177 */ 55, 56, 57, -1, -1, -1, -1, -1,
+};
+#endif
+
+#ifndef QUIET_UNUSED
+char *op0text[512] = {
+ // index by upper 9 bits of those opcodes with bit 27 == 0
+ NULL, "mme", "drl", NULL, "mme2", "mme3", NULL, "mme4",
+ NULL, "nop", "puls1", "puls2", NULL, "cioc", NULL, NULL,
+ "adlx0", "adlx1", "adlx2", "adlx3", "adlx4", "adlx5", "adlx6", "adlx7",
+ NULL, NULL, "ldqc", "adl", "ldac", "adla", "adlq", "adlaq",
+ "asx0", "asx1", "asx2", "asx3", "asx4", "asx5", "asx6", "asx7",
+ "adwp0", "adwp1", "adwp2", "adwp3", "aos", "asa", "asq", "sscr",
+ "adx0", "adx1", "adx2", "adx3", "adx4", "adx5", "adx6", "adx7",
+ NULL, "awca", "awcq", "lreg", NULL, "ada", "adq", "adaq",
+ "cmpx0", "cmpx1", "cmpx2", "cmpx3", "cmpx4", "cmpx5", "cmpx6", "cmpx7",
+ NULL, "cwl", NULL, NULL, NULL, "cmpa", "cmpq", "cmpaq",
+ "sblx0", "sblx1", "sblx2", "sblx3", "sblx4", "sblx5", "sblx6", "sblx7",
+ NULL, NULL, NULL, NULL, NULL, "sbla", "sblq", "sblaq",
+ "ssx0", "ssx1", "ssx2", "ssx3", "ssx4", "ssx5", "ssx6", "ssx7",
+ "adwp4", "adwp5", "adwp6", "adwp7", "sdbr", "ssa", "ssq", NULL,
+ "sbx0", "sbx1", "sbx2", "sbx3", "sbx4", "sbx5", "sbx6", "sbx7",
+ NULL, "swca", "swcq", "lpri", NULL, "sba", "sbq", "sbaq",
+ "cnax0", "cnax1", "cnax2", "cnax3", "cnax4", "cnax5", "cnax6", "cnax7",
+ NULL, "cmk", "absa", "epaq", "sznc", "cnaa", "cnaq", "cnaaq",
+ "ldx0", "ldx1", "ldx2", "ldx3", "ldx4", "ldx5", "ldx6", "ldx7",
+ "lbar", "rsw", "ldbr", "rmcm", "szn", "lda", "ldq", "ldaq",
+ "orsx0", "orsx1", "orsx2", "orsx3", "orsx4", "orsx5", "orsx6", "orsx7",
+ "spri0", "spbp1", "spri2", "spbp3", "spri", "orsa", "orsq", "lsdp",
+ "orx0", "orx1", "orx2", "orx3", "orx4", "orx5", "orx6", "orx7",
+ "tsp0", "tsp1", "tsp2", "tsp3", NULL, "ora", "orq", "oraq",
+ "canx0", "canx1", "canx2", "canx3", "canx4", "canx5", "canx6", "canx7",
+ "eawp0", "easp0", "eawp2", "easp2", NULL, "cana", "canq", "canaq",
+ "lcx0", "lcx1", "lcx2", "lcx3", "lcx4", "lcx5", "lcx6", "lcx7",
+ "eawp4", "easp4", "eawp6", "easp6", NULL, "lca", "lcq", "lcaq",
+ "ansx0", "ansx1", "ansx2", "ansx3", "ansx4", "ansx5", "ansx6", "ansx7",
+ "epp0", "pbp1", "epp2", "epbp3", "stac", "ansa", "ansq", "stcd",
+ "anx0", "anx1", "anx2", "anx3", "anx4", "anx5", "anx6", "anx7",
+ "epp4", "epbp5", "epp6", "epbp7", NULL, "ana", "anq", "anaq",
+ NULL, "mpf", "mpy", NULL, NULL, "cmg", NULL, NULL,
+ NULL, "lde", NULL, "rscr", NULL, "ade", NULL, NULL,
+ NULL, "ufm", NULL, "dufm", NULL, "fcmg", NULL, "dfcmg",
+ "fszn", "fld", NULL, "dfld", NULL, "ufa", NULL, "dufa",
+ "sxl0", "sxl1", "sxl2", "sxl3", "sxl4", "sxl5", "sxl6", "sxl7",
+ "stz", "smic", "scpr", NULL, "stt", "fst", "ste", "dfst",
+ NULL, "fmp", NULL, "dfmp", NULL, NULL, NULL, NULL,
+ "fstr", "frd", "dfstr", "dfrd", NULL, "fad", NULL, "dfad",
+ "rpl", NULL, NULL, NULL, NULL, "bcd", "div", "dvf",
+ NULL, NULL, NULL, "fneg", NULL, "fcmp", NULL, "dfcmp",
+ "rpt", NULL, NULL, NULL, NULL, "fdi", NULL, "dfdi",
+ NULL, "neg", "cams", "negl", NULL, "ufs", NULL, "dufs",
+ "sprp0", "sprp1", "sprp2", "sprp3", "sprp4", "sprp5", "sprp6", "sprp7",
+ "sbar", "stba", "stbq", "smcm", "stc1", NULL, NULL, "ssdp",
+ "rpd", NULL, NULL, NULL, NULL, "fdv", NULL, "dfdv",
+ NULL, NULL, NULL, "fno", NULL, "fsb", NULL, "dfsb",
+ "tze", "tnz", "tnc", "trc", "tmi", "tpl", NULL, "ttf",
+ "rtcd", NULL, NULL, "rcu", "teo", "teu", "dis", "tov",
+ "eax0", "eax1", "eax2", "eax3", "eax4", "eax5", "eax6", "eax7",
+ "ret", NULL, NULL, "rccl", "ldi", "eaa", "eaq", "ldt",
+ "ersx0", "ersx1", "ersx2", "ersx3", "ersx4", "ersx5", "ersx6", "ersx7",
+ "spri4", "spbp5", "spri6", "spbp7", "stacq", "ersa", "ersq", "scu",
+ "erx0", "erx1", "erx2", "erx3", "erx4", "erx5", "erx6", "erx7",
+ "tsp4", "tsp5", "tsp6", "tsp7", "lcpr", "era", "erq", "eraq",
+ "tsx0", "tsx1", "tsx2", "tsx3", "tsx4", "tsx5", "tsx6", "tsx7",
+ "tra", NULL, NULL, "call6", NULL, "tss", "xec", "xed",
+ "lxl0", "lxl1", "lxl2", "lxl3", "lxl4", "lxl5", "lxl6", "lxl7",
+ NULL, "ars", "qrs", "lrs", NULL, "als", "qls", "lls",
+ "stx0", "stx1", "stx2", "stx3", "stx4", "stx5", "stx6", "stx7",
+ "stc2", "stca", "stcq", "sreg", "sti", "sta", "stq", "staq",
+ "lprp0", "lprp1", "lprp2", "lprp3", "lprp4", "lprp5", "lprp6", "lprp7",
+ NULL, "arl", "qrl", "lrl", "gtb", "alr", "qlr", "llr"
+};
+#endif
+
+#ifndef QUIET_UNUSED
+char *op1text[512] = {
+ // index by upper 9 bits of those opcodes with bit 27 == 1
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ "mve", NULL, NULL, NULL, "mvne", NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ "csl", "csr", NULL, NULL, "sztl", "sztr", "cmpb", NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ "mlr", "mrl", NULL, NULL, NULL, NULL, "cmpc", NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ "scd", "scdr", NULL, NULL, "scm", "scmr", NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, "sptr", NULL, NULL, NULL,
+ "mvt", NULL, NULL, NULL, "tct", "tctr", NULL, NULL,
+ NULL, NULL, NULL, "lptr", NULL, NULL, NULL, NULL,
+ NULL, NULL, "ad2d", "sb2d", NULL, NULL, "mp2d", "dv2d",
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, "ad3d", "sb3d", NULL, NULL, "mp3d", "dv3d",
+ NULL, NULL, "lsdr", NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ "spbp0", "spri1", "spbp2", "spri3", "ssdr", NULL, NULL, "lptp",
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ "mvn", "btd", NULL, "cmpn", NULL, "dtb", NULL, NULL,
+ "easp1", "eawp1", "easp3", "eawp3", NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ "easp5", "eawp5", "easp7", "eawp7", NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ "epbp0", "epp1", "epbp2", "epp3", NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ "epbp4", "epp5", "epbp6", "epp7", NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ "emCall", NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, "sareg", NULL, NULL, NULL, "spl",
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, "lareg", NULL, NULL, NULL, "lpl",
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ "a9bd", "a6bd", "a4bd", "abd", NULL, NULL, NULL, "awd",
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ "s9bd", "s6bd", "s4bd", "sbd", NULL, NULL, NULL, "swd",
+ NULL, NULL, "camp", NULL, NULL, NULL, NULL, NULL,
+ "ara0", "ara1", "ara2", "ara3", "ara4", "ara5", "ara6", "ara7",
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, "sptp",
+ "aar0", "aar1", "aar2", "aar3", "aar4", "aar5", "aar6", "aar7",
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ "trtn", "trtf", NULL, NULL, "tmoz", "tpnz", "ttn", NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ "arn0", "arn1", "arn2", "arn3", "arn4", "arn5", "arn6", "arn7",
+ "spbp4", "spri5", "spbp6", "spri7", NULL, NULL, NULL, NULL,
+ "nar0", "nar1", "nar2", "nar3", "nar4", "nar5", "nar6", "nar7",
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ "sar0", "sar1", "sar2", "sar3", "sar4", "sar5", "sar6", "sar7",
+ NULL, NULL, NULL, NULL, "sra", NULL, NULL, NULL,
+ "lar0", "lar1", "lar2", "lar3", "lar4", "lar5", "lar6", "lar7",
+ NULL, NULL, NULL, NULL, "lra", NULL, NULL, NULL
+};
+#endif
+
+#ifndef QUIET_UNUSED
+char *opcodes2text[1024] = {
+ // index by all 10 bits of all opcodes
+ NULL, NULL, "mme", NULL, "drl", NULL, NULL, NULL,
+ "mme2", NULL, "mme3", NULL, NULL, NULL, "mme4", NULL,
+ NULL, NULL, "nop", NULL, "puls1", NULL, "puls2", NULL,
+ NULL, NULL, "cioc", NULL, NULL, NULL, NULL, NULL,
+ "adlx0", "mve", "adlx1", NULL, "adlx2", NULL, "adlx3", NULL,
+ "adlx4", "mvne", "adlx5", NULL, "adlx6", NULL, "adlx7", NULL,
+ NULL, NULL, NULL, NULL, "ldqc", NULL, "adl", NULL,
+ "ldac", NULL, "adla", NULL, "adlq", NULL, "adlaq", NULL,
+ "asx0", NULL, "asx1", NULL, "asx2", NULL, "asx3", NULL,
+ "asx4", NULL, "asx5", NULL, "asx6", NULL, "asx7", NULL,
+ "adwp0", NULL, "adwp1", NULL, "adwp2", NULL, "adwp3", NULL,
+ "aos", NULL, "asa", NULL, "asq", NULL, "sscr", NULL,
+ "adx0", "csl", "adx1", "csr", "adx2", NULL, "adx3", NULL,
+ "adx4", "sztl", "adx5", "sztr", "adx6", "cmpb", "adx7", NULL,
+ NULL, NULL, "awca", NULL, "awcq", NULL, "lreg", NULL,
+ NULL, NULL, "ada", NULL, "adq", NULL, "adaq", NULL,
+ "cmpx0", "mlr", "cmpx1", "mrl", "cmpx2", NULL, "cmpx3", NULL,
+ "cmpx4", NULL, "cmpx5", NULL, "cmpx6", "cmpc", "cmpx7", NULL,
+ NULL, NULL, "cwl", NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, "cmpa", NULL, "cmpq", NULL, "cmpaq", NULL,
+ "sblx0", "scd", "sblx1", "scdr", "sblx2", NULL, "sblx3", NULL,
+ "sblx4", "scm", "sblx5", "scmr", "sblx6", NULL, "sblx7", NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, "sbla", NULL, "sblq", NULL, "sblaq", NULL,
+ "ssx0", NULL, "ssx1", NULL, "ssx2", NULL, "ssx3", NULL,
+ "ssx4", NULL, "ssx5", NULL, "ssx6", NULL, "ssx7", NULL,
+ "adwp4", NULL, "adwp5", NULL, "adwp6", NULL, "adwp7", NULL,
+ "sdbr", "sptr", "ssa", NULL, "ssq", NULL, NULL, NULL,
+ "sbx0", "mvt", "sbx1", NULL, "sbx2", NULL, "sbx3", NULL,
+ "sbx4", "tct", "sbx5", "tctr", "sbx6", NULL, "sbx7", NULL,
+ NULL, NULL, "swca", NULL, "swcq", NULL, "lpri", "lptr",
+ NULL, NULL, "sba", NULL, "sbq", NULL, "sbaq", NULL,
+ "cnax0", NULL, "cnax1", NULL, "cnax2", "ad2d", "cnax3", "sb2d",
+ "cnax4", NULL, "cnax5", NULL, "cnax6", "mp2d", "cnax7", "dv2d",
+ NULL, NULL, "cmk", NULL, "absa", NULL, "epaq", NULL,
+ "sznc", NULL, "cnaa", NULL, "cnaq", NULL, "cnaaq", NULL,
+ "ldx0", NULL, "ldx1", NULL, "ldx2", "ad3d", "ldx3", "sb3d",
+ "ldx4", NULL, "ldx5", NULL, "ldx6", "mp3d", "ldx7", "dv3d",
+ "lbar", NULL, "rsw", NULL, "ldbr", "lsdr", "rmcm", NULL,
+ "szn", NULL, "lda", NULL, "ldq", NULL, "ldaq", NULL,
+ "orsx0", NULL, "orsx1", NULL, "orsx2", NULL, "orsx3", NULL,
+ "orsx4", NULL, "orsx5", NULL, "orsx6", NULL, "orsx7", NULL,
+ "spri0", "spbp0", "spbp1", "spri1", "spri2", "spbp2", "spbp3", "spri3",
+ "spri", "ssdr", "orsa", NULL, "orsq", NULL, "lsdp", "lptp",
+ "orx0", NULL, "orx1", NULL, "orx2", NULL, "orx3", NULL,
+ "orx4", NULL, "orx5", NULL, "orx6", NULL, "orx7", NULL,
+ "tsp0", NULL, "tsp1", NULL, "tsp2", NULL, "tsp3", NULL,
+ NULL, NULL, "ora", NULL, "orq", NULL, "oraq", NULL,
+ "canx0", "mvn", "canx1", "btd", "canx2", NULL, "canx3", "cmpn",
+ "canx4", NULL, "canx5", "dtb", "canx6", NULL, "canx7", NULL,
+ "eawp0", "easp1", "easp0", "eawp1", "eawp2", "easp3", "easp2", "eawp3",
+ NULL, NULL, "cana", NULL, "canq", NULL, "canaq", NULL,
+ "lcx0", NULL, "lcx1", NULL, "lcx2", NULL, "lcx3", NULL,
+ "lcx4", NULL, "lcx5", NULL, "lcx6", NULL, "lcx7", NULL,
+ "eawp4", "easp5", "easp4", "eawp5", "eawp6", "easp7", "easp6", "eawp7",
+ NULL, NULL, "lca", NULL, "lcq", NULL, "lcaq", NULL,
+ "ansx0", NULL, "ansx1", NULL, "ansx2", NULL, "ansx3", NULL,
+ "ansx4", NULL, "ansx5", NULL, "ansx6", NULL, "ansx7", NULL,
+ "epp0", "epbp0", "epbp1", "epp1", "epp2", "epbp2", "epbp3", "epp3",
+ "stac", NULL, "ansa", NULL, "ansq", NULL, "stcd", NULL,
+ "anx0", NULL, "anx1", NULL, "anx2", NULL, "anx3", NULL,
+ "anx4", NULL, "anx5", NULL, "anx6", NULL, "anx7", NULL,
+ "epp4", "epbp4", "epbp5", "epp5", "epp6", "epbp6", "epbp7", "epp7",
+ NULL, NULL, "ana", NULL, "anq", NULL, "anaq", NULL,
+ NULL, NULL, "mpf", NULL, "mpy", NULL, NULL, NULL,
+ NULL, NULL, "cmg", NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, "lde", NULL, NULL, NULL, "rscr", NULL,
+ NULL, NULL, "ade", NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, "ufm", NULL, NULL, NULL, "dufm", NULL,
+ NULL, NULL, "fcmg", NULL, NULL, NULL, "dfcmg", NULL,
+ "fszn", NULL, "fld", NULL, NULL, NULL, "dfld", NULL,
+ NULL, NULL, "ufa", NULL, NULL, NULL, "dufa", NULL,
+ "sxl0", NULL, "sxl1", NULL, "sxl2", NULL, "sxl3", "sareg",
+ "sxl4", NULL, "sxl5", NULL, "sxl6", NULL, "sxl7", "spl",
+ "stz", NULL, "smic", NULL, "scpr", NULL, NULL, NULL,
+ "stt", NULL, "fst", NULL, "ste", NULL, "dfst", NULL,
+ NULL, NULL, "fmp", NULL, NULL, NULL, "dfmp", "lareg",
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, "lpl",
+ "fstr", NULL, "frd", NULL, "dfstr", NULL, "dfrd", NULL,
+ NULL, NULL, "fad", NULL, NULL, NULL, "dfad", NULL,
+ "rpl", "a9bd", NULL, "a6bd", NULL, "a4bd", NULL, "abd",
+ NULL, NULL, "bcd", NULL, "div", NULL, "dvf", "awd",
+ NULL, NULL, NULL, NULL, NULL, NULL, "fneg", NULL,
+ NULL, NULL, "fcmp", NULL, NULL, NULL, "dfcmp", NULL,
+ "rpt", "s9bd", NULL, "s6bd", NULL, "s4bd", NULL, "sbd",
+ NULL, NULL, "fdi", NULL, NULL, NULL, "dfdi", "swd",
+ NULL, NULL, "neg", NULL, "cams", "camp", "negl", NULL,
+ NULL, NULL, "ufs", NULL, NULL, NULL, "dufs", NULL,
+ "sprp0", "ara0", "sprp1", "ara1", "sprp2", "ara2", "sprp3", "ara3",
+ "sprp4", "ara4", "sprp5", "ara5", "sprp6", "ara6", "sprp7", "ara7",
+ "sbar", NULL, "stba", NULL, "stbq", NULL, "smcm", NULL,
+ "stc1", NULL, NULL, NULL, NULL, NULL, "ssdp", "sptp",
+ "rpd", "aar0", NULL, "aar1", NULL, "aar2", NULL, "aar3",
+ NULL, "aar4", "fdv", "aar5", NULL, "aar6", "dfdv", "aar7",
+ NULL, NULL, NULL, NULL, NULL, NULL, "fno", NULL,
+ NULL, NULL, "fsb", NULL, NULL, NULL, "dfsb", NULL,
+ "tze", "trtn", "tnz", "trtf", "tnc", NULL, "trc", NULL,
+ "tmi", "tmoz", "tpl", "tpnz", NULL, "ttn", "ttf", NULL,
+ "rtcd", NULL, NULL, NULL, NULL, NULL, "rcu", NULL,
+ "teo", NULL, "teu", NULL, "dis", NULL, "tov", NULL,
+ "eax0", NULL, "eax1", NULL, "eax2", NULL, "eax3", NULL,
+ "eax4", NULL, "eax5", NULL, "eax6", NULL, "eax7", NULL,
+ "ret", NULL, NULL, NULL, NULL, NULL, "rccl", NULL,
+ "ldi", NULL, "eaa", NULL, "eaq", NULL, "ldt", NULL,
+ "ersx0", "arn0", "ersx1", "arn1", "ersx2", "arn2", "ersx3", "arn3",
+ "ersx4", "arn4", "ersx5", "arn5", "ersx6", "arn6", "ersx7", "arn7",
+ "spri4", "spbp4", "spbp5", "spri5", "spri6", "spbp6", "spbp7", "spri7",
+ "stacq", NULL, "ersa", NULL, "ersq", NULL, "scu", NULL,
+ "erx0", "nar0", "erx1", "nar1", "erx2", "nar2", "erx3", "nar3",
+ "erx4", "nar4", "erx5", "nar5", "erx6", "nar6", "erx7", "nar7",
+ "tsp4", NULL, "tsp5", NULL, "tsp6", NULL, "tsp7", NULL,
+ "lcpr", NULL, "era", NULL, "erq", NULL, "eraq", NULL,
+ "tsx0", NULL, "tsx1", NULL, "tsx2", NULL, "tsx3", NULL,
+ "tsx4", NULL, "tsx5", NULL, "tsx6", NULL, "tsx7", NULL,
+ "tra", NULL, NULL, NULL, NULL, NULL, "call6", NULL,
+ NULL, NULL, "tss", NULL, "xec", NULL, "xed", NULL,
+ "lxl0", NULL, "lxl1", NULL, "lxl2", NULL, "lxl3", NULL,
+ "lxl4", NULL, "lxl5", NULL, "lxl6", NULL, "lxl7", NULL,
+ NULL, NULL, "ars", NULL, "qrs", NULL, "lrs", NULL,
+ NULL, NULL, "als", NULL, "qls", NULL, "lls", NULL,
+ "stx0", "sar0", "stx1", "sar1", "stx2", "sar2", "stx3", "sar3",
+ "stx4", "sar4", "stx5", "sar5", "stx6", "sar6", "stx7", "sar7",
+ "stc2", NULL, "stca", NULL, "stcq", NULL, "sreg", NULL,
+ "sti", "sra", "sta", NULL, "stq", NULL, "staq", NULL,
+ "lprp0", "lar0", "lprp1", "lar1", "lprp2", "lar2", "lprp3", "lar3",
+ "lprp4", "lar4", "lprp5", "lar5", "lprp6", "lar6", "lprp7", "lar7",
+ NULL, NULL, "arl", NULL, "qrl", NULL, "lrl", NULL,
+ "gtb", "lra", "alr", NULL, "qlr", NULL, "llr", NULL
+};
+#endif
+
+#ifdef PANEL
+word8 insGrp [02000] =
+ {
+// nonEIS
+ GRP_UNKN, GRP_MISC, GRP_MISC, GRP_UNKN, GRP_MISC, GRP_PSC, GRP_UNKN, GRP_MISC, // 000-007
+ GRP_UNKN, GRP_MISC, GRP_MISC, GRP_MISC, GRP_UNKN, GRP_PCS, GRP_UNKN, GRP_UNKN, // 010-017
+ GRP_FXA, GRP_FXA, GRP_FXA, GRP_FXA, GRP_FXA, GRP_FXA, GRP_FXA, GRP_FXA, // 020-027
+ GRP_UNKN, GRP_UNKN, GRP_FXDML, GRP_FXA, GRP_FXDML, GRP_FXA, GRP_FXA, GRP_FXA, // 030-037
+ GRP_FXA, GRP_FXA, GRP_FXA, GRP_FXA, GRP_FXA, GRP_FXA, GRP_FXA, GRP_FXA, // 040-047
+ GRP_PRAA, GRP_PRAA, GRP_PRAA, GRP_PRAA, GRP_FXA, GRP_FXA, GRP_FXA, GRP_PSC, // 050-057
+ GRP_FXA, GRP_FXA, GRP_FXA, GRP_FXA, GRP_FXA, GRP_FXA, GRP_FXA, GRP_FXA, // 060-067
+ GRP_UNKN, GRP_FXA, GRP_FXA, GRP_FXDML, GRP_UNKN, GRP_FXA, GRP_FXA, GRP_FXA, // 070-077
+ GRP_FXC, GRP_FXC, GRP_FXC, GRP_FXC, GRP_FXC, GRP_FXC, GRP_FXC, GRP_FXC, // 100-107
+ GRP_UNKN, GRP_FXC, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_FXC, GRP_FXC, GRP_FXC, // 110-117
+ GRP_FXS, GRP_FXS, GRP_FXS, GRP_FXS, GRP_FXS, GRP_FXS, GRP_FXS, GRP_FXS, // 120-127
+ GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_FXS, GRP_FXS, GRP_FXS, // 130-137
+ GRP_FXS, GRP_FXS, GRP_FXS, GRP_FXS, GRP_FXS, GRP_FXS, GRP_FXS, GRP_FXS, // 140-147
+ GRP_PRAA, GRP_PRAA, GRP_PRAA, GRP_PRAA, GRP_PRS, GRP_FXS, GRP_FXS, GRP_UNKN, // 150-157
+ GRP_FXS, GRP_FXS, GRP_FXS, GRP_FXS, GRP_FXS, GRP_FXS, GRP_FXS, GRP_FXS, // 160-167
+ GRP_UNKN, GRP_FXS, GRP_FXS, GRP_PRDML, GRP_UNKN, GRP_FXS, GRP_FXS, GRP_FXS, // 170-177
+ GRP_BCN, GRP_BCN, GRP_BCN, GRP_BCN, GRP_BCN, GRP_BCN, GRP_BCN, GRP_BCN, // 200-207
+ GRP_UNKN, GRP_FXC, GRP_PM, GRP_PRM, GRP_FXI, GRP_BCN, GRP_BCN, GRP_BCN, // 210-217
+ GRP_FXDML, GRP_FXDML, GRP_FXDML, GRP_FXDML, GRP_FXDML, GRP_FXDML, GRP_FXDML, GRP_FXDML, // 220-227
+ GRP_MISC, GRP_PCS, GRP_PRL, GRP_PCS, GRP_FXI, GRP_FXDML, GRP_FXDML, GRP_FXDML, // 230-237
+ GRP_BO, GRP_BO, GRP_BO, GRP_BO, GRP_BO, GRP_BO, GRP_BO, GRP_BO, // 240-247
+ GRP_PRDMS, GRP_PRDMS, GRP_PRDMS, GRP_PRDMS, GRP_PRDMS, GRP_BO, GRP_BO, GRP_PRL, // 250-257
+ GRP_BO, GRP_BO, GRP_BO, GRP_BO, GRP_BO, GRP_BO, GRP_BO, GRP_BO, // 260-267
+ GRP_TRA, GRP_TRA, GRP_TRA, GRP_TRA, GRP_UNKN, GRP_BO, GRP_BO, GRP_BO, // 270-277
+ GRP_BCA, GRP_BCA, GRP_BCA, GRP_BCA, GRP_BCA, GRP_BCA, GRP_BCA, GRP_BCA, // 300-307
+ GRP_PRDML, GRP_PRDML, GRP_PRDML, GRP_PRDML, GRP_UNKN, GRP_BCA, GRP_BCA, GRP_BCA, // 310-317
+ GRP_FXDML, GRP_FXDML, GRP_FXDML, GRP_FXDML, GRP_FXDML, GRP_FXDML, GRP_FXDML, GRP_FXDML, // 320-327
+ GRP_PRDML, GRP_PRDML, GRP_PRDML, GRP_PRDML, GRP_UNKN, GRP_FXDML, GRP_FXDML, GRP_FXDML, // 330-337
+ GRP_BA, GRP_BA, GRP_BA, GRP_BA, GRP_BA, GRP_BA, GRP_BA, GRP_BA, // 340-347
+ GRP_PRDML, GRP_PRDML, GRP_PRDML, GRP_PRDML, GRP_FXDMS, GRP_BA, GRP_BA, GRP_FXDMS, // 350-357
+ GRP_BA, GRP_BA, GRP_BA, GRP_BA, GRP_BA, GRP_BA, GRP_BA, GRP_BA, // 360-367
+ GRP_PRDML, GRP_PRDML, GRP_PRDML, GRP_PRDML, GRP_UNKN, GRP_BA, GRP_BA, GRP_BA, // 370-377
+ GRP_UNKN, GRP_FXM, GRP_FXM, GRP_UNKN, GRP_UNKN, GRP_FXC, GRP_UNKN, GRP_UNKN, // 400-407
+ GRP_UNKN, GRP_FLI, GRP_UNKN, GRP_PCS, GRP_UNKN, GRP_FLI, GRP_UNKN, GRP_UNKN, // 410-417
+ GRP_UNKN, GRP_FLM, GRP_UNKN, GRP_FLM, GRP_UNKN, GRP_FLC, GRP_UNKN, GRP_FLC, // 420-427
+ GRP_FLI, GRP_FLDML, GRP_UNKN, GRP_FLDML, GRP_UNKN, GRP_FLA, GRP_UNKN, GRP_FLA, // 430-437
+ GRP_FXDMS, GRP_FXDMS, GRP_FXDMS, GRP_FXDMS, GRP_FXDMS, GRP_FXDMS, GRP_FXDMS, GRP_FXDMS, // 440-447
+ GRP_FXDMS, GRP_PSC, GRP_PRS, GRP_UNKN, GRP_FXDMS, GRP_FLDMS, GRP_UNKN, GRP_FLDMS, // 450-457
+ GRP_UNKN, GRP_FLM, GRP_UNKN, GRP_FLM, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, // 460-467
+ GRP_FLDMS, GRP_FLR, GRP_FLDMS, GRP_FLR, GRP_UNKN, GRP_FLA, GRP_UNKN, GRP_FLA, // 470-477
+ GRP_MISC, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_MISC, GRP_UNKN, GRP_FXD, // 500-507
+ GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_FLN, GRP_UNKN, GRP_FLC, GRP_UNKN, GRP_FLC, // 510-517
+ GRP_MISC, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_FLD, GRP_UNKN, GRP_FLD, // 520-527
+ GRP_UNKN, GRP_FXN, GRP_PCAM, GRP_FXN, GRP_UNKN, GRP_FLS, GRP_UNKN, GRP_FLS, // 530-537
+ GRP_PRDMS, GRP_PRDMS, GRP_PRDMS, GRP_PRDMS, GRP_PRDMS, GRP_PRDMS, GRP_PRDMS, GRP_PRDMS, // 540-547
+ GRP_MISC, GRP_FXDMS, GRP_FXDMS, GRP_PSC, GRP_FXDMS, GRP_UNKN, GRP_FLI, GRP_PRS, // 550-557
+ GRP_MISC, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_FLD, GRP_UNKN, GRP_FLD, // 560-567
+ GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_FLNOR, GRP_UNKN, GRP_FLS, GRP_UNKN, GRP_FLS, // 570-577
+ GRP_TRA, GRP_TRA, GRP_TRA, GRP_TRA, GRP_TRA, GRP_TRA, GRP_TRA, GRP_TRA, // 600-607
+ GRP_TRA, GRP_UNKN, GRP_UNKN, GRP_PRL, GRP_TRA, GRP_TRA, GRP_PM, GRP_TRA, // 610-617
+ GRP_FXDML, GRP_FXDML, GRP_FXDML, GRP_FXDML, GRP_FXDML, GRP_FXDML, GRP_FXDML, GRP_FXDML, // 620-627
+ GRP_TRA, GRP_UNKN, GRP_UNKN, GRP_MISC, GRP_UNKN, GRP_FXDML, GRP_FXDML, GRP_PRL, // 630-637
+ GRP_BE, GRP_BE, GRP_BE, GRP_BE, GRP_BE, GRP_BE, GRP_BE, GRP_BE, // 640-647
+ GRP_PRDMS, GRP_PRDMS, GRP_PRDMS, GRP_PRDMS, GRP_FXDMS, GRP_BE, GRP_BE, GRP_PRS, // 650-657
+ GRP_BE, GRP_BE, GRP_BE, GRP_BE, GRP_BE, GRP_BE, GRP_BE, GRP_BE, // 660-667
+ GRP_TRA, GRP_TRA, GRP_TRA, GRP_TRA, GRP_PRL, GRP_BE, GRP_BE, GRP_BE, // 670-677
+ GRP_TRA, GRP_TRA, GRP_TRA, GRP_TRA, GRP_TRA, GRP_TRA, GRP_TRA, GRP_TRA, // 700-707
+ GRP_TRA, GRP_UNKN, GRP_UNKN, GRP_TRA, GRP_UNKN, GRP_TRA, GRP_MISC, GRP_MISC, // 710-717
+ GRP_FXDML, GRP_FXDML, GRP_FXDML, GRP_FXDML, GRP_FXDML, GRP_FXDML, GRP_FXDML, GRP_FXDML, // 720-727
+ GRP_UNKN, GRP_FXDMR, GRP_FXDMR, GRP_FXDMR, GRP_UNKN, GRP_FXDMR, GRP_FXDMR, GRP_FXDMR, // 730-737
+ GRP_FXDMS, GRP_FXDMS, GRP_FXDMS, GRP_FXDMS, GRP_FXDMS, GRP_FXDMS, GRP_FXDMS, GRP_FXDMS, // 740-747
+ GRP_FXDMS, GRP_FXDMS, GRP_FXDMS, GRP_FXDMS, GRP_FXDMS, GRP_FXDMS, GRP_FXDMS, GRP_FXDMS, // 750-757
+ GRP_PRDML, GRP_PRDML, GRP_PRDML, GRP_PRDML, GRP_PRDML, GRP_PRDML, GRP_PRDML, GRP_PRDML, // 760-767
+ GRP_UNKN, GRP_FXDMR, GRP_FXDMR, GRP_FXDMR, GRP_MISC, GRP_FXDMR, GRP_FXDMR, GRP_FXDMR, // 770-777
+// EIS
+ GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, // 000-007
+ GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, // 010-017
+ GRP_EANM, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_ENM, GRP_UNKN, GRP_UNKN, GRP_UNKN, // 020-027
+ GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, // 030-037
+ GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, // 040-047
+ GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, // 050-057
+ GRP_EBCN, GRP_EBCN, GRP_UNKN, GRP_UNKN, GRP_EBSI, GRP_EBSI, GRP_EBCR, GRP_UNKN, // 060-067
+ GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, // 070-077
+ GRP_EANM, GRP_EANM, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_EANC, GRP_UNKN, // 100-107
+ GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, // 110-117
+ GRP_EANC, GRP_EANC, GRP_UNKN, GRP_UNKN, GRP_EANC, GRP_EANC, GRP_UNKN, GRP_UNKN, // 120-127
+ GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, // 130-137
+ GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, // 140-147
+ GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_PRS, GRP_UNKN, GRP_UNKN, GRP_UNKN, // 150-157
+ GRP_EANM, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_EANC, GRP_EANC, GRP_UNKN, GRP_UNKN, // 160-167
+ GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_PRL, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, // 170-177
+ GRP_UNKN, GRP_UNKN, GRP_EDA, GRP_EDS, GRP_UNKN, GRP_UNKN, GRP_EDM, GRP_EDD, // 200-207
+ GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, // 210-217
+ GRP_UNKN, GRP_UNKN, GRP_EDA, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_EDM, GRP_EDD, // 220-227
+ GRP_UNKN, GRP_UNKN, GRP_PRL, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, // 230-237
+ GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, // 240-247
+ GRP_PRDMS, GRP_UNKN, GRP_PRDMS, GRP_UNKN, GRP_PRS, GRP_UNKN, GRP_UNKN, GRP_PRL, // 250-257
+ GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, // 260-267
+ GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, // 270-277
+ GRP_ENM, GRP_EDC, GRP_UNKN, GRP_ENC, GRP_UNKN, GRP_EDC, GRP_UNKN, GRP_UNKN, // 300-307
+ GRP_PRDML, GRP_PRDML, GRP_PRDML, GRP_PRDML, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, // 310-317
+ GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, // 320-327
+ GRP_PRDML, GRP_PRDML, GRP_PRDML, GRP_PRDML, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, // 330-337
+ GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, // 340-347
+ GRP_PRDML, GRP_PRDML, GRP_PRDML, GRP_PRDML, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, // 350-357
+ GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, // 360-367
+ GRP_PRDML, GRP_PRDML, GRP_PRDML, GRP_PRDML, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, // 370-377
+ GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, // 400-407
+ GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, // 410-417
+ GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, // 420-427
+ GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, // 430-437
+ GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_EARS, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_EARS, // 440-447
+ GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, // 450-457
+ GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_EARL, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_EARL, // 460-467
+ GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, // 470-477
+ GRP_EARSA, GRP_EARSA, GRP_EARSA, GRP_EARSA, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_EARSA, // 500-507
+ GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, // 510-517
+ GRP_EARSA, GRP_EARSA, GRP_EARSA, GRP_EARSA, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_EARSA, // 520-527
+ GRP_UNKN, GRP_UNKN, GRP_PCAM, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, // 530-537
+ GRP_EARS, GRP_EARS, GRP_EARS, GRP_EARS, GRP_EARS, GRP_EARS, GRP_EARS, GRP_EARS, // 540-547
+ GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_PRS, // 550-557
+ GRP_EARL, GRP_EARL, GRP_EARL, GRP_EARL, GRP_EARL, GRP_EARL, GRP_EARL, GRP_EARL, // 560-567
+ GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, // 570-577
+ GRP_TRA, GRP_TRA, GRP_UNKN, GRP_UNKN, GRP_TRA, GRP_TRA, GRP_UNKN, GRP_UNKN, // 600-607
+ GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, // 610-617
+ GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, // 620-627
+ GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, // 630-637
+ GRP_EARS, GRP_EARS, GRP_EARS, GRP_EARS, GRP_EARS, GRP_EARS, GRP_EARS, GRP_EARS, // 640-647
+ GRP_PRDMS, GRP_UNKN, GRP_PRDMS, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, // 650-657
+ GRP_EARL, GRP_EARL, GRP_EARL, GRP_EARL, GRP_EARL, GRP_EARL, GRP_EARL, GRP_EARL, // 660-667
+ GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, // 670-677
+ GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, // 700-707
+ GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, // 710-717
+ GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, // 720-727
+ GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, // 730-737
+ GRP_EARS, GRP_EARS, GRP_EARS, GRP_EARS, GRP_EARS, GRP_EARS, GRP_EARS, GRP_EARS, // 740-747
+ GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_MISC, GRP_UNKN, GRP_UNKN, GRP_UNKN, // 750-757
+ GRP_EARL, GRP_EARL, GRP_EARL, GRP_EARL, GRP_EARL, GRP_EARL, GRP_EARL, GRP_EARL, // 760-767
+ GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_UNKN, GRP_PRL, GRP_UNKN, GRP_UNKN, GRP_UNKN, // 770-777
+ };
+#endif
--- /dev/null
+/*
+ Copyright (c) 2007-2013 Michael Mondy
+ Copyright 2012-2016 by Harry Reed
+ Copyright 2013-2016 by Charles Anthony
+
+ All rights reserved.
+
+ This software is made available under the terms of the
+ ICU License -- ICU 1.8.1 and later.
+ See the LICENSE file at the top-level directory of this distribution and
+ at https://sourceforge.net/p/dps8m/code/ci/master/tree/LICENSE
+ */
+
+extern char GEBcdToASCII[64]; ///< GEBCD => ASCII map
+#ifndef QUIET_UNUSED
+extern char ASCIIToGEBcd[128]; ///< ASCII => GEBCD map
+#endif
+
+#ifndef QUIET_UNUSED
+extern char *op0text[512];
+extern char *op1text[512];
+extern char *opcodes2text[1024];
+#endif
+
+struct adrMods {
+ const char *mod; ///< mnemonic
+ int Td; ///< Td value
+ int Flags;
+};
+typedef struct adrMods adrMods;
+
+extern struct adrMods extMods[0100]; ///< extended address modifiers
+extern struct opcode_s opcodes10[02000];
+#ifdef PANEL
+extern word8 insGrp [02000];
+// CPT 3U 0-35, 3L 0-17
+enum { GRP_UNKN = 0,
+ GRP_FXDML = 1, // Fixed-Point Data Movement Load
+ GRP_FXDMS = 2, // Fixed-Point Data Movement Store
+ GRP_FXDMR = 3, // Fixed-Point Data Movement Shift
+ GRP_FXA = 4, // Fixed-Point Addition
+ GRP_FXS = 5, // Fixed-Point Subtraction
+ GRP_FXM = 6, // Fixed-Point Multiplication
+ GRP_FXD = 7, // Fixed-Point Division
+ GRP_FXN = 8, // Fixed-Point Negate
+ GRP_FXC = 9, // Fixed-Point Comparision
+ GRP_FXI = 10, // Fixed-Point Miscellaneous
+ GRP_BA = 11, // Boolean And
+ GRP_BO = 12, // Boolean Or
+ GRP_BE = 13, // Boolean Exclusive Or
+ GRP_BCA = 14, // Boolean Comparative And
+ GRP_BCN = 15, // Boolean Comparative Not
+ GRP_FLDML = 16, // Floating-Point Data Movement Load
+ GRP_FLDMS = 17, // Floating-Point Data Movement Store
+ GRP_FLA = 18, // Floating-Point Addition
+ GRP_FLS = 19, // Floating-Point Subtraction
+ GRP_FLM = 20, // Floating-Point Multiplication
+ GRP_FLD = 21, // Floating-Point Division
+ GRP_FLN = 22, // Floating-Point Negate
+ GRP_FLNOR = 23, // Floating-Point Normalize
+ GRP_FLR = 24, // Floating-Point Round
+ GRP_FLC = 25, // Floating-Point Compare
+ GRP_FLI = 26, // Floating-Point Miscellaneous
+ GRP_TRA = 27, // Transfer
+ GRP_PRDML = 28, // Pointer Register Data Movement Load
+ GRP_PRDMS = 29, // Pointer Register Data Movement Store
+ GRP_PRAA = 30, // Pointer Register Address Arithmetic
+ GRP_PRM = 31, // Pointer Register Miscellaneous
+ GRP_MISC = 32, // Miscellaneous
+ GRP_PRL = 33, // Privileged - Register Load
+ GRP_PRS = 34, // Privileged - Register Store
+ GRP_PCAM = 35, // Privileged - Clear Associative Memory
+ GRP_PCS = 36, // Privileged - Configuration and Status
+ GRP_PSC = 37, // Privileged - System Control
+ GRP_PM = 38, // Privileged - Miscellaneous
+ GRP_EARL = 39, // EIS - Address Register Load
+ GRP_EARS = 40, // EIS - Address Register Store
+ GRP_EARSA = 41, // EIS - Address Register Special Arithmetic
+ GRP_EANC = 42, // EIS - Alphanumeric Compare
+ GRP_EANM = 43, // EIS - Alphanumeric Move
+ GRP_ENC = 44, // EIS - Numeric Compare
+ GRP_ENM = 45, // EIS - Numeric Move
+ GRP_EBCN = 46, // EIS - Bit String Combine
+ GRP_EBCR = 47, // EIS - Bit String Compare
+ GRP_EBSI = 48, // EIS - Bit String Set Indicators
+ GRP_EDC = 49, // EIS - Data Conversion
+ GRP_EDA = 50, // EIS - Decimal Addition
+ GRP_EDS = 51, // EIS - Decimal Subtration
+ GRP_EDM = 52, // EIS - Decimal Multiplication
+ GRP_EDD = 53, // EIS - Decimal Divison
+};
+#endif
--- /dev/null
+/*
+ Copyright (c) 2007-2013 Michael Mondy
+ Copyright 2012-2016 by Harry Reed
+ Copyright 2013-2016 by Charles Anthony
+
+ All rights reserved.
+
+ This software is made available under the terms of the
+ ICU License -- ICU 1.8.1 and later.
+ See the LICENSE file at the top-level directory of this distribution and
+ at https://sourceforge.net/p/dps8m/code/ci/master/tree/LICENSE
+ */
+
+extern DEVICE scu_dev;
+
+#ifdef ISOLTS
+#define isISOLTS (current_running_cpu_idx != 0)
+#else
+#if 0 // CPU1 only
+#define isISOLTS (current_running_cpu_idx != 0)
+#else // Any CPU
+#define isISOLTS true
+#endif
+#endif
+
+#ifdef SPEED
+#define if_sim_debug(dbits, dptr) if ((0))
+
+#else
+ // ((dptr != & cpu_dev) || current_running_cpu_idx == 1) &&
+
+#define if_sim_debug(dbits, dptr) \
+ if ( \
+ sim_deb && \
+ isISOLTS && \
+ (((dptr)->dctrl & (dbits)) || (dbits) == 0) && \
+ ((dptr != & cpu_dev) || ((1 << current_running_cpu_idx) & dbgCPUMask)) && \
+ ((dptr != & cpu_dev) || (((dptr)->dctrl & (DBG_INTR | DBG_FAULT))) || (! sim_deb_segno_on) || sim_deb_segno[cpu.PPR.PSR & (DEBUG_SEGNO_LIMIT - 1)]) && \
+ ((dptr != & cpu_dev) || sim_deb_ringno == NO_SUCH_RINGNO || sim_deb_ringno == cpu . PPR. PRR) && \
+ ((dptr != & cpu_dev) || (! sim_deb_bar) || (! TST_I_NBAR)) && \
+ cpu.cycleCnt >= sim_deb_start && \
+ (sim_deb_stop == 0 || cpu.cycleCnt < sim_deb_stop) && \
+ (sim_deb_mme_cntdwn == 0) && \
+ ((dptr != & cpu_dev) | (((dbits) & DBG_TRACE) ? (sim_deb_skip_cnt ++ >= sim_deb_skip_limit) : (sim_deb_skip_cnt >= sim_deb_skip_limit))) \
+ )
+#endif
+
+#if !defined(THREADZ) && !defined(LOCKLESS)
+#define dps8_sim_debug _sim_debug
+#endif
+
+#undef sim_debug
+#if defined(THREADZ) || defined(LOCKLESS)
+#define sim_debug(dbits, dptr, ...) \
+ if_sim_debug((dbits), dptr) \
+ dps8_sim_debug ((dbits), dptr, DBG_CTR, __VA_ARGS__); \
+ else \
+ (void) 0
+#else
+#define sim_debug(dbits, dptr, ...) \
+ if_sim_debug((dbits), dptr) \
+ dps8_sim_debug ((dbits), dptr, __VA_ARGS__); \
+ else \
+ (void) 0
+#endif
+
+/* scp Debug flags */
+
+#define DBG_TRACE (1U << 0) ///< instruction trace
+#define DBG_MSG (1U << 1) ///< misc output
+
+#define DBG_REGDUMPAQI (1U << 2) ///< A/Q/IR register dump
+#define DBG_REGDUMPIDX (1U << 3) ///< index register dump
+#define DBG_REGDUMPPR (1U << 4) ///< pointer registers dump
+//#define DBG_REGDUMPADR (1U << 5) ///< address registers dump
+#define DBG_REGDUMPPPR (1U << 6) ///< PPR register dump
+#define DBG_REGDUMPDSBR (1U << 7) ///< descritptor segment base register dump
+#define DBG_REGDUMPFLT (1U << 8) ///< C(EAQ) floating-point register dump
+
+//#define DBG_REGDUMP (DBG_REGDUMPAQI | DBG_REGDUMPIDX | DBG_REGDUMPPR | DBG_REGDUMPADR | DBG_REGDUMPPPR | DBG_REGDUMPDSBR | DBG_REGDUMPFLT)
+#define DBG_REGDUMP (DBG_REGDUMPAQI | DBG_REGDUMPIDX | DBG_REGDUMPPR | DBG_REGDUMPPPR | DBG_REGDUMPDSBR | DBG_REGDUMPFLT)
+
+#define DBG_ADDRMOD (1U << 9) ///< follow address modifications
+#define DBG_APPENDING (1U << 10) ///< follow appending unit operations
+#define DBG_TRACEEXT (1U << 11) ///< extended instruction trace
+#define DBG_WARN (1U << 12)
+#define DBG_DEBUG (1U << 13)
+#define DBG_INFO (1U << 14)
+#define DBG_NOTIFY (1U << 15)
+#define DBG_SIM_USES_16 (1U << 16)
+#define DBG_SIM_USES_17 (1U << 17)
+#define DBG_SIM_USES_18 (1U << 18)
+#define DBG_ERR (1U << 19)
+#define DBG_ALL (DBG_NOTIFY | DBG_INFO | DBG_ERR | DBG_DEBUG | DBG_WARN | \
+ DBG_ERR | DBG_TRACE )
+#define DBG_FAULT (1U << 20) ///< follow fault handling
+#define DBG_INTR (1U << 21) // follow interrupt handling
+#define DBG_CORE (1U << 22)
+#define DBG_CYCLE (1U << 23)
+#define DBG_CAC (1U << 24)
+#define DBG_FINAL (1U << 25)
+#define DBG_AVC (1U << 26)
+
+// Abort codes, used to sort out longjmp's back to the main loop.
+// Codes > 0 are simulator stop codes
+// Codes < 0 are internal aborts
+// Code = 0 stops execution for an interrupt check (XXX Don't know if I like
+// this or not)
+// XXX above is not entirely correct (anymore).
+
+
+//#define SCPE_OK 0
+#define STOP_STOP 1
+#define STOP_BKPT 2
+
+
+// not really STOP codes, but get returned from instruction loops
+#define CONT_TRA -1 // encountered a transfer instruction; don't bump PPR.IC
+#define CONT_DIS -2 // instruction was a DIS
+#define CONT_XEC -3 // instruction was a XEC or XED
+#define CONT_RET -5 // encountered a return instruction; don't bump PPR.IC,
+ // do instruction fetch
+
+//
+// mask entry flags
+// MTAB_XTD extended entry
+// MTAB_VDV valid for devices
+// MTAB_VUN valid for units
+// MTAB_VALO takes a value (optional)
+// MTAB_VALR takes a value (required)
+// MTAB_NMO valid only in named SHOW
+// MTAB_NC do not convert option value to upper case
+// MTAB_SHP SHOW parameter takes optional value
+
+// Requires a value, DEV and DEVn both work, not in "show"
+#define MTAB_unit_value MTAB_XTD | MTAB_VUN | MTAB_VDV | MTAB_NMO | MTAB_VALR
+// Requires a value, DEVn, not in "show"
+#define MTAB_unitonly_value MTAB_XTD | MTAB_VUN | MTAB_NMO | MTAB_VALR
+// Requires a value, DEV and DEVn both work, in "show"
+#define MTAB_unit_value_show MTAB_XTD | MTAB_VUN | MTAB_VDV | MTAB_VALR
+// Requires a value, DEV only, not in "show", uppercase value
+#define MTAB_dev_valr_noshow MTAB_XTD | MTAB_VDV | MTAB_NMO | MTAB_VALR
+// Requires a value, DEV only, not in "show", don't uppercase value
+#define MTAB_dev_valr_nouc MTAB_XTD | MTAB_VDV | MTAB_NMO | MTAB_VALR | MTAB_NC
+// Requires a value, DEV only, not in "show"
+#define MTAB_dev_value MTAB_XTD | MTAB_VDV | MTAB_NMO | MTAB_VALR
+// No value, DEV only, in "show"
+#define MTAB_dev_novalue MTAB_XTD | MTAB_VDV
+// Requires a value, DEVn only, in "show", don't uppercase value
+#define MTAB_unit_valr_nouc MTAB_XTD | MTAB_VUN | MTAB_VALR | MTAB_NC
+// Value optional, DEVn only, do not uppercase value
+#define MTAB_unit_nouc MTAB_XTD | MTAB_VUN | MTAB_NC
+// Value optional, DEVn only, uppercase value
+#define MTAB_unit_uc MTAB_XTD | MTAB_VUN
+// Value required, DEV only
+#define MTAB_dev_valr MTAB_XTD | MTAB_VDV | MTAB_VALR
+// End of list marker
+#define MTAB_eol { 0, 0, NULL, NULL, 0, 0, NULL, NULL }
+
+extern uint32 sim_brk_summ, sim_brk_types, sim_brk_dflt;
+extern FILE *sim_deb;
+void sim_printf( const char * format, ... ) // not really simh, by my impl
+#ifdef __GNUC__
+ __attribute__ ((format (printf, 1, 2)))
+#endif
+;
+void sim_puts (char * str);
+#if 0
+void sim_fatal (const char * format, ...) NO_RETURN
+#ifdef __GNUC__
+ __attribute__ ((format (printf, 1, 2)))
+#endif
+;
+#endif
+void sim_printl (const char * format, ...)
+#ifdef __GNUC__
+ __attribute__ ((format (printf, 1, 2)))
+#endif
+;
+#if 0
+void sim_warn (const char * format, ...)
+#ifdef __GNUC__
+ __attribute__ ((format (printf, 1, 2)))
+#endif
+;
+#endif
+
+#if defined(THREADZ) || defined(LOCKLESS)
+void dps8_sim_debug (uint32 dbits, DEVICE* dptr, unsigned long long cnt, const char* fmt, ...)
+#ifdef __GNUC__
+ __attribute__ ((format (printf, 4, 5)))
+#endif
+;
+#endif
+//#define sim_warn(format, ...) _sim_err (format, ##__VA_ARGS__)
+//#define sim_err(format, ...) { _sim_err (format, ##__VA_ARGS__); longjmp (cpu.jmpMain, JMP_STOP); }
+#define sim_fatal(format, ...) { _sim_err (format, ##__VA_ARGS__); exit (1); }
+#ifdef COLOR
+void sim_msg (const char * fmt, ...);
+void sim_warn (const char * fmt, ...);
+void sim_print (const char * fmt, ...);
+#else
+#define sim_msg sim_printf
+#define sim_warn sim_printf
+#define sim_print sim_printf
+#endif
--- /dev/null
+#include "dps8_sys.h"
+
+FILE *sim_log;
+
+#ifndef SCUMEM
+/*vol*/ word36 * M;
+#endif
--- /dev/null
+#ifndef _DPS8_SYS
+#define _DPS8_SYS
+
+#include <stdio.h>
+#include "dps8.h"
+
+#define sim_printf printf
+
+extern FILE *sim_log;
+
+#ifndef SCUMEM
+extern /*vol*/ word36 * M;
+#endif
+
+#endif
--- /dev/null
+/*
+ Copyright (c) 2007-2013 Michael Mondy
+ Copyright 2012-2016 by Harry Reed
+ Copyright 2013-2016 by Charles Anthony
+
+ All rights reserved.
+
+ This software is made available under the terms of the
+ ICU License -- ICU 1.8.1 and later.
+ See the LICENSE file at the top-level directory of this distribution and
+ at https://sourceforge.net/p/dps8m/code/ci/master/tree/LICENSE
+ */
+
+/**
+ * \file dps8_utils.c
+ * \project dps8
+ * \date 9/25/12
+ * \copyright Copyright (c) 2012 Harry Reed. All rights reserved.
+*/
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "dps8.h"
+#include "dps8_sys.h"
+//#include "dps8_faults.h"
+//#include "dps8_scu.h"
+//#include "dps8_iom.h"
+//#include "dps8_cable.h"
+#include "dps8_cpu.h"
+#include "dps8_ins.h"
+#include "dps8_opcodetable.h"
+#include "dps8_utils.h"
+
+#define DBG_CTR 1
+
+/*
+ * misc utility routines used by simulator
+ */
+
+char * dump_flags(char * buffer, word18 flags)
+{
+ //static char buffer[256] = "";
+
+ sprintf(buffer, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
+#ifdef DPS8M
+ flags & I_HEX ? "Hex " : "",
+#endif
+#ifdef L68
+ "",
+#endif
+ flags & I_ABS ? "Abs " : "",
+ flags & I_MIF ? "MIF " : "",
+ flags & I_TRUNC ? "Trunc " : "",
+ flags & I_NBAR ? "~BAR " : "",
+ flags & I_PMASK ? "PMask " : "",
+ flags & I_PERR ? "PErr" : "",
+ flags & I_TALLY ? "Tally " : "",
+ flags & I_OMASK ? "OMASK " : "",
+ flags & I_EUFL ? "EUFL " : "",
+ flags & I_EOFL ? "EOFL " : "",
+ flags & I_OFLOW ? "Ovr " : "",
+ flags & I_CARRY ? "Carry " : "",
+ flags & I_NEG ? "Neg " : "",
+ flags & I_ZERO ? "Zero " : ""
+ );
+ return buffer;
+
+}
+
+static char * dps8_strupr(char *str)
+{
+ char *s;
+
+ for(s = str; *s; s++)
+ *s = (char) toupper((unsigned char)*s);
+ return str;
+}
+
+//! get instruction info for IWB ...
+
+static struct opcode_s unimplented = {"(unimplemented)", 0, 0, 0, 0};
+
+struct opcode_s * get_iwb_info (DCDstruct * i)
+ {
+ struct opcode_s * p = &opcodes10[i->opcode10];
+ return p->mne ? p : &unimplented;
+ }
+
+char *disassemble(char * result, word36 instruction)
+{
+ uint32 opcode = GET_OP(instruction); ///< get opcode
+ uint32 opcodeX = GET_OPX(instruction); ///< opcode extension
+ uint32 opcode10 = opcode | (opcodeX ? 01000 : 0);
+ word18 address = GET_ADDR(instruction);
+ word1 a = GET_A(instruction);
+ //int32 i = GET_I(instruction);
+ word6 tag = GET_TAG(instruction);
+
+ //static char result[132] = "???";
+ strcpy(result, "???");
+
+ // get mnemonic ...
+ if (opcodes10[opcode10].mne)
+ strcpy(result, opcodes10[opcode10].mne);
+
+ // XXX need to reconstruct multi-word EIS instruction.
+
+ char buff[64];
+
+ if (a)
+ {
+ int n = (address >> 15) & 07;
+ int offset = address & 077777;
+
+ sprintf(buff, " pr%d|%o", n, offset);
+ strcat (result, buff);
+ // return dps8_strupr(result);
+ } else {
+ sprintf(buff, " %06o", address);
+ strcat (result, buff);
+ }
+ // get mod
+ strcpy(buff, "");
+ for(uint n = 0 ; n < 0100 ; n++)
+ if (extMods[n].mod)
+ if(n == tag)
+ {
+ strcpy(buff, extMods[n].mod);
+ break;
+ }
+
+ if (strlen(buff))
+ {
+ strcat(result, ",");
+ strcat(result, buff);
+ }
+
+ return dps8_strupr(result);
+}
+
+/*
+ * get_mod__string ()
+ *
+ * Convert instruction address modifier tag to printable string
+ * WARNING: returns pointer to statically allocated string
+ *
+ */
+
+char *get_mod_string(char * msg, word6 tag)
+{
+ strcpy(msg, "none");
+
+ if (tag >= 0100)
+ {
+ sprintf(msg, "getModReg(tag out-of-range %o)", tag);
+ } else {
+ for(uint n = 0 ; n < 0100 ; n++)
+ if (extMods[n].mod)
+ if(n == tag)
+ {
+ strcpy(msg, extMods[n].mod);
+ break;
+ }
+
+ }
+ return msg;
+}
+
+
+/*
+ * 36-bit arithmetic stuff ...
+ */
+/* Single word integer routines */
+
+word36 Add36b (word36 op1, word36 op2, word1 carryin, word18 flagsToSet, word18 * flags, bool * ovf)
+ {
+ CPT (cpt2L, 17); // Add36b
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "Add36b op1 %012"PRIo64" op2 %012"PRIo64" carryin %o flagsToSet %06o flags %06o\n", op1, op2, carryin, flagsToSet, * flags);
+// https://en.wikipedia.org/wiki/Two%27s_complement#Addition
+//
+// In general, any two N-bit numbers may be added without overflow, by first
+// sign-extending both of them to N + 1 bits, and then adding as above. The
+// N + 1 bits result is large enough to represent any possible sum (N = 5 two's
+// complement can represent values in the range −16 to 15) so overflow will
+// never occur. It is then possible, if desired, to 'truncate' the result back
+// to N bits while preserving the value if and only if the discarded bit is a
+// proper sign extension of the retained result bits. This provides another
+// method of detecting overflow—which is equivalent to the method of comparing
+// the carry bits—but which may be easier to implement in some situations,
+// because it does not require access to the internals of the addition.
+
+ // 37 bit arithmetic for the above N+1 algorithm
+ word38 op1e = op1 & MASK36;
+ word38 op2e = op2 & MASK36;
+ word38 ci = carryin ? 1 : 0;
+
+ // extend sign bits
+ if (op1e & SIGN36)
+ op1e |= BIT37;
+ if (op2e & SIGN36)
+ op2e |= BIT37;
+
+ // Do the math
+ word38 res = op1e + op2e + ci;
+
+ // Extract the overflow bits
+ bool r37 = res & BIT37 ? true : false;
+ bool r36 = res & SIGN36 ? true : false;
+
+ // Extract the carry bit
+ bool r38 = res & BIT38 ? true : false;
+
+ // Check for overflow
+ * ovf = r37 ^ r36;
+
+ // Check for carry
+ bool cry = r38;
+
+ // Truncate the result
+ res &= MASK36;
+
+#ifdef PANEL
+ if (cry) CPT (cpt2L, 28); // carry
+ if (ovf) CPT (cpt2L, 29); // ovf
+ if (!res) CPT (cpt2L, 30); // zero
+ if (res & SIGN36) CPT (cpt2L, 31); // neg
+#endif
+
+ if (flagsToSet & I_CARRY)
+ {
+ if (cry)
+ SETF (* flags, I_CARRY);
+ else
+ CLRF (* flags, I_CARRY);
+ }
+
+ if (chkOVF () && (flagsToSet & I_OFLOW))
+ {
+ if (* ovf)
+ SETF (* flags, I_OFLOW); // overflow
+ }
+
+ if (flagsToSet & I_ZERO)
+ {
+ if (res)
+ CLRF (* flags, I_ZERO);
+ else
+ SETF (* flags, I_ZERO); // zero result
+ }
+
+ if (flagsToSet & I_NEG)
+ {
+ if (res & SIGN36)
+ SETF (* flags, I_NEG);
+ else
+ CLRF (* flags, I_NEG);
+ }
+
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "Add36b res %012"PRIo64" flags %06o ovf %o\n", res, * flags, * ovf);
+ return res;
+ }
+
+word36 Sub36b (word36 op1, word36 op2, word1 carryin, word18 flagsToSet, word18 * flags, bool * ovf)
+ {
+ CPT (cpt2L, 18); // Sub36b
+
+// https://en.wikipedia.org/wiki/Two%27s_complement
+//
+// As for addition, overflow in subtraction may be avoided (or detected after
+// the operation) by first sign-extending both inputs by an extra bit.
+//
+// AL39:
+//
+// If carry indicator ON, then C(A) - C(Y) -> C(A)
+// If carry indicator OFF, then C(A) - C(Y) - 1 -> C(A)
+
+ // 38 bit arithmetic for the above N+1 algorithm
+ word38 op1e = op1 & MASK36;
+ word38 op2e = op2 & MASK36;
+ // Note that carryin has an inverted sense for borrow
+ word38 ci = carryin ? 0 : 1;
+
+ // extend sign bits
+ if (op1e & SIGN36)
+ op1e |= BIT37;
+ if (op2e & SIGN36)
+ op2e |= BIT37;
+
+ // Do the math
+ word38 res = op1e - op2e - ci;
+
+ // Extract the overflow bits
+ bool r37 = (res & BIT37) ? true : false;
+ bool r36 = (res & SIGN36) ? true : false;
+
+ // Extract the carry bit
+ bool r38 = res & BIT38 ? true : false;
+
+ // Truncate the result
+ res &= MASK36;
+
+ // Check for overflow
+ * ovf = r37 ^ r36;
+
+ // Check for carry
+ bool cry = r38;
+
+#ifdef PANEL
+ if (cry) CPT (cpt2L, 28); // carry
+ if (ovf) CPT (cpt2L, 29); // ovf
+ if (!res) CPT (cpt2L, 30); // zero
+ if (res & SIGN36) CPT (cpt2L, 31); // neg
+#endif
+
+ if (flagsToSet & I_CARRY)
+ {
+ if (cry) // Note inverted logic for subtraction
+ CLRF (* flags, I_CARRY);
+ else
+ SETF (* flags, I_CARRY);
+ }
+
+ if (chkOVF () && (flagsToSet & I_OFLOW))
+ {
+ if (* ovf)
+ SETF (* flags, I_OFLOW); // overflow
+ }
+
+ if (flagsToSet & I_ZERO)
+ {
+ if (res)
+ CLRF (* flags, I_ZERO);
+ else
+ SETF (* flags, I_ZERO); // zero result
+ }
+
+ if (flagsToSet & I_NEG)
+ {
+ if (res & SIGN36)
+ SETF (* flags, I_NEG);
+ else
+ CLRF (* flags, I_NEG);
+ }
+
+ return res;
+ }
+
+word18 Add18b (word18 op1, word18 op2, word1 carryin, word18 flagsToSet, word18 * flags, bool * ovf)
+ {
+ CPT (cpt2L, 19); // Add18b
+
+// https://en.wikipedia.org/wiki/Two%27s_complement#Addition
+//
+// In general, any two N-bit numbers may be added without overflow, by first
+// sign-extending both of them to N + 1 bits, and then adding as above. The
+// N + 1 bits result is large enough to represent any possible sum (N = 5 two's
+// complement can represent values in the range −16 to 15) so overflow will
+// never occur. It is then possible, if desired, to 'truncate' the result back
+// to N bits while preserving the value if and only if the discarded bit is a
+// proper sign extension of the retained result bits. This provides another
+// method of detecting overflow—which is equivalent to the method of comparing
+// the carry bits—but which may be easier to implement in some situations,
+// because it does not require access to the internals of the addition.
+
+ // 19 bit arithmetic for the above N+1 algorithm
+ word20 op1e = op1 & MASK18;
+ word20 op2e = op2 & MASK18;
+ word20 ci = carryin ? 1 : 0;
+
+ // extend sign bits
+ if (op1e & SIGN18)
+ op1e |= BIT19;
+ if (op2e & SIGN18)
+ op2e |= BIT19;
+
+ // Do the math
+ word20 res = op1e + op2e + ci;
+
+ // Extract the overflow bits
+ bool r19 = (res & BIT19) ? true : false;
+ bool r18 = (res & SIGN18) ? true : false;
+
+ // Extract the carry bit
+ bool r20 = res & BIT20 ? true : false;
+
+ // Truncate the result
+ res &= MASK18;
+
+ // Check for overflow
+ * ovf = r19 ^ r18;
+
+ // Check for carry
+ bool cry = r20;
+
+#ifdef PANEL
+ if (cry) CPT (cpt2L, 28); // carry
+ if (ovf) CPT (cpt2L, 29); // ovf
+ if (!res) CPT (cpt2L, 30); // zero
+ if (res & SIGN36) CPT (cpt2L, 31); // neg
+#endif
+
+ if (flagsToSet & I_CARRY)
+ {
+ if (cry)
+ SETF (* flags, I_CARRY);
+ else
+ CLRF (* flags, I_CARRY);
+ }
+
+ if (chkOVF () && (flagsToSet & I_OFLOW))
+ {
+ if (* ovf)
+ SETF (* flags, I_OFLOW); // overflow
+ }
+
+ if (flagsToSet & I_ZERO)
+ {
+ if (res)
+ CLRF (* flags, I_ZERO);
+ else
+ SETF (* flags, I_ZERO); // zero result
+ }
+
+ if (flagsToSet & I_NEG)
+ {
+ if (res & SIGN18)
+ SETF (* flags, I_NEG);
+ else
+ CLRF (* flags, I_NEG);
+ }
+
+ return (word18) res;
+ }
+
+word18 Sub18b (word18 op1, word18 op2, word1 carryin, word18 flagsToSet, word18 * flags, bool * ovf)
+ {
+ CPT (cpt2L, 20); // Sub18b
+
+// https://en.wikipedia.org/wiki/Two%27s_complement
+//
+// As for addition, overflow in subtraction may be avoided (or detected after
+// the operation) by first sign-extending both inputs by an extra bit.
+//
+// AL39:
+//
+// If carry indicator ON, then C(A) - C(Y) -> C(A)
+// If carry indicator OFF, then C(A) - C(Y) - 1 -> C(A)
+
+ // 19 bit arithmetic for the above N+1 algorithm
+ word20 op1e = op1 & MASK18;
+ word20 op2e = op2 & MASK18;
+ // Note that carryin has an inverted sense for borrow
+ word20 ci = carryin ? 0 : 1;
+
+ // extend sign bits
+ if (op1e & SIGN18)
+ op1e |= BIT19;
+ if (op2e & SIGN18)
+ op2e |= BIT19;
+
+ // Do the math
+ word20 res = op1e - op2e - ci;
+
+ // Extract the overflow bits
+ bool r19 = res & BIT19 ? true : false;
+ bool r18 = res & SIGN18 ? true : false;
+
+ // Extract the carry bit
+ bool r20 = res & BIT20 ? true : false;
+
+ // Truncate the result
+ res &= MASK18;
+
+ // Check for overflow
+ * ovf = r19 ^ r18;
+
+ // Check for carry
+ bool cry = r20;
+
+#ifdef PANEL
+ if (cry) CPT (cpt2L, 28); // carry
+ if (ovf) CPT (cpt2L, 29); // ovf
+ if (!res) CPT (cpt2L, 30); // zero
+ if (res & SIGN36) CPT (cpt2L, 31); // neg
+#endif
+
+ if (flagsToSet & I_CARRY)
+ {
+ if (cry) // Note inverted logic for subtraction
+ CLRF (* flags, I_CARRY);
+ else
+ SETF (* flags, I_CARRY);
+ }
+
+ if (chkOVF () && (flagsToSet & I_OFLOW))
+ {
+ if (* ovf)
+ SETF (* flags, I_OFLOW); // overflow
+ }
+
+ if (flagsToSet & I_ZERO)
+ {
+ if (res)
+ CLRF (* flags, I_ZERO);
+ else
+ SETF (* flags, I_ZERO); // zero result
+ }
+
+ if (flagsToSet & I_NEG)
+ {
+ if (res & SIGN18)
+ SETF (* flags, I_NEG);
+ else
+ CLRF (* flags, I_NEG);
+ }
+
+ return res;
+ }
+
+word72 Add72b (word72 op1, word72 op2, word1 carryin, word18 flagsToSet, word18 * flags, bool * ovf)
+ {
+ CPT (cpt2L, 21); // Add72b
+#ifdef ISOLTS
+//if (current_running_cpu_idx)
+//sim_printf ("Add72b op1 %012"PRIo64"%012"PRIo64" op2 %012"PRIo64"%012"PRIo64" carryin %o flagsToSet %06o flags %06o ovf %o\n",
+ //(word36) ((op1 >> 36) & MASK36), (word36) (op1 & MASK36), (word36) ((op2 >> 36) & MASK36), (word36) (op2 & MASK36), carryin, flagsToSet, * flags, * ovf);
+#endif
+
+// https://en.wikipedia.org/wiki/Two%27s_complement#Addition
+//
+// In general, any two N-bit numbers may be added without overflow, by first
+// sign-extending both of them to N + 1 bits, and then adding as above. The
+// N + 1 bits result is large enough to represent any possible sum (N = 5 two's
+// complement can represent values in the range −16 to 15) so overflow will
+// never occur. It is then possible, if desired, to 'truncate' the result back
+// to N bits while preserving the value if and only if the discarded bit is a
+// proper sign extension of the retained result bits. This provides another
+// method of detecting overflow—which is equivalent to the method of comparing
+// the carry bits—but which may be easier to implement in some situations,
+// because it does not require access to the internals of the addition.
+
+ // 73 bit arithmetic for the above N+1 algorithm
+#ifdef NEED_128
+ word74 op1e = and_128 (op1, MASK72);
+ word74 op2e = and_128 (op2, MASK72);
+ word74 ci = construct_128 (0, carryin ? 1 : 0);
+#else
+ word74 op1e = op1 & MASK72;
+ word74 op2e = op2 & MASK72;
+ word74 ci = carryin ? 1 : 0;
+#endif
+
+ // extend sign bits
+#ifdef NEED_128
+ if (isnonzero_128 (and_128 (op1e, SIGN72)))
+ op1e = or_128 (op1e, BIT73);
+ if (isnonzero_128 (and_128 (op2e, SIGN72)))
+ op2e = or_128 (op2e, BIT73);
+#else
+ if (op1e & SIGN72)
+ op1e |= BIT73;
+ if (op2e & SIGN72)
+ op2e |= BIT73;
+#endif
+
+ // Do the math
+#ifdef NEED_128
+ word74 res = add_128 (op1e, add_128 (op2e, ci));
+#else
+ word74 res = op1e + op2e + ci;
+#endif
+
+ // Extract the overflow bits
+#ifdef NEED_128
+ bool r73 = isnonzero_128 (and_128 (res, BIT73));
+ bool r72 = isnonzero_128 (and_128 (res, SIGN72));
+#else
+ bool r73 = res & BIT73 ? true : false;
+ bool r72 = res & SIGN72 ? true : false;
+#endif
+
+ // Extract the carry bit
+#ifdef NEED_128
+ bool r74 = isnonzero_128 (and_128 (res, BIT74));
+#else
+ bool r74 = res & BIT74 ? true : false;
+#endif
+
+ // Truncate the result
+#ifdef NEED_128
+ res = and_128 (res, MASK72);
+#else
+ res &= MASK72;
+#endif
+
+ // Check for overflow
+ * ovf = r73 ^ r72;
+
+ // Check for carry
+ bool cry = r74;
+
+#ifdef PANEL
+ if (cry) CPT (cpt2L, 28); // carry
+ if (ovf) CPT (cpt2L, 29); // ovf
+ if (!res) CPT (cpt2L, 30); // zero
+ if (res & SIGN36) CPT (cpt2L, 31); // neg
+#endif
+
+#ifdef ISOLTS
+//if (current_running_cpu_idx)
+//{
+////char buf [1024];
+////print_int128 (res, buf);
+//sim_printf ("res %012"PRIo64"%012"PRIo64"\nr72 %d r73 %d r74 %d ovf %d cry %d\n", ((word36) (res >> 36)) & MASK36, (word36) res & MASK36, r72, r73, r74, * ovf, cry);
+//}
+#endif
+ if (flagsToSet & I_CARRY)
+ {
+ if (cry)
+ SETF (* flags, I_CARRY);
+ else
+ CLRF (* flags, I_CARRY);
+ }
+
+ if (chkOVF () && (flagsToSet & I_OFLOW))
+ {
+ if (* ovf)
+ SETF (* flags, I_OFLOW); // overflow
+ }
+
+ if (flagsToSet & I_ZERO)
+ {
+#ifdef NEED_128
+ if (isnonzero_128 (res))
+#else
+ if (res)
+#endif
+ CLRF (* flags, I_ZERO);
+ else
+ SETF (* flags, I_ZERO); // zero result
+ }
+
+ if (flagsToSet & I_NEG)
+ {
+#ifdef NEED_128
+ if (isnonzero_128 (and_128 (res, SIGN72)))
+#else
+ if (res & SIGN72)
+#endif
+ SETF (* flags, I_NEG);
+ else
+ CLRF (* flags, I_NEG);
+ }
+
+#ifdef ISOLTS
+//if (current_running_cpu_idx)
+//{
+//sim_printf ("Sub72b res %012"PRIo64"%012"PRIo64" flags %06o ovf %o\n", (word36) ((res >> 36) & MASK36), (word36) (res & MASK36), * flags, * ovf);
+//}
+#endif
+ return res;
+ }
+
+
+word72 Sub72b (word72 op1, word72 op2, word1 carryin, word18 flagsToSet, word18 * flags, bool * ovf)
+ {
+ CPT (cpt2L, 22); // Sub72b
+#ifdef ISOLTS
+//if (current_running_cpu_idx)
+//sim_printf ("Sub72b op1 %012"PRIo64"%012"PRIo64" op2 %012"PRIo64"%012"PRIo64" carryin %o flagsToSet %06o flags %06o ovf %o\n",
+ //(word36) ((op1 >> 36) & MASK36), (word36) (op1 & MASK36), (word36) ((op2 >> 36) & MASK36), (word36) (op2 & MASK36), carryin, flagsToSet, * flags, * ovf);
+#endif
+#ifdef NEED_128
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "Sub72b op1 %012"PRIo64"%012"PRIo64" op2 %012"PRIo64"%012"PRIo64" carryin %o flagsToSet %06o flags %06o\n",
+ (word36) ((rshift_128 (op1, 36).l) & MASK36), (word36) (op1.l & MASK36), (word36) (rshift_128 (op2, 36).l & MASK36), (word36) (op2.l & MASK36), carryin, flagsToSet, * flags);
+#else
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "Sub72b op1 %012"PRIo64"%012"PRIo64" op2 %012"PRIo64"%012"PRIo64" carryin %o flagsToSet %06o flags %06o\n",
+ (word36) ((op1 >> 36) & MASK36), (word36) (op1 & MASK36), (word36) ((op2 >> 36) & MASK36), (word36) (op2 & MASK36), carryin, flagsToSet, * flags);
+#endif
+
+// https://en.wikipedia.org/wiki/Two%27s_complement
+//
+// As for addition, overflow in subtraction may be avoided (or detected after
+// the operation) by first sign-extending both inputs by an extra bit.
+//
+// AL39:
+//
+// If carry indicator ON, then C(A) - C(Y) -> C(A)
+// If carry indicator OFF, then C(A) - C(Y) - 1 -> C(A)
+
+ // 73 bit arithmetic for the above N+1 algorithm
+#ifdef NEED_128
+ word74 op1e = and_128 (op1, MASK72);
+ word74 op2e = and_128 (op2, MASK72);
+#else
+ word74 op1e = op1 & MASK72;
+ word74 op2e = op2 & MASK72;
+#endif
+ // Note that carryin has an inverted sense for borrow
+#ifdef NEED_128
+ word74 ci = construct_128 (0, carryin ? 0 : 1);
+#else
+ word74 ci = carryin ? 0 : 1;
+#endif
+
+ // extend sign bits
+#ifdef NEED_128
+ if (isnonzero_128 (and_128 (op1e, SIGN72)))
+ op1e = or_128 (op1e, BIT73);
+ if (isnonzero_128 (and_128 (op2e, SIGN72)))
+ op2e = or_128 (op2e, BIT73);
+#else
+ if (op1e & SIGN72)
+ op1e |= BIT73;
+ if (op2e & SIGN72)
+ op2e |= BIT73;
+#endif
+
+ // Do the math
+#ifdef NEED_128
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "Sub72b op1e %012"PRIo64"%012"PRIo64" op2e %012"PRIo64"%012"PRIo64" carryin %o flagsToSet %06o flags %06o\n",
+ (word36) ((rshift_128 (op1e, 36).l) & MASK36), (word36) (op1e.l & MASK36), (word36) (rshift_128 (op2e, 36).l & MASK36), (word36) (op2e.l & MASK36), carryin, flagsToSet, * flags);
+#else
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "Sub72b op1e %012"PRIo64"%012"PRIo64" op2e %012"PRIo64"%012"PRIo64" carryin %o flagsToSet %06o flags %06o\n",
+ (word36) ((op1e >> 36) & MASK36), (word36) (op1e & MASK36), (word36) ((op2e >> 36) & MASK36), (word36) (op2e & MASK36), carryin, flagsToSet, * flags);
+#endif
+#ifdef NEED_128
+ word74 res = subtract_128 (subtract_128 (op1e, op2e), ci);
+#else
+ word74 res = op1e - op2e - ci;
+#endif
+#ifdef NEED_128
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "Sub72b res %012"PRIo64"%012"PRIo64" flags %06o ovf %o\n", (word36) (rshift_128 (res, 36).l & MASK36), (word36) (res.l & MASK36), * flags, * ovf);
+#else
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "Sub72b res %012"PRIo64"%012"PRIo64" flags %06o ovf %o\n", (word36) ((res >> 36) & MASK36), (word36) (res & MASK36), * flags, * ovf);
+#endif
+
+ // Extract the overflow bits
+#ifdef NEED_128
+ bool r73 = isnonzero_128 (and_128 (res, BIT73));
+ bool r72 = isnonzero_128 (and_128 (res, SIGN72));
+#else
+ bool r73 = res & BIT73 ? true : false;
+ bool r72 = res & SIGN72 ? true : false;
+#endif
+
+ // Extract the carry bit
+#ifdef NEED_128
+ bool r74 = isnonzero_128 (and_128 (res, BIT74));
+#else
+ bool r74 = res & BIT74 ? true : false;
+#endif
+
+ // Truncate the result
+#ifdef NEED_128
+ res = and_128 (res, MASK72);
+#else
+ res &= MASK72;
+#endif
+
+ // Check for overflow
+ * ovf = r73 ^ r72;
+
+ // Check for carry
+ bool cry = r74;
+
+#ifdef ISOLTS
+//if (current_running_cpu_idx)
+//{
+////char buf [1024];
+////print_int128 (res, buf);
+//sim_printf ("res %012"PRIo64"%012"PRIo64"\nr72 %d r73 %d r74 %d ovf %d cry %d\n", ((word36) (res >> 36)) & MASK36, (word36) res & MASK36, r72, r73, r74, * ovf, cry);
+//}
+#endif
+
+#ifdef PANEL
+ if (cry) CPT (cpt2L, 28); // carry
+ if (ovf) CPT (cpt2L, 29); // ovf
+ if (!res) CPT (cpt2L, 30); // zero
+ if (res & SIGN36) CPT (cpt2L, 31); // neg
+#endif
+
+ if (flagsToSet & I_CARRY)
+ {
+ if (cry) // Note inverted logic for subtraction
+ CLRF (* flags, I_CARRY);
+ else
+ SETF (* flags, I_CARRY);
+ }
+
+ if (chkOVF () && (flagsToSet & I_OFLOW))
+ {
+ if (* ovf)
+ SETF (* flags, I_OFLOW); // overflow
+ }
+
+ if (flagsToSet & I_ZERO)
+ {
+#ifdef NEED_128
+ if (isnonzero_128 (res))
+#else
+ if (res)
+#endif
+ CLRF (* flags, I_ZERO);
+ else
+ SETF (* flags, I_ZERO); // zero result
+ }
+
+ if (flagsToSet & I_NEG)
+ {
+#ifdef NEED_128
+ if (isnonzero_128 (and_128 (res, SIGN72)))
+#else
+ if (res & SIGN72)
+#endif
+ SETF (* flags, I_NEG);
+ else
+ CLRF (* flags, I_NEG);
+ }
+
+#ifdef NEED_128
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "Sub72b res %012"PRIo64"%012"PRIo64" flags %06o ovf %o\n", (word36) (rshift_128 (res, 36).l & MASK36), (word36) (res.l & MASK36), * flags, * ovf);
+#else
+ sim_debug (DBG_TRACEEXT, & cpu_dev, "Sub72b res %012"PRIo64"%012"PRIo64" flags %06o ovf %o\n", (word36) ((res >> 36) & MASK36), (word36) (res & MASK36), * flags, * ovf);
+#endif
+ return res;
+ }
+
+// CANFAULT
+word36 compl36(word36 op1, word18 *flags, bool * ovf)
+{
+ CPT (cpt2L, 23); // compl36
+ //printf("op1 = %"PRIo64" %"PRIo64"\n", op1, (-op1) & DMASK);
+
+ op1 &= DMASK;
+
+ word36 res = -op1 & DMASK;
+
+ * ovf = op1 == MAXNEG;
+
+#ifdef PANEL
+ if (* ovf) CPT (cpt2L, 29); // ovf
+ if (!res) CPT (cpt2L, 30); // zero
+ if (res & SIGN36) CPT (cpt2L, 31); // neg
+#endif
+
+ if (chkOVF () && * ovf)
+ SETF(*flags, I_OFLOW);
+
+ if (res & SIGN36)
+ SETF(*flags, I_NEG);
+ else
+ CLRF(*flags, I_NEG);
+
+ if (res == 0)
+ SETF(*flags, I_ZERO);
+ else
+ CLRF(*flags, I_ZERO);
+
+ return res;
+}
+
+// CANFAULT
+word18 compl18(word18 op1, word18 *flags, bool * ovf)
+{
+ CPT (cpt2L, 24); // compl18
+ //printf("op1 = %"PRIo64" %"PRIo64"\n", op1, (-op1) & DMASK);
+
+ op1 &= MASK18;
+
+ word18 res = -op1 & MASK18;
+
+ * ovf = op1 == MAX18NEG;
+#ifdef PANEL
+ if (* ovf) CPT (cpt2L, 29); // ovf
+ if (!res) CPT (cpt2L, 30); // zero
+ if (res & SIGN18) CPT (cpt2L, 31); // neg
+#endif
+
+ if (chkOVF () && * ovf)
+ SETF(*flags, I_OFLOW);
+ if (res & SIGN18)
+ SETF(*flags, I_NEG);
+ else
+ CLRF(*flags, I_NEG);
+
+ if (res == 0)
+ SETF(*flags, I_ZERO);
+ else
+ CLRF(*flags, I_ZERO);
+
+ return res;
+}
+
+void copyBytes(int posn, word36 src, word36 *dst)
+{
+ word36 mask = 0;
+
+ if (posn & 8) // bit 30 - byte 0 - (bits 0-8)
+ mask |= 0777000000000LL;
+
+ if (posn & 4) // bit 31 - byte 1 - (bits 9-17)
+ mask |= 0000777000000LL;
+
+ if (posn & 2) // bit 32 - byte 2 - (bits 18-26)
+ mask |= 0000000777000LL;
+
+ if (posn & 1) // bit 33 - byte 3 - (bits 27-35)
+ mask |= 0000000000777LL;
+
+ word36 byteVals = src & mask; // get byte bits
+
+ // clear the bits in dst
+ *dst &= ~mask;
+
+ // and set the bits in dst
+ *dst |= byteVals;
+}
+
+
+
+void copyChars(int posn, word36 src, word36 *dst)
+{
+ word36 mask = 0;
+
+ if (posn & 32) // bit 30 - char 0 - (bits 0-5)
+ mask |= 0770000000000LL;
+
+ if (posn & 16) // bit 31 - char 1 - (bits 6-11)
+ mask |= 0007700000000LL;
+
+ if (posn & 8) // bit 32 - char 2 - (bits 12-17)
+ mask |= 0000077000000LL;
+
+ if (posn & 4) // bit 33 - char 3 - (bits 18-23)
+ mask |= 0000000770000LL;
+
+ if (posn & 2) // bit 34 - char 4 - (bits 24-29)
+ mask |= 0000000007700LL;
+
+ if (posn & 1) // bit 35 - char 5 - (bits 30-35)
+ mask |= 0000000000077LL;
+
+ word36 byteVals = src & mask; // get byte bits
+
+ // clear the bits in dst
+ *dst &= ~mask;
+
+ // and set the bits in dst
+ *dst |= byteVals;
+
+}
+
+
+/*!
+ * write 9-bit byte into 36-bit word....
+ */
+void putByte(word36 *dst, word9 data, int posn)
+{
+ // XXX which is faster switch() or calculation?
+
+// int offset = 27 - (9 * posn);// 0;
+// switch (posn)
+// {
+// case 0:
+// offset = 27;
+// break;
+// case 1:
+// offset = 18;
+// break;
+// case 2:
+// offset = 9;
+// break;
+// case 3:
+// offset = 0;
+// break;
+// }
+ putbits36_9 (dst, (uint) posn * 9, data);
+}
+
+void putChar(word36 *dst, word6 data, int posn)
+{
+ // XXX which is faster switch() or calculation?
+
+// int offset = 30 - (6 * posn); //0;
+// switch (posn)
+// {
+// case 0:
+// offset = 30;
+// break;
+// case 1:
+// offset = 24;
+// break;
+// case 2:
+// offset = 18;
+// break;
+// case 3:
+// offset = 12;
+// break;
+// case 4:
+// offset = 6;
+// break;
+// case 5:
+// offset = 0;
+// break;
+// }
+ putbits36_6 (dst, (uint) posn * 6, data);
+}
+
+word72 convert_to_word72(word36 even, word36 odd)
+{
+#ifdef NEED_128
+ return or_128 (lshift_128 (construct_128 (0, even), 36), construct_128 (0, odd));
+#else
+ return ((word72)even << 36) | (word72)odd;
+#endif
+}
+
+void convert_to_word36 (word72 src, word36 *even, word36 *odd)
+{
+#ifdef NEED_128
+ *even = rshift_128 (src, 36).l & DMASK;
+ *odd = src.l & DMASK;
+#else
+ *even = (word36)(src >> 36) & DMASK;
+ *odd = (word36)src & DMASK;
+#endif
+}
+
+void cmp36(word36 oP1, word36 oP2, word18 *flags)
+ {
+ CPT (cpt2L, 25); // cmp36
+#ifdef L68
+ cpu.ou.cycle |= ou_GOS;
+#endif
+ t_int64 op1 = SIGNEXT36_64(oP1 & DMASK);
+ t_int64 op2 = SIGNEXT36_64(oP2 & DMASK);
+
+ word36 sign1 = (word36) op1 & SIGN36;
+ word36 sign2 = (word36) op2 & SIGN36;
+
+
+ if ((! sign1) && sign2) // op1 > 0, op2 < 0 :: op1 > op2
+ CLRF (* flags, I_ZERO | I_NEG | I_CARRY);
+
+ else if (sign1 == sign2) // both operands have the same sogn
+ {
+ if (op1 > op2)
+ {
+ CPT (cpt2L, 28); // carry
+ SETF (* flags, I_CARRY);
+ CLRF (* flags, I_ZERO | I_NEG);
+ }
+ else if (op1 == op2)
+ {
+ CPT (cpt2L, 28); // carry
+ CPT (cpt2L, 30); // zero
+ SETF (* flags, I_ZERO | I_CARRY);
+ CLRF (* flags, I_NEG);
+ }
+ else // op1 < op2
+ {
+ CPT (cpt2L, 31); // neg
+ SETF (* flags, I_NEG);
+ CLRF (* flags, I_ZERO | I_CARRY);
+ }
+ }
+ else // op1 < 0, op2 > 0 :: op1 < op2
+ {
+ CPT (cpt2L, 28); // carry
+ CPT (cpt2L, 31); // neg
+ SETF (* flags, I_CARRY | I_NEG);
+ CLRF (* flags, I_ZERO);
+ }
+ }
+
+void cmp18(word18 oP1, word18 oP2, word18 *flags)
+ {
+ CPT (cpt2L, 26); // cmp18
+#ifdef L68
+ cpu.ou.cycle |= ou_GOS;
+#endif
+ int32 op1 = SIGNEXT18_32 (oP1 & MASK18);
+ int32 op2 = SIGNEXT18_32 (oP2 & MASK18);
+
+ word18 sign1 = (word18) op1 & SIGN18;
+ word18 sign2 = (word18) op2 & SIGN18;
+
+
+ if ((! sign1) && sign2) // op1 > 0, op2 < 0 :: op1 > op2
+ CLRF (* flags, I_ZERO | I_NEG | I_CARRY);
+
+ else if (sign1 == sign2) // both operands have the same sogn
+ {
+ if (op1 > op2)
+ {
+ CPT (cpt2L, 28); // carry
+ SETF (* flags, I_CARRY);
+ CLRF (* flags, I_ZERO | I_NEG);
+ }
+ else if (op1 == op2)
+ {
+ CPT (cpt2L, 28); // carry
+ CPT (cpt2L, 30); // zero
+ SETF (* flags, I_ZERO | I_CARRY);
+ CLRF (* flags, I_NEG);
+ }
+ else // op1 < op2
+ {
+ CPT (cpt2L, 31); // neg
+ SETF (* flags, I_NEG);
+ CLRF (* flags, I_ZERO | I_CARRY);
+ }
+ }
+ else // op1 < 0, op2 > 0 :: op1 < op2
+ {
+ CPT (cpt2L, 28); // carry
+ CPT (cpt2L, 31); // neg
+ SETF (* flags, I_CARRY | I_NEG);
+ CLRF (* flags, I_ZERO);
+ }
+ }
+
+void cmp36wl(word36 A, word36 Y, word36 Q, word18 *flags)
+{
+ CPT (cpt2L, 26); // cmp36wl
+ // This is wrong; signed math is needed.
+
+ //bool Z = (A <= Y && Y <= Q) || (A >= Y && Y >= Q);
+
+#ifdef L68
+ cpu.ou.cycle |= ou_GOS;
+#endif
+ t_int64 As = (word36s) SIGNEXT36_64(A & DMASK);
+ t_int64 Ys = (word36s) SIGNEXT36_64(Y & DMASK);
+ t_int64 Qs = (word36s) SIGNEXT36_64(Q & DMASK);
+ bool Z = (As <= Ys && Ys <= Qs) || (As >= Ys && Ys >= Qs);
+
+ SCF(Z, *flags, I_ZERO);
+
+ if (!(Q & SIGN36) && (Y & SIGN36) && (Qs > Ys))
+ CLRF(*flags, I_NEG | I_CARRY);
+ else if (((Q & SIGN36) == (Y & SIGN36)) && (Qs >= Ys))
+ {
+ CPT (cpt2L, 28); // carry
+ SETF(*flags, I_CARRY);
+ CLRF(*flags, I_NEG);
+ } else if (((Q & SIGN36) == (Y & SIGN36)) && (Qs < Ys))
+ {
+ CPT (cpt2L, 31); // neg
+ CLRF(*flags, I_CARRY);
+ SETF(*flags, I_NEG);
+ } else if ((Q & SIGN36) && !(Y & SIGN36) && (Qs < Ys))
+ {
+ CPT (cpt2L, 28); // carry
+ CPT (cpt2L, 31); // neg
+ SETF(*flags, I_NEG | I_CARRY);
+ }
+}
+
+void cmp72(word72 op1, word72 op2, word18 *flags)
+{
+ CPT (cpt2L, 27); // cmp72
+ // The case of op1 == 400000000000000000000000 and op2 == 0 falls through
+ // this code.
+#ifdef L68
+ cpu.ou.cycle |= ou_GOS;
+#endif
+#ifdef NEED_128
+sim_debug (DBG_TRACEEXT, & cpu_dev, "op1 %016"PRIx64"%016"PRIx64"\n", op1.h, op1.l);
+sim_debug (DBG_TRACEEXT, & cpu_dev, "op2 %016"PRIx64"%016"PRIx64"\n", op2.h, op2.l);
+ int128 op1s = SIGNEXT72_128 (and_128 (op1, MASK72));
+ int128 op2s = SIGNEXT72_128 (and_128 (op2, MASK72));
+sim_debug (DBG_TRACEEXT, & cpu_dev, "op1s %016"PRIx64"%016"PRIx64"\n", op1s.h, op1s.l);
+sim_debug (DBG_TRACEEXT, & cpu_dev, "op2s %016"PRIx64"%016"PRIx64"\n", op2s.h, op2s.l);
+#else
+sim_debug (DBG_TRACEEXT, & cpu_dev, "op1 %016"PRIx64"%016"PRIx64"\n", (uint64_t) (op1>>64), (uint64_t) op1);
+sim_debug (DBG_TRACEEXT, & cpu_dev, "op2 %016"PRIx64"%016"PRIx64"\n", (uint64_t) (op2>>64), (uint64_t) op2);
+ int128 op1s = SIGNEXT72_128 (op1 & MASK72);
+ int128 op2s = SIGNEXT72_128 (op2 & MASK72);
+sim_debug (DBG_TRACEEXT, & cpu_dev, "op1s %016"PRIx64"%016"PRIx64"\n", (uint64_t) (op1s>>64), (uint64_t) op1s);
+sim_debug (DBG_TRACEEXT, & cpu_dev, "op2s %016"PRIx64"%016"PRIx64"\n", (uint64_t) (op2s>>64), (uint64_t) op2s);
+#endif
+#ifdef NEED_128
+ if (isgt_s128 (op1s, op2s))
+#else
+ if (op1s > op2s)
+#endif
+ {
+#ifdef NEED_128
+ if (isnonzero_128 (and_128 (op2, SIGN72)))
+#else
+ if (op2 & SIGN72)
+#endif
+ CLRF (* flags, I_CARRY);
+ else
+ {
+ CPT (cpt2L, 28); // carry
+ SETF (* flags, I_CARRY);
+ }
+ CLRF (* flags, I_ZERO | I_NEG);
+ }
+#ifdef NEED_128
+ else if (iseq_128 (cast_128 (op1s), cast_128 (op2s)))
+#else
+ else if (op1s == op2s)
+#endif
+ {
+ CPT (cpt2L, 28); // carry
+ CPT (cpt2L, 30); // zero
+ SETF (* flags, I_CARRY | I_ZERO);
+ CLRF (* flags, I_NEG);
+ }
+ else /* op1s < op2s */
+ {
+ CPT (cpt2L, 31); // neg
+#ifdef NEED_128
+ if (isnonzero_128 (and_128 (op1, SIGN72)))
+#else
+ if (op1 & SIGN72)
+#endif
+ {
+ CPT (cpt2L, 28); // carry
+ SETF (* flags, I_CARRY);
+ }
+ else
+ CLRF (* flags, I_CARRY);
+ CLRF (* flags, I_ZERO);
+ SETF (* flags, I_NEG);
+ }
+}
+
+/*
+ * String utilities ...
+ */
+
+/* ------------------------------------------------------------------------- */
+
+char * strlower(char *q)
+{
+ char *s = q;
+
+ while (*s) {
+ if (isupper(*s))
+ *s = (char) tolower(*s);
+ s++;
+ }
+ return q;
+}
+
+/* ------------------------------------------------------------------------- */
+
+/* state definitions */
+#define STAR 0
+#define NOTSTAR 1
+#define RESET 2
+
+int strmask (char * str, char * mask)
+/*!
+ Tests string 'str' against mask string 'mask'
+ Returns TRUE if the string matches the mask.
+
+ The mask can contain '?' and '*' wild card characters.
+ '?' matches any single character.
+ '*' matches any number of any characters.
+
+ For example:
+ strmask("Hello", "Hello"); ---> TRUE
+ strmask("Hello", "Jello"); ---> FALSE
+ strmask("Hello", "H*o"); ---> TRUE
+ strmask("Hello", "H*g"); ---> FALSE
+ strmask("Hello", "?ello"); ---> TRUE
+ strmask("Hello", "H????"); ---> TRUE
+ strmask("H", "H????"); ---> FALSE
+ */
+ {
+ char * sp, * mp, * reset_string, * reset_mask, * sn;
+ int state;
+
+ sp = str;
+ mp = mask;
+
+ while (1)
+ {
+ switch (* mp)
+ {
+ case '\0':
+ return * sp ? false : true;
+
+ case '?':
+ sp ++;
+ mp ++;
+ break;
+
+ default:
+ if (* mp == * sp)
+ {
+ sp ++;
+ mp ++;
+ break;
+ }
+ else
+ {
+ return false;
+ }
+
+ case '*':
+ if (* (mp + 1) == '\0')
+ {
+ return true;
+ }
+ if ((sn = strchr (sp, * (mp + 1))) == NULL)
+ {
+ return false;
+ }
+
+ /* save place -- match rest of string */
+ /* if fail, reset to here */
+ reset_mask = mp;
+ reset_string = sn + 1;
+
+ mp = mp + 2;
+ sp = sn + 1;
+ state = NOTSTAR;
+ while (state == NOTSTAR)
+ {
+ switch (* mp)
+ {
+ case '\0':
+ if (* sp == '\0')
+ return false;
+ else
+ state = RESET;
+ break;
+ case '?':
+ sp ++;
+ mp ++;
+ break;
+ default:
+ if (* mp == * sp)
+ {
+ sp ++;
+ mp ++;
+ }
+ else
+ state = RESET;
+ break;
+ case '*':
+ state = STAR;
+ break;
+ }
+ } // while STATE == NOTSTAR
+ /* we've reach a new star or should reset to last star */
+ if (state == RESET)
+ {
+ sp = reset_string;
+ mp = reset_mask;
+ }
+ break;
+ } // switch (* mp)
+ } // while (1)
+ }
+
+/**
+ * strtok() with string quoting...
+ * (implemented as a small fsm, kinda...
+ * (add support for embedded " later, much later...)
+ */
+#define NORMAL 1
+#define IN_STRING 2
+#define EOB 3
+
+char *
+Strtok(char *line, char *sep)
+{
+
+ static char *p; /*!< current pointer position in input line*/
+ static int state = NORMAL;
+
+ char *q; /*!< beginning of current field*/
+
+ if (line) { /* 1st invocation */
+ p = line;
+ state = NORMAL;
+ }
+
+ q = p;
+ while (state != EOB) {
+ switch (state) {
+ case NORMAL:
+ switch (*p) {
+ case 0: // at end of buffer
+ state = EOB; // set state to "end Of Buffer
+ return q;
+
+ case '"': // beginning of a quoted string
+ state = IN_STRING; // we're in a string
+ p++;
+ continue;
+
+ default: // only a few special characters
+ if (strchr(sep, *p) == NULL) { // not a sep
+ p++; // goto next char
+ continue;
+ } else {
+ *p++ = (char)0; /* ... iff >0 */
+ while (*p && strchr(sep, *p)) /* skip over seperator(s)*/
+ p++;
+ return q; /* return field */
+ }
+ }
+
+ case IN_STRING:
+ if (*p == 0) { /*!< incomplete quoted string */
+ state = EOB;
+ return q;
+ }
+
+ if (*p != '"') { // not end of line and still in a string
+ p++;
+ continue;
+ }
+ state = NORMAL; /* end of quoted string */
+ p++;
+
+ continue;
+
+ case EOB: /* just in case */
+ state = NORMAL;
+ return NULL;
+
+ default:
+ fprintf(stderr, "(Strtok):unknown state - %d",state);
+ state = EOB;
+ return NULL;
+ }
+
+ }
+
+ return NULL; /* no more fields in buffer */
+
+}
+#if 0
+bool startsWith(const char *str, const char *pre)
+{
+ size_t lenpre = strlen(pre),
+ lenstr = strlen(str);
+ return lenstr < lenpre ? false : strncasecmp(pre, str, lenpre) == 0;
+}
+#endif
+
+/**
+ * Removes the trailing spaces from a string.
+ */
+char *rtrim(char *s)
+{
+ if (! s)
+ return s;
+ int index;
+
+ //for (index = (int)strlen(s) - 1; index >= 0 && (s[index] == ' ' || s[index] == '\t'); index--)
+ for (index = (int)strlen(s) - 1; index >= 0 && isspace(s[index]); index--)
+ {
+ s[index] = '\0';
+ }
+ return(s);
+}
+/** ------------------------------------------------------------------------- */
+char *ltrim(char *s)
+/**
+ * Removes the leading spaces from a string.
+ */
+{
+ char *p;
+ if (s == NULL)
+ return NULL;
+
+ //for (p = s; (*p == ' ' || *p == '\t') && *p != '\0'; p++)
+ for (p = s; isspace(*p) && *p != '\0'; p++)
+ ;
+
+ //strcpy(s, p);
+ memmove(s, p, strlen(p) + 1);
+ return(s);
+}
+
+/** ------------------------------------------------------------------------- */
+
+char *trim(char *s)
+{
+ return ltrim(rtrim(s));
+}
+
+
+char *
+stripquotes(char *s)
+{
+ if (! s || ! *s)
+ return s;
+ /*
+ char *p;
+
+ while ((p = strchr(s, '"')))
+ *p = ' ';
+ strchop(s);
+
+ return s;
+ */
+ int nLast = (int)strlen(s) - 1;
+ // trim away leading/trailing "'s
+ if (s[0] == '"')
+ s[0] = ' ';
+ if (s[nLast] == '"')
+ s[nLast] = ' ';
+ return trim(s);
+}
+
+#include <ctype.h>
+
+// No longer putting the tty in "no output proceesing mode"; all of this
+// is irrelevant...
+//
+// simh puts the tty in raw mode when the sim is running;
+// this means that test output to the console will lack CR's and
+// be all messed up.
+//
+// There are three ways around this:
+// 1: switch back to cooked mode for the message
+// 2: walk the message and output CRs before LFs with sim_putchar()
+// 3: walk the message and output CRs before LFs with sim_os_putchar()
+//
+// The overhead and obscure side effects of switching are unknown.
+//
+// sim_putchar adds the text to the log file, but not the debug file; and
+// sim_putchar puts the CRs into the log file.
+//
+// sim_os_putchar skips checks that sim_putchar does that verify that
+// we are actually talking to tty, instead of a telnet connection or other
+// non-tty thingy.
+//
+// USE_COOKED does the wrong thing when stdout is piped; ie not a terminal
+
+//#define USE_COOKED
+#define USE_PUTCHAR
+//#define USE_NONE
+
+#if 0
+void sim_printf( const char * format, ... )
+{
+ char buffer[4096];
+
+ va_list args;
+ va_start (args, format);
+ vsnprintf (buffer, sizeof(buffer), format, args);
+
+#ifdef USE_COOKED
+ if (sim_is_running)
+ sim_ttcmd ();
+#endif
+
+ for(uint i = 0 ; i < sizeof(buffer); i += 1)
+ {
+ if (buffer[i]) {
+#ifdef USE_PUTCHAR
+ if (sim_is_running && buffer [i] == '\n')
+ sim_putchar ('\r');
+#endif
+#ifdef USE_OS_PUTCHAR
+ if (sim_is_running && buffer [i] == '\n')
+ sim_os_putchar ('\r');
+#endif
+ sim_putchar(buffer[i]);
+ //if (sim_deb)
+ //fputc (buffer [i], sim_deb);
+ } else
+ break;
+ }
+
+#ifdef USE_COOKED
+ if (sim_is_running)
+ sim_ttrun ();
+#endif
+
+ va_end (args);
+}
+#endif
+
+#if 0
+// Rework sim_printf.
+//
+// Distinguish between the console device and the window in which dps8 is
+// running.
+//
+// There are (up to) three outputs:
+//
+// - the window in which dps8 is running (stdout)
+// - the log file (which may be stdout or stderr)
+// - the console device
+//
+// sim_debug --
+// prints time stamped strings to the logfile; controlled by scp commands.
+// sim_err --
+// prints sim_debug style messages regardless of scp commands and returns
+// to scp; not necessarily restartable.
+// sim_printf --
+// prints strings to logfile and stdout
+// sim_printl --
+// prints strings to console logfile
+// sim_putchar/sim_os_putchar/sim_puts
+// prints char/string to the console
+
+void sim_printf (const char * format, ...)
+ {
+ char buffer [4096];
+ bool bOut = (sim_deb ? fileno (sim_deb) != fileno (stdout) : false);
+
+ va_list args;
+ va_start (args, format);
+ vsnprintf (buffer, sizeof (buffer), format, args);
+ va_end (args);
+
+#ifdef USE_COOKED
+ if (sim_is_running)
+ sim_ttcmd ();
+#endif
+
+ for (uint i = 0 ; i < sizeof (buffer); i ++)
+ {
+ if (! buffer [i])
+ break;
+
+ // stdout
+
+#ifdef USE_PUTCHAR
+ if (sim_is_running && buffer [i] == '\n')
+ {
+ putchar ('\r');
+ }
+#endif
+#ifdef USE_OS_PUTCHAR
+ if (sim_is_running && buffer [i] == '\n')
+ sim_os_putchar ('\r');
+#endif
+ putchar (buffer [i]);
+
+ // logfile
+
+ if (bOut)
+ {
+ //if (sim_is_running && buffer [i] == '\n')
+ //fputc ('\r', sim_deb);
+ fputc (buffer [i], sim_deb);
+ }
+ }
+
+#ifdef USE_COOKED
+ if (sim_is_running)
+ sim_ttrun ();
+#endif
+
+ fflush (sim_deb);
+ if (bOut)
+ fflush (stdout);
+}
+#endif
+
+void sim_printl (const char * format, ...)
+ {
+ if (! sim_log)
+ return;
+ char buffer [4096];
+
+ va_list args;
+ va_start (args, format);
+ vsnprintf (buffer, sizeof (buffer), format, args);
+
+ for (uint i = 0 ; i < sizeof (buffer); i ++)
+ {
+ if (! buffer [i])
+ break;
+
+ // logfile
+
+ fputc (buffer [i], sim_log);
+ }
+ va_end (args);
+ fflush (sim_log);
+}
+
+void sim_puts (char * str)
+ {
+ char * p = str;
+ while (* p)
+ /*sim_*/putchar (* (p ++));
+ }
+
+// XXX what about config=addr7=123, where clist has a "addr%"?
+
+// return -2: error; -1: done; >= 0 option found
+int cfg_parse (const char * tag, const char * cptr, config_list_t * clist, config_state_t * state, int64_t * result)
+ {
+ if (! cptr)
+ return -2;
+ char * start = NULL;
+ if (! state -> copy)
+ {
+ state -> copy = strdup (cptr);
+ start = state -> copy;
+ state -> statement_save = NULL;
+ }
+
+ int ret = -2; // error
+
+ // grab every thing up to the next semicolon
+ char * statement;
+ statement = strtok_r (start, ";", & state -> statement_save);
+ start = NULL;
+ if (! statement)
+ {
+ ret = -1; // done
+ goto done;
+ }
+
+ // extract name
+ char * name_start = statement;
+ char * name_save = NULL;
+ char * name;
+ name = strtok_r (name_start, "=", & name_save);
+ if (! name)
+ {
+ sim_printf ("error: %s: can't parse name\n", tag);
+ goto done;
+ }
+
+ // lookup name
+ config_list_t * p = clist;
+ while (p -> name)
+ {
+ if (strcasecmp (name, p -> name) == 0)
+ break;
+ p ++;
+ }
+ if (! p -> name)
+ {
+ sim_printf ("error: %s: don't know name <%s>\n", tag, name);
+ goto done;
+ }
+
+ // extract value
+ char * value;
+ value = strtok_r (NULL, "", & name_save);
+ if (! value)
+ {
+ // Special case; min>max and no value list
+ // means that a missing value is ok
+ if (p -> min > p -> max && ! p -> value_list)
+ {
+ return (int) (p - clist);
+ }
+ sim_printf ("error: %s: can't parse value\n", tag);
+ goto done;
+ }
+
+ // first look to value in the value list
+ config_value_list_t * v = p -> value_list;
+ if (v)
+ {
+ while (v -> value_name)
+ {
+ if (strcasecmp (value, v -> value_name) == 0)
+ break;
+ v ++;
+ }
+
+ // Hit?
+ if (v -> value_name)
+ {
+ * result = v -> value;
+ return (int) (p - clist);
+ }
+ }
+
+ // Must be a number
+
+ if (p -> min > p -> max)
+ {
+ sim_printf ("error: %s: can't parse value\n", tag);
+ goto done;
+ }
+
+ if (strlen (value) == 0)
+ {
+ sim_printf ("error: %s: missing value\n", tag);
+ goto done;
+ }
+ char * endptr;
+ int64_t n = strtoll (value, & endptr, 0);
+ if (* endptr)
+ {
+ sim_printf ("error: %s: can't parse value\n", tag);
+ goto done;
+ }
+
+// XXX small bug; doesn't check for junk after number...
+ if (n < p -> min || n > p -> max)
+ {
+ sim_printf ("error: %s: value out of range\n", tag);
+ goto done;
+ }
+
+ * result = n;
+ return (int) (p - clist);
+
+done:
+ free (state -> copy);
+ state -> copy= NULL;
+ return ret;
+ }
+
+void cfg_parse_done (config_state_t * state)
+ {
+ if (state -> copy)
+ free (state -> copy);
+ state -> copy = NULL;
+ }
+
+// strdup with limited C-style escape processing
+//
+// strdupesc ("foo\nbar") --> 'f' 'o' 'o' 012 'b' 'a' 'r'
+//
+// Handles:
+// \\ backslash
+// \n newline
+// \t tab
+// \f formfeed
+// \r carrriage return
+// \0 null // doesn't work, commented out.
+//
+// \\ doesn't seem to work...
+// Also, a simh specific:
+//
+// \e (end simulation)
+//
+// the simh parser doesn't handle these very well...
+//
+// \_ space
+// \c comma
+// \s semicolon
+// \d dollar
+// \q double quote
+// \w <backslash>
+// \z ^D eof (DECism)
+// \^ caret
+// \x expect; used by the autoinput parserj
+//
+// And a special case:
+//
+// \TZ replaced with the timezone string. Three characters are used
+// to allow for space in the buffer.
+//
+// all others silently ignored and left unprocessed
+//
+
+char * strdupesc (const char * str)
+ {
+ char * buf = strdup (str);
+ char * p = buf;
+ while (* p)
+ {
+ if (* p != '\\')
+ {
+ p ++;
+ continue;
+ }
+ if (p [1] == '\\') // \\ backslash
+ * p = '\\';
+ else if (p [1] == 'a') // \a ^A
+ * p = '\001';
+ else if (p [1] == 'w') // \w backslash
+ * p = '\\';
+ else if (p [1] == 'n') // \n newline
+ * p = '\n';
+ else if (p [1] == 't') // \t tab
+ * p = '\t';
+ else if (p [1] == 'f') // \f formfeed
+ * p = '\f';
+ else if (p [1] == 'r') // \r carriage return
+ * p = '\r';
+ else if (p [1] == 'e') // \e control E; Multics escape char.
+ * p = '\005';
+ else if (p [1] == '_') // \_ space; needed for leading or
+ // trailing spaces (simh parser
+ // issue)
+ * p = ' ';
+ else if (p [1] == 'c') // \c comma (simh parser issue)
+ * p = ',';
+ else if (p [1] == 's') // \s semicolon (simh parser issue)
+ * p = ';';
+ else if (p [1] == 'd') // \d dollar sign (simh parser issue)
+ * p = '$';
+ else if (p [1] == 'q') // \q double quote (simh parser issue)
+ * p = '"';
+ else if (p [1] == 'z') // \z ^D eof (VAXism)
+ * p = '\004';
+ else if (p [1] == 'k') // \k caret
+ * p = '^';
+ else if (p [1] == 'x') // \x expect
+ * p = '\030';
+ else if (p [1] == 'y') // \y expect
+ * p = '\031';
+ //else if (p [1] == '0') // \0 null; used as end of expect string
+ //* p = 0;
+
+#if 0
+ else if (p [1] == 'T' && p [2] == 'Z') // \TZ time zone
+ {
+ strncpy (p, "pst", 3);
+ time_t t = time (NULL);
+ struct tm * lt = localtime (& t);
+ if (strlen (lt -> tm_zone) == 3)
+ {
+ //strncpy (p, lt -> tm_zone, 3);
+ p [0] = tolower (lt -> tm_zone [0]);
+ p [1] = tolower (lt -> tm_zone [1]);
+ p [2] = tolower (lt -> tm_zone [2]);
+ }
+ p += 2;
+ }
+#endif
+ else
+ {
+ p ++;
+ continue;
+ }
+ p ++;
+//sim_printf ("was <%s>\n", buf);
+ memmove (p, p + 1, strlen (p + 1) + 1);
+//sim_printf ("is <%s>\n", buf);
+ }
+ return buf;
+ }
+
+
+
+// Layout of data as read from simh tape format
+//
+// bits: buffer of bits from a simh tape. The data is
+// packed as 2 36 bit words in 9 eight bit bytes (2 * 36 == 7 * 9)
+// The of the bytes in bits is
+// byte value
+// 0 most significant byte in word 0
+// 1 2nd msb in word 0
+// 2 3rd msb in word 0
+// 3 4th msb in word 0
+// 4 upper half is 4 least significant bits in word 0
+// lower half is 4 most significant bit in word 1
+// 5 5th to 13th most signicant bits in word 1
+// 6 ...
+// 7 ...
+// 8 least significant byte in word 1
+//
+
+// Multics humor: this is idiotic
+
+
+// Data conversion routines
+//
+// 'bits' is the packed bit stream read from the simh tape
+// it is assumed to start at an even word36 address
+//
+// extr36
+// extract the word36 at woffset
+//
+
+static word36 extrASCII36 (uint8 * bits, uint woffset)
+ {
+ uint8 * p = bits + woffset * 4;
+
+ uint64 w;
+ w = ((uint64) p [0]) << 27;
+ w |= ((uint64) p [1]) << 18;
+ w |= ((uint64) p [2]) << 9;
+ w |= ((uint64) p [3]);
+ // mask shouldn't be neccessary but is robust
+ return (word36) (w & MASK36);
+ }
+
+// Data conversion routines
+//
+// 'bits' is the packed bit stream read from the simh tape
+// it is assumed to start at an even word36 address
+//
+// extr36
+// extract the word36 at woffset
+//
+
+word36 extr36 (uint8 * bits, uint woffset)
+ {
+ uint isOdd = woffset % 2;
+ uint dwoffset = woffset / 2;
+ uint8 * p = bits + dwoffset * 9;
+
+ uint64 w;
+ if (isOdd)
+ {
+ w = (((uint64) p [4]) & 0xf) << 32;
+ w |= ((uint64) p [5]) << 24;
+ w |= ((uint64) p [6]) << 16;
+ w |= ((uint64) p [7]) << 8;
+ w |= ((uint64) p [8]);
+ }
+ else
+ {
+ w = ((uint64) p [0]) << 28;
+ w |= ((uint64) p [1]) << 20;
+ w |= ((uint64) p [2]) << 12;
+ w |= ((uint64) p [3]) << 4;
+ w |= (((uint64) p [4]) >> 4) & 0xf;
+ }
+ // mask shouldn't be neccessary but is robust
+ return (word36) (w & MASK36);
+ }
+
+static void putASCII36 (word36 val, uint8 * bits, uint woffset)
+ {
+ uint8 * p = bits + woffset * 4;
+ p [0] = (val >> 27) & 0xff;
+ p [1] = (val >> 18) & 0xff;
+ p [2] = (val >> 9) & 0xff;
+ p [3] = (val ) & 0xff;
+ }
+
+void put36 (word36 val, uint8 * bits, uint woffset)
+ {
+ uint isOdd = woffset % 2;
+ uint dwoffset = woffset / 2;
+ uint8 * p = bits + dwoffset * 9;
+
+ if (isOdd)
+ {
+ p [4] &= 0xf0;
+ p [4] |= (val >> 32) & 0x0f;
+ p [5] = (val >> 24) & 0xff;
+ p [6] = (val >> 16) & 0xff;
+ p [7] = (val >> 8) & 0xff;
+ p [8] = (val >> 0) & 0xff;
+ //w = ((uint64) (p [4] & 0xf)) << 32;
+ //w |= (uint64) (p [5]) << 24;
+ //w |= (uint64) (p [6]) << 16;
+ //w |= (uint64) (p [7]) << 8;
+ //w |= (uint64) (p [8]);
+ }
+ else
+ {
+ p [0] = (val >> 28) & 0xff;
+ p [1] = (val >> 20) & 0xff;
+ p [2] = (val >> 12) & 0xff;
+ p [3] = (val >> 4) & 0xff;
+ p [4] &= 0x0f;
+ p [4] |= (val << 4) & 0xf0;
+ //w = (uint64) (p [0]) << 28;
+ //w |= (uint64) (p [1]) << 20;
+ //w |= (uint64) (p [2]) << 12;
+ //w |= (uint64) (p [3]) << 4;
+ //w |= ((uint64) (p [4]) >> 4) & 0xf;
+ }
+ // mask shouldn't be neccessary but is robust
+ }
+
+
+//int extractASCII36FromBuffer (uint8 * bufp, t_mtrlnt tbc, uint * words_processed, word36 *wordp)
+// {
+// uint wp = * words_processed; // How many words have been processed
+//
+// // 1 dps8m word == 4 bytes
+//
+// uint bytes_processed = wp * 4;
+// if (bytes_processed >= tbc)
+// return 1;
+// //sim_printf ("store 0%08lo@0%012"PRIo64"\n", wordp - M, extr36 (bufp, wp));
+//
+// * wordp = extrASCII36 (bufp, wp);
+////if (* wordp & ~MASK36) sim_printf (">>>>>>> extr %012"PRIo64"\n", * wordp);
+// //sim_printf ("* %06lo = %012"PRIo64"\n", wordp - M, * wordp);
+// (* words_processed) ++;
+//
+// return 0;
+// }
+//
+//int extractWord36FromBuffer (uint8 * bufp, t_mtrlnt tbc, uint * words_processed, word36 *wordp)
+// {
+// uint wp = * words_processed; // How many words have been processed
+//
+// // 2 dps8m words == 9 bytes
+//
+// uint bytes_processed = (wp * 9 + 1) / 2;
+// if (bytes_processed >= tbc)
+// return 1;
+// //sim_printf ("store 0%08lo@0%012"PRIo64"\n", wordp - M, extr36 (bufp, wp));
+//
+// * wordp = extr36 (bufp, wp);
+////if (* wordp & ~MASK36) sim_printf (">>>>>>> extr %012"PRIo64"\n", * wordp);
+// //sim_printf ("* %06lo = %012"PRIo64"\n", wordp - M, * wordp);
+// (* words_processed) ++;
+//
+// return 0;
+// }
+//
+//int insertASCII36toBuffer (uint8 * bufp, t_mtrlnt tbc, uint * words_processed, word36 word)
+// {
+// uint wp = * words_processed; // How many words have been processed
+//
+// // 1 dps8m word == 4 bytes
+//
+// uint bytes_processed = wp * 4;
+// if (bytes_processed >= tbc)
+// return 1;
+// //sim_printf ("store 0%08lo@0%012"PRIo64"\n", wordp - M, extr36 (bufp, wp));
+//
+// putASCII36 (word, bufp, wp);
+// //sim_printf ("* %06lo = %012"PRIo64"\n", wordp - M, * wordp);
+// (* words_processed) ++;
+//
+// return 0;
+// }
+//
+//int insertWord36toBuffer (uint8 * bufp, t_mtrlnt tbc, uint * words_processed, word36 word)
+// {
+// uint wp = * words_processed; // How many words have been processed
+//
+// // 2 dps8m words == 9 bytes
+//
+// uint bytes_processed = (wp * 9 + 1) / 2;
+// if (bytes_processed >= tbc)
+// return 1;
+// //sim_printf ("store 0%08lo@0%012"PRIo64"\n", wordp - M, extr36 (bufp, wp));
+//
+// put36 (word, bufp, wp);
+// //sim_printf ("* %06lo = %012"PRIo64"\n", wordp - M, * wordp);
+// (* words_processed) ++;
+//
+// return 0;
+// }
+
+#ifndef NEED_128
+static void print_uint128_r (uint128 n, char * p)
+ {
+ if (n == 0)
+ return;
+
+ print_uint128_r(n / 10, p);
+ if (p)
+ {
+ char s [2];
+ s [0] = n % 10 + '0';
+ s [1] = '\0';
+ strcat (p, s);
+ }
+ else
+ sim_printf("%c", (int) (n%10+0x30));
+ }
+
+void print_int128 (int128 n, char * p)
+ {
+ if (n == 0)
+ {
+ if (p)
+ strcat (p, "0");
+ else
+ sim_printf ("0");
+ return;
+ }
+ if (n < 0)
+ {
+ if (p)
+ strcat (p, "-");
+ else
+ sim_printf ("-");
+ n = -n;
+ }
+ print_uint128_r ((uint128) n, p);
+ }
+#endif
+
+// https://gist.github.com/diabloneo/9619917
+
+void timespec_diff(struct timespec * start, struct timespec * stop,
+ struct timespec * result)
+{
+ if ((stop->tv_nsec - start->tv_nsec) < 0) {
+ result->tv_sec = stop->tv_sec - start->tv_sec - 1;
+ result->tv_nsec = stop->tv_nsec - start->tv_nsec + 1000000000;
+ } else {
+ result->tv_sec = stop->tv_sec - start->tv_sec;
+ result->tv_nsec = stop->tv_nsec - start->tv_nsec;
+ }
+
+ return;
+}
+
+#if 0
+// Calculate current TR value
+
+void currentTR (word27 * trunits, bool * ovf)
+ {
+ struct timespec now, delta;
+ clock_gettime (CLOCK_BOOTTIME, & now);
+ timespec_diff (& cpu.rTRTime, & now, & delta);
+ if (delta.tv_sec > 263)
+ {
+ // The elapsed time is manifestly larger then the TR range
+ * trunits = (~0llu) & MASK27;
+ * ovf = true;
+ return;
+ }
+ // difference in nSecs
+ unsigned long dns = (unsigned long) delta.tv_sec * 1000000000 +
+ (unsigned long) delta.tv_nsec;
+ // in Timer ticks
+ unsigned long ticks = dns / 1953 /* 1953.125 */;
+
+ // Runout?
+ if (ticks >= cpu.rTR)
+ {
+ * trunits = (~0llu) & MASK27;
+ * ovf = true;
+ return;
+ }
+ * trunits = (cpu.rTR - ticks) & MASK27;
+ //sim_printf ("time left %f\n", (float) (* trunits) / 5120000);
+ * ovf = false;
+ }
+#endif
+
--- /dev/null
+/*
+ Copyright (c) 2007-2013 Michael Mondy
+ Copyright 2012-2016 by Harry Reed
+ Copyright 2013-2018 by Charles Anthony
+
+ All rights reserved.
+
+ This software is made available under the terms of the
+ ICU License -- ICU 1.8.1 and later.
+ See the LICENSE file at the top-level directory of this distribution and
+ at https://sourceforge.net/p/dps8m/code/ci/master/tree/LICENSE
+ */
+
+// Interface for cfg_parse
+
+typedef struct config_value_list_s
+ {
+ const char * value_name;
+ int64_t value;
+ } config_value_list_t;
+
+typedef struct config_list_s
+ {
+ const char * name; // opt name
+ int64_t min, max; // value limits
+ config_value_list_t * value_list;
+ } config_list_t;
+
+typedef struct config_state_s
+ {
+ char * copy;
+ char * statement_save;
+ } config_state_t;
+
+int cfg_parse (const char * tag, const char * cptr, config_list_t * clist, config_state_t * state, int64_t * result);
+void cfg_parse_done (config_state_t * state);
+
+struct opcode_s * get_iwb_info (DCDstruct *i);
+char * dump_flags(char * buffer, word18 flags);
+char *disassemble(char * result, word36 instruction);
+char *get_mod_string(char * msg, word6 tag);
+word72 convert_to_word72 (word36 even, word36 odd);
+void convert_to_word36 (word72 src, word36 *even, word36 *odd);
+
+word36 compl36(word36 op1, word18 *flags, bool * ovf);
+word18 compl18(word18 op1, word18 *flags, bool * ovf);
+
+void copyBytes(int posn, word36 src, word36 *dst);
+void copyChars(int posn, word36 src, word36 *dst);
+
+void putByte(word36 *dst, word9 data, int posn);
+void putChar(word36 *dst, word6 data, int posn);
+
+void cmp36(word36 op1, word36 op2, word18 *flags);
+void cmp36wl(word36 A, word36 Y, word36 Q, word18 *flags);
+void cmp18(word18 op1, word18 op2, word18 *flags);
+void cmp72(word72 op1, word72 op2, word18 *flags);
+
+char *strlower(char *q);
+int strmask(char *str, char *mask);
+char *Strtok(char *, char *);
+char *stripquotes(char *s);
+char *trim(char *s);
+char *ltrim(char *s);
+char *rtrim(char *s);
+
+//
+// getbitsNN/setbitsNN/putbitsNN
+//
+// Manipluate bitfields.
+// NN the word size (18, 36, 72).
+// data the incoming word
+// i the starting bit number (DPS8 notation, 0 is the MSB)
+// n the number of bits, starting at i
+// val the bits
+//
+// val = getbitsNN (data, i, n)
+//
+// extract n bits from data, starting at i.
+//
+// val = getbits36 (data, 0, 1) --> the sign bit
+// val = getbits36 (data, 18, 18) --> the low eighteen bits
+//
+// newdata = setbitsNN (data, i, n, val)
+//
+// return 'data' with n bits of val inserted.
+//
+// newdata = setbits36 (data, 0, 18, 1) --> move '1' into the high
+// 18 bits.
+//
+// putbitsNN (& data, i, n, val)
+//
+// put val into data (equivalent to 'data = setbitsNN (data, ....)'
+//
+// putbits (& data, 0, 18, 1) --> set the high 18 bits to '1'.
+//
+
+#if 0
+static inline word72 getbits72 (word72 x, uint i, uint n)
+ {
+ // bit 71 is right end, bit zero is 72nd from the right
+ int shift = 71 - (int) i - (int) n + 1;
+ if (shift < 0 || shift > 71)
+ {
+ sim_printf ("getbits72: bad args (i=%d,n=%d)\n", i, n);
+ return 0;
+ }
+ else
+ return (x >> (unsigned) shift) & ~ (~0U << n);
+ }
+#endif
+
+static inline word36 getbits36(word36 x, uint i, uint n) {
+ // bit 35 is right end, bit zero is 36th from the right
+ int shift = 35-(int)i-(int)n+1;
+ if (shift < 0 || shift > 35) {
+ sim_printf ("getbits36: bad args (%012"PRIo64",i=%d,n=%d)\n", x, i, n);
+ return 0;
+ } else
+ return (x >> (unsigned) shift) & ~ (~0U << n);
+}
+
+static inline word1 getbits36_1 (word36 x, uint i)
+ {
+ // bit 35 is right end, bit zero is 36th from the right
+ int shift = 35-(int)i;
+ if (shift < 0 || shift > 35) {
+ sim_printf ("getbits36_1: bad args (%012"PRIo64",i=%d)\n", x, i);
+ return 0;
+ } else
+ return (x >> (unsigned) shift) & 01;
+ }
+
+static inline word2 getbits36_2 (word36 x, uint i)
+ {
+ // bit 35 is right end, bit zero is 36th from the right
+ int shift = 35-(int)i-(int)2+1;
+ if (shift < 0 || shift > 35) {
+ sim_printf ("getbits36_2: bad args (%012"PRIo64",i=%d)\n", x, i);
+ return 0;
+ } else
+ return (x >> (unsigned) shift) & 03;
+ }
+
+static inline word3 getbits36_3 (word36 x, uint i)
+ {
+ // bit 35 is right end, bit zero is 36th from the right
+ int shift = 35-(int)i-(int)3+1;
+ if (shift < 0 || shift > 35) {
+ sim_printf ("getbits36_3: bad args (%012"PRIo64",i=%d)\n", x, i);
+ return 0;
+ } else
+ return (x >> (unsigned) shift) & 07;
+ }
+
+static inline word4 getbits36_4 (word36 x, uint i)
+ {
+ // bit 35 is right end, bit zero is 36th from the right
+ int shift = 35-(int)i-(int)4+1;
+ if (shift < 0 || shift > 35) {
+ sim_printf ("getbits36_4: bad args (%012"PRIo64",i=%d)\n", x, i);
+ return 0;
+ } else
+ return (x >> (unsigned) shift) & 017;
+ }
+
+static inline word5 getbits36_5 (word36 x, uint i)
+ {
+ // bit 35 is right end, bit zero is 36th from the right
+ int shift = 35-(int)i-(int)5+1;
+ if (shift < 0 || shift > 35) {
+ sim_printf ("getbits36_5: bad args (%012"PRIo64",i=%d)\n", x, i);
+ return 0;
+ } else
+ return (x >> (unsigned) shift) & 037;
+ }
+
+static inline word6 getbits36_6 (word36 x, uint i)
+ {
+ // bit 35 is right end, bit zero is 36th from the right
+ int shift = 35-(int)i-(int)6+1;
+ if (shift < 0 || shift > 35) {
+ sim_printf ("getbits36_6: bad args (%012"PRIo64",i=%d)\n", x, i);
+ return 0;
+ } else
+ return (x >> (unsigned) shift) & 077;
+ }
+
+static inline word7 getbits36_7 (word36 x, uint i)
+ {
+ // bit 35 is right end, bit zero is 36th from the right
+ int shift = 35-(int)i-(int)7+1;
+ if (shift < 0 || shift > 35) {
+ sim_printf ("getbits36_7: bad args (%012"PRIo64",i=%d)\n", x, i);
+ return 0;
+ } else
+ return (x >> (unsigned) shift) & 0177;
+ }
+
+static inline word8 getbits36_8 (word36 x, uint i)
+ {
+ // bit 35 is right end, bit zero is 36th from the right
+ int shift = 35-(int)i-(int)8+1;
+ if (shift < 0 || shift > 35) {
+ sim_printf ("getbits36_8: bad args (%012"PRIo64",i=%d)\n", x, i);
+ return 0;
+ } else
+ return (x >> (unsigned) shift) & 0377;
+ }
+
+static inline word9 getbits36_9 (word36 x, uint i)
+ {
+ // bit 35 is right end, bit zero is 36th from the right
+ int shift = 35-(int)i-(int)9+1;
+ if (shift < 0 || shift > 35) {
+ sim_printf ("getbits36_9: bad args (%012"PRIo64",i=%d)\n", x, i);
+ return 0;
+ } else
+ return (x >> (unsigned) shift) & 0777;
+ }
+
+static inline word10 getbits36_10 (word36 x, uint i)
+ {
+ // bit 35 is right end, bit zero is 36th from the right
+ int shift = 35-(int)i-(int)10+1;
+ if (shift < 0 || shift > 35) {
+ sim_printf ("getbits36_10: bad args (%012"PRIo64",i=%d)\n", x, i);
+ return 0;
+ } else
+ return (x >> (unsigned) shift) & 01777;
+ }
+
+static inline word12 getbits36_12 (word36 x, uint i)
+ {
+ // bit 35 is right end, bit zero is 36th from the right
+ int shift = 35-(int)i-(int)12+1;
+ if (shift < 0 || shift > 35) {
+ sim_printf ("getbits36_12: bad args (%012"PRIo64",i=%d)\n", x, i);
+ return 0;
+ } else
+ return (x >> (unsigned) shift) & 07777;
+ }
+
+static inline word14 getbits36_14 (word36 x, uint i)
+ {
+ // bit 35 is right end, bit zero is 36th from the right
+ int shift = 35-(int)i-(int)14+1;
+ if (shift < 0 || shift > 35) {
+ sim_printf ("getbits36_14: bad args (%012"PRIo64",i=%d)\n", x, i);
+ return 0;
+ } else
+ return (x >> (unsigned) shift) & 037777;
+ }
+
+static inline word15 getbits36_15 (word36 x, uint i)
+ {
+ // bit 35 is right end, bit zero is 36th from the right
+ int shift = 35-(int)i-(int)15+1;
+ if (shift < 0 || shift > 35) {
+ sim_printf ("getbits36_15: bad args (%012"PRIo64",i=%d)\n", x, i);
+ return 0;
+ } else
+ return (x >> (unsigned) shift) & 077777;
+ }
+
+static inline word16 getbits36_16 (word36 x, uint i)
+ {
+ // bit 35 is right end, bit zero is 36th from the right
+ int shift = 35-(int)i-(int)16+1;
+ if (shift < 0 || shift > 35) {
+ sim_printf ("getbits36_16: bad args (%012"PRIo64",i=%d)\n", x, i);
+ return 0;
+ } else
+ return (x >> (unsigned) shift) & 0177777;
+ }
+
+static inline word18 getbits36_18 (word36 x, uint i)
+ {
+ // bit 35 is right end, bit zero is 36th from the right
+ int shift = 35-(int)i-(int)18+1;
+ if (shift < 0 || shift > 35) {
+ sim_printf ("getbits36_18: bad args (%012"PRIo64",i=%d)\n", x, i);
+ return 0;
+ } else
+ return (x >> (unsigned) shift) & 0777777;
+ }
+
+static inline word24 getbits36_24 (word36 x, uint i)
+ {
+ // bit 35 is right end, bit zero is 36th from the right
+ int shift = 35-(int)i-(int)24+1;
+ if (shift < 0 || shift > 35) {
+ sim_printf ("getbits36_24: bad args (%012"PRIo64",i=%d)\n", x, i);
+ return 0;
+ } else
+ return (x >> (unsigned) shift) & MASK24;
+ }
+
+static inline word28 getbits36_28 (word36 x, uint i)
+ {
+ // bit 35 is right end, bit zero is 36th from the right
+ int shift = 35-(int)i-(int)28+1;
+ if (shift < 0 || shift > 35) {
+ sim_printf ("getbits36_28: bad args (%012"PRIo64",i=%d)\n", x, i);
+ return 0;
+ } else
+ return (x >> (unsigned) shift) & 01777777777;
+ }
+
+static inline word36 setbits36(word36 x, uint p, uint n, word36 val)
+{
+ int shift = 36 - (int) p - (int) n;
+ if (shift < 0 || shift > 35) {
+ sim_printf ("setbits36: bad args (%012"PRIo64",pos=%d,n=%d)\n", x, p, n);
+ return 0;
+ }
+ word36 mask = ~ (~0U<<n); // n low bits on
+ mask <<= (unsigned) shift; // shift 1s to proper position; result 0*1{n}0*
+ // caller may provide val that is too big, e.g., a word with all bits
+ // set to one, so we mask val
+ word36 result = (x & ~ mask) | ((val&MASKBITS(n)) << (36 - p - n));
+ return result;
+}
+
+static inline word36 setbits36_1 (word36 x, uint p, word1 val)
+{
+ const int n = 1;
+ int shift = 36 - (int) p - (int) n;
+ if (shift < 0 || shift > 35) {
+ sim_printf ("setbits36_1: bad args (%012"PRIo64",pos=%d)\n", x, p);
+ return 0;
+ }
+ word36 mask = ~ (~0U<<n); // n low bits on
+ word36 smask = mask << (unsigned) shift; // shift 1s to proper position; result 0*1{n}0*
+ // caller may provide val that is too big, e.g., a word with all bits
+ // set to one, so we mask val
+ word36 result = (x & ~ smask) | (((word36) val & mask) << shift);
+ return result;
+}
+
+static inline word36 setbits36_4 (word36 x, uint p, word4 val)
+{
+ const int n = 4;
+ int shift = 36 - (int) p - (int) n;
+ if (shift < 0 || shift > 35) {
+ sim_printf ("setbits36_4: bad args (%012"PRIo64",pos=%d)\n", x, p);
+ return 0;
+ }
+ word36 mask = ~ (~0U<<n); // n low bits on
+ word36 smask = mask << (unsigned) shift; // shift 1s to proper position; result 0*1{n}0*
+ // caller may provide val that is too big, e.g., a word with all bits
+ // set to one, so we mask val
+ word36 result = (x & ~ smask) | (((word36) val & mask) << shift);
+ return result;
+}
+
+static inline word36 setbits36_5 (word36 x, uint p, word5 val)
+{
+ const int n = 5;
+ int shift = 36 - (int) p - (int) n;
+ if (shift < 0 || shift > 35) {
+ sim_printf ("setbits36_5: bad args (%012"PRIo64",pos=%d)\n", x, p);
+ return 0;
+ }
+ word36 mask = ~ (~0U<<n); // n low bits on
+ word36 smask = mask << (unsigned) shift; // shift 1s to proper position; result 0*1{n}0*
+ // caller may provide val that is too big, e.g., a word with all bits
+ // set to one, so we mask val
+ word36 result = (x & ~ smask) | (((word36) val & mask) << shift);
+ return result;
+}
+
+static inline word36 setbits36_6 (word36 x, uint p, word6 val)
+{
+ const int n = 6;
+ int shift = 36 - (int) p - (int) n;
+ if (shift < 0 || shift > 35) {
+ sim_printf ("setbits36_6: bad args (%012"PRIo64",pos=%d)\n", x, p);
+ return 0;
+ }
+ word36 mask = ~ (~0U<<n); // n low bits on
+ word36 smask = mask << (unsigned) shift; // shift 1s to proper position; result 0*1{n}0*
+ // caller may provide val that is too big, e.g., a word with all bits
+ // set to one, so we mask val
+ word36 result = (x & ~ smask) | (((word36) val & mask) << shift);
+ return result;
+}
+
+static inline word36 setbits36_8 (word36 x, uint p, word8 val)
+{
+ const int n = 8;
+ int shift = 36 - (int) p - (int) n;
+ if (shift < 0 || shift > 35) {
+ sim_printf ("setbits36_8: bad args (%012"PRIo64",pos=%d)\n", x, p);
+ return 0;
+ }
+ word36 mask = ~ (~0U<<n); // n low bits on
+ word36 smask = mask << (unsigned) shift; // shift 1s to proper position; result 0*1{n}0*
+ // caller may provide val that is too big, e.g., a word with all bits
+ // set to one, so we mask val
+ word36 result = (x & ~ smask) | (((word36) val & mask) << shift);
+ return result;
+}
+
+static inline word36 setbits36_9 (word36 x, uint p, word9 val)
+{
+ const int n = 9;
+ int shift = 36 - (int) p - (int) n;
+ if (shift < 0 || shift > 35) {
+ sim_printf ("setbits36_9: bad args (%012"PRIo64",pos=%d)\n", x, p);
+ return 0;
+ }
+ word36 mask = ~ (~0U<<n); // n low bits on
+ word36 smask = mask << (unsigned) shift; // shift 1s to proper position; result 0*1{n}0*
+ // caller may provide val that is too big, e.g., a word with all bits
+ // set to one, so we mask val
+ word36 result = (x & ~ smask) | (((word36) val & mask) << shift);
+ return result;
+}
+
+static inline word36 setbits36_16 (word36 x, uint p, word16 val)
+{
+ const int n = 16;
+ int shift = 36 - (int) p - (int) n;
+ if (shift < 0 || shift > 35) {
+ sim_printf ("setbits36_16: bad args (%012"PRIo64",pos=%d)\n", x, p);
+ return 0;
+ }
+ word36 mask = ~ (~0U<<n); // n low bits on
+ word36 smask = mask << (unsigned) shift; // shift 1s to proper position; result 0*1{n}0*
+ // caller may provide val that is too big, e.g., a word with all bits
+ // set to one, so we mask val
+ word36 result = (x & ~ smask) | (((word36) val & mask) << shift);
+ return result;
+}
+
+static inline word36 setbits36_24 (word36 x, uint p, word24 val)
+{
+ const int n = 24;
+ int shift = 36 - (int) p - (int) n;
+ if (shift < 0 || shift > 35) {
+ sim_printf ("setbits36_24: bad args (%012"PRIo64",pos=%d)\n", x, p);
+ return 0;
+ }
+ word36 mask = ~ (~0U<<n); // n low bits on
+ word36 smask = mask << (unsigned) shift; // shift 1s to proper position; result 0*1{n}0*
+ // caller may provide val that is too big, e.g., a word with all bits
+ // set to one, so we mask val
+ word36 result = (x & ~ smask) | (((word36) val & mask) << shift);
+ return result;
+}
+
+static inline void putbits36 (word36 * x, uint p, uint n, word36 val)
+ {
+ int shift = 36 - (int) p - (int) n;
+ if (shift < 0 || shift > 35)
+ {
+ sim_printf ("putbits36: bad args (%012"PRIo64",pos=%d,n=%d)\n", * x, p, n);
+ return;
+ }
+ word36 mask = ~ (~0U << n); // n low bits on
+ mask <<= (unsigned) shift; // shift 1s to proper position; result 0*1{n}0*
+ // caller may provide val that is too big, e.g., a word with all bits
+ // set to one, so we mask val
+ * x = (* x & ~mask) | ((val & MASKBITS (n)) << (36 - p - n));
+ return;
+ }
+
+static inline void putbits36_1 (word36 * x, uint p, word1 val)
+{
+ const int n = 1;
+ int shift = 36 - (int) p - (int) n;
+ if (shift < 0 || shift > 35) {
+ sim_printf ("putbits36_1: bad args (%012"PRIo64",pos=%d)\n", *x, p);
+ return;
+ }
+ word36 mask = ~ (~0U<<n); // n low bits on
+ word36 smask = mask << (unsigned) shift; // shift 1s to proper position; result 0*1{n}0*
+ // caller may provide val that is too big, e.g., a word with all bits
+ // set to one, so we mask val
+ * x = (* x & ~ smask) | (((word36) val & mask) << shift);
+}
+
+static inline void putbits36_2 (word36 * x, uint p, word2 val)
+{
+ const int n = 2;
+ int shift = 36 - (int) p - (int) n;
+ if (shift < 0 || shift > 35) {
+ sim_printf ("putbits36_2: bad args (%012"PRIo64",pos=%d)\n", *x, p);
+ return;
+ }
+ word36 mask = ~ (~0U<<n); // n low bits on
+ word36 smask = mask << (unsigned) shift; // shift 1s to proper position; result 0*1{n}0*
+ // caller may provide val that is too big, e.g., a word with all bits
+ // set to one, so we mask val
+ * x = (* x & ~ smask) | (((word36) val & mask) << shift);
+}
+
+static inline void putbits36_3 (word36 * x, uint p, word3 val)
+{
+ const int n = 3;
+ int shift = 36 - (int) p - (int) n;
+ if (shift < 0 || shift > 35) {
+ sim_printf ("putbits36_3: bad args (%012"PRIo64",pos=%d)\n", *x, p);
+ return;
+ }
+ word36 mask = ~ (~0U<<n); // n low bits on
+ word36 smask = mask << (unsigned) shift; // shift 1s to proper position; result 0*1{n}0*
+ // caller may provide val that is too big, e.g., a word with all bits
+ // set to one, so we mask val
+ * x = (* x & ~ smask) | (((word36) val & mask) << shift);
+}
+
+static inline void putbits36_4 (word36 * x, uint p, word4 val)
+{
+ const int n = 4;
+ int shift = 36 - (int) p - (int) n;
+ if (shift < 0 || shift > 35) {
+ sim_printf ("putbits36_4: bad args (%012"PRIo64",pos=%d)\n", *x, p);
+ return;
+ }
+ word36 mask = ~ (~0U<<n); // n low bits on
+ word36 smask = mask << (unsigned) shift; // shift 1s to proper position; result 0*1{n}0*
+ // caller may provide val that is too big, e.g., a word with all bits
+ // set to one, so we mask val
+ * x = (* x & ~ smask) | (((word36) val & mask) << shift);
+}
+
+static inline void putbits36_5 (word36 * x, uint p, word5 val)
+{
+ const int n = 5;
+ int shift = 36 - (int) p - (int) n;
+ if (shift < 0 || shift > 35) {
+ sim_printf ("putbits36_5: bad args (%012"PRIo64",pos=%d)\n", *x, p);
+ return;
+ }
+ word36 mask = ~ (~0U<<n); // n low bits on
+ word36 smask = mask << (unsigned) shift; // shift 1s to proper position; result 0*1{n}0*
+ // caller may provide val that is too big, e.g., a word with all bits
+ // set to one, so we mask val
+ * x = (* x & ~ smask) | (((word36) val & mask) << shift);
+}
+
+static inline void putbits36_6 (word36 * x, uint p, word6 val)
+{
+ const int n = 6;
+ int shift = 36 - (int) p - (int) n;
+ if (shift < 0 || shift > 35) {
+ sim_printf ("putbits36_6: bad args (%012"PRIo64",pos=%d)\n", *x, p);
+ return;
+ }
+ word36 mask = ~ (~0U<<n); // n low bits on
+ word36 smask = mask << (unsigned) shift; // shift 1s to proper position; result 0*1{n}0*
+ // caller may provide val that is too big, e.g., a word with all bits
+ // set to one, so we mask val
+ * x = (* x & ~ smask) | (((word36) val & mask) << shift);
+}
+
+static inline void putbits36_7 (word36 * x, uint p, word7 val)
+{
+ const int n = 7;
+ int shift = 36 - (int) p - (int) n;
+ if (shift < 0 || shift > 35) {
+ sim_printf ("putbits36_7: bad args (%012"PRIo64",pos=%d)\n", *x, p);
+ return;
+ }
+ word36 mask = ~ (~0U<<n); // n low bits on
+ word36 smask = mask << (unsigned) shift; // shift 1s to proper position; result 0*1{n}0*
+ // caller may provide val that is too big, e.g., a word with all bits
+ // set to one, so we mask val
+ * x = (* x & ~ smask) | (((word36) val & mask) << shift);
+}
+
+static inline void putbits36_8 (word36 * x, uint p, word8 val)
+{
+ const int n = 8;
+ int shift = 36 - (int) p - (int) n;
+ if (shift < 0 || shift > 35) {
+ sim_printf ("putbits36_8: bad args (%012"PRIo64",pos=%d)\n", *x, p);
+ return;
+ }
+ word36 mask = ~ (~0U<<n); // n low bits on
+ word36 smask = mask << (unsigned) shift; // shift 1s to proper position; result 0*1{n}0*
+ // caller may provide val that is too big, e.g., a word with all bits
+ // set to one, so we mask val
+ * x = (* x & ~ smask) | (((word36) val & mask) << shift);
+}
+
+static inline void putbits36_9 (word36 * x, uint p, word9 val)
+{
+ const int n = 9;
+ int shift = 36 - (int) p - (int) n;
+ if (shift < 0 || shift > 35) {
+ sim_printf ("putbits36_9: bad args (%012"PRIo64",pos=%d)\n", *x, p);
+ return;
+ }
+ word36 mask = ~ (~0U<<n); // n low bits on
+ word36 smask = mask << (unsigned) shift; // shift 1s to proper position; result 0*1{n}0*
+ // caller may provide val that is too big, e.g., a word with all bits
+ // set to one, so we mask val
+ * x = (* x & ~ smask) | (((word36) val & mask) << shift);
+}
+
+static inline void putbits36_10 (word36 * x, uint p, word10 val)
+{
+ const int n = 10;
+ int shift = 36 - (int) p - (int) n;
+ if (shift < 0 || shift > 35) {
+ sim_printf ("putbits36_10: bad args (%012"PRIo64",pos=%d)\n", *x, p);
+ return;
+ }
+ word36 mask = ~ (~0U<<n); // n low bits on
+ word36 smask = mask << (unsigned) shift; // shift 1s to proper position; result 0*1{n}0*
+ // caller may provide val that is too big, e.g., a word with all bits
+ // set to one, so we mask val
+ * x = (* x & ~ smask) | (((word36) val & mask) << shift);
+}
+
+static inline void putbits36_12 (word36 * x, uint p, word12 val)
+{
+ const int n = 12;
+ int shift = 36 - (int) p - (int) n;
+ if (shift < 0 || shift > 35) {
+ sim_printf ("putbits36_12: bad args (%012"PRIo64",pos=%d)\n", *x, p);
+ return;
+ }
+ word36 mask = ~ (~0U<<n); // n low bits on
+ word36 smask = mask << (unsigned) shift; // shift 1s to proper position; result 0*1{n}0*
+ // caller may provide val that is too big, e.g., a word with all bits
+ // set to one, so we mask val
+ * x = (* x & ~ smask) | (((word36) val & mask) << shift);
+}
+
+static inline void putbits36_13 (word36 * x, uint p, word13 val)
+{
+ const int n = 13;
+ int shift = 36 - (int) p - (int) n;
+ if (shift < 0 || shift > 35) {
+ sim_printf ("putbits36_13: bad args (%012"PRIo64",pos=%d)\n", *x, p);
+ return;
+ }
+ word36 mask = ~ (~0U<<n); // n low bits on
+ word36 smask = mask << (unsigned) shift; // shift 1s to proper position; result 0*1{n}0*
+ // caller may provide val that is too big, e.g., a word with all bits
+ // set to one, so we mask val
+ * x = (* x & ~ smask) | (((word36) val & mask) << shift);
+}
+
+static inline void putbits36_14 (word36 * x, uint p, word14 val)
+{
+ const int n = 14;
+ int shift = 36 - (int) p - (int) n;
+ if (shift < 0 || shift > 35) {
+ sim_printf ("putbits36_14: bad args (%012"PRIo64",pos=%d)\n", *x, p);
+ return;
+ }
+ word36 mask = ~ (~0U<<n); // n low bits on
+ word36 smask = mask << (unsigned) shift; // shift 1s to proper position; result 0*1{n}0*
+ // caller may provide val that is too big, e.g., a word with all bits
+ // set to one, so we mask val
+ * x = (* x & ~ smask) | (((word36) val & mask) << shift);
+}
+
+static inline void putbits36_15 (word36 * x, uint p, word15 val)
+{
+ const int n = 15;
+ int shift = 36 - (int) p - (int) n;
+ if (shift < 0 || shift > 35) {
+ sim_printf ("putbits36_15: bad args (%012"PRIo64",pos=%d)\n", *x, p);
+ return;
+ }
+ word36 mask = ~ (~0U<<n); // n low bits on
+ word36 smask = mask << (unsigned) shift; // shift 1s to proper position; result 0*1{n}0*
+ // caller may provide val that is too big, e.g., a word with all bits
+ // set to one, so we mask val
+ * x = (* x & ~ smask) | (((word36) val & mask) << shift);
+}
+
+static inline void putbits36_16 (word36 * x, uint p, word16 val)
+{
+ const int n = 16;
+ int shift = 36 - (int) p - (int) n;
+ if (shift < 0 || shift > 35) {
+ sim_printf ("putbits36_16: bad args (%012"PRIo64",pos=%d)\n", *x, p);
+ return;
+ }
+ word36 mask = ~ (~0U<<n); // n low bits on
+ word36 smask = mask << (unsigned) shift; // shift 1s to proper position; result 0*1{n}0*
+ // caller may provide val that is too big, e.g., a word with all bits
+ // set to one, so we mask val
+ * x = (* x & ~ smask) | (((word36) val & mask) << shift);
+}
+
+static inline void putbits36_17 (word36 * x, uint p, word17 val)
+{
+ const int n = 17;
+ int shift = 36 - (int) p - (int) n;
+ if (shift < 0 || shift > 35) {
+ sim_printf ("putbits36_17: bad args (%012"PRIo64",pos=%d)\n", *x, p);
+ return;
+ }
+ word36 mask = ~ (~0U<<n); // n low bits on
+ word36 smask = mask << (unsigned) shift; // shift 1s to proper position; result 0*1{n}0*
+ // caller may provide val that is too big, e.g., a word with all bits
+ // set to one, so we mask val
+ * x = (* x & ~ smask) | (((word36) val & mask) << shift);
+}
+
+static inline void putbits36_18 (word36 * x, uint p, word18 val)
+{
+ const int n = 18;
+ int shift = 36 - (int) p - (int) n;
+ if (shift < 0 || shift > 35) {
+ sim_printf ("putbits36_18: bad args (%012"PRIo64",pos=%d)\n", *x, p);
+ return;
+ }
+ word36 mask = ~ (~0U<<n); // n low bits on
+ word36 smask = mask << (unsigned) shift; // shift 1s to proper position; result 0*1{n}0*
+ // caller may provide val that is too big, e.g., a word with all bits
+ // set to one, so we mask val
+ * x = (* x & ~ smask) | (((word36) val & mask) << shift);
+}
+
+static inline void putbits36_23 (word36 * x, uint p, word23 val)
+{
+ const int n = 23;
+ int shift = 36 - (int) p - (int) n;
+ if (shift < 0 || shift > 35) {
+ sim_printf ("putbits36_23: bad args (%012"PRIo64",pos=%d)\n", *x, p);
+ return;
+ }
+ word36 mask = ~ (~0U<<n); // n low bits on
+ word36 smask = mask << (unsigned) shift; // shift 1s to proper position; result 0*1{n}0*
+ // caller may provide val that is too big, e.g., a word with all bits
+ // set to one, so we mask val
+ * x = (* x & ~ smask) | (((word36) val & mask) << shift);
+}
+
+static inline void putbits36_24 (word36 * x, uint p, word24 val)
+{
+ const int n = 24;
+ int shift = 36 - (int) p - (int) n;
+ if (shift < 0 || shift > 35) {
+ sim_printf ("putbits36_24: bad args (%012"PRIo64",pos=%d)\n", *x, p);
+ return;
+ }
+ word36 mask = ~ (~0U<<n); // n low bits on
+ word36 smask = mask << (unsigned) shift; // shift 1s to proper position; result 0*1{n}0*
+ // caller may provide val that is too big, e.g., a word with all bits
+ // set to one, so we mask val
+ * x = (* x & ~ smask) | (((word36) val & mask) << shift);
+}
+
+static inline void putbits36_28 (word36 * x, uint p, word28 val)
+{
+ const int n = 28;
+ int shift = 36 - (int) p - (int) n;
+ if (shift < 0 || shift > 35) {
+ sim_printf ("putbits36_28: bad args (%012"PRIo64",pos=%d)\n", *x, p);
+ return;
+ }
+ word36 mask = ~ (~0U<<n); // n low bits on
+ word36 smask = mask << (unsigned) shift; // shift 1s to proper position; result 0*1{n}0*
+ // caller may provide val that is too big, e.g., a word with all bits
+ // set to one, so we mask val
+ * x = (* x & ~ smask) | (((word36) val & mask) << shift);
+}
+
+static inline void putbits72 (word72 * x, uint p, uint n, word72 val)
+ {
+ int shift = 72 - (int) p - (int) n;
+ if (shift < 0 || shift > 71)
+ {
+ sim_printf ("putbits72: bad args (pos=%d,n=%d)\n", p, n);
+ return;
+ }
+#ifdef NEED_128
+// n low bits on
+ uint64_t lowmask = 0;
+ uint64_t highmask = 0;
+ if (n >= 64)
+ {
+ lowmask = MASK64;
+ highmask = ~ ((~(uint64_t)0) << n);
+ highmask &= 0377U;
+ }
+ else
+ {
+ lowmask = ~ ((~(uint64_t)0) << n);
+ }
+ word72 mask = construct_128 (highmask, lowmask);
+ mask = lshift_128 (mask, (uint) shift);
+ word72 notmask = complement_128 (mask);
+ * x = or_128 (and_128 (* x, notmask), and_128 (lshift_128 (val, 72 - p - n), mask));
+#else
+ word72 mask = ~ ((~(word72)0) << n); // n low bits on
+ mask <<= (unsigned) shift; // shift 1s to proper position; result 0*1{n}0*
+ // caller may provide val that is too big, e.g., a word with all bits
+ // set to one, so we mask val
+ * x = (* x & ~mask) | ((val & MASKBITS72 (n)) << (72 - p - n));
+#endif
+ return;
+ }
+
+
+// getbits18 (data, starting bit, number of bits)
+
+static inline word18 getbits18 (word18 x, uint i, uint n)
+ {
+ // bit 17 is right end, bit zero is 18th from the right
+ int shift = 17 - (int) i - (int) n + 1;
+ if (shift < 0 || shift > 17)
+ {
+ sim_printf ("getbits18: bad args (%06o,i=%d,n=%d)\n", x, i, n);
+ return 0;
+ }
+ else
+ return (x >> (unsigned) shift) & ~ (~0U << n);
+ }
+
+// putbits18 (data, starting bit, number of bits, bits)
+
+static inline void putbits18 (word18 * x, uint p, uint n, word18 val)
+ {
+ int shift = 18 - (int) p - (int) n;
+ if (shift < 0 || shift > 17)
+ {
+ sim_printf ("putbits18: bad args (%06o,pos=%d,n=%d)\n", * x, p, n);
+ return;
+ }
+ word18 mask = ~ (~0U << n); // n low bits on
+ mask <<= (unsigned) shift; // shift 1s to proper position; result 0*1{n}0*
+ // caller may provide val that is too big, e.g., a word with all bits
+ // set to one, so we mask val
+ * x = (* x & ~mask) | ((val & MASKBITS18 (n)) << (18 - p - n));
+ return;
+ }
+
+//
+// setmask -- set bits from mask
+//
+
+static inline void setmask (word36 * v, word36 mask)
+ {
+ * v |= mask;
+ }
+
+//
+// clrmask -- clear bits from mask
+//
+
+static inline void clrmask (word36 * v, word36 mask)
+ {
+ * v &= ~mask;
+ }
+
+char * strdupesc (const char * str);
+word36 extr36 (uint8 * bits, uint woffset);
+void put36 (word36 val, uint8 * bits, uint woffset);
+//int extractASCII36FromBuffer (uint8 * bufp, t_mtrlnt tbc, uint * words_processed, word36 *wordp);
+//int extractWord36FromBuffer (uint8 * bufp, t_mtrlnt tbc, uint * words_processed, uint64 *wordp);
+//int insertASCII36toBuffer (uint8 * bufp, t_mtrlnt tbc, uint * words_processed, word36 word);
+//int insertWord36toBuffer (uint8 * bufp, t_mtrlnt tbc, uint * words_processed, word36 word);
+void print_int128 (int128 n, char * p);
+word36 Add36b (word36 op1, word36 op2, word1 carryin, word18 flagsToSet, word18 * flags, bool * ovf);
+word36 Sub36b (word36 op1, word36 op2, word1 carryin, word18 flagsToSet, word18 * flags, bool * ovf);
+word18 Add18b (word18 op1, word18 op2, word1 carryin, word18 flagsToSet, word18 * flags, bool * ovf);
+word18 Sub18b (word18 op1, word18 op2, word1 carryin, word18 flagsToSet, word18 * flags, bool * ovf);
+word72 Add72b (word72 op1, word72 op2, word1 carryin, word18 flagsToSet, word18 * flags, bool * ovf);
+word72 Sub72b (word72 op1, word72 op2, word1 carryin, word18 flagsToSet, word18 * flags, bool * ovf);
+
+void timespec_diff(struct timespec *start, struct timespec *stop,
+ struct timespec *result);
+
+#if defined(THREADZ) || defined(LOCKLESS)
+void currentTR (word27 * trunits, bool * ovf);
+#endif
--- /dev/null
+dps8_addrmods.c: In function ‘do_caf’:
+dps8_addrmods.c:561:9: warning: implicit declaration of function ‘ReadIndirect’ [-Wimplicit-function-declaration]
+ ReadIndirect ();
+ ^~~~~~~~~~~~
+dps8_addrmods.c:850:17: warning: implicit declaration of function ‘Read’; did you mean ‘fread’? [-Wimplicit-function-declaration]
+ Read (indaddr, & indword, APU_DATA_READ);
+ ^~~~
+ fread
+dps8_addrmods.c:1019:21: warning: implicit declaration of function ‘Write’; did you mean ‘fwrite’? [-Wimplicit-function-declaration]
+ Write (indaddr, indword, APU_DATA_STORE);
+ ^~~~~
+ fwrite
+dps8_cpu.c: In function ‘cpu_show_config’:
+dps8_cpu.c:133:17: error: ‘scu’ undeclared (first use in this function)
+ scu [0].steady_clock);
+ ^~~
+dps8_cpu.c:133:17: note: each undeclared identifier is reported only once for each function it appears in
+dps8_cpu.c: In function ‘cpu_set_config’:
+dps8_cpu.c:419:11: error: ‘scu’ undeclared (first use in this function)
+ scu [0].steady_clock = (uint) v;
+ ^~~
+dps8_cpu.c: In function ‘cpu_show_stall’:
+dps8_cpu.c:517:40: warning: format ‘%u’ expects argument of type ‘unsigned int’, but argument 5 has type ‘long long int’ [-Wformat=]
+ sim_printf ("%2d %05o:%06o %6u\n", i, stall_points[i].segno, stall_points[i].offset, stall_points[i].time);
+ ~~^ ~~~~~~~~~~~~~~~~~~~~
+ %6llu
+dps8_cpu.c: In function ‘cpu_reset_unit_idx’:
+dps8_cpu.c:647:13: error: ‘M’ undeclared (first use in this function)
+ M[i] &= (MASK36 | MEM_UNINITIALIZED);
+ ^
+dps8_cpu.c: At top level:
+dps8_cpu.c:746:8: error: unknown type name ‘uv_loop_t’
+ static uv_loop_t * ev_poll_loop;
+ ^~~~~~~~~
+dps8_cpu.c:747:8: error: unknown type name ‘uv_timer_t’
+ static uv_timer_t ev_poll_handle;
+ ^~~~~~~~~~
+dps8_cpu.c: In function ‘setup_scbank_map’:
+dps8_cpu.c:969:15: error: ‘cables’ undeclared (first use in this function); did you mean ‘mblen’?
+ if (! cables->cpu_to_scu[current_running_cpu_idx][port_num].in_use)
+ ^~~~~~
+ mblen
+dps8_cpu.c: At top level:
+dps8_cpu.c:1155:25: error: unknown type name ‘uv_timer_t’; did you mean ‘__timer_t’?
+ static void ev_poll_cb (uv_timer_t * UNUSED handle)
+ ^~~~~~~~~~
+ __timer_t
+dps8_cpu.c: In function ‘cpu_init’:
+dps8_cpu.c:1228:5: error: ‘M’ undeclared (first use in this function)
+ M = system_state->M;
+ ^
+dps8_cpu.c:1228:9: error: ‘system_state’ undeclared (first use in this function); did you mean ‘setstate’?
+ M = system_state->M;
+ ^~~~~~~~~~~~
+ setstate
+dps8_cpu.c:1245:20: warning: implicit declaration of function ‘uv_default_loop’; did you mean ‘set_default_cmd’? [-Wimplicit-function-declaration]
+ ev_poll_loop = uv_default_loop ();
+ ^~~~~~~~~~~~~~~
+ set_default_cmd
+dps8_cpu.c:1245:18: warning: assignment makes pointer from integer without a cast [-Wint-conversion]
+ ev_poll_loop = uv_default_loop ();
+ ^
+dps8_cpu.c:1246:5: warning: implicit declaration of function ‘uv_timer_init’ [-Wimplicit-function-declaration]
+ uv_timer_init (ev_poll_loop, & ev_poll_handle);
+ ^~~~~~~~~~~~~
+dps8_cpu.c:1248:5: warning: implicit declaration of function ‘uv_timer_start’; did you mean ‘futimesat’? [-Wimplicit-function-declaration]
+ uv_timer_start (& ev_poll_handle, ev_poll_cb, sys_opts.sys_poll_interval, sys_opts.sys_poll_interval);
+ ^~~~~~~~~~~~~~
+ futimesat
+dps8_cpu.c:1248:39: error: ‘ev_poll_cb’ undeclared (first use in this function); did you mean ‘ev_poll_loop’?
+ uv_timer_start (& ev_poll_handle, ev_poll_cb, sys_opts.sys_poll_interval, sys_opts.sys_poll_interval);
+ ^~~~~~~~~~
+ ev_poll_loop
+dps8_cpu.c:1248:51: error: ‘sys_opts’ undeclared (first use in this function); did you mean ‘sim_puts’?
+ uv_timer_start (& ev_poll_handle, ev_poll_cb, sys_opts.sys_poll_interval, sys_opts.sys_poll_interval);
+ ^~~~~~~~
+ sim_puts
+dps8_cpu.c: In function ‘cpu_ex’:
+dps8_cpu.c:1309:17: error: ‘M’ undeclared (first use in this function)
+ *vptr = M[addr] & DMASK;
+ ^
+dps8_cpu.c: In function ‘cpu_dep’:
+dps8_cpu.c:1325:5: error: ‘M’ undeclared (first use in this function)
+ M[addr] = val & DMASK;
+ ^
+dps8_cpu.c: In function ‘get_highest_intr’:
+dps8_cpu.c:1418:18: warning: implicit declaration of function ‘scu_get_highest_intr’; did you mean ‘get_highest_intr’? [-Wimplicit-function-declaration]
+ fp = scu_get_highest_intr (scu_unit_idx); // CALLED WITH SCU LOCK
+ ^~~~~~~~~~~~~~~~~~~~
+ get_highest_intr
+dps8_cpu.c: In function ‘simh_hooks’:
+dps8_cpu.c:1443:9: error: ‘breakEnable’ undeclared (first use in this function)
+ if (breakEnable && stop_cpu)
+ ^~~~~~~~~~~
+dps8_cpu.c: In function ‘sim_instr’:
+dps8_cpu.c:1915:39: error: ‘sys_opts’ undeclared (first use in this function); did you mean ‘sim_puts’?
+ if (fast_queue_subsample ++ > sys_opts.sys_poll_check_rate) // ~ 1KHz
+ ^~~~~~~~
+ sim_puts
+dps8_cpu.c:1923:13: warning: implicit declaration of function ‘uv_run’ [-Wimplicit-function-declaration]
+ uv_run (ev_poll_loop, UV_RUN_NOWAIT);
+ ^~~~~~
+dps8_cpu.c:1923:35: error: ‘UV_RUN_NOWAIT’ undeclared (first use in this function)
+ uv_run (ev_poll_loop, UV_RUN_NOWAIT);
+ ^~~~~~~~~~~~~
+dps8_cpu.c:1972:28: warning: implicit declaration of function ‘check_attn_key’; did you mean ‘check_events’? [-Wimplicit-function-declaration]
+ int con_unit_idx = check_attn_key ();
+ ^~~~~~~~~~~~~~
+ check_events
+dps8_cpu.c:1974:11: warning: implicit declaration of function ‘console_attn_idx’ [-Wimplicit-function-declaration]
+ console_attn_idx (con_unit_idx);
+ ^~~~~~~~~~~~~~~~
+dps8_cpu.c:2270:20: error: ‘luf_flag’ undeclared (first use in this function); did you mean ‘opc_flag’?
+ if (! (luf_flag && tmp_priv_mode))
+ ^~~~~~~~
+ opc_flag
+dps8_cpu.c: In function ‘read_operand’:
+dps8_cpu.c:2980:13: warning: implicit declaration of function ‘Read’; did you mean ‘read’? [-Wimplicit-function-declaration]
+ Read (addr, & cpu.CY, cyctyp);
+ ^~~~
+ read
+dps8_cpu.c:2985:13: warning: implicit declaration of function ‘Read2’; did you mean ‘read’? [-Wimplicit-function-declaration]
+ Read2 (addr, cpu.Ypair, cyctyp);
+ ^~~~~
+ read
+dps8_cpu.c:2990:13: warning: implicit declaration of function ‘Read8’; did you mean ‘read’? [-Wimplicit-function-declaration]
+ Read8 (addr, cpu.Yblock8, cpu.currentInstruction.b29);
+ ^~~~~
+ read
+dps8_cpu.c:2995:13: warning: implicit declaration of function ‘Read16’; did you mean ‘read’? [-Wimplicit-function-declaration]
+ Read16 (addr, cpu.Yblock16);
+ ^~~~~~
+ read
+dps8_cpu.c: In function ‘write_operand’:
+dps8_cpu.c:3019:13: warning: implicit declaration of function ‘Write’; did you mean ‘write’? [-Wimplicit-function-declaration]
+ Write (addr, cpu.CY, OPERAND_STORE);
+ ^~~~~
+ write
+dps8_cpu.c:3024:13: warning: implicit declaration of function ‘Write2’; did you mean ‘write’? [-Wimplicit-function-declaration]
+ Write2 (addr + 0, cpu.Ypair, OPERAND_STORE);
+ ^~~~~~
+ write
+dps8_cpu.c:3029:13: warning: implicit declaration of function ‘Write8’; did you mean ‘write’? [-Wimplicit-function-declaration]
+ Write8 (addr, cpu.Yblock8, cpu.currentInstruction.b29);
+ ^~~~~~
+ write
+dps8_cpu.c:3034:13: warning: implicit declaration of function ‘Write16’; did you mean ‘write’? [-Wimplicit-function-declaration]
+ Write16 (addr, cpu.Yblock16);
+ ^~~~~~~
+ write
+dps8_cpu.c:3041:13: warning: implicit declaration of function ‘Write32’; did you mean ‘write’? [-Wimplicit-function-declaration]
+ Write32 (addr, cpu.Yblock32);
+ ^~~~~~~
+ write
+dps8_cpu.c: In function ‘core_read’:
+dps8_cpu.c:3169:9: error: ‘M’ undeclared (first use in this function)
+ if (M[addr] & MEM_UNINITIALIZED)
+ ^
+dps8_cpu.c: In function ‘core_write’:
+dps8_cpu.c:3293:5: error: ‘M’ undeclared (first use in this function)
+ M[addr] = data & DMASK;
+ ^
+dps8_cpu.c: In function ‘core_write_zone’:
+dps8_cpu.c:3416:5: error: ‘M’ undeclared (first use in this function)
+ M[addr] = (M[addr] & ~cpu.zone) | (data & cpu.zone);
+ ^
+dps8_cpu.c: In function ‘core_read2’:
+dps8_cpu.c:3518:9: error: ‘M’ undeclared (first use in this function)
+ if (M[addr] & MEM_UNINITIALIZED)
+ ^
+dps8_cpu.c: In function ‘core_write2’:
+dps8_cpu.c:3672:5: error: ‘M’ undeclared (first use in this function)
+ M[addr++] = even & DMASK;
+ ^
+dps8_cpu.c: In function ‘get_serial_number’:
+dps8_cpu.c:1089:9: warning: ignoring return value of ‘fgets’, declared with attribute warn_unused_result [-Wunused-result]
+ fgets (buffer, sizeof (buffer), fp);
+ ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+dps8_eis.c: In function ‘EISWriteCache’:
+dps8_eis.c:549:13: warning: implicit declaration of function ‘Write8’; did you mean ‘fwrite’? [-Wimplicit-function-declaration]
+ Write8 (p->cachedAddr, p -> cachedParagraph, true);
+ ^~~~~~
+ fwrite
+dps8_eis.c: In function ‘EISReadCache’:
+dps8_eis.c:620:9: warning: implicit declaration of function ‘Read8’; did you mean ‘seed48’? [-Wimplicit-function-declaration]
+ Read8 (paragraphAddress, p -> cachedParagraph, true);
+ ^~~~~
+ seed48
+dps8_eis.c: In function ‘EISReadPage’:
+dps8_eis.c:786:9: warning: implicit declaration of function ‘ReadPage’; did you mean ‘EISReadPage’? [-Wimplicit-function-declaration]
+ ReadPage (addressN, data, true);
+ ^~~~~~~~
+ EISReadPage
+dps8_eis.c: In function ‘EISWritePage’:
+dps8_eis.c:848:9: warning: implicit declaration of function ‘WritePage’; did you mean ‘EISWritePage’? [-Wimplicit-function-declaration]
+ WritePage (addressN, data, true);
+ ^~~~~~~~~
+ EISWritePage
+dps8_ins.c: In function ‘writeOperands’:
+dps8_ins.c:134:9: warning: implicit declaration of function ‘Write’; did you mean ‘fwrite’? [-Wimplicit-function-declaration]
+ Write (cpu.ou.character_address, cpu.ou.character_data, OPERAND_STORE);
+ ^~~~~
+ fwrite
+dps8_ins.c: In function ‘read_tra_op’:
+dps8_ins.c:244:7: warning: implicit declaration of function ‘Read’; did you mean ‘fread’? [-Wimplicit-function-declaration]
+ Read (cpu.TPR.CA, &cpu.CY, OPERAND_READ);
+ ^~~~
+ fread
+dps8_ins.c:246:7: warning: implicit declaration of function ‘Read2’ [-Wimplicit-function-declaration]
+ Read2 (cpu.TPR.CA, cpu.Ypair, OPERAND_READ);
+ ^~~~~
+dps8_ins.c: In function ‘doInstruction’:
+dps8_ins.c:6669:19: warning: implicit declaration of function ‘get_scu_in_use’; did you mean ‘get_iwb_info’? [-Wimplicit-function-declaration]
+ if (! get_scu_in_use (current_running_cpu_idx, cpu_port_num))
+ ^~~~~~~~~~~~~~
+ get_iwb_info
+dps8_ins.c:6675:31: warning: implicit declaration of function ‘get_scu_idx’; did you mean ‘set_cpu_idx’? [-Wimplicit-function-declaration]
+ uint scuUnitIdx = get_scu_idx (current_running_cpu_idx, cpu_port_num);
+ ^~~~~~~~~~~
+ set_cpu_idx
+dps8_ins.c:6677:25: warning: implicit declaration of function ‘scu_rscr’; did you mean ‘strstr’? [-Wimplicit-function-declaration]
+ t_stat rc = scu_rscr ((uint) scuUnitIdx, current_running_cpu_idx,
+ ^~~~~~~~
+ strstr
+dps8_ins.c:7976:25: warning: implicit declaration of function ‘scu_rmcm’ [-Wimplicit-function-declaration]
+ t_stat rc = scu_rmcm ((uint) scuUnitIdx,
+ ^~~~~~~~
+dps8_ins.c:8570:13: warning: implicit declaration of function ‘scu_cioc’ [-Wimplicit-function-declaration]
+ scu_cioc (current_running_cpu_idx, (uint) scuUnitIdx, scu_port_num,
+ ^~~~~~~~
+dps8_ins.c:8594:25: warning: implicit declaration of function ‘scu_smcm’ [-Wimplicit-function-declaration]
+ t_stat rc = scu_smcm ((uint) scuUnitIdx,
+ ^~~~~~~~
+dps8_ins.c:8637:25: warning: implicit declaration of function ‘scu_smic’ [-Wimplicit-function-declaration]
+ t_stat rc = scu_smic ((uint) scuUnitIdx, current_running_cpu_idx,
+ ^~~~~~~~
+dps8_ins.c:8668:25: warning: implicit declaration of function ‘scu_sscr’ [-Wimplicit-function-declaration]
+ t_stat rc = scu_sscr ((uint) scuUnitIdx, current_running_cpu_idx,
+ ^~~~~~~~
--- /dev/null
+/*
+ Copyright 2016 by Charles Anthony
+
+ All rights reserved.
+
+ This software is made available under the terms of the
+ ICU License -- ICU 1.8.1 and later.
+ See the LICENSE file at the top-level directory of this distribution and
+ at https://sourceforge.net/p/dps8m/code/ci/master/tree/LICENSE
+ */
+
+// history debugging
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "dps8.h"
+#include "dps8_sys.h"
+#include "dps8_cpu.h"
+#include "dps8_utils.h"
+#include "hdbg.h"
+
+
+#ifdef HDBG
+#include "dps8_faults.h"
+
+enum hevtType { hevtEmpty = 0, hevtTrace, hevtMRead, hevtMWrite, hevtIWBUpdate, hevtRegs, hevtFault, hevtIntrSet, hevtIntr, hevtReg, hevtPAReg };
+
+struct hevt
+ {
+ enum hevtType type;
+ uint64 time;
+ union
+ {
+ struct
+ {
+ addr_modes_e addrMode;
+ word15 segno;
+ word18 ic;
+ word3 ring;
+ word36 inst;
+ } trace;
+
+ struct
+ {
+ word24 addr;
+ word36 data;
+ } memref;
+
+ struct
+ {
+ _fault faultNumber;
+ _fault_subtype subFault;
+ char faultMsg [64];
+ } fault;
+
+ struct
+ {
+ uint inum;
+ uint cpuUnitIdx;
+ uint scuUnitIdx;
+ } intrSet;
+
+ struct
+ {
+ uint intr_pair_addr;
+ } intr;
+
+ struct
+ {
+ enum hregs_t type;
+ word36 data;
+ } reg;
+
+ struct
+ {
+ enum hregs_t type;
+ struct par_s data;
+ } par;
+ };
+ };
+
+static struct hevt * hevents = NULL;
+static unsigned long hdbgSize = 0;
+static unsigned long hevtPtr = 0;
+static unsigned long hevtMark = 0;
+
+#ifdef THREADZ
+static pthread_mutex_t hdbg_lock = PTHREAD_MUTEX_INITIALIZER;
+#endif
+
+static void createBuffer (void)
+ {
+ if (hevents)
+ {
+ free (hevents);
+ hevents = NULL;
+ }
+ if (! hdbgSize)
+ return;
+ hevents = malloc (sizeof (struct hevt) * hdbgSize);
+ if (! hevents)
+ {
+ sim_printf ("hdbg createBuffer failed\n");
+ return;
+ }
+ memset (hevents, 0, sizeof (struct hevt) * hdbgSize);
+
+ hevtPtr = 0;
+ }
+
+static void hdbg_inc (void)
+ {
+ hevtPtr = (hevtPtr + 1) % hdbgSize;
+ if (hevtMark)
+ {
+ hevtMark --;
+ if (hevtMark == 0)
+ hdbgPrint ();
+ }
+ }
+
+void hdbgTrace (void)
+ {
+#ifdef THREADZ
+ pthread_mutex_lock (& hdbg_lock);
+#endif
+ if (! hevents)
+ goto done;
+#ifdef ISOLTS
+if (current_running_cpu_idx == 0)
+ goto done;
+#endif
+ hevents [hevtPtr] . type = hevtTrace;
+ hevents [hevtPtr] . time = cpu.cycleCnt;
+ hevents [hevtPtr] . trace . addrMode = get_addr_mode ();
+ hevents [hevtPtr] . trace . segno = cpu . PPR.PSR;
+ hevents [hevtPtr] . trace . ic = cpu . PPR.IC;
+ hevents [hevtPtr] . trace . ring = cpu . PPR.PRR;
+ hevents [hevtPtr] . trace . inst = cpu.cu.IWB;
+ hdbg_inc ();
+done: ;
+#ifdef THREADZ
+ pthread_mutex_unlock (& hdbg_lock);
+#endif
+ }
+
+void hdbgMRead (word24 addr, word36 data)
+ {
+#ifdef THREADZ
+ pthread_mutex_lock (& hdbg_lock);
+#endif
+ if (! hevents)
+ goto done;
+#ifdef ISOLTS
+if (current_running_cpu_idx == 0)
+ goto done;
+#endif
+ hevents [hevtPtr] . type = hevtMRead;
+ hevents [hevtPtr] . time = cpu.cycleCnt;
+ hevents [hevtPtr] . memref . addr = addr;
+ hevents [hevtPtr] . memref . data = data;
+ hdbg_inc ();
+done: ;
+#ifdef THREADZ
+ pthread_mutex_unlock (& hdbg_lock);
+#endif
+ }
+
+void hdbgMWrite (word24 addr, word36 data)
+ {
+#ifdef THREADZ
+ pthread_mutex_lock (& hdbg_lock);
+#endif
+ if (! hevents)
+ goto done;
+#ifdef ISOLTS
+if (current_running_cpu_idx == 0)
+ goto done;
+#endif
+ hevents [hevtPtr] . type = hevtMWrite;
+ hevents [hevtPtr] . time = cpu.cycleCnt;
+ hevents [hevtPtr] . memref . addr = addr;
+ hevents [hevtPtr] . memref . data = data;
+ hdbg_inc ();
+done: ;
+#ifdef THREADZ
+ pthread_mutex_unlock (& hdbg_lock);
+#endif
+ }
+
+void hdbgFault (_fault faultNumber, _fault_subtype subFault,
+ const char * faultMsg)
+ {
+#ifdef THREADZ
+ pthread_mutex_lock (& hdbg_lock);
+#endif
+ if (! hevents)
+ goto done;
+#ifdef ISOLTS
+if (current_running_cpu_idx == 0)
+ goto done;
+#endif
+ hevents [hevtPtr] . type = hevtFault;
+ hevents [hevtPtr] . time = cpu.cycleCnt;
+ hevents [hevtPtr] . fault . faultNumber = faultNumber;
+ hevents [hevtPtr] . fault . subFault = subFault;
+ strncpy (hevents [hevtPtr] . fault . faultMsg, faultMsg, 63);
+ hevents [hevtPtr] . fault . faultMsg [63] = 0;
+ hdbg_inc ();
+done: ;
+#ifdef THREADZ
+ pthread_mutex_unlock (& hdbg_lock);
+#endif
+ }
+
+void hdbgIntrSet (uint inum, uint cpuUnitIdx, uint scuUnitIdx)
+ {
+#ifdef THREADZ
+ pthread_mutex_lock (& hdbg_lock);
+#endif
+ if (! hevents)
+ goto done;
+#ifdef ISOLTS
+if (current_running_cpu_idx == 0)
+ goto done;
+#endif
+ hevents [hevtPtr].type = hevtIntrSet;
+ hevents [hevtPtr].time = cpu.cycleCnt;
+ hevents [hevtPtr].intrSet.inum = inum;
+ hevents [hevtPtr].intrSet.cpuUnitIdx = cpuUnitIdx;
+ hevents [hevtPtr].intrSet.scuUnitIdx = scuUnitIdx;
+ hdbg_inc ();
+done: ;
+#ifdef THREADZ
+ pthread_mutex_unlock (& hdbg_lock);
+#endif
+ }
+
+void hdbgIntr (uint intr_pair_addr)
+ {
+#ifdef THREADZ
+ pthread_mutex_lock (& hdbg_lock);
+#endif
+ if (! hevents)
+ goto done;
+#ifdef ISOLTS
+if (current_running_cpu_idx == 0)
+ goto done;
+#endif
+ hevents [hevtPtr].type = hevtIntr;
+ hevents [hevtPtr].time = cpu.cycleCnt;
+ hevents [hevtPtr].intr.intr_pair_addr = intr_pair_addr;
+ hdbg_inc ();
+done: ;
+#ifdef THREADZ
+ pthread_mutex_unlock (& hdbg_lock);
+#endif
+ }
+
+void hdbgReg (enum hregs_t type, word36 data)
+ {
+#ifdef THREADZ
+ pthread_mutex_lock (& hdbg_lock);
+#endif
+ if (! hevents)
+ goto done;
+#ifdef ISOLTS
+if (current_running_cpu_idx == 0)
+ goto done;
+#endif
+ hevents[hevtPtr].type = hevtReg;
+ hevents[hevtPtr].time = cpu.cycleCnt;
+ hevents[hevtPtr].reg.type = type;
+ hevents[hevtPtr].reg.data = data;
+ hdbg_inc ();
+done: ;
+#ifdef THREADZ
+ pthread_mutex_unlock (& hdbg_lock);
+#endif
+ }
+
+
+void hdbgPAReg (enum hregs_t type, struct par_s * data)
+ {
+#ifdef THREADZ
+ pthread_mutex_lock (& hdbg_lock);
+#endif
+ if (! hevents)
+ goto done;
+#ifdef ISOLTS
+if (current_running_cpu_idx == 0)
+ goto done;
+#endif
+ hevents[hevtPtr].type = hevtPAReg;
+ hevents[hevtPtr].time = cpu.cycleCnt;
+ hevents[hevtPtr].par.type = type;
+ hevents[hevtPtr].par.data = * data;
+ hdbg_inc ();
+done: ;
+#ifdef THREADZ
+ pthread_mutex_unlock (& hdbg_lock);
+#endif
+ }
+
+static FILE * hdbgOut = NULL;
+
+static void printMRead (struct hevt * p)
+ {
+ fprintf (hdbgOut, "DBG(%"PRId64")> CPU FINAL: Read %08o %012"PRIo64"\n",
+ p -> time,
+ p -> memref . addr, p -> memref . data);
+ }
+
+static void printMWrite (struct hevt * p)
+ {
+ fprintf (hdbgOut, "DBG(%"PRId64")> CPU FINAL: Write %08o %012"PRIo64"\n",
+ p -> time,
+ p -> memref . addr, p -> memref . data);
+ }
+
+static void printTrace (struct hevt * p)
+ {
+ char buf [256];
+ if (p -> trace . addrMode == ABSOLUTE_mode)
+ {
+ fprintf (hdbgOut, "DBG(%"PRId64")> CPU TRACE: %06o %o %012"PRIo64" (%s)\n",
+ p -> time,
+ p -> trace . ic, p -> trace . ring,
+ p -> trace . inst, disassemble (buf, p -> trace . inst));
+ }
+ else
+ {
+ fprintf (hdbgOut, "DBG(%"PRId64")> CPU TRACE: %05o:%06o %o %012"PRIo64" (%s)\n",
+ p -> time, p -> trace . segno,
+ p -> trace . ic, p -> trace . ring,
+ p -> trace . inst, disassemble (buf, p -> trace . inst));
+ }
+ }
+
+static void printFault (struct hevt * p)
+ {
+ fprintf (hdbgOut, "DBG(%"PRId64")> CPU FAULT: Fault %d(0%o), sub %"PRId64"(0%"PRIo64"), '%s'\n",
+ p -> time,
+ p -> fault.faultNumber, p -> fault.faultNumber,
+ p -> fault.subFault.bits, p -> fault.subFault.bits,
+ p -> fault.faultMsg);
+ }
+
+static void printIntrSet (struct hevt * p)
+ {
+ fprintf (hdbgOut, "DBG(%"PRId64")> CPU INTR_SET: Number %d(0%o), CPU %u SCU %u\n",
+ p -> time,
+ p -> intrSet.inum, p -> intrSet.inum,
+ p -> intrSet.cpuUnitIdx,
+ p -> intrSet.scuUnitIdx);
+ }
+
+static void printIntr (struct hevt * p)
+ {
+ fprintf (hdbgOut, "DBG(%"PRId64")> CPU INTR: Interrupt pair address %o\n",
+ p -> time,
+ p -> intr.intr_pair_addr);
+ }
+
+static char * regNames [] =
+ {
+ "A ",
+ "Q ",
+ "X0", "X1", "X2", "X3", "X4", "X5", "X6", "X7",
+ "AR0", "AR1", "AR2", "AR3", "AR4", "AR5", "AR6", "AR7",
+ "PR0", "PR1", "PR2", "PR3", "PR4", "PR5", "PR6", "PR7"
+ };
+
+static void printReg (struct hevt * p)
+ {
+ if (p->reg.type >= hreg_X0 && p->reg.type <= hreg_X7)
+ fprintf (hdbgOut, "DBG(%"PRId64")> CPU REG: %s %06"PRIo64"\n",
+ p->time,
+ regNames[p->reg.type],
+ p->reg.data);
+ else
+ fprintf (hdbgOut, "DBG(%"PRId64")> CPU REG: %s %012"PRIo64"\n",
+ p->time,
+ regNames[p->reg.type],
+ p->reg.data);
+ }
+
+static void printPAReg (struct hevt * p)
+ {
+ if (p->reg.type >= hreg_PR0 && p->reg.type <= hreg_PR7)
+ fprintf (hdbgOut, "DBG(%"PRId64")> CPU REG: %s "
+ "%05o:%06o BIT %2o RNR %o\n",
+ p->time,
+ regNames[p->reg.type],
+ p->par.data.SNR,
+ p->par.data.WORDNO,
+ p->par.data.PR_BITNO,
+ p->par.data.RNR);
+ else
+ fprintf (hdbgOut, "DBG(%"PRId64")> CPU REG: %s "
+ "%05o:%06o CHAR %o BIT %2o RNR %o\n",
+ p->time,
+ regNames[p->reg.type],
+ p->par.data.SNR,
+ p->par.data.WORDNO,
+ p->par.data.AR_CHAR,
+ p->par.data.AR_BITNO,
+ p->par.data.RNR);
+ }
+
+void hdbgPrint (void)
+ {
+#ifdef THREADZ
+ pthread_mutex_lock (& hdbg_lock);
+#endif
+ if (! hevents)
+ goto done;
+ hdbgOut = fopen ("hdbg.list", "w");
+ if (! hdbgOut)
+ {
+ sim_printf ("can't open hdbg.list\n");
+ goto done;
+ }
+ time_t curtime;
+ time (& curtime);
+ fprintf (hdbgOut, "%s\n", ctime (& curtime));
+
+ for (unsigned long p = 0; p < hdbgSize; p ++)
+ {
+ unsigned long q = (hevtPtr + p) % hdbgSize;
+ struct hevt * evtp = hevents + q;
+ switch (evtp -> type)
+ {
+ case hevtEmpty:
+ break;
+
+ case hevtTrace:
+ printTrace (evtp);
+ break;
+
+ case hevtMRead:
+ printMRead (evtp);
+ break;
+
+ case hevtMWrite:
+ printMWrite (evtp);
+ break;
+
+ case hevtFault:
+ printFault (evtp);
+ break;
+
+ case hevtIntrSet:
+ printIntrSet (evtp);
+ break;
+
+ case hevtIntr:
+ printIntr (evtp);
+ break;
+
+ case hevtReg:
+ printReg (evtp);
+ break;
+
+ case hevtPAReg:
+ printPAReg (evtp);
+ break;
+
+ default:
+ fprintf (hdbgOut, "hdbgPrint ? %d\n", evtp -> type);
+ break;
+ }
+ }
+ fclose (hdbgOut);
+ int fd = open ("M.dump", O_WRONLY | O_CREAT, 0660);
+ if (fd == -1)
+ {
+ sim_printf ("can't open M.dump\n");
+ goto done;
+ }
+ // cast discards volatile
+ /* ssize_t n = */ write (fd, (const void *) M, MEMSIZE * sizeof (word36));
+ close (fd);
+done: ;
+#ifdef THREADZ
+ pthread_mutex_unlock (& hdbg_lock);
+#endif
+ }
+
+void hdbg_mark (void)
+ {
+#ifdef THREADZ
+ pthread_mutex_lock (& hdbg_lock);
+#endif
+ hevtMark = hdbgSize;
+ sim_printf ("hdbg mark set to %ld\n", hevtMark);
+#ifdef THREADZ
+ pthread_mutex_unlock (& hdbg_lock);
+#endif
+ }
+
+// set buffer size
+t_stat hdbg_size (UNUSED int32 arg, const char * buf)
+ {
+#ifdef THREADZ
+ pthread_mutex_lock (& hdbg_lock);
+#endif
+ hdbgSize = strtoul (buf, NULL, 0);
+ sim_printf ("hdbg size set to %ld\n", hdbgSize);
+ createBuffer ();
+#ifdef THREADZ
+ pthread_mutex_unlock (& hdbg_lock);
+#endif
+ return SCPE_OK;
+ }
+
+t_stat hdbg_print (UNUSED int32 arg, const char * buf)
+ {
+ hdbgPrint ();
+ return SCPE_OK;
+ }
+#else
+t_stat hdbg_size (UNUSED int32 arg, UNUSED const char * buf)
+ {
+ sim_printf ("hdbg not enabled; ignoring\n");
+ return SCPE_OK;
+ }
+#endif // HDBG
--- /dev/null
+/*
+ Copyright 2016 by Charles Anthony
+
+ All rights reserved.
+
+ This software is made available under the terms of the
+ ICU License -- ICU 1.8.1 and later.
+ See the LICENSE file at the top-level directory of this distribution and
+ at https://sourceforge.net/p/dps8m/code/ci/master/tree/LICENSE
+ */
+#ifndef HDBG_H
+#define HDBG_H
+
+void hdbg_mark (void);
+t_stat hdbg_size (int32 arg, UNUSED const char * buf);
+t_stat hdbg_print (int32 arg, UNUSED const char * buf);
+#ifdef HDBG
+void hdbgTrace (void);
+void hdbgPrint (void);
+void hdbgMRead (word24 addr, word36 data);
+void hdbgMWrite (word24 addr, word36 data);
+void hdbgFault (_fault faultNumber, _fault_subtype subFault,
+ const char * faultMsg);
+void hdbgIntrSet (uint inum, uint cpuUnitIdx, uint scuUnitIdx);
+void hdbgIntr (uint intr_pair_addr);
+enum hregs_t
+ {
+ hreg_A,
+ hreg_Q,
+ hreg_X0, hreg_X1, hreg_X2, hreg_X3, hreg_X4, hreg_X5, hreg_X6, hreg_X7,
+ hreg_AR0, hreg_AR1, hreg_AR2, hreg_AR3, hreg_AR4, hreg_AR5, hreg_AR6, hreg_AR7,
+ hreg_PR0, hreg_PR1, hreg_PR2, hreg_PR3, hreg_PR4, hreg_PR5, hreg_PR6, hreg_PR7
+ };
+void hdbgReg (enum hregs_t type, word36 data);
+struct par_s;
+void hdbgPAReg (enum hregs_t type, struct par_s * data);
+#endif
+
+#ifdef HDBG
+#define HDBGMRead(a, d) hdbgMRead (a, d)
+#define HDBGMWrite(a, d) hdbgMWrite (a, d)
+#define HDBGRegA() hdbgReg (hreg_A, cpu.rA)
+#define HDBGRegQ() hdbgReg (hreg_Q, cpu.rQ)
+#define HDBGRegX(i) hdbgReg (hreg_X0+(i), (word36) cpu.rX[i])
+#define HDBGRegPR(i) hdbgPAReg (hreg_PR0+(i), & cpu.PAR[i]);
+#define HDBGRegAR(i) hdbgPAReg (hreg_AR0+(i), & cpu.PAR[i]);
+#else
+#define HDBGMRead(a, d)
+#define HDBGMWrite(a, d)
+#define HDBGRegA()
+#define HDBGRegQ()
+#define HDBGRegX(i)
+#define HDBGRegPR(i)
+#define HDBGRegAR(i)
+#endif
+#endif
--- /dev/null
+#!/bin/sh
+CFLAGS="-g -O3 -std=c99 -U__STRICT_ANSI__ -D_GNU_SOURCE -DUSE_READER_THREAD -DUSE_INT64 -DNO_EV_POLL -Wno-unused-result"
+#gcc $CFLAGS -c dps8_cpu.c
+gcc $CFLAGS -o dps8 *.c -lm
--- /dev/null
+/* scp.h: simulator control program headers\r
+\r
+ Copyright (c) 1993-2009, Robert M Supnik\r
+\r
+ Permission is hereby granted, free of charge, to any person obtaining a\r
+ copy of this software and associated documentation files (the "Software"),\r
+ to deal in the Software without restriction, including without limitation\r
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,\r
+ and/or sell copies of the Software, and to permit persons to whom the\r
+ Software is furnished to do so, subject to the following conditions:\r
+\r
+ The above copyright notice and this permission notice shall be included in\r
+ all copies or substantial portions of the Software.\r
+\r
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\r
+ ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\r
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
+\r
+ Except as contained in this notice, the name of Robert M Supnik shall not\r
+ be used in advertising or otherwise to promote the sale, use or other dealings\r
+ in this Software without prior written authorization from Robert M Supnik.\r
+\r
+ 05-Dec-10 MP Added macro invocation of sim_debug \r
+ 09-Aug-06 JDB Added assign_device and deassign_device\r
+ 14-Jul-06 RMS Added sim_activate_abs\r
+ 06-Jan-06 RMS Added fprint_stopped_gen\r
+ Changed arg type in sim_brk_test\r
+ 07-Feb-05 RMS Added ASSERT command\r
+ 09-Sep-04 RMS Added reset_all_p\r
+ 14-Feb-04 RMS Added debug prototypes (from Dave Hittner)\r
+ 02-Jan-04 RMS Split out from SCP\r
+*/\r
+\r
+#ifndef SIM_SCP_H_\r
+#define SIM_SCP_H_ 0\r
+\r
+#ifdef __cplusplus\r
+extern "C" {\r
+#endif\r
+\r
+/* run_cmd parameters */\r
+\r
+#define RU_RUN 0 /* run */\r
+#define RU_GO 1 /* go */\r
+#define RU_STEP 2 /* step */\r
+#define RU_NEXT 3 /* step or step/over */\r
+#define RU_CONT 4 /* continue */\r
+#define RU_BOOT 5 /* boot */\r
+\r
+/* exdep_cmd parameters */\r
+\r
+#define EX_D 0 /* deposit */\r
+#define EX_E 1 /* examine */\r
+#define EX_I 2 /* interactive */\r
+\r
+/* brk_cmd parameters */\r
+\r
+#define SSH_ST 0 /* set */\r
+#define SSH_SH 1 /* show */\r
+#define SSH_CL 2 /* clear */\r
+\r
+/* get_sim_opt parameters */\r
+\r
+#define CMD_OPT_SW 001 /* switches */\r
+#define CMD_OPT_OF 002 /* output file */\r
+#define CMD_OPT_SCH 004 /* search */\r
+#define CMD_OPT_DFT 010 /* defaults */\r
+\r
+/* Command processors */\r
+\r
+t_stat reset_cmd (int32 flag, CONST char *ptr);\r
+t_stat exdep_cmd (int32 flag, CONST char *ptr);\r
+t_stat eval_cmd (int32 flag, CONST char *ptr);\r
+t_stat load_cmd (int32 flag, CONST char *ptr);\r
+t_stat run_cmd (int32 flag, CONST char *ptr);\r
+void run_cmd_message (const char *unechod_cmdline, t_stat r);\r
+t_stat attach_cmd (int32 flag, CONST char *ptr);\r
+t_stat detach_cmd (int32 flag, CONST char *ptr);\r
+t_stat assign_cmd (int32 flag, CONST char *ptr);\r
+t_stat deassign_cmd (int32 flag, CONST char *ptr);\r
+t_stat save_cmd (int32 flag, CONST char *ptr);\r
+t_stat restore_cmd (int32 flag, CONST char *ptr);\r
+t_stat exit_cmd (int32 flag, CONST char *ptr);\r
+t_stat set_cmd (int32 flag, CONST char *ptr);\r
+t_stat show_cmd (int32 flag, CONST char *ptr);\r
+t_stat set_default_cmd (int32 flg, CONST char *cptr);\r
+t_stat pwd_cmd (int32 flg, CONST char *cptr);\r
+t_stat dir_cmd (int32 flg, CONST char *cptr);\r
+t_stat type_cmd (int32 flg, CONST char *cptr);\r
+t_stat brk_cmd (int32 flag, CONST char *ptr);\r
+t_stat do_cmd (int32 flag, CONST char *ptr);\r
+t_stat goto_cmd (int32 flag, CONST char *ptr);\r
+t_stat return_cmd (int32 flag, CONST char *ptr);\r
+t_stat shift_cmd (int32 flag, CONST char *ptr);\r
+t_stat call_cmd (int32 flag, CONST char *ptr);\r
+t_stat on_cmd (int32 flag, CONST char *ptr);\r
+t_stat noop_cmd (int32 flag, CONST char *ptr);\r
+t_stat assert_cmd (int32 flag, CONST char *ptr);\r
+t_stat send_cmd (int32 flag, CONST char *ptr);\r
+t_stat expect_cmd (int32 flag, CONST char *ptr);\r
+t_stat help_cmd (int32 flag, CONST char *ptr);\r
+t_stat screenshot_cmd (int32 flag, CONST char *ptr);\r
+t_stat spawn_cmd (int32 flag, CONST char *ptr);\r
+t_stat echo_cmd (int32 flag, CONST char *ptr);\r
+\r
+/* Allow compiler to help validate printf style format arguments */\r
+#if !defined __GNUC__\r
+#define GCC_FMT_ATTR(n, m)\r
+#endif\r
+#if !defined(GCC_FMT_ATTR)\r
+#define GCC_FMT_ATTR(n, m) __attribute__ ((format (__printf__, n, m)))\r
+#endif\r
+\r
+/* Utility routines */\r
+\r
+t_stat sim_process_event (void);\r
+t_stat sim_activate (UNIT *uptr, int32 interval);\r
+t_stat _sim_activate (UNIT *uptr, int32 interval);\r
+t_stat sim_activate_abs (UNIT *uptr, int32 interval);\r
+t_stat sim_activate_notbefore (UNIT *uptr, int32 rtime);\r
+t_stat sim_activate_after (UNIT *uptr, uint32 usecs_walltime);\r
+t_stat _sim_activate_after (UNIT *uptr, uint32 usecs_walltime);\r
+t_stat sim_activate_after_abs (UNIT *uptr, uint32 usecs_walltime);\r
+t_stat _sim_activate_after_abs (UNIT *uptr, uint32 usecs_walltime);\r
+t_stat sim_cancel (UNIT *uptr);\r
+t_bool sim_is_active (UNIT *uptr);\r
+int32 sim_activate_time (UNIT *uptr);\r
+t_stat sim_run_boot_prep (int32 flag);\r
+double sim_gtime (void);\r
+uint32 sim_grtime (void);\r
+int32 sim_qcount (void);\r
+t_stat attach_unit (UNIT *uptr, CONST char *cptr);\r
+t_stat detach_unit (UNIT *uptr);\r
+t_stat assign_device (DEVICE *dptr, const char *cptr);\r
+t_stat deassign_device (DEVICE *dptr);\r
+t_stat reset_all (uint32 start_device);\r
+t_stat reset_all_p (uint32 start_device);\r
+const char *sim_dname (DEVICE *dptr);\r
+const char *sim_uname (UNIT *dptr);\r
+t_stat get_yn (const char *ques, t_stat deflt);\r
+int sim_isspace (char c);\r
+int sim_islower (char c);\r
+int sim_isalpha (char c);\r
+int sim_isprint (char c);\r
+int sim_isdigit (char c);\r
+int sim_isgraph (char c);\r
+int sim_isalnum (char c);\r
+int sim_strncasecmp (const char *string1, const char *string2, size_t len);\r
+CONST char *get_sim_opt (int32 opt, CONST char *cptr, t_stat *st);\r
+const char *put_switches (char *buf, size_t bufsize, uint32 sw);\r
+CONST char *get_glyph (const char *iptr, char *optr, char mchar);\r
+CONST char *get_glyph_nc (const char *iptr, char *optr, char mchar);\r
+CONST char *get_glyph_quoted (const char *iptr, char *optr, char mchar);\r
+CONST char *get_glyph_cmd (const char *iptr, char *optr);\r
+t_value get_uint (const char *cptr, uint32 radix, t_value max, t_stat *status);\r
+CONST char *get_range (DEVICE *dptr, CONST char *cptr, t_addr *lo, t_addr *hi,\r
+ uint32 rdx, t_addr max, char term);\r
+t_stat sim_decode_quoted_string (const char *iptr, uint8 *optr, uint32 *osize);\r
+char *sim_encode_quoted_string (const uint8 *iptr, uint32 size);\r
+void fprint_buffer_string (FILE *st, const uint8 *buf, uint32 size);\r
+t_value strtotv (CONST char *cptr, CONST char **endptr, uint32 radix);\r
+int Fprintf (FILE *f, const char *fmt, ...) GCC_FMT_ATTR(2, 3);\r
+t_stat sim_set_memory_load_file (const unsigned char *data, size_t size);\r
+int Fgetc (FILE *f);\r
+t_stat fprint_val (FILE *stream, t_value val, uint32 rdx, uint32 wid, uint32 fmt);\r
+t_stat sprint_val (char *buf, t_value val, uint32 rdx, uint32 wid, uint32 fmt);\r
+t_stat sim_print_val (t_value val, uint32 radix, uint32 width, uint32 format);\r
+const char *sim_fmt_secs (double seconds);\r
+const char *sprint_capac (DEVICE *dptr, UNIT *uptr);\r
+char *read_line (char *cptr, int32 size, FILE *stream);\r
+void fprint_reg_help (FILE *st, DEVICE *dptr);\r
+void fprint_set_help (FILE *st, DEVICE *dptr);\r
+void fprint_show_help (FILE *st, DEVICE *dptr);\r
+CTAB *find_cmd (const char *gbuf);\r
+DEVICE *find_dev (const char *ptr);\r
+DEVICE *find_unit (const char *ptr, UNIT **uptr);\r
+DEVICE *find_dev_from_unit (UNIT *uptr);\r
+t_stat sim_register_internal_device (DEVICE *dptr);\r
+void sim_sub_args (char *in_str, size_t in_str_size, char *do_arg[]);\r
+REG *find_reg (CONST char *ptr, CONST char **optr, DEVICE *dptr);\r
+CTAB *find_ctab (CTAB *tab, const char *gbuf);\r
+C1TAB *find_c1tab (C1TAB *tab, const char *gbuf);\r
+SHTAB *find_shtab (SHTAB *tab, const char *gbuf);\r
+t_stat get_aval (t_addr addr, DEVICE *dptr, UNIT *uptr);\r
+BRKTAB *sim_brk_fnd (t_addr loc);\r
+uint32 sim_brk_test (t_addr bloc, uint32 btyp);\r
+void sim_brk_clrspc (uint32 spc, uint32 btyp);\r
+void sim_brk_npc (uint32 cnt);\r
+void sim_brk_setact (const char *action);\r
+const char *sim_brk_message(void);\r
+t_stat sim_send_input (SEND *snd, uint8 *data, size_t size, uint32 after, uint32 delay);\r
+t_stat sim_show_send_input (FILE *st, const SEND *snd);\r
+t_bool sim_send_poll_data (SEND *snd, t_stat *stat);\r
+t_stat sim_send_clear (SEND *snd);\r
+t_stat sim_set_expect (EXPECT *exp, CONST char *cptr);\r
+t_stat sim_set_noexpect (EXPECT *exp, const char *cptr);\r
+t_stat sim_exp_set (EXPECT *exp, const char *match, int32 cnt, uint32 after, int32 switches, const char *act);\r
+t_stat sim_exp_clr (EXPECT *exp, const char *match);\r
+t_stat sim_exp_clrall (EXPECT *exp);\r
+t_stat sim_exp_show (FILE *st, CONST EXPECT *exp, const char *match);\r
+t_stat sim_exp_showall (FILE *st, const EXPECT *exp);\r
+t_stat sim_exp_check (EXPECT *exp, uint8 data);\r
+CONST char *match_ext (CONST char *fnam, const char *ext);\r
+t_stat show_version (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);\r
+t_stat set_dev_debug (DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);\r
+t_stat show_dev_debug (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);\r
+const char *sim_error_text (t_stat stat);\r
+t_stat sim_string_to_stat (const char *cptr, t_stat *cond);\r
+t_stat sim_cancel_step (void);\r
+void sim_printf (const char *fmt, ...) GCC_FMT_ATTR(1, 2);\r
+void sim_perror (const char *msg);\r
+void _sim_err (const char* fmt, ...);\r
+t_stat sim_messagef (t_stat stat, const char *fmt, ...) GCC_FMT_ATTR(2, 3);\r
+void sim_data_trace(DEVICE *dptr, UNIT *uptr, const uint8 *data, const char *position, size_t len, const char *txt, uint32 reason);\r
+void sim_debug_bits_hdr (uint32 dbits, DEVICE* dptr, const char *header, \r
+ BITFIELD* bitdefs, uint32 before, uint32 after, int terminate);\r
+void sim_debug_bits (uint32 dbits, DEVICE* dptr, BITFIELD* bitdefs,\r
+ uint32 before, uint32 after, int terminate);\r
+#if defined (__DECC) && defined (__VMS) && (defined (__VAX) || (__DECC_VER < 60590001))\r
+#define CANT_USE_MACRO_VA_ARGS 1\r
+#endif\r
+#if defined(__cplusplus)\r
+#ifdef CANT_USE_MACRO_VA_ARGS\r
+#define _sim_debug sim_debug\r
+void sim_debug (uint32 dbits, void* dptr, const char *fmt, ...) GCC_FMT_ATTR(3, 4);\r
+#else\r
+void _sim_debug (uint32 dbits, void* dptr, const char *fmt, ...) GCC_FMT_ATTR(3, 4);\r
+#define sim_debug(dbits, dptr, ...) do { if (sim_deb && dptr && ((dptr)->dctrl & dbits)) _sim_debug (dbits, dptr, __VA_ARGS__);} while (0)\r
+#endif\r
+#else\r
+#ifdef CANT_USE_MACRO_VA_ARGS\r
+#define _sim_debug sim_debug\r
+void sim_debug (uint32 dbits, DEVICE* dptr, const char *fmt, ...) GCC_FMT_ATTR(3, 4);\r
+#else\r
+void _sim_debug (uint32 dbits, DEVICE* dptr, const char *fmt, ...) GCC_FMT_ATTR(3, 4);\r
+#define sim_debug(dbits, dptr, ...) do { if (sim_deb && dptr && ((dptr)->dctrl & dbits)) _sim_debug (dbits, dptr, __VA_ARGS__);} while (0)\r
+#endif\r
+#endif\r
+void fprint_stopped_gen (FILE *st, t_stat v, REG *pc, DEVICE *dptr);\r
+#define SCP_HELP_FLAT (1u << 31) /* Force flat help when prompting is not possible */\r
+#define SCP_HELP_ONECMD (1u << 30) /* Display one topic, do not prompt */\r
+#define SCP_HELP_ATTACH (1u << 29) /* Top level topic is ATTACH help */\r
+t_stat scp_help (FILE *st, DEVICE *dptr,\r
+ UNIT *uptr, int32 flag, const char *help, const char *cptr, ...);\r
+t_stat scp_vhelp (FILE *st, DEVICE *dptr,\r
+ UNIT *uptr, int32 flag, const char *help, const char *cptr, va_list ap);\r
+t_stat scp_helpFromFile (FILE *st, DEVICE *dptr,\r
+ UNIT *uptr, int32 flag, const char *help, const char *cptr, ...);\r
+t_stat scp_vhelpFromFile (FILE *st, DEVICE *dptr,\r
+ UNIT *uptr, int32 flag, const char *help, const char *cptr, va_list ap);\r
+\r
+/* Global data */\r
+\r
+extern DEVICE *sim_dflt_dev;\r
+extern int32 sim_interval;\r
+extern int32 sim_switches;\r
+extern int32 sim_quiet;\r
+extern int32 sim_step;\r
+extern t_stat sim_last_cmd_stat; /* Command Status */\r
+extern FILE *sim_log; /* log file */\r
+extern FILEREF *sim_log_ref; /* log file file reference */\r
+extern FILE *sim_deb; /* debug file */\r
+extern FILEREF *sim_deb_ref; /* debug file file reference */\r
+extern int32 sim_deb_switches; /* debug display flags */\r
+extern struct timespec sim_deb_basetime; /* debug base time for relative time output */\r
+extern DEVICE **sim_internal_devices;\r
+extern uint32 sim_internal_device_count;\r
+extern UNIT *sim_clock_queue;\r
+extern int32 sim_is_running;\r
+extern t_bool sim_processing_event; /* Called from sim_process_event */\r
+extern char *sim_prompt; /* prompt string */\r
+extern const char *sim_savename; /* Simulator Name used in Save/Restore files */\r
+extern t_value *sim_eval;\r
+extern volatile int32 stop_cpu;\r
+extern uint32 sim_brk_types; /* breakpoint info */\r
+extern uint32 sim_brk_dflt;\r
+extern uint32 sim_brk_summ;\r
+extern uint32 sim_brk_match_type;\r
+extern t_addr sim_brk_match_addr;\r
+extern BRKTYPTAB *sim_brk_type_desc; /* type descriptions */\r
+extern FILE *stdnul;\r
+extern t_bool sim_asynch_enabled;\r
+#if defined(SIM_ASYNCH_IO)\r
+int sim_aio_update_queue (void);\r
+void sim_aio_activate (ACTIVATE_API caller, UNIT *uptr, int32 event_time);\r
+#endif\r
+\r
+/* VM interface */\r
+\r
+extern char sim_name[];\r
+extern DEVICE *sim_devices[];\r
+extern REG *sim_PC;\r
+extern const char *sim_stop_messages[];\r
+extern t_stat sim_instr (void);\r
+extern t_stat sim_load (FILE *ptr, CONST char *cptr, CONST char *fnam, int flag);\r
+extern int32 sim_emax;\r
+extern t_stat fprint_sym (FILE *ofile, t_addr addr, t_value *val,\r
+ UNIT *uptr, int32 sw);\r
+extern t_stat parse_sym (CONST char *cptr, t_addr addr, UNIT *uptr, t_value *val,\r
+ int32 sw);\r
+\r
+/* The per-simulator init routine is a weak global that defaults to NULL\r
+ The other per-simulator pointers can be overrriden by the init routine */\r
+\r
+WEAK extern void (*sim_vm_init) (void);\r
+extern char *(*sim_vm_read) (char *ptr, int32 size, FILE *stream);\r
+extern void (*sim_vm_post) (t_bool from_scp);\r
+extern CTAB *sim_vm_cmd;\r
+extern void (*sim_vm_sprint_addr) (char *buf, DEVICE *dptr, t_addr addr);\r
+extern void (*sim_vm_fprint_addr) (FILE *st, DEVICE *dptr, t_addr addr);\r
+extern t_addr (*sim_vm_parse_addr) (DEVICE *dptr, CONST char *cptr, CONST char **tptr);\r
+extern t_bool (*sim_vm_fprint_stopped) (FILE *st, t_stat reason);\r
+extern t_value (*sim_vm_pc_value) (void);\r
+extern t_bool (*sim_vm_is_subroutine_call) (t_addr **ret_addrs);\r
+\r
+#ifdef __cplusplus\r
+}\r
+#endif\r
+\r
+#endif\r
--- /dev/null
+/* sim_defs.h: simulator definitions\r
+\r
+ Copyright (c) 1993-2008, Robert M Supnik\r
+\r
+ Permission is hereby granted, free of charge, to any person obtaining a\r
+ copy of this software and associated documentation files (the "Software"),\r
+ to deal in the Software without restriction, including without limitation\r
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,\r
+ and/or sell copies of the Software, and to permit persons to whom the\r
+ Software is furnished to do so, subject to the following conditions:\r
+\r
+ The above copyright notice and this permission notice shall be included in\r
+ all copies or substantial portions of the Software.\r
+\r
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\r
+ ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\r
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
+\r
+ Except as contained in this notice, the name of Robert M Supnik shall not be\r
+ used in advertising or otherwise to promote the sale, use or other dealings\r
+ in this Software without prior written authorization from Robert M Supnik.\r
+\r
+ 05-Jan-11 MP Added Asynch I/O support\r
+ 18-Jan-11 MP Added log file reference count support\r
+ 21-Jul-08 RMS Removed inlining support\r
+ 28-May-08 RMS Added inlining support\r
+ 28-Jun-07 RMS Added IA64 VMS support (from Norm Lastovica)\r
+ 18-Jun-07 RMS Added UNIT_IDLE flag\r
+ 18-Mar-07 RMS Added UNIT_TEXT flag\r
+ 07-Mar-07 JDB Added DEBUG_PRJ macro\r
+ 18-Oct-06 RMS Added limit check for clock synchronized keyboard waits\r
+ 13-Jul-06 RMS Guarantee CBUFSIZE is at least 256\r
+ 07-Jan-06 RMS Added support for breakpoint spaces\r
+ Added REG_FIT flag\r
+ 16-Aug-05 RMS Fixed C++ declaration and cast problems\r
+ 11-Mar-05 RMS Moved 64b data type definitions outside USE_INT64\r
+ 07-Feb-05 RMS Added assertion fail stop\r
+ 05-Nov-04 RMS Added support for SHOW opt=val\r
+ 20-Oct-04 RMS Converted all base types to typedefs\r
+ 21-Sep-04 RMS Added switch to flag stop message printout\r
+ 06-Feb-04 RMS Moved device and unit user flags fields (V3.2)\r
+ RMS Added REG_VMAD\r
+ 29-Dec-03 RMS Added output stall status\r
+ 15-Jun-03 RMS Added register flag REG_VMIO\r
+ 23-Apr-03 RMS Revised for 32b/64b t_addr\r
+ 14-Mar-03 RMS Lengthened default serial output wait\r
+ 31-Mar-03 RMS Added u5, u6 fields\r
+ 18-Mar-03 RMS Added logical name support\r
+ Moved magtape definitions to sim_tape.h\r
+ Moved breakpoint definitions from scp.c\r
+ 03-Mar-03 RMS Added sim_fsize\r
+ 08-Feb-03 RMS Changed sim_os_sleep to void, added match_ext\r
+ 05-Jan-03 RMS Added hidden switch definitions, device dyn memory support,\r
+ parameters for function pointers, case sensitive SET support\r
+ 22-Dec-02 RMS Added break flag\r
+ 08-Oct-02 RMS Increased simulator error code space\r
+ Added Telnet errors\r
+ Added end of medium support\r
+ Added help messages to CTAB\r
+ Added flag and context fields to DEVICE\r
+ Added restore flag masks\r
+ Revised 64b definitions\r
+ 02-May-02 RMS Removed log status codes\r
+ 22-Apr-02 RMS Added magtape record length error\r
+ 30-Dec-01 RMS Generalized timer package, added circular arrays\r
+ 07-Dec-01 RMS Added breakpoint package\r
+ 01-Dec-01 RMS Added read-only unit support, extended SET/SHOW features,\r
+ improved error messages\r
+ 24-Nov-01 RMS Added unit-based registers\r
+ 27-Sep-01 RMS Added queue count prototype\r
+ 17-Sep-01 RMS Removed multiple console support\r
+ 07-Sep-01 RMS Removed conditional externs on function prototypes\r
+ 31-Aug-01 RMS Changed int64 to t_int64 for Windoze\r
+ 17-Jul-01 RMS Added additional function prototypes\r
+ 27-May-01 RMS Added multiple console support\r
+ 15-May-01 RMS Increased string buffer size\r
+ 25-Feb-01 RMS Revisions for V2.6\r
+ 15-Oct-00 RMS Editorial revisions for V2.5\r
+ 11-Jul-99 RMS Added unsigned int data types\r
+ 14-Apr-99 RMS Converted t_addr to unsigned\r
+ 04-Oct-98 RMS Additional definitions for V2.4\r
+\r
+ The interface between the simulator control package (SCP) and the\r
+ simulator consists of the following routines and data structures\r
+\r
+ sim_name simulator name string\r
+ sim_devices[] array of pointers to simulated devices\r
+ sim_PC pointer to saved PC register descriptor\r
+ sim_interval simulator interval to next event\r
+ sim_stop_messages[] array of pointers to stop messages\r
+ sim_instr() instruction execution routine\r
+ sim_load() binary loader routine\r
+ sim_emax maximum number of words in an instruction\r
+\r
+ In addition, the simulator must supply routines to print and parse\r
+ architecture specific formats\r
+\r
+ print_sym print symbolic output\r
+ parse_sym parse symbolic input\r
+*/\r
+\r
+#ifndef SIM_DEFS_H_\r
+#define SIM_DEFS_H_ 0\r
+\r
+#include <stddef.h>\r
+#include <stdlib.h>\r
+#include <stdio.h>\r
+#if defined(_MSC_VER) && (_MSC_VER < 1900)\r
+#define snprintf _snprintf /* poor man's snprintf which will work most of the time but has different return value */\r
+#endif\r
+#include <stdarg.h>\r
+#include <string.h>\r
+#include <errno.h>\r
+#include <limits.h>\r
+\r
+#ifdef _WIN32\r
+#include <winsock2.h>\r
+#undef PACKED /* avoid macro name collision */\r
+#undef ERROR /* avoid macro name collision */\r
+#undef MEM_MAPPED /* avoid macro name collision */\r
+#include <process.h>\r
+#endif\r
+\r
+#ifdef USE_REGEX\r
+#undef USE_REGEX\r
+#endif\r
+#if defined(HAVE_PCREPOSIX_H)\r
+#include <pcreposix.h>\r
+#define USE_REGEX 1\r
+#elif defined(HAVE_REGEX_H)\r
+#include <regex.h>\r
+#define USE_REGEX 1\r
+#endif\r
+\r
+#ifdef __cplusplus\r
+extern "C" {\r
+#endif\r
+\r
+/* avoid macro names collisions */\r
+#ifdef MAX\r
+#undef MAX\r
+#endif\r
+#ifdef MIN\r
+#undef MIN\r
+#endif\r
+#ifdef PMASK\r
+#undef PMASK\r
+#endif\r
+#ifdef RS\r
+#undef RS\r
+#endif\r
+#ifdef PAGESIZE\r
+#undef PAGESIZE\r
+#endif\r
+\r
+\r
+#ifndef TRUE\r
+#define TRUE 1\r
+#define FALSE 0\r
+#endif\r
+\r
+/* SCP API shim.\r
+\r
+ The SCP API for version 4.0 introduces a number of "pointer-to-const"\r
+ parameter qualifiers that were not present in the 3.x versions. To maintain\r
+ compatibility with the earlier versions, the new qualifiers are expressed as\r
+ "CONST" rather than "const". This allows macro removal of the qualifiers\r
+ when compiling for SIMH 3.x.\r
+*/\r
+#ifndef CONST\r
+#define CONST const\r
+#endif\r
+\r
+/* Length specific integer declarations */\r
+\r
+#if defined (VMS)\r
+#include <ints.h>\r
+#else\r
+typedef signed char int8;\r
+typedef signed short int16;\r
+typedef signed int int32;\r
+typedef unsigned char uint8;\r
+typedef unsigned short uint16;\r
+typedef unsigned int uint32;\r
+#endif\r
+typedef int t_stat; /* status */\r
+typedef int t_bool; /* boolean */\r
+\r
+/* 64b integers */\r
+\r
+#if defined (__GNUC__) /* GCC */\r
+typedef signed long long t_int64;\r
+typedef unsigned long long t_uint64;\r
+#elif defined (_WIN32) /* Windows */\r
+typedef signed __int64 t_int64;\r
+typedef unsigned __int64 t_uint64;\r
+#elif (defined (__ALPHA) || defined (__ia64)) && defined (VMS) /* 64b VMS */\r
+typedef signed __int64 t_int64;\r
+typedef unsigned __int64 t_uint64;\r
+#elif defined (__ALPHA) && defined (__unix__) /* Alpha UNIX */\r
+typedef signed long t_int64;\r
+typedef unsigned long t_uint64;\r
+#else /* default */\r
+#define t_int64 signed long long\r
+#define t_uint64 unsigned long long\r
+#endif /* end 64b */\r
+#ifndef INT64_C\r
+#define INT64_C(x) x ## LL\r
+#endif\r
+\r
+#if defined (USE_INT64) /* 64b data */\r
+typedef t_int64 t_svalue; /* signed value */\r
+typedef t_uint64 t_value; /* value */\r
+#else /* 32b data */\r
+typedef int32 t_svalue;\r
+typedef uint32 t_value;\r
+#endif /* end 64b data */\r
+\r
+#if defined (USE_INT64) && defined (USE_ADDR64) /* 64b address */\r
+typedef t_uint64 t_addr;\r
+#define T_ADDR_W 64\r
+#define T_ADDR_FMT LL_FMT\r
+#else /* 32b address */\r
+typedef uint32 t_addr;\r
+#define T_ADDR_W 32\r
+#define T_ADDR_FMT ""\r
+#endif /* end 64b address */\r
+\r
+#if defined (_WIN32)\r
+#define vsnprintf _vsnprintf\r
+#endif\r
+#if defined (__DECC) && defined (__VMS) && (defined (__VAX) || (__CRTL_VER <= 70311000))\r
+#define NO_vsnprintf\r
+#endif\r
+#if defined( NO_vsnprintf)\r
+#define STACKBUFSIZE 16384\r
+#else\r
+#define STACKBUFSIZE 2048\r
+#endif\r
+\r
+#if defined (_WIN32) /* Actually, a GCC issue */\r
+#define LL_FMT "I64"\r
+#else\r
+#define LL_FMT "ll"\r
+#endif\r
+\r
+#if defined (VMS) && (defined (__ia64) || defined (__ALPHA))\r
+#define HAVE_GLOB\r
+#endif\r
+\r
+#if defined (__linux) || defined (VMS) || defined (__APPLE__)\r
+#define HAVE_C99_STRFTIME 1\r
+#endif\r
+\r
+#if defined (_WIN32)\r
+#define NULL_DEVICE "NUL:"\r
+#elif defined (_VMS)\r
+#define NULL_DEVICE "NL:"\r
+#else\r
+#define NULL_DEVICE "/dev/null"\r
+#endif\r
+\r
+/* Stubs for inlining */\r
+\r
+#if defined(_MSC_VER)\r
+#define SIM_INLINE _inline\r
+#elif defined(__GNUC__)\r
+#define SIM_INLINE inline\r
+#else\r
+#define SIM_INLINE \r
+#endif\r
+\r
+/* Storage class modifier for weak link definition for sim_vm_init() */\r
+\r
+#if defined(__cplusplus)\r
+#if defined(__GNUC__)\r
+#define WEAK __attribute__((weak))\r
+#elif defined(_MSC_VER)\r
+#define WEAK __declspec(selectany) \r
+#else\r
+#define WEAK extern \r
+#endif\r
+#else\r
+#define WEAK \r
+#endif\r
+\r
+/* System independent definitions */\r
+\r
+#define FLIP_SIZE (1 << 16) /* flip buf size */\r
+#if !defined (PATH_MAX) /* usually in limits */\r
+#define PATH_MAX 512\r
+#endif\r
+#if (PATH_MAX >= 128)\r
+#define CBUFSIZE (128 + PATH_MAX) /* string buf size */\r
+#else\r
+#define CBUFSIZE 256\r
+#endif\r
+\r
+/* Breakpoint spaces definitions */\r
+\r
+#define SIM_BKPT_N_SPC (1 << (32 - SIM_BKPT_V_SPC)) /* max number spaces */\r
+#define SIM_BKPT_V_SPC (BRK_TYP_MAX + 1) /* location in arg */\r
+\r
+/* Extended switch definitions (bits >= 26) */\r
+\r
+#define SIM_SW_HIDE (1u << 26) /* enable hiding */\r
+#define SIM_SW_REST (1u << 27) /* attach/restore */\r
+#define SIM_SW_REG (1u << 28) /* register value */\r
+#define SIM_SW_STOP (1u << 29) /* stop message */\r
+#define SIM_SW_SHUT (1u << 30) /* shutdown */\r
+\r
+/* Simulator status codes\r
+\r
+ 0 ok\r
+ 1 - (SCPE_BASE - 1) simulator specific\r
+ SCPE_BASE - n general\r
+*/\r
+\r
+#define SCPE_OK 0 /* normal return */\r
+#define SCPE_BASE 64 /* base for messages */\r
+#define SCPE_NXM (SCPE_BASE + 0) /* nxm */\r
+#define SCPE_UNATT (SCPE_BASE + 1) /* no file */\r
+#define SCPE_IOERR (SCPE_BASE + 2) /* I/O error */\r
+#define SCPE_CSUM (SCPE_BASE + 3) /* loader cksum */\r
+#define SCPE_FMT (SCPE_BASE + 4) /* loader format */\r
+#define SCPE_NOATT (SCPE_BASE + 5) /* not attachable */\r
+#define SCPE_OPENERR (SCPE_BASE + 6) /* open error */\r
+#define SCPE_MEM (SCPE_BASE + 7) /* alloc error */\r
+#define SCPE_ARG (SCPE_BASE + 8) /* argument error */\r
+#define SCPE_STEP (SCPE_BASE + 9) /* step expired */\r
+#define SCPE_UNK (SCPE_BASE + 10) /* unknown command */\r
+#define SCPE_RO (SCPE_BASE + 11) /* read only */\r
+#define SCPE_INCOMP (SCPE_BASE + 12) /* incomplete */\r
+#define SCPE_STOP (SCPE_BASE + 13) /* sim stopped */\r
+#define SCPE_EXIT (SCPE_BASE + 14) /* sim exit */\r
+#define SCPE_TTIERR (SCPE_BASE + 15) /* console tti err */\r
+#define SCPE_TTOERR (SCPE_BASE + 16) /* console tto err */\r
+#define SCPE_EOF (SCPE_BASE + 17) /* end of file */\r
+#define SCPE_REL (SCPE_BASE + 18) /* relocation error */\r
+#define SCPE_NOPARAM (SCPE_BASE + 19) /* no parameters */\r
+#define SCPE_ALATT (SCPE_BASE + 20) /* already attached */\r
+#define SCPE_TIMER (SCPE_BASE + 21) /* hwre timer err */\r
+#define SCPE_SIGERR (SCPE_BASE + 22) /* signal err */\r
+#define SCPE_TTYERR (SCPE_BASE + 23) /* tty setup err */\r
+#define SCPE_SUB (SCPE_BASE + 24) /* subscript err */\r
+#define SCPE_NOFNC (SCPE_BASE + 25) /* func not imp */\r
+#define SCPE_UDIS (SCPE_BASE + 26) /* unit disabled */\r
+#define SCPE_NORO (SCPE_BASE + 27) /* rd only not ok */\r
+#define SCPE_INVSW (SCPE_BASE + 28) /* invalid switch */\r
+#define SCPE_MISVAL (SCPE_BASE + 29) /* missing value */\r
+#define SCPE_2FARG (SCPE_BASE + 30) /* too few arguments */\r
+#define SCPE_2MARG (SCPE_BASE + 31) /* too many arguments */\r
+#define SCPE_NXDEV (SCPE_BASE + 32) /* nx device */\r
+#define SCPE_NXUN (SCPE_BASE + 33) /* nx unit */\r
+#define SCPE_NXREG (SCPE_BASE + 34) /* nx register */\r
+#define SCPE_NXPAR (SCPE_BASE + 35) /* nx parameter */\r
+#define SCPE_NEST (SCPE_BASE + 36) /* nested DO */\r
+#define SCPE_IERR (SCPE_BASE + 37) /* internal error */\r
+#define SCPE_MTRLNT (SCPE_BASE + 38) /* tape rec lnt error */\r
+#define SCPE_LOST (SCPE_BASE + 39) /* Telnet conn lost */\r
+#define SCPE_TTMO (SCPE_BASE + 40) /* Telnet conn timeout */\r
+#define SCPE_STALL (SCPE_BASE + 41) /* Telnet conn stall */\r
+#define SCPE_AFAIL (SCPE_BASE + 42) /* assert failed */\r
+#define SCPE_INVREM (SCPE_BASE + 43) /* invalid remote console command */\r
+#define SCPE_NOTATT (SCPE_BASE + 44) /* not attached */\r
+#define SCPE_EXPECT (SCPE_BASE + 45) /* expect matched */\r
+#define SCPE_REMOTE (SCPE_BASE + 46) /* remote console command */\r
+\r
+#define SCPE_MAX_ERR (SCPE_BASE + 47) /* Maximum SCPE Error Value */\r
+#define SCPE_KFLAG 0x1000 /* tti data flag */\r
+#define SCPE_BREAK 0x2000 /* tti break flag */\r
+#define SCPE_NOMESSAGE 0x10000000 /* message display supression flag */\r
+#define SCPE_BARE_STATUS(stat) ((stat) & ~(SCPE_NOMESSAGE|SCPE_KFLAG|SCPE_BREAK))\r
+\r
+/* Print value format codes */\r
+\r
+#define PV_RZRO 0 /* right, zero fill */\r
+#define PV_RSPC 1 /* right, space fill */\r
+#define PV_RCOMMA 2 /* right, space fill. Comma separate every 3 */\r
+#define PV_LEFT 3 /* left justify */\r
+\r
+/* Default timing parameters */\r
+\r
+#define KBD_POLL_WAIT 5000 /* keyboard poll */\r
+#define KBD_MAX_WAIT 500000\r
+#define SERIAL_IN_WAIT 100 /* serial in time */\r
+#define SERIAL_OUT_WAIT 100 /* serial output */\r
+#define NOQUEUE_WAIT 1000000 /* min check time */\r
+#define KBD_LIM_WAIT(x) (((x) > KBD_MAX_WAIT)? KBD_MAX_WAIT: (x))\r
+#define KBD_WAIT(w,s) ((w)? w: KBD_LIM_WAIT (s))\r
+\r
+/* Convert switch letter to bit mask */\r
+\r
+#define SWMASK(x) (1u << (((int) (x)) - ((int) 'A')))\r
+\r
+/* String match - at least one character required */\r
+\r
+#define MATCH_CMD(ptr,cmd) ((NULL == (ptr)) || (!*(ptr)) || strncmp ((ptr), (cmd), strlen (ptr)))\r
+\r
+/* End of Linked List/Queue value */\r
+/* Chosen for 2 reasons: */\r
+/* 1 - to not be NULL, this allowing the NULL value to */\r
+/* indicate inclusion on a list */\r
+/* and */\r
+/* 2 - to not be a valid/possible pointer (alignment) */\r
+#define QUEUE_LIST_END ((UNIT *)1)\r
+\r
+/* Typedefs for principal structures */\r
+\r
+typedef struct DEVICE DEVICE;\r
+typedef struct UNIT UNIT;\r
+typedef struct REG REG;\r
+typedef struct CTAB CTAB;\r
+typedef struct C1TAB C1TAB;\r
+typedef struct SHTAB SHTAB;\r
+typedef struct MTAB MTAB;\r
+typedef struct SCHTAB SCHTAB;\r
+typedef struct BRKTAB BRKTAB;\r
+typedef struct BRKTYPTAB BRKTYPTAB;\r
+typedef struct EXPTAB EXPTAB;\r
+typedef struct EXPECT EXPECT;\r
+typedef struct SEND SEND;\r
+typedef struct DEBTAB DEBTAB;\r
+typedef struct FILEREF FILEREF;\r
+typedef struct BITFIELD BITFIELD;\r
+\r
+typedef t_stat (*ACTIVATE_API)(UNIT *unit, int32 interval);\r
+\r
+/* Device data structure */\r
+\r
+struct DEVICE {\r
+ const char *name; /* name */\r
+ UNIT *units; /* units */\r
+ REG *registers; /* registers */\r
+ MTAB *modifiers; /* modifiers */\r
+ uint32 numunits; /* #units */\r
+ uint32 aradix; /* address radix */\r
+ uint32 awidth; /* address width */\r
+ uint32 aincr; /* addr increment */\r
+ uint32 dradix; /* data radix */\r
+ uint32 dwidth; /* data width */\r
+ t_stat (*examine)(t_value *v, t_addr a, UNIT *up,\r
+ int32 sw); /* examine routine */\r
+ t_stat (*deposit)(t_value v, t_addr a, UNIT *up,\r
+ int32 sw); /* deposit routine */\r
+ t_stat (*reset)(DEVICE *dp); /* reset routine */\r
+ t_stat (*boot)(int32 u, DEVICE *dp);\r
+ /* boot routine */\r
+ t_stat (*attach)(UNIT *up, CONST char *cp);\r
+ /* attach routine */\r
+ t_stat (*detach)(UNIT *up); /* detach routine */\r
+ void *ctxt; /* context */\r
+ uint32 flags; /* flags */\r
+ uint32 dctrl; /* debug control */\r
+ DEBTAB *debflags; /* debug flags */\r
+ t_stat (*msize)(UNIT *up, int32 v, CONST char *cp, void *dp);\r
+ /* mem size routine */\r
+ char *lname; /* logical name */\r
+ t_stat (*help)(FILE *st, DEVICE *dptr,\r
+ UNIT *uptr, int32 flag, const char *cptr); \r
+ /* help */\r
+ t_stat (*attach_help)(FILE *st, DEVICE *dptr,\r
+ UNIT *uptr, int32 flag, const char *cptr);\r
+ /* attach help */\r
+ void *help_ctx; /* Context available to help routines */\r
+ const char *(*description)(DEVICE *dptr); /* Device Description */\r
+ BRKTYPTAB *brk_types; /* Breakpoint types */\r
+ };\r
+\r
+/* Device flags */\r
+\r
+#define DEV_V_DIS 0 /* dev disabled */\r
+#define DEV_V_DISABLE 1 /* dev disable-able */\r
+#define DEV_V_DYNM 2 /* mem size dynamic */\r
+#define DEV_V_DEBUG 3 /* debug capability */\r
+#define DEV_V_TYPE 4 /* Attach type */\r
+#define DEV_S_TYPE 3 /* Width of Type Field */\r
+#define DEV_V_SECTORS 7 /* Unit Capacity is in 512byte sectors */\r
+#define DEV_V_DONTAUTO 8 /* Do not auto detach already attached units */\r
+#define DEV_V_FLATHELP 9 /* Use traditional (unstructured) help */\r
+#define DEV_V_NOSAVE 10 /* Don't save device state */\r
+#define DEV_V_UF_31 12 /* user flags, V3.1 */\r
+#define DEV_V_UF 16 /* user flags */\r
+#define DEV_V_RSV 31 /* reserved */\r
+\r
+#define DEV_DIS (1 << DEV_V_DIS) /* device is currently disabled */\r
+#define DEV_DISABLE (1 << DEV_V_DISABLE) /* device can be set enabled or disabled */\r
+#define DEV_DYNM (1 << DEV_V_DYNM) /* device requires call on msize routine to change memory size */\r
+#define DEV_DEBUG (1 << DEV_V_DEBUG) /* device supports SET DEBUG command */\r
+#define DEV_SECTORS (1 << DEV_V_SECTORS) /* capacity is 512 byte sectors */\r
+#define DEV_DONTAUTO (1 << DEV_V_DONTAUTO) /* Do not auto detach already attached units */\r
+#define DEV_FLATHELP (1 << DEV_V_FLATHELP) /* Use traditional (unstructured) help */\r
+#define DEV_NOSAVE (1 << DEV_V_NOSAVE) /* Don't save device state */\r
+#define DEV_NET 0 /* Deprecated - meaningless */\r
+\r
+\r
+#define DEV_TYPEMASK (((1 << DEV_S_TYPE) - 1) << DEV_V_TYPE)\r
+#define DEV_DISK (1 << DEV_V_TYPE) /* sim_disk Attach */\r
+#define DEV_TAPE (2 << DEV_V_TYPE) /* sim_tape Attach */\r
+#define DEV_MUX (3 << DEV_V_TYPE) /* sim_tmxr Attach */\r
+#define DEV_ETHER (4 << DEV_V_TYPE) /* Ethernet Device */\r
+#define DEV_DISPLAY (5 << DEV_V_TYPE) /* Display Device */\r
+#define DEV_TYPE(dptr) ((dptr)->flags & DEV_TYPEMASK)\r
+\r
+#define DEV_UFMASK_31 (((1u << DEV_V_RSV) - 1) & ~((1u << DEV_V_UF_31) - 1))\r
+#define DEV_UFMASK (((1u << DEV_V_RSV) - 1) & ~((1u << DEV_V_UF) - 1))\r
+#define DEV_RFLAGS (DEV_UFMASK|DEV_DIS) /* restored flags */\r
+\r
+/* Unit data structure\r
+\r
+ Parts of the unit structure are device specific, that is, they are\r
+ not referenced by the simulator control package and can be freely\r
+ used by device simulators. Fields starting with 'buf', and flags\r
+ starting with 'UF', are device specific. The definitions given here\r
+ are for a typical sequential device.\r
+*/\r
+\r
+struct UNIT {\r
+ UNIT *next; /* next active */\r
+ t_stat (*action)(UNIT *up); /* action routine */\r
+ char *filename; /* open file name */\r
+ FILE *fileref; /* file reference */\r
+ void *filebuf; /* memory buffer */\r
+ uint32 hwmark; /* high water mark */\r
+ int32 time; /* time out */\r
+ uint32 flags; /* flags */\r
+ uint32 dynflags; /* dynamic flags */\r
+ t_addr capac; /* capacity */\r
+ t_addr pos; /* file position */\r
+ void (*io_flush)(UNIT *up); /* io flush routine */\r
+ uint32 iostarttime; /* I/O start time */\r
+ int32 buf; /* buffer */\r
+ int32 wait; /* wait */\r
+ int32 u3; /* device specific */\r
+ int32 u4; /* device specific */\r
+ int32 u5; /* device specific */\r
+ int32 u6; /* device specific */\r
+ void *up7; /* device specific */\r
+ void *up8; /* device specific */\r
+ void *tmxr; /* TMXR linkage */\r
+ void (*cancel)(UNIT *);\r
+#ifdef SIM_ASYNCH_IO\r
+ void (*a_check_completion)(UNIT *);\r
+ t_bool (*a_is_active)(UNIT *);\r
+ UNIT *a_next; /* next asynch active */\r
+ int32 a_event_time;\r
+ ACTIVATE_API a_activate_call;\r
+ /* Asynchronous Polling control */\r
+ /* These fields should only be referenced when holding the sim_tmxr_poll_lock */\r
+ t_bool a_polling_now; /* polling active flag */\r
+ int32 a_poll_waiter_count; /* count of polling threads */\r
+ /* waiting for this unit */\r
+ /* Asynchronous Timer control */\r
+ double a_due_time; /* due time for timer event */\r
+ double a_due_gtime; /* due time (in instructions) for timer event */\r
+ int32 a_usec_delay; /* time delay for timer event */\r
+#endif\r
+ };\r
+\r
+/* Unit flags */\r
+\r
+#define UNIT_V_UF_31 12 /* dev spec, V3.1 */\r
+#define UNIT_V_UF 16 /* device specific */\r
+#define UNIT_V_RSV 31 /* reserved!! */\r
+\r
+#define UNIT_ATTABLE 0000001 /* attachable */\r
+#define UNIT_RO 0000002 /* read only */\r
+#define UNIT_FIX 0000004 /* fixed capacity */\r
+#define UNIT_SEQ 0000010 /* sequential */\r
+#define UNIT_ATT 0000020 /* attached */\r
+#define UNIT_BINK 0000040 /* K = power of 2 */\r
+#define UNIT_BUFABLE 0000100 /* bufferable */\r
+#define UNIT_MUSTBUF 0000200 /* must buffer */\r
+#define UNIT_BUF 0000400 /* buffered */\r
+#define UNIT_ROABLE 0001000 /* read only ok */\r
+#define UNIT_DISABLE 0002000 /* disable-able */\r
+#define UNIT_DIS 0004000 /* disabled */\r
+#define UNIT_IDLE 0040000 /* idle eligible */\r
+\r
+/* Unused/meaningless flags */\r
+#define UNIT_TEXT 0000000 /* text mode - no effect */\r
+\r
+#define UNIT_UFMASK_31 (((1u << UNIT_V_RSV) - 1) & ~((1u << UNIT_V_UF_31) - 1))\r
+#define UNIT_UFMASK (((1u << UNIT_V_RSV) - 1) & ~((1u << UNIT_V_UF) - 1))\r
+#define UNIT_RFLAGS (UNIT_UFMASK|UNIT_DIS) /* restored flags */\r
+\r
+/* Unit dynamic flags (dynflags) */\r
+\r
+/* These flags are only set dynamically */\r
+\r
+#define UNIT_ATTMULT 0000001 /* Allow multiple attach commands */\r
+#define UNIT_TM_POLL 0000002 /* TMXR Polling unit */\r
+#define UNIT_NO_FIO 0000004 /* fileref is NOT a FILE * */\r
+#define UNIT_DISK_CHK 0000010 /* disk data debug checking (sim_disk) */\r
+#define UNIT_TMR_UNIT 0000020 /* Unit registered as a calibrated timer */\r
+#define UNIT_V_DF_TAPE 5 /* Bit offset for Tape Density reservation */\r
+#define UNIT_S_DF_TAPE 3 /* Bits Reserved for Tape Density */\r
+\r
+struct BITFIELD {\r
+ const char *name; /* field name */\r
+ uint32 offset; /* starting bit */\r
+ uint32 width; /* width */\r
+ const char **valuenames; /* map of values to strings */\r
+ const char *format; /* value format string */\r
+ };\r
+\r
+/* Register data structure */\r
+\r
+struct REG {\r
+ CONST char *name; /* name */\r
+ void *loc; /* location */\r
+ uint32 radix; /* radix */\r
+ uint32 width; /* width */\r
+ uint32 offset; /* starting bit */\r
+ uint32 depth; /* save depth */\r
+ const char *desc; /* description */\r
+ BITFIELD *fields; /* bit fields */\r
+ uint32 flags; /* flags */\r
+ uint32 qptr; /* circ q ptr */\r
+ size_t str_size; /* structure size */\r
+ };\r
+\r
+/* Register flags */\r
+\r
+#define REG_FMT 00003 /* see PV_x */\r
+#define REG_RO 00004 /* read only */\r
+#define REG_HIDDEN 00010 /* hidden */\r
+#define REG_NZ 00020 /* must be non-zero */\r
+#define REG_UNIT 00040 /* in unit struct */\r
+#define REG_STRUCT 00100 /* in structure array */\r
+#define REG_CIRC 00200 /* circular array */\r
+#define REG_VMIO 00400 /* use VM data print/parse */\r
+#define REG_VMAD 01000 /* use VM addr print/parse */\r
+#define REG_FIT 02000 /* fit access to size */\r
+#define REG_HRO (REG_RO | REG_HIDDEN) /* hidden, read only */\r
+\r
+#define REG_V_UF 16 /* device specific */\r
+#define REG_UFMASK (~((1u << REG_V_UF) - 1)) /* user flags mask */\r
+#define REG_VMFLAGS (REG_VMIO | REG_UFMASK) /* call VM routine if any of these are set */\r
+\r
+/* Command tables, base and alternate formats */\r
+\r
+struct CTAB {\r
+ const char *name; /* name */\r
+ t_stat (*action)(int32 flag, CONST char *cptr);\r
+ /* action routine */\r
+ int32 arg; /* argument */\r
+ const char *help; /* help string/structured locator */\r
+ const char *help_base; /* structured help base*/\r
+ void (*message)(const char *unechoed_cmdline, t_stat stat);\r
+ /* message printing routine */\r
+ };\r
+\r
+struct C1TAB {\r
+ const char *name; /* name */\r
+ t_stat (*action)(DEVICE *dptr, UNIT *uptr,\r
+ int32 flag, CONST char *cptr);/* action routine */\r
+ int32 arg; /* argument */\r
+ const char *help; /* help string */\r
+ };\r
+\r
+struct SHTAB {\r
+ const char *name; /* name */\r
+ t_stat (*action)(FILE *st, DEVICE *dptr,\r
+ UNIT *uptr, int32 flag, CONST char *cptr);\r
+ int32 arg; /* argument */\r
+ const char *help; /* help string */\r
+ };\r
+\r
+/* Modifier table - only extended entries have disp, reg, or flags */\r
+\r
+struct MTAB {\r
+ uint32 mask; /* mask */\r
+ uint32 match; /* match */\r
+ const char *pstring; /* print string */\r
+ const char *mstring; /* match string */\r
+ t_stat (*valid)(UNIT *up, int32 v, CONST char *cp, void *dp);\r
+ /* validation routine */\r
+ t_stat (*disp)(FILE *st, UNIT *up, int32 v, CONST void *dp);\r
+ /* display routine */\r
+ void *desc; /* value descriptor */\r
+ /* REG * if MTAB_VAL */\r
+ /* int * if not */\r
+ const char *help; /* help string */\r
+ };\r
+\r
+\r
+/* mtab mask flag bits */\r
+/* NOTE: MTAB_VALR and MTAB_VALO are only used to display help */\r
+#define MTAB_XTD (1u << UNIT_V_RSV) /* ext entry flag */\r
+#define MTAB_VDV (0001 | MTAB_XTD) /* valid for dev */\r
+#define MTAB_VUN (0002 | MTAB_XTD) /* valid for unit */\r
+#define MTAB_VALR (0004 | MTAB_XTD) /* takes a value (required) */\r
+#define MTAB_VALO (0010 | MTAB_XTD) /* takes a value (optional) */\r
+#define MTAB_NMO (0020 | MTAB_XTD) /* only if named */\r
+#define MTAB_NC (0040 | MTAB_XTD) /* no UC conversion */\r
+#define MTAB_QUOTE (0100 | MTAB_XTD) /* quoted string */\r
+#define MTAB_SHP (0200 | MTAB_XTD) /* show takes parameter */\r
+#define MODMASK(mptr,flag) (((mptr)->mask & (uint32)(flag)) == (uint32)(flag))/* flag mask test */\r
+\r
+/* Search table */\r
+\r
+struct SCHTAB {\r
+ int32 logic; /* logical operator */\r
+ int32 boolop; /* boolean operator */\r
+ uint32 count; /* value count in mask and comp arrays */\r
+ t_value *mask; /* mask for logical */\r
+ t_value *comp; /* comparison for boolean */\r
+ };\r
+\r
+/* Breakpoint table */\r
+\r
+struct BRKTAB {\r
+ t_addr addr; /* address */\r
+ uint32 typ; /* mask of types */\r
+#define BRK_TYP_USR_TYPES ((1 << ('Z'-'A'+1)) - 1)/* all types A-Z */\r
+#define BRK_TYP_DYN_STEPOVER (SWMASK ('Z'+1))\r
+#define BRK_TYP_DYN_USR (SWMASK ('Z'+2))\r
+#define BRK_TYP_DYN_ALL (BRK_TYP_DYN_USR|BRK_TYP_DYN_STEPOVER) /* Mask of All Dynamic types */\r
+#define BRK_TYP_TEMP (SWMASK ('Z'+3)) /* Temporary (one-shot) */\r
+#define BRK_TYP_MAX (('Z'-'A')+3) /* Maximum breakpoint type */\r
+ int32 cnt; /* proceed count */\r
+ char *act; /* action string */\r
+ double time_fired[SIM_BKPT_N_SPC]; /* instruction count when match occurred */\r
+ BRKTAB *next; /* list with same address value */\r
+ };\r
+\r
+/* Breakpoint table */\r
+\r
+struct BRKTYPTAB {\r
+ uint32 btyp; /* type mask */\r
+ const char *desc; /* description */\r
+ };\r
+#define BRKTYPE(typ,descrip) {SWMASK(typ), descrip}\r
+\r
+/* Expect rule */\r
+\r
+struct EXPTAB {\r
+ uint8 *match; /* match string */\r
+ uint32 size; /* match string size */\r
+ char *match_pattern; /* match pattern for format */\r
+ int32 cnt; /* proceed count */\r
+ int32 switches; /* flags */\r
+#define EXP_TYP_PERSIST (SWMASK ('P')) /* rule persists after match, default is once a rule matches, it is removed */\r
+#define EXP_TYP_CLEARALL (SWMASK ('C')) /* clear all rules after matching this rule, default is to once a rule matches, it is removed */\r
+#define EXP_TYP_REGEX (SWMASK ('R')) /* rule pattern is a regular expression */\r
+#define EXP_TYP_REGEX_I (SWMASK ('I')) /* regular expression pattern matching should be case independent */\r
+#define EXP_TYP_TIME (SWMASK ('T')) /* halt delay is in microseconds instead of instructions */\r
+#if defined(USE_REGEX)\r
+ regex_t regex; /* compiled regular expression */\r
+#endif\r
+ char *act; /* action string */\r
+ };\r
+\r
+/* Expect Context */\r
+\r
+struct EXPECT {\r
+ DEVICE *dptr; /* Device (for Debug) */\r
+ uint32 dbit; /* Debugging Bit */\r
+ EXPTAB *rules; /* match rules */\r
+ int32 size; /* count of match rules */\r
+ uint32 after; /* delay before halting */\r
+ uint8 *buf; /* buffer of output data which has produced */\r
+ uint32 buf_ins; /* buffer insertion point for the next output data */\r
+ uint32 buf_size; /* buffer size */\r
+ };\r
+\r
+/* Send Context */\r
+\r
+struct SEND {\r
+ uint32 delay; /* instruction delay between sent data */\r
+#define SEND_DEFAULT_DELAY 1000 /* default delay instruction count */\r
+ DEVICE *dptr; /* Device (for Debug) */\r
+ uint32 dbit; /* Debugging Bit */\r
+ uint32 after; /* instruction delay before sending any data */\r
+ double next_time; /* execution time when next data can be sent */\r
+ uint8 *buffer; /* buffer */\r
+ size_t bufsize; /* buffer size */\r
+ int32 insoff; /* insert offset */\r
+ int32 extoff; /* extra offset */\r
+ };\r
+\r
+/* Debug table */\r
+\r
+struct DEBTAB {\r
+ const char *name; /* control name */\r
+ uint32 mask; /* control bit */\r
+ const char *desc; /* description */\r
+ };\r
+\r
+/* Deprecated Debug macros. Use sim_debug() */\r
+\r
+#define DEBUG_PRS(d) (sim_deb && d.dctrl)\r
+#define DEBUG_PRD(d) (sim_deb && d->dctrl)\r
+#define DEBUG_PRI(d,m) (sim_deb && (d.dctrl & (m)))\r
+#define DEBUG_PRJ(d,m) (sim_deb && ((d)->dctrl & (m)))\r
+\r
+#define SIM_DBG_EVENT 0x10000\r
+#define SIM_DBG_ACTIVATE 0x20000\r
+#define SIM_DBG_AIO_QUEUE 0x40000\r
+\r
+/* File Reference */\r
+struct FILEREF {\r
+ char name[CBUFSIZE]; /* file name */\r
+ FILE *file; /* file handle */\r
+ int32 refcount; /* reference count */\r
+ };\r
+\r
+/* \r
+ The following macros exist to help populate structure contents\r
+\r
+ They are dependent on the declaration order of the fields \r
+ of the structures they exist to populate.\r
+\r
+ */\r
+\r
+#define UDATA(act,fl,cap) NULL,act,NULL,NULL,NULL,0,0,(fl),0,(cap),0,NULL,0,0\r
+\r
+#if defined (__STDC__) || defined (_WIN32) /* Variants which depend on how macro arguments are convered to strings */\r
+/* Generic Register declaration for all fields. \r
+ If the register structure is extended, this macro will be retained and a \r
+ new macro will be provided that populates the new register structure */\r
+#define REGDATA(nm,loc,rdx,wd,off,dep,desc,flds,fl,qptr,siz) \\r
+ #nm, &(loc), (rdx), (wd), (off), (dep), (desc), (flds), (fl), (qptr), (siz)\r
+/* Right Justified Octal Register Data */\r
+#define ORDATA(nm,loc,wd) #nm, &(loc), 8, (wd), 0, 1, NULL, NULL\r
+/* Right Justified Decimal Register Data */\r
+#define DRDATA(nm,loc,wd) #nm, &(loc), 10, (wd), 0, 1, NULL, NULL\r
+/* Right Justified Hexadecimal Register Data */\r
+#define HRDATA(nm,loc,wd) #nm, &(loc), 16, (wd), 0, 1, NULL, NULL\r
+/* Right Justified Binary Register Data */\r
+#define BINRDATA(nm,loc,wd) #nm, &(loc), 2, (wd), 0, 1, NULL, NULL\r
+/* One-bit binary flag at an arbitrary offset in a 32-bit word Register */\r
+#define FLDATA(nm,loc,pos) #nm, &(loc), 2, 1, (pos), 1, NULL, NULL\r
+/* Arbitrary location and Radix Register */\r
+#define GRDATA(nm,loc,rdx,wd,pos) #nm, &(loc), (rdx), (wd), (pos), 1, NULL, NULL\r
+/* Arrayed register whose data is kept in a standard C array Register */\r
+#define BRDATA(nm,loc,rdx,wd,dep) #nm, (loc), (rdx), (wd), 0, (dep), NULL, NULL\r
+/* Same as above, but with additional description initializer */\r
+#define ORDATAD(nm,loc,wd,desc) #nm, &(loc), 8, (wd), 0, 1, (desc), NULL\r
+#define DRDATAD(nm,loc,wd,desc) #nm, &(loc), 10, (wd), 0, 1, (desc), NULL\r
+#define HRDATAD(nm,loc,wd,desc) #nm, &(loc), 16, (wd), 0, 1, (desc), NULL\r
+#define BINRDATAD(nm,loc,wd,desc) #nm, &(loc), 2, (wd), 0, 1, (desc), NULL\r
+#define FLDATAD(nm,loc,pos,desc) #nm, &(loc), 2, 1, (pos), 1, (desc), NULL\r
+#define GRDATAD(nm,loc,rdx,wd,pos,desc) #nm, &(loc), (rdx), (wd), (pos), 1, (desc), NULL\r
+#define BRDATAD(nm,loc,rdx,wd,dep,desc) #nm, (loc), (rdx), (wd), 0, (dep), (desc), NULL\r
+/* Same as above, but with additional description initializer, and bitfields */\r
+#define ORDATADF(nm,loc,wd,desc,flds) #nm, &(loc), 8, (wd), 0, 1, (desc), (flds)\r
+#define DRDATADF(nm,loc,wd,desc,flds) #nm, &(loc), 10, (wd), 0, 1, (desc), (flds)\r
+#define HRDATADF(nm,loc,wd,desc,flds) #nm, &(loc), 16, (wd), 0, 1, (desc), (flds)\r
+#define BINRDATADF(nm,loc,wd) #nm, &(loc), 2, (wd), 0, 1, NULL, NULL\r
+#define FLDATADF(nm,loc,pos,desc,flds) #nm, &(loc), 2, 1, (pos), 1, (desc), (flds)\r
+#define GRDATADF(nm,loc,rdx,wd,pos,desc,flds) #nm, &(loc), (rdx), (wd), (pos), 1, (desc), (flds)\r
+#define BRDATADF(nm,loc,rdx,wd,dep,desc,flds) #nm, (loc), (rdx), (wd), 0, (dep), (desc), (flds)\r
+#define BIT(nm) {#nm, 0xffffffff, 1} /* Single Bit definition */\r
+#define BITNC {"", 0xffffffff, 1} /* Don't care Bit definition */\r
+#define BITF(nm,sz) {#nm, 0xffffffff, sz} /* Bit Field definition */\r
+#define BITNCF(sz) {"", 0xffffffff, sz} /* Don't care Bit Field definition */\r
+#define BITFFMT(nm,sz,fmt) {#nm, 0xffffffff, sz, NULL, #fmt}/* Bit Field definition with Output format */\r
+#define BITFNAM(nm,sz,names) {#nm, 0xffffffff, sz, names} /* Bit Field definition with value->name map */\r
+#else /* For non-STD-C compiler which can't stringify macro arguments with # */\r
+#define REGDATA(nm,loc,rdx,wd,off,dep,desc,flds,fl,qptr,siz) \\r
+ "nm", &(loc), (rdx), (wd), (off), (dep), (desc), (flds), (fl), (qptr), (siz)\r
+#define ORDATA(nm,loc,wd) "nm", &(loc), 8, (wd), 0, 1, NULL, NULL\r
+#define DRDATA(nm,loc,wd) "nm", &(loc), 10, (wd), 0, 1, NULL, NULL\r
+#define HRDATA(nm,loc,wd) "nm", &(loc), 16, (wd), 0, 1, NULL, NULL\r
+#define BINRDATA(nm,loc,wd) "nm", &(loc), 2, (wd), 0, 1, NULL, NULL\r
+#define FLDATA(nm,loc,pos) "nm", &(loc), 2, 1, (pos), 1, NULL, NULL\r
+#define GRDATA(nm,loc,rdx,wd,pos) "nm", &(loc), (rdx), (wd), (pos), 1, NULL, NULL\r
+#define BRDATA(nm,loc,rdx,wd,dep) "nm", (loc), (rdx), (wd), 0, (dep), NULL, NULL\r
+#define ORDATAD(nm,loc,wd,desc) "nm", &(loc), 8, (wd), 0, 1, (desc), NULL\r
+#define DRDATAD(nm,loc,wd,desc) "nm", &(loc), 10, (wd), 0, 1, (desc), NULL\r
+#define HRDATAD(nm,loc,wd,desc) "nm", &(loc), 16, (wd), 0, 1, (desc), NULL\r
+#define BINRDATAD(nm,loc,wd,desc) "nm", &(loc), 2, (wd), 0, 1, (desc), NULL\r
+#define FLDATAD(nm,loc,pos,desc) "nm", &(loc), 2, 1, (pos), 1, (desc), NULL\r
+#define GRDATAD(nm,loc,rdx,wd,pos,desc) "nm", &(loc), (rdx), (wd), (pos), 1, (desc), NULL\r
+#define BRDATAD(nm,loc,rdx,wd,dep,desc) "nm", (loc), (rdx), (wd), 0, (dep), (desc), NULL\r
+#define ORDATADF(nm,loc,wd,desc,flds) "nm", &(loc), 8, (wd), 0, 1, (desc), (flds)\r
+#define DRDATADF(nm,loc,wd,desc,flds) "nm", &(loc), 10, (wd), 0, 1, (desc), (flds)\r
+#define HRDATADF(nm,loc,wd,desc,flds) "nm", &(loc), 16, (wd), 0, 1, (desc), (flds)\r
+#define BINRDATADF(nm,loc,wd,desc,flds) "nm", &(loc), 2, (wd), 0, 1, (desc), (flds)\r
+#define FLDATADF(nm,loc,pos,desc,flds) "nm", &(loc), 2, 1, (pos), 1, (desc), (flds)\r
+#define GRDATADF(nm,loc,rdx,wd,pos,desc,flds) "nm", &(loc), (rdx), (wd), (pos), 1, (desc), (flds)\r
+#define BRDATADF(nm,loc,rdx,wd,dep,desc,flds) "nm", (loc), (rdx), (wd), 0, (dep), (desc), (flds)\r
+#define BIT(nm) {"nm", 0xffffffff, 1} /* Single Bit definition */\r
+#define BITNC {"", 0xffffffff, 1} /* Don't care Bit definition */\r
+#define BITF(nm,sz) {"nm", 0xffffffff, sz} /* Bit Field definition */\r
+#define BITNCF(sz) {"", 0xffffffff, sz} /* Don't care Bit Field definition */\r
+#define BITFFMT(nm,sz,fmt) {"nm", 0xffffffff, sz, NULL, "fmt"}/* Bit Field definition with Output format */\r
+#define BITFNAM(nm,sz,names) {"nm", 0xffffffff, sz, names} /* Bit Field definition with value->name map */\r
+#endif\r
+#define ENDBITS {NULL} /* end of bitfield list */\r
+\r
+/* Arrayed register whose data is part of the UNIT structure */\r
+#define URDATA(nm,loc,rdx,wd,off,dep,fl) \\r
+ REGDATA(nm,(loc),(rdx),(wd),(off),(dep),NULL,NULL,((fl) | REG_UNIT),0,0)\r
+/* Arrayed register whose data is part of an arbitrary structure */\r
+#define STRDATA(nm,loc,rdx,wd,off,dep,siz,fl) \\r
+ REGDATA(nm,(loc),(rdx),(wd),(off),(dep),NULL,NULL,((fl) | REG_STRUCT),0,(siz))\r
+/* Same as above, but with additional description initializer */\r
+#define URDATAD(nm,loc,rdx,wd,off,dep,fl,desc) \\r
+ REGDATA(nm,(loc),(rdx),(wd),(off),(dep),(desc),NULL,((fl) | REG_UNIT),0,0)\r
+#define STRDATAD(nm,loc,rdx,wd,off,dep,siz,fl,desc) \\r
+ REGDATA(nm,(loc),(rdx),(wd),(off),(dep),(desc),NULL,((fl) | REG_STRUCT),0,(siz))\r
+/* Same as above, but with additional description initializer, and bitfields */\r
+#define URDATADF(nm,loc,rdx,wd,off,dep,fl,desc,flds) \\r
+ REGDATA(nm,(loc),(rdx),(wd),(off),(dep),(desc),(flds),((fl) | REG_UNIT),0,0)\r
+#define STRDATADF(nm,loc,rdx,wd,off,dep,siz,fl,desc,flds) \\r
+ REGDATA(nm,(loc),(rdx),(wd),(off),(dep),(desc),(flds),((fl) | REG_STRUCT),0,(siz))\r
+\r
+/* Function prototypes */\r
+\r
+#include "scp.h"\r
+//#include "sim_console.h"\r
+//#include "sim_timer.h"\r
+//#include "sim_fio.h"\r
+\r
+/* Macro to ALWAYS execute the specified expression and fail if it evaluates to false. */\r
+/* This replaces any references to "assert()" which should never be invoked */\r
+/* with an expression which causes side effects (i.e. must be executed for */\r
+/* the program to work correctly) */\r
+#define ASSURE(_Expression) while (!(_Expression)) {fprintf(stderr, "%s failed at %s line %d\n", #_Expression, __FILE__, __LINE__); \\r
+ sim_printf("%s failed at %s line %d\n", #_Expression, __FILE__, __LINE__); \\r
+ abort();}\r
+\r
+/* Asynch/Threaded I/O support */\r
+\r
+#if defined (SIM_ASYNCH_IO)\r
+#include <pthread.h>\r
+\r
+#define SIM_ASYNCH_CLOCKS 1\r
+\r
+extern pthread_mutex_t sim_asynch_lock;\r
+extern pthread_cond_t sim_asynch_wake;\r
+extern pthread_mutex_t sim_timer_lock;\r
+extern pthread_cond_t sim_timer_wake;\r
+extern t_bool sim_timer_event_canceled;\r
+extern int32 sim_tmxr_poll_count;\r
+extern pthread_cond_t sim_tmxr_poll_cond;\r
+extern pthread_mutex_t sim_tmxr_poll_lock;\r
+extern pthread_t sim_asynch_main_threadid;\r
+extern UNIT * volatile sim_asynch_queue;\r
+extern volatile t_bool sim_idle_wait;\r
+extern int32 sim_asynch_check;\r
+extern int32 sim_asynch_latency;\r
+extern int32 sim_asynch_inst_latency;\r
+\r
+/* Thread local storage */\r
+#if defined(__GNUC__) && !defined(__APPLE__) && !defined(__hpux) && !defined(__OpenBSD__) && !defined(_AIX)\r
+#define AIO_TLS __thread\r
+#elif defined(_MSC_VER)\r
+#define AIO_TLS __declspec(thread)\r
+#else\r
+/* Other compiler environment, then don't worry about thread local storage. */\r
+/* It is primarily used only used in debugging messages */\r
+#define AIO_TLS\r
+#endif\r
+#define AIO_QUEUE_CHECK(que, lock) \\r
+ do { \\r
+ UNIT *_cptr; \\r
+ if (lock) \\r
+ pthread_mutex_lock (lock); \\r
+ for (_cptr = que; \\r
+ (_cptr != QUEUE_LIST_END); \\r
+ _cptr = _cptr->next) \\r
+ if (!_cptr->next) { \\r
+ if (sim_deb) { \\r
+ sim_debug (SIM_DBG_EVENT, sim_dflt_dev, "Queue Corruption detected\n");\\r
+ fclose(sim_deb); \\r
+ } \\r
+ sim_printf("Queue Corruption detected\n"); \\r
+ abort(); \\r
+ } \\r
+ if (lock) \\r
+ pthread_mutex_unlock (lock); \\r
+ } while (0)\r
+#define AIO_MAIN_THREAD (pthread_equal ( pthread_self(), sim_asynch_main_threadid ))\r
+#define AIO_LOCK \\r
+ pthread_mutex_lock(&sim_asynch_lock)\r
+#define AIO_UNLOCK \\r
+ pthread_mutex_unlock(&sim_asynch_lock)\r
+#define AIO_IS_ACTIVE(uptr) (((uptr)->a_is_active ? (uptr)->a_is_active (uptr) : FALSE) || ((uptr)->a_next))\r
+#if defined(SIM_ASYNCH_MUX)\r
+#define AIO_CANCEL(uptr) \\r
+ if ((uptr)->cancel) \\r
+ (uptr)->cancel (uptr); \\r
+ else { \\r
+ if (((uptr)->dynflags & UNIT_TM_POLL) && \\r
+ !((uptr)->next) && !((uptr)->a_next)) { \\r
+ (uptr)->a_polling_now = FALSE; \\r
+ sim_tmxr_poll_count -= (uptr)->a_poll_waiter_count; \\r
+ (uptr)->a_poll_waiter_count = 0; \\r
+ } \\r
+ }\r
+#endif /* defined(SIM_ASYNCH_MUX) */\r
+#if !defined(AIO_CANCEL)\r
+#define AIO_CANCEL(uptr) \\r
+ if ((uptr)->cancel) \\r
+ (uptr)->cancel (uptr)\r
+#endif /* !defined(AIO_CANCEL) */\r
+#if defined(SIM_ASYNCH_CLOCKS)\r
+#define AIO_RETURN_TIME(uptr) \\r
+ do { \\r
+ int32 rtime = sim_timer_activate_time (uptr); \\r
+ if (rtime >= 0) \\r
+ return rtime; \\r
+ } while (0)\r
+#else\r
+#define AIO_RETURN_TIME(uptr) (void)0\r
+#endif\r
+#define AIO_EVENT_BEGIN(uptr) \\r
+ do { \\r
+ int __was_poll = uptr->dynflags & UNIT_TM_POLL\r
+#define AIO_EVENT_COMPLETE(uptr, reason) \\r
+ if (__was_poll) { \\r
+ pthread_mutex_lock (&sim_tmxr_poll_lock); \\r
+ uptr->a_polling_now = FALSE; \\r
+ if (uptr->a_poll_waiter_count) { \\r
+ sim_tmxr_poll_count -= uptr->a_poll_waiter_count; \\r
+ uptr->a_poll_waiter_count = 0; \\r
+ if (0 == sim_tmxr_poll_count) \\r
+ pthread_cond_broadcast (&sim_tmxr_poll_cond); \\r
+ } \\r
+ pthread_mutex_unlock (&sim_tmxr_poll_lock); \\r
+ } \\r
+ AIO_UPDATE_QUEUE; \\r
+ } while (0)\r
+\r
+#if defined(__DECC_VER)\r
+#include <builtins>\r
+#if defined(__IA64)\r
+#define USE_AIO_INTRINSICS 1\r
+#endif\r
+#endif\r
+#if defined(_WIN32) || defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4) || defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_8)\r
+#define USE_AIO_INTRINSICS 1\r
+#endif\r
+/* Provide a way to test both Intrinsic and Lock based queue manipulations */\r
+/* when both are available on a particular platform */\r
+#if defined(DONT_USE_AIO_INTRINSICS) && defined(USE_AIO_INTRINSICS)\r
+#undef USE_AIO_INTRINSICS\r
+#endif\r
+#ifdef USE_AIO_INTRINSICS\r
+/* This approach uses intrinsics to manage access to the link list head */\r
+/* sim_asynch_queue. This implementation is a completely lock free design */\r
+/* which avoids the potential ABA issues. */\r
+#define AIO_QUEUE_MODE "Lock free asynchronous event queue access"\r
+#define AIO_INIT \\r
+ do { \\r
+ int tmr; \\r
+ sim_asynch_main_threadid = pthread_self(); \\r
+ /* Empty list/list end uses the point value (void *)1. \\r
+ This allows NULL in an entry's a_next pointer to \\r
+ indicate that the entry is not currently in any list */ \\r
+ sim_asynch_queue = QUEUE_LIST_END; \\r
+ for (tmr=0; tmr<SIM_NTIMERS; tmr++) \\r
+ sim_clock_cosched_queue[tmr] = QUEUE_LIST_END; \\r
+ } while (0)\r
+#define AIO_CLEANUP \\r
+ do { \\r
+ pthread_mutex_destroy(&sim_asynch_lock); \\r
+ pthread_cond_destroy(&sim_asynch_wake); \\r
+ pthread_mutex_destroy(&sim_timer_lock); \\r
+ pthread_cond_destroy(&sim_timer_wake); \\r
+ pthread_mutex_destroy(&sim_tmxr_poll_lock); \\r
+ pthread_cond_destroy(&sim_tmxr_poll_cond); \\r
+ } while (0)\r
+#ifdef _WIN32\r
+#elif defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4) || defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_8)\r
+#define InterlockedCompareExchangePointer(Destination, Exchange, Comparand) __sync_val_compare_and_swap(Destination, Comparand, Exchange)\r
+#elif defined(__DECC_VER)\r
+#define InterlockedCompareExchangePointer(Destination, Exchange, Comparand) (void *)((int32)_InterlockedCompareExchange64(Destination, Exchange, Comparand))\r
+#else\r
+#error "Implementation of function InterlockedCompareExchangePointer() is needed to build with USE_AIO_INTRINSICS"\r
+#endif\r
+#define AIO_QUEUE_VAL (UNIT *)(InterlockedCompareExchangePointer((void * volatile *)&sim_asynch_queue, (void *)sim_asynch_queue, NULL))\r
+#define AIO_QUEUE_SET(val, queue) (UNIT *)(InterlockedCompareExchangePointer((void * volatile *)&sim_asynch_queue, (void *)val, queue))\r
+#define AIO_UPDATE_QUEUE sim_aio_update_queue ()\r
+#define AIO_ACTIVATE(caller, uptr, event_time) \\r
+ if (!pthread_equal ( pthread_self(), sim_asynch_main_threadid )) { \\r
+ sim_aio_activate ((ACTIVATE_API)caller, uptr, event_time); \\r
+ return SCPE_OK; \\r
+ } else (void)0\r
+#else /* !USE_AIO_INTRINSICS */\r
+/* This approach uses a pthread mutex to manage access to the link list */\r
+/* head sim_asynch_queue. It will always work, but may be slower than the */\r
+/* lock free approach when using USE_AIO_INTRINSICS */\r
+#define AIO_QUEUE_MODE "Lock based asynchronous event queue access"\r
+#define AIO_INIT \\r
+ do { \\r
+ int tmr; \\r
+ pthread_mutexattr_t attr; \\r
+ \\r
+ pthread_mutexattr_init (&attr); \\r
+ pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); \\r
+ pthread_mutex_init (&sim_asynch_lock, &attr); \\r
+ pthread_mutexattr_destroy (&attr); \\r
+ sim_asynch_main_threadid = pthread_self(); \\r
+ /* Empty list/list end uses the point value (void *)1. \\r
+ This allows NULL in an entry's a_next pointer to \\r
+ indicate that the entry is not currently in any list */ \\r
+ sim_asynch_queue = QUEUE_LIST_END; \\r
+ for (tmr=0; tmr<SIM_NTIMERS; tmr++) \\r
+ sim_clock_cosched_queue[tmr] = QUEUE_LIST_END; \\r
+ } while (0)\r
+#define AIO_CLEANUP \\r
+ do { \\r
+ pthread_mutex_destroy(&sim_asynch_lock); \\r
+ pthread_cond_destroy(&sim_asynch_wake); \\r
+ pthread_mutex_destroy(&sim_timer_lock); \\r
+ pthread_cond_destroy(&sim_timer_wake); \\r
+ pthread_mutex_destroy(&sim_tmxr_poll_lock); \\r
+ pthread_cond_destroy(&sim_tmxr_poll_cond); \\r
+ } while (0)\r
+#define AIO_UPDATE_QUEUE \\r
+ do { \\r
+ UNIT *uptr; \\r
+ AIO_LOCK; \\r
+ while (sim_asynch_queue != QUEUE_LIST_END) { /* List !Empty */ \\r
+ int32 a_event_time; \\r
+ uptr = sim_asynch_queue; \\r
+ sim_debug (SIM_DBG_AIO_QUEUE, sim_dflt_dev, "Migrating Asynch event for %s after %d instructions\n", sim_uname(uptr), uptr->a_event_time);\\r
+ sim_asynch_queue = uptr->a_next; \\r
+ uptr->a_next = NULL; /* hygiene */ \\r
+ if (uptr->a_activate_call != &sim_activate_notbefore) { \\r
+ a_event_time = uptr->a_event_time-((sim_asynch_inst_latency+1)/2); \\r
+ if (a_event_time < 0) \\r
+ a_event_time = 0; \\r
+ } \\r
+ else \\r
+ a_event_time = uptr->a_event_time; \\r
+ AIO_UNLOCK; \\r
+ uptr->a_activate_call (uptr, a_event_time); \\r
+ if (uptr->a_check_completion) { \\r
+ sim_debug (SIM_DBG_AIO_QUEUE, sim_dflt_dev, "Calling Completion Check for asynch event on %s\n", sim_uname(uptr));\\r
+ uptr->a_check_completion (uptr); \\r
+ } \\r
+ AIO_LOCK; \\r
+ } \\r
+ AIO_UNLOCK; \\r
+ } while (0)\r
+#define AIO_ACTIVATE(caller, uptr, event_time) \\r
+ if (!pthread_equal ( pthread_self(), sim_asynch_main_threadid )) { \\r
+ sim_debug (SIM_DBG_AIO_QUEUE, sim_dflt_dev, "Queueing Asynch event for %s after %d instructions\n", sim_uname(uptr), event_time);\\r
+ AIO_LOCK; \\r
+ if (uptr->a_next) { /* already queued? */ \\r
+ uptr->a_activate_call = sim_activate_abs; \\r
+ } else { \\r
+ uptr->a_next = sim_asynch_queue; \\r
+ uptr->a_event_time = event_time; \\r
+ uptr->a_activate_call = (ACTIVATE_API)&caller; \\r
+ sim_asynch_queue = uptr; \\r
+ } \\r
+ if (sim_idle_wait) { \\r
+ sim_debug (TIMER_DBG_IDLE, &sim_timer_dev, "waking due to event on %s after %d instructions\n", sim_uname(uptr), event_time);\\r
+ pthread_cond_signal (&sim_asynch_wake); \\r
+ } \\r
+ AIO_UNLOCK; \\r
+ sim_asynch_check = 0; \\r
+ return SCPE_OK; \\r
+ } else (void)0\r
+#endif /* USE_AIO_INTRINSICS */\r
+#define AIO_VALIDATE if (!pthread_equal ( pthread_self(), sim_asynch_main_threadid )) {sim_printf("Improper thread context for operation\n"); abort();}\r
+#define AIO_CHECK_EVENT \\r
+ if (0 > --sim_asynch_check) { \\r
+ AIO_UPDATE_QUEUE; \\r
+ sim_asynch_check = sim_asynch_inst_latency; \\r
+ } else (void)0\r
+#define AIO_SET_INTERRUPT_LATENCY(instpersec) \\r
+ do { \\r
+ sim_asynch_inst_latency = (int32)((((double)(instpersec))*sim_asynch_latency)/1000000000);\\r
+ if (sim_asynch_inst_latency == 0) \\r
+ sim_asynch_inst_latency = 1; \\r
+ } while (0)\r
+#else /* !SIM_ASYNCH_IO */\r
+#define AIO_QUEUE_MODE "Asynchronous I/O is not available"\r
+#define AIO_UPDATE_QUEUE\r
+#define AIO_ACTIVATE(caller, uptr, event_time)\r
+#define AIO_VALIDATE\r
+#define AIO_CHECK_EVENT\r
+#define AIO_INIT\r
+#define AIO_MAIN_THREAD TRUE\r
+#define AIO_LOCK\r
+#define AIO_UNLOCK\r
+#define AIO_CLEANUP\r
+#define AIO_RETURN_TIME(uptr)\r
+#define AIO_EVENT_BEGIN(uptr)\r
+#define AIO_EVENT_COMPLETE(uptr, reason)\r
+#define AIO_IS_ACTIVE(uptr) FALSE\r
+#define AIO_CANCEL(uptr) \\r
+ if ((uptr)->cancel) \\r
+ (uptr)->cancel (uptr)\r
+#define AIO_SET_INTERRUPT_LATENCY(instpersec)\r
+#define AIO_TLS\r
+#endif /* SIM_ASYNCH_IO */\r
+\r
+#ifdef __cplusplus\r
+}\r
+#endif\r
+\r
+#endif\r
--- /dev/null
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include "definition.h"
+#include "object_map.h"
+#include "rassert.h"
+
+#define N_PATHS 1
+char *paths[] = {
+ "../tape/word/system_library_standard/"
+};
+
+int main(int argc, char **argv) {
+ if (argc < 2) {
+ printf("usage: %s executable [arguments]\n", argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ int fd;
+ int path_index;
+ for (path_index = 0; path_index < N_PATHS; ++path_index) {
+ char name[0x1000];
+ strcpy(name, paths[path_index]);
+ strcat(name, argv[1]);
+
+ fd = open(name, O_RDONLY);
+ if (fd != -1)
+ goto found_path;
+ }
+ fprintf(stderr, "can't find %s in paths list\n", argv[1]);
+ exit(EXIT_FAILURE);
+
+found_path:
+ ;
+ uint64_t *seg = mmap(
+ NULL,
+ 01000000 * sizeof(uint64_t),
+ PROT_READ,
+ MAP_SHARED,
+ fd,
+ (off_t)0
+ );
+ rassert(seg != (uint64_t *)-1);
+
+ uint64_t bitcount;
+ {
+ char name[0x1000];
+ strcpy(name, paths[path_index]);
+ strcat(name, ".dir");
+
+ FILE *fp = fopen(name, "r");
+ char line[0x100];
+ while (fgets(line, 0x100, fp)) {
+ char *p = strchr(line, ' ');
+ if (p) {
+ *p++ = 0;
+ if (strcmp(line, argv[1]) == 0) {
+ bitcount = strtol(p, NULL, 0);
+ goto found_bitcount;
+ }
+ }
+ }
+
+ fprintf(stderr, "can't find %s in %s\n", argv[1], name);
+ exit(EXIT_FAILURE);
+ }
+
+found_bitcount:
+ rassert(bitcount % 36 == 0);
+ uint32_t wordcount = (bitcount / 36) & 0777777;
+
+ struct object_map *object_map = (struct object_map *)(
+ seg + ((seg[(wordcount - 1) & 0777777] >> 18) & 0777777)
+ );
+ rassert(object_map->decl_vers == 2);
+ rassert(object_map->identifier[0] == 0157142152137); // 'obj_'
+ rassert(object_map->identifier[1] == 0155141160040); // 'map '
+
+ struct definition *definition = (struct definition *)(
+ seg + object_map->definition_offset
+ );
+
+ printf(
+ "%06o %06o\n",
+ object_map->definition_offset + definition->segname,
+ object_map->definition_offset + definition->symbol
+ );
+ return 0;
+}
--- /dev/null
+#ifndef _OBJECT_MAP_H
+#define _OBJECT_MAP_H
+
+// see object_map.incl.pl1
+
+// declare 1 object_map aligned based, /* Structure describing standard object map */
+struct object_map {
+ // 2 decl_vers fixed bin, /* Version number of current structure format */
+ uint64_t decl_vers;
+
+ // 2 identifier char (8) aligned, /* Must be the constant "obj_map" */
+ uint64_t identifier[2];
+
+ // 2 text_offset bit (18) unaligned, /* Offset relative to base of object segment of base of text section */
+ // 2 text_length bit (18) unaligned, /* Length in words of text section */
+ uint64_t text_length : 18;
+ uint64_t text_offset : 18;
+ uint64_t dummy0 : 28;
+
+ // 2 definition_offset bit (18) unaligned, /* Offset relative to base of object seg of base of definition section */
+ // 2 definition_length bit (18) unaligned, /* Length in words of definition section */
+ uint64_t definition_length : 18;
+ uint64_t definition_offset : 18;
+ uint64_t dummy1 : 28;
+
+ // 2 linkage_offset bit (18) unaligned, /* Offset relative to base of object seg of base of linkage section */
+ // 2 linkage_length bit (18) unaligned, /* Length in words of linkage section */
+ uint64_t linkage_length : 18;
+ uint64_t linkage_offset : 18;
+ uint64_t dummy2 : 28;
+
+ // 2 static_offset bit (18) unaligned, /* Offset relative to base of obj seg of static section */
+ // 2 static_length bit (18) unaligned, /* Length in words of static section */
+ uint64_t static_length : 18;
+ uint64_t static_offset : 18;
+ uint64_t dummy3 : 28;
+
+ // 2 symbol_offset bit (18) unaligned, /* Offset relative to base of object seg of base of symbol section */
+ // 2 symbol_length bit (18) unaligned, /* Length in words of symbol section */
+ uint64_t symbol_length : 18;
+ uint64_t symbol_offset : 18;
+ uint64_t dummy4 : 28;
+
+ // 2 break_map_offset bit (18) unaligned, /* Offset relative to base of object seg of base of break map */
+ // 2 break_map_length bit (18) unaligned, /* Length in words of break map */
+ uint64_t break_map_length : 18;
+ uint64_t break_map_offset : 18;
+ uint64_t dummy5 : 28;
+
+ // 2 entry_bound bit (18) unaligned, /* Offset in text of last gate entry */
+ // 2 text_link_offset bit (18) unaligned, /* Offset of first text-embedded link */
+ uint64_t text_link_offset : 18;
+ uint64_t entry_bound : 18;
+ uint64_t dummy6 : 28;
+
+ // 2 format aligned, /* Word containing bit flags about object type */
+ // 3 bound bit (1) unaligned, /* On if segment is bound */
+ // 3 relocatable bit (1) unaligned, /* On if segment has relocation info in its first symbol block */
+ // 3 procedure bit (1) unaligned, /* On if segment is an executable object program */
+ // 3 standard bit (1) unaligned, /* On if segment is in standard format (more than just standard map) */
+ // 3 separate_static bit(1) unaligned, /* On if static is a separate section from linkage */
+ // 3 links_in_text bit (1) unaligned, /* On if there are text-embedded links */
+ // 3 perprocess_static bit (1) unaligned, /* On if static is not to be per run unit */
+ // 3 unused bit (29) unaligned; /* Reserved */
+ uint64_t format_unused : 29;
+ uint64_t format_perprocess_static : 1;
+ uint64_t format_links_in_text : 1;
+ uint64_t format_separate_static : 1;
+ uint64_t format_standard : 1;
+ uint64_t format_procedure : 1;
+ uint64_t format_relocatable : 1;
+ uint64_t format_bound : 1;
+ uint64_t dummy7 : 28;
+};
+
+#endif
--- /dev/null
+/* BEGIN INCLUDE FILE definition.incl.pl1 */
+
+
+
+/****^ HISTORY COMMENTS:
+ 1) change(86-05-02,Elhard), approve(86-05-02,MCR7391),
+ audit(86-07-18,DGHowe), install(86-11-20,MR12.0-1222):
+ Modified to add indirect bit to definition flags.
+ END HISTORY COMMENTS */
+
+
+dcl 1 definition aligned based,
+ 2 forward unal bit(18), /* offset of next def */
+ 2 backward unal bit(18), /* offset of previous def */
+ 2 value unal bit(18),
+ 2 flags unal,
+ 3 new bit(1),
+ 3 ignore bit(1),
+ 3 entry bit(1),
+ 3 retain bit(1),
+ 3 argcount bit(1),
+ 3 descriptors bit(1),
+ 3 indirect bit(1),
+ 3 unused bit(8),
+ 2 class unal bit(3),
+ 2 symbol unal bit(18), /* offset of ACC for symbol */
+ 2 segname unal bit(18); /* offset of segname def */
+
+/* END INCLUDE FILE definition.incl.pl1 */
--- /dev/null
+/* Begin include file definition_dcls.incl.pl1 BIM 1981 */
+
+
+/****^ HISTORY COMMENTS:
+ 1) change(86-05-02,Elhard), approve(86-05-02,MCR7391),
+ audit(86-07-18,DGHowe), install(86-11-20,MR12.0-1222):
+ Modified to add indirect bit to definition flags, add msf_map_relp to the
+ definition header, declare the msf_map, and add structures and constants
+ for deferred initialization.
+ 2) change(86-06-24,DGHowe), approve(86-06-24,MCR7420),
+ audit(86-08-05,Schroth), install(86-11-20,MR12.0-1222):
+ added the structures for pointer initialization. pointer_init_template.
+ changed list_template_entry
+ END HISTORY COMMENTS */
+
+
+/* Modified: */
+/* 13 Dec 1982 by Lee A. Newcomb to put definition_header.hash_table_relp */
+/* after unused half-word instead of before it. */
+/* 1 March 1983 by M. Weaver to add list template init type */
+
+/* format: style3,idind25 */
+/* everything for the definition section */
+
+declare (
+ CLASS_TEXT init (0), /* text section definition */
+ CLASS_LINKAGE init (1), /* linkage section definition */
+ CLASS_SYMBOL init (2), /* symbol section definition */
+ CLASS_SEGNAME init (3), /* segment name definition */
+ CLASS_STATIC init (4), /* static section definition */
+ CLASS_SYSTEM init (5), /* valid only in self links, not def class */
+ CLASS_HEAP init (6) /* valid only in self links, not def class */
+ ) fixed bin (3) unsigned internal static options (constant);
+
+declare CLASS_NAMES (0:6) character (12) internal static options (constant)
+ init ("text", "linkage", "symbol", "segname", "static", "system", "heap");
+
+declare SYMBOLIC_SECTION_NAMES (0:6) character (8)
+ init ("*text", "*link", "*symbol", *, "*static", "*system", "*heap") internal static
+ options (constant);
+
+declare 1 definition_flags unaligned based,
+ 2 new bit (1), /* should be "1"b */
+ 2 ignore bit (1), /* cannot snap link to this */
+ 2 entry bit (1), /* can tra here */
+ 2 retain bit (1), /* binder respects this */
+ 2 argcount bit (1), /* OBSOLETE */
+ 2 descriptors bit (1), /* OBSOLETE */
+ 2 indirect bit (1), /* target is a pointer to actual target */
+ 2 unused bit (8); /* Must be zero */
+
+
+
+
+/* Header of the definition section */
+
+declare def_header_ptr pointer;
+declare 1 definition_header aligned based (def_header_ptr),
+ 2 def_list_relp fixed bin (18) unsigned unaligned,
+ /* first definition, reloc def18 */
+ 2 msf_map_relp fixed bin (18) unsigned unaligned,
+ /* msf_map if any, or 0 if none. reloc def18 unless none */
+ 2 hash_table_relp fixed bin (18) unsigned unaligned,
+ /* def hash table, if any, or 0 if none. reloc def18 unless none */
+ 2 flags unaligned like definition_flags;
+ /* both new and ignore must be "1"b here */
+
+/* A non class=3 definition. See segname_definition below for class=3 */
+
+
+declare def_ptr pointer;
+declare 1 definition aligned based (def_ptr),
+ 2 forward_relp unal fixed bin (18) unsigned,
+ /* offset of next def */
+ 2 backward_relp unal fixed bin (18) unsigned,
+ /* offset of previous def */
+ 2 thing_relp unal fixed bin (18) unsigned,
+ /* offset in section specified by class of thing this defines */
+ 2 flags unaligned like definition_flags,
+ 2 class unal fixed bin (3) unsigned,
+ /* Type of definition */
+ 2 name_relp unal fixed bin (18) unsigned,
+ /* offset of ACC for symbol */
+ 2 segname_relp unal fixed bin (18) unsigned;
+ /* offset of segname def to which this belongs */
+
+/* Class=3, segname definition */
+
+declare segname_ptr pointer;
+declare 1 segname_definition aligned based (segname_ptr),
+ 2 forward_relp unal fixed bin (18) unsigned,
+ /* offset of next def */
+ 2 backward_relp unal fixed bin (18) unsigned,
+ /* offset of previous def */
+ 2 next_segname_relp unal fixed bin (18) unsigned,
+ /* offset of next segname def */
+ 2 flags unaligned like definition_flags,
+ 2 class unal fixed bin (3) unsigned,
+ /* 3 for segname */
+ 2 name_relp unal fixed bin (18) unsigned,
+ /* offset of ACC for symbol */
+ 2 first_relp unal fixed bin (18) unsigned;
+ /* see following : */
+
+/* Definition blocks are chained off of segname definitions.
+ segname_definition.first_relp is one of three things:
+ (1) the def section offset of the first ordinary (class^=3) definition
+ belonging to this segname block. In the case where there are more than
+ one segname's on a block, all their first_relp will point
+ to the same place.
+
+ (2) if there are no ordinary definitions associated with this segname,
+ then it is the def section offset of the next segname.
+
+ (3) if there are no ordinary definitions in the block, and it
+ is the last block, then it points to a word containing 0.
+
+ Thus the end of a list of synonym segnames can be detected by forward_relp
+ pointing to a class=3 definition whose first_relp is not the same as
+ the current definitions first_relp.
+*/
+
+/* All the definitions are linked through the forward and
+ backward thread variables. The end of the chain can is indicated
+ by forward pointing to a zero word. */
+
+
+declare exp_ptr pointer;
+declare 1 exp_word based (exp_ptr) aligned, /* expression word in link definition */
+ 2 type_relp fixed bin (18) unsigned unal,
+ /* pointer (rel to defs) of type pair structure */
+ 2 expression fixed bin (17) unal; /* constant expression to be added in when snapping link */
+
+declare (
+ LINK_SELF_BASE init (1), /* *section|0+expression,modifier */
+ /* which section determined by segname_relp */
+ LINK_OBSOLETE_2 init (2), /* not used */
+ LINK_REFNAME_BASE init (3), /* refname|0+expression,modifier */
+ LINK_REFNAME_OFFSETNAME init (4), /* refname|offsetname+expression,modifier */
+ LINK_SELF_OFFSETNAME init (5), /* *section|offsetname+expression,modifier */
+ LINK_CREATE_IF_NOT_FOUND init (6), /* OBSOLETE: like LINK_REFNAME_OFFSETNAME except that it will create instead of taking linkage_error */
+ SECTION_TEXT init (0), /* *text */
+ SECTION_LINK init (1), /* *link */
+ SECTION_SYMBOL init (2), /* *symbol */
+ SECTION_UNUSED init (3), /* reserved */
+ SECTION_STATIC init (4), /* *static */
+ SECTION_SYSTEM init (5), /* *system */
+ SECTION_HEAP init (6) /* *heap */
+ ) fixed bin (18) unsigned unaligned internal static options (constant);
+
+/* use CLASS_NAMES for section names */
+
+declare LINK_TYPE_NAMES (1:6)
+ init ("absolute in section", "unused", "absolute off of refname",
+ "symbolic off of refname", "symbolic in section", "symbolic off of refname; create")
+ character (32) varying internal static options (constant);
+
+
+declare type_ptr pointer;
+declare 1 type_pair based (type_ptr) aligned,/* type pair in link definition */
+ 2 type fixed bin (18) unsigned unal,
+ /* see above */
+ 2 trap_relp fixed bin (18) unsigned unal,
+ /* pointer (rel to defs) to the trap word */
+ /* unless LINK_SELF_OFFSETNAME off of *system or create link */
+ 2 segname_relp fixed bin (18) unsigned unal,
+ /* pointer (rel to defs) to ACC reference name for segment referenced,
+ /*or section code for SELF links */
+ 2 offsetname_relp fixed bin (18) unsigned unal;
+ /* for OFFSETNAME links, ACC string of name of location. */
+ /* for others, must be ZERO */
+
+
+/* Link Trap Pair */
+
+declare link_trap_ptr pointer;
+declare 1 link_trap_pair aligned based (link_trap_ptr),
+ 2 call_relp fixed bin (18) unsigned unaligned,
+ /* LINK18, link to thing to call */
+ 2 info_relp fixed bin (18) unsigned unaligned;
+ /* LINK18, link to argument list */
+
+
+/* initialization info for *system or *heap link */
+
+
+/* NOTE --------------------------------------------------
+ the following structures defining initialization information are also
+ defined in fortran_storage.incl.pl1 system_link_init_info.incl.pl1
+ and should be kept equivalent
+ -------------------------------------------------------
+*/
+
+declare (
+ INIT_NO_INIT init (0),
+ INIT_COPY_INFO init (3),
+ INIT_DEFINE_AREA init (4),
+ INIT_LIST_TEMPLATE init (5),
+ INIT_DEFERRED init (6)
+ ) fixed bin internal static options (constant);
+
+/* for type = 0 or 4 */
+
+declare link_init_ptr pointer;
+declare 1 link_init aligned based (link_init_ptr),
+ 2 n_words fixed bin (35), /* number to invent */
+ 2 type fixed bin; /* see types above */
+
+/* for type=3, there is data to copy */
+
+declare 1 link_init_copy_info aligned based (link_init_ptr),
+ 2 header aligned like link_init,
+ 2 initial_data (link_init_n_words refer (link_init_copy_info.header.n_words)) bit (36) aligned;
+
+declare link_init_n_words fixed bin;
+
+/* for type = 5, there is a list template to copy */
+
+declare 1 link_init_list_template
+ aligned based (link_init_ptr),
+ 2 header aligned like link_init,
+ 2 pad bit (18) unaligned,
+ 2 n_words_in_list fixed bin (18) unsigned unaligned,
+ 2 template (link_init_n_words_in_list refer (link_init_list_template.n_words_in_list));
+
+declare link_init_n_words_in_list
+ fixed bin;
+
+/* A list template consists of a series of entries with the following
+ description, concatenated together. n_bits and datum are bit items,
+ to permit a wide range of inputs.
+
+ 1. A 'repeat' of '0' signifies skipping of 'n_bits' bits.
+ 2. A 'n_bits' of '0' signifies the last item of the list.
+
+ COMMON, VLA's, and LA's are presumed to start at the base pointer
+ of their particular storage section. */
+
+declare 1 list_template_entry aligned based,
+ 2 n_bits fixed bin (35) aligned, /* size of datum */
+ 2 mbz bit (3) unaligned, /* future expansion */
+ 2 init_type fixed bin (3) unsigned unaligned, /* 0 normal init, 1 ptr init, 2 packed ptr init */
+ 2 repeat fixed bin (30) unsigned unaligned,
+ /* number of times to repeat datum */
+ 2 datum bit (link_init_n_bits_in_datum refer (list_template_entry.n_bits));
+
+
+/* the pointer_init_template represents the initialization information
+ for ITS and packed pointers. Both pointer types require the entire
+ 72 bit structure.
+*/
+
+dcl 1 pointer_init_template based,
+ 2 ptr_type fixed bin (18) unsigned unaligned, /* 0 text section, 1 linkage section, 2 static section */
+ 2 section_offset fixed bin (18) unsigned unaligned, /* offset to item in specified section */
+ 2 word_offset fixed bin (18) unsigned unaligned, /* offset from section item to target in words */
+ 2 mbz bit (12) unaligned,
+ 2 bit_offset fixed bin (6) unsigned unaligned; /* offset from section item|word offset to target in bits */
+
+
+declare link_init_n_bits_in_datum
+ fixed bin (35);
+
+/* for type = 6, the init_info resides in another MSF component */
+/* target_relp is a linkage section offset to a partial link to */
+/* the base of the linkage section of the component containing */
+/* the actual init_info. link_relp is the offset of the actual */
+/* link within that linkage section. */
+
+declare 1 link_init_deferred aligned based (link_init_ptr),
+ 2 header aligned like link_init,
+ 2 target_relp fixed bin (18) unsigned unaligned,
+ 2 link_relp fixed bin (18) unsigned unaligned;
+
+/* Definition section hash table */
+
+declare def_ht_ptr pointer;
+declare 1 definition_ht aligned based (def_ht_ptr),
+ 2 n_entries fixed bin,
+ 2 table (def_ht_n_entries refer (definition_ht.n_entries)) aligned,
+ 3 def_relp fixed bin (18) unsigned unaligned,
+ 3 unused bit (18) unaligned;
+
+declare def_ht_n_entries fixed bin;
+
+
+/* Component name ht */
+declare comp_ht_ptr pointer;
+declare 1 component_ht aligned based (comp_ht_ptr),
+ 2 n_entries fixed bin,
+ 2 table (comp_ht_n_entries refer (component_ht.n_entries)) aligned,
+ 3 def_relp fixed bin (18) unsigned unaligned,
+ /* hashed segname */
+ 3 block_hdr_relp fixed bin (18) unsigned unaligned;
+ /* first segname def of block containing def_relp */
+
+declare comp_ht_n_entries fixed bin;
+
+/* Duplicate name table */
+
+declare dup_table_ptr pointer;
+declare 1 duplicate_table aligned based (dup_table_ptr),
+ 2 mbz bit (18) unaligned, /* to tell it from a definition */
+ 2 n_names fixed bin (18) unsigned unaligned,
+ /* n in table */
+ 2 table (dup_table_n_names refer (duplicate_table.n_names)) aligned,
+ 3 def_relp fixed bin (18) unsigned unaligned,
+ 3 block_hdr_relp fixed bin (18) unsigned unaligned;
+
+declare dup_table_n_names fixed bin;
+
+/* The msf_map is found in the definition section of an */
+/* object MSF component. It is used by the linker to */
+/* determine whether a segment is a component of an object */
+/* MSF or a standard single-segment object. */
+
+ dcl msf_map_ptr ptr;
+ dcl 01 msf_map aligned based (msf_map_ptr),
+ 02 version char (8),
+ 02 component_count fixed bin (15) unsigned,
+ 02 my_component fixed bin (15) unsigned;
+
+ dcl msf_map_version_1 char (8) static options (constant)
+ init ("msfmp1.0");
+
+declare acc_string_ptr pointer;
+declare 1 acc_string aligned based (acc_string_ptr),
+ 2 count fixed bin (9) unsigned unaligned,
+ 2 string character (max (3, acc_string_length) refer (acc_string.count)) unaligned,
+ 2 mbz bit (0) aligned; /* this causes the statement */
+ /* unspec (acc_string) = ""b to zero out */
+ /* the last word, if the string is not of length 0mod4 */
+
+declare acc_string_length fixed bin (21);
+
+
+/* end include file definitions_dcls.incl.pl1 */
--- /dev/null
+/* BEGIN INCLUDE FILE ... object_map.incl.pl1 */
+/* coded February 8, 1972 by Michael J. Spier */
+/* Last modified on 05/20/72 at 13:29:38 by R F Mabee. */
+/* Made to agree with Spier's document on 20 May 1972 by R F Mabee. */
+/* modified on 6 May 1972 by R F Mabee to add map_ptr at end of object map. */
+/* modified May, 1972 by M. Weaver */
+/* modified 5/75 by E. Wiatrowski and 6/75 by M. Weaver */
+/* modified 5/77 by M. Weaver to add perprocess_static bit */
+
+ declare 1 object_map aligned based, /* Structure describing standard object map */
+
+ 2 decl_vers fixed bin, /* Version number of current structure format */
+ 2 identifier char (8) aligned, /* Must be the constant "obj_map" */
+ 2 text_offset bit (18) unaligned, /* Offset relative to base of object segment of base of text section */
+ 2 text_length bit (18) unaligned, /* Length in words of text section */
+ 2 definition_offset bit (18) unaligned, /* Offset relative to base of object seg of base of definition section */
+ 2 definition_length bit (18) unaligned, /* Length in words of definition section */
+ 2 linkage_offset bit (18) unaligned, /* Offset relative to base of object seg of base of linkage section */
+ 2 linkage_length bit (18) unaligned, /* Length in words of linkage section */
+ 2 static_offset bit (18) unaligned, /* Offset relative to base of obj seg of static section */
+ 2 static_length bit (18) unaligned, /* Length in words of static section */
+ 2 symbol_offset bit (18) unaligned, /* Offset relative to base of object seg of base of symbol section */
+ 2 symbol_length bit (18) unaligned, /* Length in words of symbol section */
+ 2 break_map_offset bit (18) unaligned, /* Offset relative to base of object seg of base of break map */
+ 2 break_map_length bit (18) unaligned, /* Length in words of break map */
+ 2 entry_bound bit (18) unaligned, /* Offset in text of last gate entry */
+ 2 text_link_offset bit (18) unaligned, /* Offset of first text-embedded link */
+ 2 format aligned, /* Word containing bit flags about object type */
+ 3 bound bit (1) unaligned, /* On if segment is bound */
+ 3 relocatable bit (1) unaligned, /* On if segment has relocation info in its first symbol block */
+ 3 procedure bit (1) unaligned, /* On if segment is an executable object program */
+ 3 standard bit (1) unaligned, /* On if segment is in standard format (more than just standard map) */
+ 3 separate_static bit(1) unaligned, /* On if static is a separate section from linkage */
+ 3 links_in_text bit (1) unaligned, /* On if there are text-embedded links */
+ 3 perprocess_static bit (1) unaligned, /* On if static is not to be per run unit */
+ 3 unused bit (29) unaligned; /* Reserved */
+
+declare map_ptr bit(18) aligned based; /* Last word of the segment. It points to the base of the object map. */
+
+declare object_map_version_2 fixed bin static init(2);
+
+/* END INCLUDE FILE ... object_map.incl.pl1 */
--- /dev/null
+#ifndef _RASSERT_H
+#define _RASSERT_H 1
+
+// tries to be the same as assert(), but always present even with NDEBUG
+
+#ifdef __GLIBC__
+extern void __assert_fail (const char *__assertion, const char *__file,
+ unsigned int __line, const char *__function)
+ __THROW __attribute__ ((__noreturn__));
+
+# define rassert(expr) \
+ ((expr) \
+ ? (void) (0) \
+ : __assert_fail (#expr, __FILE__, __LINE__, __extension__ __PRETTY_FUNCTION__))
+#else
+// for unsupported platforms, it won't work with NDEBUG
+#include <assert.h>
+
+#define rassert(expr) assert(expr)
+#endif
+
+#endif