blob: 623e163b6445d97315c8bc011171a2e2d30d9b41 [file] [log] [blame] [raw]
/* sigma_cis.c: Sigma decimal instructions
Copyright (c) 2007-2018, Robert M Supnik
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the name of Robert M Supnik shall not be
used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from Robert M Supnik.
Questions:
1. On the Sigma 9, in ASCII mode, is an ASCII blank used in EBS?
02-Jun-2018 RMS Fixed unsigned < 0 in decimal compare (Mark Pizzolato)
*/
#include "sigma_defs.h"
/* Decimal string structure */
#define DSTRLNT 4 /* words per dec string */
#define DECA 12 /* first dec accum reg */
/* Standard characters */
#define ZONE_E 0xF0 /* EBCDIC zone bits */
#define ZONE_A 0x30 /* ASCII zone bits */
#define ZONE ((PSW1 & PSW1_AS)? ZONE_A: ZONE_E)
#define PKPLUS_E 0xC /* EBCDIC preferred plus */
#define PKPLUS_A 0xA /* ASCII preferred plus */
#define PKPLUS ((PSW1 & PSW1_AS)? PKPLUS_A: PKPLUS_E)
#define BLANK_E 0x40 /* EBCDIC blank */
#define BLANK_A 0x20 /* ASCII blank */
#define BLANK ((PSW1 & PSW1_AS)? BLANK_A: BLANK_E)
/* Edit special characters */
#define ED_DS 0x20 /* digit select */
#define ED_SS 0x21 /* start significance */
#define ED_FS 0x22 /* field separator */
#define ED_SI 0x23 /* immediate significance */
/* Decimal strings run low order (word 0/R15) to high order (word 3/R12) */
typedef struct {
uint32 sign;
uint32 val[DSTRLNT];
} dstr_t;
/* Copy decimal accumulator to decimal string, no validation or sign separation */
#define ReadDecA(src) for (i = 0; i < DSTRLNT; i++) \
src.val[DSTRLNT - 1 - i] = R[DECA + i];
static dstr_t Dstr_zero = { 0, 0, 0, 0, 0 };
extern uint32 *R;
extern uint32 CC;
extern uint32 PSW1;
extern uint32 bvamqrx;
extern uint32 cpu_model;
uint32 ReadDstr (uint32 lnt, uint32 addr, dstr_t *dec);
uint32 WriteDstr (uint32 lnt, uint32 addr, dstr_t *dec);
void WriteDecA (dstr_t *dec, t_bool cln);
void SetCC2Dstr (uint32 lnt, dstr_t *dst);
uint32 TestDstrValid (dstr_t *src);
uint32 DstrInvd (void);
uint32 AddDstr (dstr_t *src1, dstr_t *src2, dstr_t *dst, uint32 cin);
void SubDstr (dstr_t *src1, dstr_t *src2, dstr_t *dst);
int32 CmpDstr (dstr_t *src1, dstr_t *src2);
uint32 LntDstr (dstr_t *dsrc);
uint32 NibbleLshift (dstr_t *dsrc, uint32 sc, uint32 cin);
uint32 NibbleRshift (dstr_t *dsrc, uint32 sc, uint32 cin);
t_bool GenLshift (dstr_t *dsrc, uint32 sc);
void GenRshift (dstr_t *dsrc, uint32 sc);
uint32 ed_getsrc (uint32 sa, uint32 *c, uint32 *d);
void ed_advsrc (uint32 rn, uint32 c);
t_bool cis_test_int (dstr_t *src1, uint32 *kint);
void cis_dm_int (dstr_t *src, dstr_t *dst, uint32 kint);
void cis_dd_int (dstr_t *src, dstr_t *dst, uint32 t, uint32 *kint);
/* Decimal instructions */
uint32 cis_dec (uint32 op, uint32 lnt, uint32 bva)
{
dstr_t src1, src2, src2x, dst;
uint32 i, t, kint, ldivr, ldivd, ad, c, d, end;
int32 sc, scmp;
uint32 tr;
if (lnt == 0) /* adjust length */
lnt = 16;
CC &= ~(CC1|CC2); /* clear CC1, CC2 */
switch (op) { /* case on opcode */
case OP_DL: /* decimal load */
if ((tr = ReadDstr (lnt, bva, &dst)) != 0) /* read mem string */
return tr;
WriteDecA (&dst, FALSE); /* store result */
break;
case OP_DST: /* decimal store */
ReadDecA (dst); /* read dec accum */
if ((tr = TestDstrValid (&dst)) != 0) /* valid? */
return tr;
if ((tr = WriteDstr (lnt, bva, &dst)) != 0) /* write to mem */
return tr;
break;
case OP_DS: /* decimal subtract */
case OP_DA: /* decimal add */
ReadDecA (src1); /* read dec accum */
if ((tr = TestDstrValid (&src1)) != 0) /* valid? */
return tr;
if ((tr = ReadDstr (lnt, bva, &src2)) != 0) /* read mem string */
return tr;
if (op == OP_DS) /* sub? invert sign */
src2.sign = src2.sign ^ 1;
if (src1.sign ^ src2.sign) { /* opp signs? sub */
if (CmpDstr (&src1, &src2) < 0) { /* src1 < src2? */
SubDstr (&src1, &src2, &dst); /* src2 - src1 */
dst.sign = src2.sign; /* sign = src2 */
}
else {
SubDstr (&src2, &src1, &dst); /* src1 - src2 */
dst.sign = src1.sign; /* sign = src1 */
}
}
else { /* addition */
if (AddDstr (&src1, &src2, &dst, 0)) { /* add, overflow? */
CC |= CC2; /* set CC2 */
return (PSW1 & PSW1_DM)? TR_DEC: 0; /* trap if enabled */
}
dst.sign = src1.sign; /* set result sign */
}
WriteDecA (&dst, TRUE); /* store result */
break;
case OP_DC: /* decimal compare */
ReadDecA ( src1); /* read dec accum */
if ((tr = TestDstrValid (&src1)) != 0) /* valid? */
return tr;
if ((tr = ReadDstr (lnt, bva, &src2)) != 0) /* read mem string */
return tr;
LntDstr (&src1); /* clean -0 */
LntDstr (&src2);
if (src1.sign ^ src2.sign) /* signs differ? */
CC = src1.sign? CC4: CC3; /* set < or > */
else { /* same signs */
scmp = CmpDstr (&src1, &src2); /* compare strings */
if (scmp < 0)
CC = (src1.sign? CC3: CC4);
else if (scmp > 0)
CC = (src1.sign? CC4: CC3);
else CC = 0;
}
break;
/* Decimal multiply - algorithm from George Plue.
The Sigma does decimal multiply one digit at a time, using the multiplicand
and a doubled copy of the multiplicand. Multiplying by digits 1-5 is
synthesized by 1-3 adds; multiplying by digits 6-9 is synthesized by 1-2
subtractions, and adding 1 to the next multiplier digit. (That is,
multiplying by 7 is done by multiplying by "10 - 3".) This requires at
most one extra add to fixup the last digit, and minimizes the overall
number of adds (average 1.5 adds per multiplier digit). Note that
multiplication proceeds from right to left.
The Sigma 5-9 allowed decimal multiply to be interrupted; the 5X0 series
did not. An interrupted multiply uses a sign digit in R12 and R13 as the
divider between the remaining multiplier (to the left of the sign, and
in the low-order digit of R15) and the partial product (to the right of
the sign). Because the partial product may be negative, leading 0x99's
may have been stripped and need to be restored.
The real Sigma's probably didn't run a validty test after separation of
the partial product and multiplier, but it doesn't hurt, and prevents
certain corner cases from causing errors. */
case OP_DM: /* decimal multiply */
if (lnt >= 9) /* invalid length? */
return DstrInvd ();
ReadDecA (src1); /* get dec accum */
if ((tr = ReadDstr (lnt, bva, &src2)) != 0) /* read mem string */
return tr;
dst = Dstr_zero; /* clear result */
kint = 0; /* assume no int */
if (!QCPU_5X0 && /* S5-9? */
(cis_test_int (&src1, &kint))) { /* interrupted? */
src1.sign = 0;
cis_dm_int (&src1, &dst, kint); /* restore */
}
else if ((tr = TestDstrValid (&src1)) != 0) /* mpyr valid? */
return tr;
if (LntDstr (&src1) && LntDstr (&src2)) { /* both opnds != 0? */
dst.sign = src1.sign ^ src2.sign; /* sign of result */
AddDstr (&src2, &src2, &src2x, 0); /* get 2*mplcnd */
for (i = 1; i <= 16; i++) { /* 16 iterations */
if (i >= kint) { /* past int point? */
NibbleRshift (&src1, 1, 0); /* mpyr right 4 */
d = src1.val[0] & 0xF; /* get digit */
switch (d) { /* case */
case 5: /* + 2 + 2 + 1 */
AddDstr (&src2x, &dst, &dst, 0);
case 3: /* + 2 + 1 */
AddDstr (&src2x, &dst, &dst, 0);
case 1: /* + 1 */
AddDstr (&src2, &dst, &dst, 0);
case 0:
break;
case 4: /* + 2 + 2 */
AddDstr (&src2x, &dst, &dst, 0);
case 2: /* + 2 */
AddDstr (&src2x, &dst, &dst, 0);
break;
case 6: /* - 2 - 2 + 10 */
SubDstr (&src2x, &dst, &dst);
case 8: /* - 2 + 10 */
SubDstr (&src2x, &dst, &dst);
src1.val[0] += 0x10; /* + 10 */
break;
case 7: /* -2 - 1 + 10 */
SubDstr (&src2x, &dst, &dst);
case 9: /* -1 + 10 */
SubDstr (&src2, &dst, &dst);
default: /* + 10 */
src1.val[0] += 0x10;
} /* end switch */
} /* end if >= kint */
NibbleLshift (&src2, 1, 0); /* shift mplcnds */
NibbleLshift (&src2x, 1, 0);
} /* end for */
} /* end if != 0 */
WriteDecA (&dst, TRUE); /* store result */
break;
/* Decimal divide overflow calculation - if the dividend has true length d,
and the divisor true length r, then the quotient will have (d - r) or
(d - r + 1) digits. Therefore, if (d - r) > 15, the quotient will not
fit. However, if (d - r) == 15, it may or may not fit, depending on
whether the first subtract succeeds. Therefore, it's necessary to test
after the divide to see if the quotient has one extra digit. */
case OP_DD: /* decimal divide */
if (lnt >= 9) /* invalid length? */
return DstrInvd ();
ReadDecA (src1); /* read dec accum */
if ((tr = ReadDstr (lnt, bva, &src2)) != 0) /* read mem string */
return tr;
dst = Dstr_zero; /* clear result */
kint = 0; /* no interrupt */
if (!QCPU_5X0 && /* S5-9? */
(cis_test_int (&src1, &t))) { /* interrupted? */
src1.sign = 0;
cis_dd_int (&src1, &dst, t, &kint); /* restore */
t = t - 1;
}
else { /* normal start? */
if ((tr = TestDstrValid (&src1)) != 0) /* divd valid? */
return tr;
ldivr = LntDstr (&src2); /* divr lnt */
ldivd = LntDstr (&src1); /* divd lnt */
if ((ldivr == 0) || /* div by zero? */
(ldivd > (ldivr + 15))) { /* quo too big? */
CC |= CC2; /* divide check */
return (PSW1 & PSW1_DM)? TR_DEC: 0; /* trap if enabled */
}
if (CmpDstr (&src1, &src2) < 0) { /* no divide? */
R[12] = src1.val[1]; /* remainder */
R[13] = src1.val[0] | (PKPLUS + src1.sign);
R[14] = 0; /* quotient */
R[15] = PKPLUS;
CC = 0;
return SCPE_OK;
}
t = ldivd - ldivr;
}
dst.sign = src1.sign ^ src2.sign; /* calculate sign */
GenLshift (&src2, t); /* align */
for (i = 0; i <= t; i++) { /* divide loop */
for (d = kint; /* find digit */
(d < 10) && (CmpDstr (&src1, &src2) >= 0);
d++)
SubDstr (&src2, &src1, &src1);
dst.val[0] = (dst.val[0] & ~0xF) | d; /* insert quo dig */
NibbleLshift (&dst, 1, 0); /* shift quotient */
NibbleRshift (&src2, 1, 0); /* shift divisor */
kint = 0; /* no more int */
} /* end divide loop */
if (dst.val[2]) { /* quotient too big? */
CC |= CC2; /* divide check */
return (PSW1 & PSW1_DM)? TR_DEC: 0; /* trap if enabled */
}
CC = dst.sign? CC4: CC3; /* set CC's */
R[12] = src1.val[1]; /* remainder */
R[13] = src1.val[0] | (PKPLUS + src1.sign);
R[14] = dst.val[1]; /* quotient */
R[15] = dst.val[0] | (PKPLUS + dst.sign);
break;
case OP_DSA: /* decimal shift */
ReadDecA (dst); /* read dec accum */
if ((tr = TestDstrValid (&dst)) != 0) /* valid? */
return tr;
CC = 0; /* clear CC's */
sc = SEXT_H_W (bva >> 2); /* shift count */
if (sc > 31) /* sc in [-31,31] */
sc = 31;
if (sc < -31)
sc = -31;
if (sc < 0) { /* right shift? */
sc = -sc; /* |shift| */
GenRshift (&dst, sc); /* do shift */
dst.val[0] = dst.val[0] & ~0xF; /* clear sign */
} /* end right shift */
else if (sc) { /* left shift? */
if (GenLshift (&dst, sc)) /* do shift */
CC |= CC2;
} /* end left shift */
WriteDecA (&dst, FALSE); /* store result */
break;
case OP_PACK: /* zoned to packed */
dst = Dstr_zero; /* clear result */
end = (2 * lnt) - 1; /* zoned length */
for (i = 1; i <= end; i++) { /* loop thru char */
ad = (bva + end - i) & bvamqrx; /* zoned character */
if ((tr = ReadB (ad, &c, VR)) != 0) /* read char */
return tr;
if (i == 1) { /* sign + digit? */
uint32 s;
s = (c >> 4) & 0xF; /* get sign */
if (s < 0xA)
return DstrInvd ();
if ((s == 0xB) || (s == 0xD)) /* negative */
dst.sign = 1;
}
d = c & 0xF; /* get digit */
if (d > 0x9)
return DstrInvd ();
dst.val[i / 8] = dst.val[i / 8] | (d << ((i % 8) * 4));
}
WriteDecA (&dst, FALSE); /* write result */
break;
case OP_UNPK: /* packed to zoned */
ReadDecA (dst); /* read dec accum */
if ((tr = TestDstrValid (&dst)) != 0) /* valid? */
return tr;
end = (2 * lnt) - 1; /* zoned length */
if ((tr = ReadB (bva, &c, VW)) != 0) /* prove writeable */
return tr;
for (i = 1; i <= end; i++) { /* loop thru chars */
c = (dst.val[i / 8] >> ((i % 8) * 4)) & 0xF; /* get digit */
if (i == 1) /* first? */
c |= ((PKPLUS + dst.sign) << 4); /* or in sign */
else c |= ZONE; /* no, or in zone */
ad = (bva + end - i) & bvamqrx;
if ((tr = WriteB (ad, c, VW)) != 0) /* write to memory */
return tr;
}
SetCC2Dstr (lnt, &dst); /* see if too long */
break;
}
return 0;
}
/* Test for interrupted multiply or divide */
t_bool cis_test_int (dstr_t *src, uint32 *kint)
{
int32 i;
uint32 wd, sc, d;
for (i = 15; i >= 1; i--) { /* test 15 nibbles */
wd = (DSTRLNT/2) + (i / 8);
sc = (i % 8) * 4;
d = (src->val[wd] >> sc) & 0xF;
if (d >= 0xA) {
*kint = (uint32) i;
return TRUE;
}
}
return FALSE;
}
/* Resume interrupted multiply
The sign that was found is the "fence" between the the remaining multiplier
and the partial product:
R val
+--+--+--+--+--+--+--+--+
| mpyer |sn|pp| 12 3
+--+--+--+--+--+--+--+--+
| partial product | 13 2
+--+--+--+--+--+--+--+--+
| partial product | 14 1
+--+--+--+--+--+--+--+--+
| partial product |mp| 15 0
+--+--+--+--+--+--+--+--+
This routine separates the multiplier and partial product, returns the
multiplier as a valid decimal string in src, and the partial product
as a value with no sign in dst */
void cis_dm_int (dstr_t *src, dstr_t *dst, uint32 kint)
{
uint32 ppneg, wd, sc, d, curd;
int32 k;
*dst = *src; /* copy input */
wd = (DSTRLNT/2) + (kint / 8);
sc = (kint % 8) * 4;
d = (src->val[wd] >> sc) & 0xF; /* get sign fence */
ppneg = ((d >> 2) & 1) ^ 1; /* partial prod neg? */
curd = (src->val[0] & 0xF) + ppneg; /* bias cur digit */
src->val[wd] = (src->val[wd] & ~(0xF << sc)) | /* replace sign */
(curd << sc); /* with digit */
GenRshift (src, kint + 15); /* right justify */
src->sign = ((d == 0xB) || (d == 0xD))? 1: 0; /* set mpyr sign */
src->val[0] = src->val[0] & ~0xF; /* clear sign pos */
/* Mask out multiplier */
for (k = DSTRLNT - 1; k >= (int32) wd; k--) /* words hi to lo */
dst->val[k] &= ~(0xFFFFFFFFu <<
((k > (int32) wd)? 0: sc));
/* Recreate missing high order digits for negative partial product */
if (ppneg) { /* negative? */
for (k = (DSTRLNT * 4) - 1; k != 0; k--) { /* bytes hi to lo */
wd = k / 4;
sc = (k % 4) * 8;
if (((dst->val[wd] >> sc) & 0xFF) != 0)
break;
dst->val[wd] |= (0x99 << sc); /* repl 00 with 99 */
} /* end for */
}
dst->val[0] &= ~0xF; /* clear pp sign */
return;
}
/* Resume interrupted divide
The sign that was found is the "fence" between the the quotient and the
remaining dividend product:
R val
+--+--+--+--+--+--+--+--+
| quotient |sn|dv| 12 3
+--+--+--+--+--+--+--+--+
| dividend | 13 2
+--+--+--+--+--+--+--+--+
| dividend | 14 1
+--+--+--+--+--+--+--+--+
| dividend |qu| 15 0
+--+--+--+--+--+--+--+--+
This routine separates the quotient and the remaining dividend, returns
the dividend as a valid decimal string, the quotient as a decimal string
without sign, and kint is the partial value of the last quotient digit. */
void cis_dd_int (dstr_t *src, dstr_t *dst, uint32 nib, uint32 *kint)
{
uint32 wd, sc, d, curd;
int32 k;
wd = (DSTRLNT/2) + (nib / 8);
sc = (nib % 8) * 4;
curd = src->val[0] & 0xF; /* last quo digit */
*dst = *src; /* copy input */
GenRshift (dst, nib + 16); /* right justify quo */
d = dst->val[0] & 0xF; /* get sign fence */
dst->val[0] = (dst->val[0] & ~0xF) | curd; /* repl with digit */
*kint = curd;
/* Mask out quotient */
for (k = DSTRLNT - 1; k >= (int32) wd; k--) /* words hi to lo */
src->val[k] &= ~(0xFFFFFFFFu <<
((k > (int32) wd)? 0: sc));
src->sign = ((d == 0xB) || (d == 0xD))? 1: 0; /* set divd sign */
src->val[0] = src->val[0] & ~0xF; /* clr sign digit */
return;
}
/* Get packed decimal string from memory
Arguments:
lnt = decimal string length
adr = decimal string address
src = decimal string structure
Output:
trap or abort signal
Per the Sigma spec, bad digits or signs cause a fault or abort */
uint32 ReadDstr (uint32 lnt, uint32 adr, dstr_t *src)
{
uint32 i, c, bva;
uint32 tr;
*src = Dstr_zero; /* clear result */
for (i = 0; i < lnt; i++) { /* loop thru string */
bva = (adr + lnt - i - 1) & bvamqrx; /* from low to high */
if ((tr = ReadB (bva, &c, VR)) != 0) /* read byte */
return tr;
src->val[i / 4] = src->val[i / 4] | (c << ((i % 4) * 8));
} /* end for */
return TestDstrValid (src);
}
/* Separate sign, validate sign and digits of decimal string */
uint32 TestDstrValid (dstr_t *src)
{
uint32 i, j, s, t;
s = src->val[0] & 0xF; /* get sign */
if (s < 0xA) /* valid? */
return DstrInvd ();
if ((s == 0xB) || (s == 0xD)) /* negative? */
src->sign = 1;
else src->sign = 0;
src->val[0] &= ~0xF; /* clear sign */
for (i = 0; i < DSTRLNT; i++) { /* check 4 words */
for (j = 0; j < 8; j++) { /* 8 digit/word */
t = (src->val[i] >> (28 - (j * 4))) & 0xF; /* get digit */
if (t > 0x9) /* invalid digit? */
return DstrInvd (); /* exception */
}
}
return 0;
}
/* Invalid digit or sign: set CC1, trap or abort instruction */
uint32 DstrInvd (void)
{
CC |= CC1; /* set CC1 */
if (PSW1 & PSW1_DM) /* if enabled, trap */
return TR_DEC;
return WSIGN; /* otherwise, abort */
}
/* Store decimal string
Arguments:
lnt = decimal string length
adr = decimal string address
dst = decimal string structure
Returns memory management traps (if any)
Bad digits and invalid sign are impossible
*/
uint32 WriteDstr (uint32 lnt, uint32 adr, dstr_t *dst)
{
uint32 i, bva, c;
uint32 tr;
dst->val[0] = dst->val[0] | (PKPLUS + dst->sign); /* set sign */
if ((tr = ReadB (adr, &c, VW)) != 0) /* prove writeable */
return tr;
for (i = 0; i < lnt; i++) { /* loop thru bytes */
c = (dst->val[i / 4] >> ((i % 4) * 8)) & 0xFF; /* from low to high */
bva = (adr + lnt - i - 1) & bvamqrx;
if ((tr = WriteB (bva, c, VW)) != 0) /* store byte */
return tr;
} /* end for */
SetCC2Dstr (lnt, dst); /* check overflow */
return 0;
}
/* Store result in decimal accumulator
Arguments:
dst = decimal string structure
cln = clean -0 if true
Sets condition codes CC3 and CC4
Bad digits and invalid sign are impossible */
void WriteDecA (dstr_t *dst, t_bool cln)
{
uint32 i, nz;
CC &= ~(CC3|CC4); /* assume zero */
for (i = 0, nz = 0; i < DSTRLNT; i++) { /* save 32 digits */
R[DECA + i] = dst->val[DSTRLNT - 1 - i];
nz |= dst->val[DSTRLNT - 1 - i];
}
if (nz) /* non-zero? */
CC |= (dst->sign)? CC4: CC3; /* set CC3 or CC4 */
else if (cln) /* zero, clean? */
dst->sign = 0; /* clear sign */
R[DECA + DSTRLNT - 1] |= (PKPLUS + dst->sign); /* or in sign */
return;
}
/* Set CC2 for decimal string store
Arguments:
lnt = string length
dst = decimal string structure
Output:
sets CC2 if information won't fit */
void SetCC2Dstr (uint32 lnt, dstr_t *dst)
{
uint32 i, limit, mask;
static uint32 masktab[8] = {
0xFFFFFFF0, 0xFFFFFF00, 0xFFFFF000, 0xFFFF0000,
0xFFF00000, 0xFF000000, 0xF0000000, 0x00000000
};
lnt = (lnt * 2) - 1; /* number of digits */
mask = 0; /* can't ovflo */
limit = lnt / 8; /* limit for test */
for (i = 0; i < DSTRLNT; i++) { /* loop thru value */
if (i == limit) /* @limit, get mask */
mask = masktab[lnt % 8];
else if (i > limit) /* >limit, test all */
mask = 0xFFFFFFFF;
if (dst->val[i] & mask) /* test for ovflo */
CC |= CC2;
}
return;
}
/* Add decimal string magnitudes
Arguments:
s1 = src1 decimal string
s2 = src2 decimal string
ds = dest decimal string
cy = carry in
Output:
1 if carry, 0 if no carry
This algorithm courtesy Anton Chernoff, circa 1992 or even earlier.
We trace the history of a pair of adjacent digits to see how the
carry is fixed; each parenthesized item is a 4b digit.
Assume we are adding:
(a)(b) I
+ (x)(y) J
First compute I^J:
(a^x)(b^y) TMP
Note that the low bit of each digit is the same as the low bit of
the sum of the digits, ignoring the carry, since the low bit of the
sum is the xor of the bits.
Now compute I+J+66 to get decimal addition with carry forced left
one digit:
(a+x+6+carry mod 16)(b+y+6 mod 16) SUM
Note that if there was a carry from b+y+6, then the low bit of the
left digit is different from the expected low bit from the xor.
If we xor this SUM into TMP, then the low bit of each digit is 1
if there was a carry, and 0 if not. We need to subtract 6 from each
digit that did not have a carry, so take ~(SUM ^ TMP) & 0x11, shift
it right 4 to the digits that are affected, and subtract 6*adjustment
(actually, shift it right 3 and subtract 3*adjustment).
*/
uint32 AddDstr (dstr_t *s1, dstr_t *s2, dstr_t *ds, uint32 cy)
{
uint32 i;
uint32 sm1, sm2, tm1, tm2, tm3, tm4;
for (i = 0; i < DSTRLNT; i++) { /* loop low to high */
tm1 = s1->val[i] ^ (s2->val[i] + cy); /* xor operands */
sm1 = s1->val[i] + (s2->val[i] + cy); /* sum operands */
sm2 = sm1 + 0x66666666; /* force carry out */
cy = ((sm1 < s1->val[i]) || (sm2 < sm1)); /* check for ovflo */
tm2 = tm1 ^ sm2; /* get carry flags */
tm3 = (tm2 >> 3) | (cy << 29); /* compute adjust */
tm4 = 0x22222222 & ~tm3; /* clrr where carry */
ds->val[i] = (sm2 - (3 * tm4)) & WMASK; /* final result */
}
return cy;
}
/* Subtract decimal string magnitudes
Arguments:
s1 = src1 decimal string
s2 = src2 decimal string
ds = dest decimal string
Note: the routine assumes that s1 <= s2
*/
void SubDstr (dstr_t *s1, dstr_t *s2, dstr_t *ds)
{
uint32 i;
dstr_t complm;
for (i = 0; i < DSTRLNT; i++) /* 9's comp s2 */
complm.val[i] = 0x99999999 - s1->val[i];
AddDstr (&complm, s2, ds, 1); /* s1 + ~s2 + 1 */
return;
}
/* Compare decimal string magnitudes
Arguments:
s1 = src1 decimal string
s2 = src2 decimal string
Output:
1 if >, 0 if =, -1 if <
*/
int32 CmpDstr (dstr_t *s1, dstr_t *s2)
{
int32 i;
for (i = DSTRLNT - 1; i >=0; i--) {
if (s1->val[i] > s2->val[i])
return 1;
if (s1->val[i] < s2->val[i])
return -1;
}
return 0;
}
/* Get exact length of decimal string, clean -0
Arguments:
dst = decimal string structure
Output:
number of non-zero digits
*/
uint32 LntDstr (dstr_t *dst)
{
int32 nz, i;
for (nz = DSTRLNT - 1; nz >= 0; nz--) {
if (dst->val[nz]) {
for (i = 7; i >= 0; i--) {
if ((dst->val[nz] >> (i * 4)) & 0xF)
return (nz * 8) + i;
}
}
}
dst->sign = 0;
return 0;
}
/* Word shift right
Arguments:
dsrc = decimal string structure
sc = shift count in nibbles
*/
void GenRshift (dstr_t *dsrc, uint32 cnt)
{
uint32 i, sc, sc1;
sc = cnt / 8;
sc1 = cnt % 8;
if (sc) {
for (i = 0; i < DSTRLNT; i++) {
if ((i + sc) < DSTRLNT)
dsrc->val[i] = dsrc->val[i + sc];
else dsrc->val[i] = 0;
}
}
if (sc1)
NibbleRshift (dsrc, sc1, 0);
return;
}
/* General shift left
Arguments:
dsrc = decimal string structure
cnt = shift count in nibbles
*/
t_bool GenLshift (dstr_t *dsrc, uint32 cnt)
{
t_bool i, c, sc, sc1;
c = 0;
sc = cnt / 8;
sc1 = cnt % 8;
if (sc) {
for (i = DSTRLNT - 1; (int32) i >= 0; i--) {
if (i >= sc)
dsrc->val[i] = dsrc->val[i - sc];
else {
c |= dsrc->val[i];
dsrc->val[i] = 0;
}
}
}
if (sc1)
c |= NibbleLshift (dsrc, sc1, 0);
return (c? TRUE: FALSE);
}
/* Nibble shift right
Arguments:
dsrc = decimal string structure
sc = shift count in nibbles
cin = carry in
*/
uint32 NibbleRshift (dstr_t *dsrc, uint32 sc, uint32 cin)
{
int32 i;
uint32 s, nc;
if ((s = sc * 4)) {
for (i = DSTRLNT - 1; (int32) i >= 0; i--) {
nc = (dsrc->val[i] << (32 - s)) & WMASK;
dsrc->val[i] = ((dsrc->val[i] >> s) |
cin) & WMASK;
cin = nc;
}
return cin;
}
return 0;
}
/* Nibble shift left
Arguments:
dsrc = decimal string structure
sc = shift count in nibbles
cin = carry in
*/
uint32 NibbleLshift (dstr_t *dsrc, uint32 sc, uint32 cin)
{
uint32 i, s, nc;
if ((s = sc * 4)) {
for (i = 0; i < DSTRLNT; i++) {
nc = dsrc->val[i] >> (32 - s);
dsrc->val[i] = ((dsrc->val[i] << s) |
cin) & WMASK;
cin = nc;
}
return cin;
}
return 0;
}
/* Edit instruction */
uint32 cis_ebs (uint32 rn, uint32 disp)
{
uint32 sa, da, c, d, dst, fill, pat;
uint32 tr;
disp = SEXT_LIT_W (disp) & WMASK; /* sext operand */
fill = S_GETMCNT (R[rn]); /* fill char */
while (S_GETMCNT (R[rn|1])) { /* while pattern */
sa = (disp + R[rn]) & bvamqrx; /* dec str addr */
da = R[rn|1] & bvamqrx; /* pattern addr */
if ((tr = ReadB (da, &pat, VR)) != 0) /* get pattern byte */
return tr;
switch (pat) { /* case on pattern */
case ED_DS: /* digit select */
if ((tr = ed_getsrc (sa, &c, &d)) != 0) /* get src digit */
return tr;
if (CC & CC4) /* signif? unpack */
dst = ZONE | d;
else if (d) { /* non-zero? */
R[1] = da; /* save addr */
dst = ZONE | d; /* unpack */
CC |= CC4; /* set signif */
}
else dst = fill; /* otherwise fill */
if ((tr = WriteB (da, dst, VW)) != 0) /* overwrite dst */
return tr;
ed_advsrc (rn, c); /* next src digit */
break;
case ED_SS: /* signif start */
if ((tr = ed_getsrc (sa, &c, &d)) != 0) /* get src digit */
return tr;
if (CC & CC4) /* signif? unpack */
dst = ZONE | d;
else if (d) { /* non-zero? */
R[1] = da; /* save addr */
dst = ZONE | d; /* unpack */
}
else { /* otherwise */
R[1] = da + 1; /* save next */
dst = fill; /* fill */
}
CC |= CC4; /* set signif */
if ((tr = WriteB (da, dst, VW)) != 0) /* overwrite dst */
return tr;
ed_advsrc (rn, c); /* next src digit */
break;
case ED_SI: /* signif immediate */
if ((tr = ed_getsrc (sa, &c, &d)) != 0) /* get src digit */
return tr;
R[1] = da; /* save addr */
dst = ZONE | d; /* unpack */
CC |= CC4; /* set signif */
if ((tr = WriteB (da, dst, VW)) != 0) /* overwrite dst */
return tr;
ed_advsrc (rn, c); /* next src digit */
break;
case ED_FS: /* field separator */
CC &= ~(CC1|CC3|CC4); /* clr all exc CC2 */
if ((tr = WriteB (da, fill, VW)) != 0) /* overwrite dst */
return tr;
break;
default: /* all others */
if ((CC & CC4) == 0) { /* signif off? */
dst = (CC & CC1)? BLANK: fill; /* blank or fill */
if ((tr = WriteB (da, dst, VW)) != 0) /* overwrite dst */
return tr;
}
break;
} /* end switch dst */
R[rn|1] = (R[rn|1] + S_ADDRINC) & WMASK; /* next pattern */
} /* end while */
return 0;
}
/* Routine to get and validate the next source digit */
uint32 ed_getsrc (uint32 sa, uint32 *c, uint32 *d)
{
uint32 tr;
if ((tr = ReadB (sa, c, VR)) != 0) /* read source byte */
return tr;
*d = ((CC & CC2)? *c: *c >> 4) & 0xF; /* isolate digit */
if (*d > 0x9) /* invalid? */
return TR_DEC;
if (*d) /* non-zero? */
CC |= CC3;
return 0;
}
/* Routine to advance source string */
void ed_advsrc (uint32 rn, uint32 c)
{
c = c & 0xF; /* get low digit */
if (((CC & CC2) == 0) && (c > 0x9)) { /* sel left, with sign? */
if ((c == 0xB) || (c == 0xD)) /* minus? */
CC = CC | (CC1|CC4); /* CC1, CC4 */
else CC = (CC | CC1) & ~CC4; /* no, CC1, ~CC4 */
R[rn] = R[rn] + 1; /* skip two digits */
}
else { /* adv 1 digit */
if (CC & CC2)
R[rn] = R[rn] + 1;
CC = CC ^ CC2;
}
return;
}