1 /*******************************************************************
3 ** Forth Inspired Command Language - 64 bit math support routines
4 ** Author: John Sadler (john_sadler@alum.mit.edu)
5 ** Created: 25 January 1998
6 ** Rev 2.03: Support for 128 bit DP math. This file really ouught to
8 *******************************************************************/
10 /* $FreeBSD: src/sys/boot/ficl/math64.c,v 1.2 1999/09/29 04:43:06 dcs Exp $ */
16 /**************************************************************************
18 ** Returns the absolute value of an DPINT
19 **************************************************************************/
29 /**************************************************************************
30 m 6 4 F l o o r e d D i v I
32 ** FROM THE FORTH ANS...
33 ** Floored division is integer division in which the remainder carries
34 ** the sign of the divisor or is zero, and the quotient is rounded to
35 ** its arithmetic floor. Symmetric division is integer division in which
36 ** the remainder carries the sign of the dividend or is zero and the
37 ** quotient is the mathematical quotient rounded towards zero or
38 ** truncated. Examples of each are shown in tables 3.3 and 3.4.
40 ** Table 3.3 - Floored Division Example
41 ** Dividend Divisor Remainder Quotient
42 ** -------- ------- --------- --------
49 ** Table 3.4 - Symmetric Division Example
50 ** Dividend Divisor Remainder Quotient
51 ** -------- ------- --------- --------
56 **************************************************************************/
57 INTQR m64FlooredDivI(DPINT num, FICL_INT den)
64 if (m64IsNegative(num))
77 uqr = ficlLongDiv(m64CastIU(num), (FICL_UNS)den);
78 qr = m64CastQRUI(uqr);
85 qr.rem = den - qr.rem;
96 /**************************************************************************
97 m 6 4 I s N e g a t i v e
98 ** Returns TRUE if the specified DPINT has its sign bit set.
99 **************************************************************************/
100 int m64IsNegative(DPINT x)
106 /**************************************************************************
108 ** Mixed precision multiply and accumulate primitive for number building.
109 ** Multiplies DPUNS u by FICL_UNS mul and adds FICL_UNS add. Mul is typically
110 ** the numeric base, and add represents a digit to be appended to the
112 ** Returns the result of the operation
113 **************************************************************************/
114 DPUNS m64Mac(DPUNS u, FICL_UNS mul, FICL_UNS add)
116 DPUNS resultLo = ficlLongMul(u.lo, mul);
117 DPUNS resultHi = ficlLongMul(u.hi, mul);
118 resultLo.hi += resultHi.lo;
119 resultHi.lo = resultLo.lo + add;
121 if (resultHi.lo < resultLo.lo)
124 resultLo.lo = resultHi.lo;
130 /**************************************************************************
132 ** Multiplies a pair of FICL_INTs and returns an DPINT result.
133 **************************************************************************/
134 DPINT m64MulI(FICL_INT x, FICL_INT y)
151 prod = ficlLongMul(x, y);
153 return m64CastUI(prod);
155 return m64Negate(m64CastUI(prod));
159 /**************************************************************************
161 ** Negates an DPINT by complementing and incrementing.
162 **************************************************************************/
163 DPINT m64Negate(DPINT x)
175 /**************************************************************************
177 ** Push an DPINT onto the specified stack in the order required
178 ** by ANS Forth (most significant cell on top)
179 ** These should probably be macros...
180 **************************************************************************/
181 void i64Push(FICL_STACK *pStack, DPINT i64)
183 stackPushINT(pStack, i64.lo);
184 stackPushINT(pStack, i64.hi);
188 void u64Push(FICL_STACK *pStack, DPUNS u64)
190 stackPushINT(pStack, u64.lo);
191 stackPushINT(pStack, u64.hi);
196 /**************************************************************************
198 ** Pops an DPINT off the stack in the order required by ANS Forth
199 ** (most significant cell on top)
200 ** These should probably be macros...
201 **************************************************************************/
202 DPINT i64Pop(FICL_STACK *pStack)
205 ret.hi = stackPopINT(pStack);
206 ret.lo = stackPopINT(pStack);
210 DPUNS u64Pop(FICL_STACK *pStack)
213 ret.hi = stackPopINT(pStack);
214 ret.lo = stackPopINT(pStack);
219 /**************************************************************************
220 m 6 4 S y m m e t r i c D i v
221 ** Divide an DPINT by a FICL_INT and return a FICL_INT quotient and a
222 ** FICL_INT remainder. The absolute values of quotient and remainder are not
223 ** affected by the signs of the numerator and denominator (the operation
224 ** is symmetric on the number line)
225 **************************************************************************/
226 INTQR m64SymmetricDivI(DPINT num, FICL_INT den)
233 if (m64IsNegative(num))
235 num = m64Negate(num);
237 signQuot = -signQuot;
243 signQuot = -signQuot;
246 uqr = ficlLongDiv(m64CastIU(num), (FICL_UNS)den);
247 qr = m64CastQRUI(uqr);
258 /**************************************************************************
260 ** Divides a DPUNS by base (an UNS16) and returns an UNS16 remainder.
261 ** Writes the quotient back to the original DPUNS as a side effect.
262 ** This operation is typically used to convert an DPUNS to a text string
263 ** in any base. See words.c:numberSignS, for example.
264 ** Mechanics: performs 4 ficlLongDivs, each of which produces 16 bits
265 ** of the quotient. C does not provide a way to divide an FICL_UNS by an
266 ** UNS16 and get an FICL_UNS quotient (ldiv is closest, but it's signed,
267 ** unfortunately), so I've used ficlLongDiv.
268 **************************************************************************/
269 #if (BITS_PER_CELL == 32)
271 #define UMOD_SHIFT 16
272 #define UMOD_MASK 0x0000ffff
274 #elif (BITS_PER_CELL == 64)
276 #define UMOD_SHIFT 32
277 #define UMOD_MASK 0x00000000ffffffff
281 UNS16 m64UMod(DPUNS *pUD, UNS16 base)
287 result.hi = result.lo = 0;
290 ud.lo = pUD->hi >> UMOD_SHIFT;
291 qr = ficlLongDiv(ud, (FICL_UNS)base);
292 result.hi = qr.quot << UMOD_SHIFT;
294 ud.lo = (qr.rem << UMOD_SHIFT) | (pUD->hi & UMOD_MASK);
295 qr = ficlLongDiv(ud, (FICL_UNS)base);
296 result.hi |= qr.quot & UMOD_MASK;
298 ud.lo = (qr.rem << UMOD_SHIFT) | (pUD->lo >> UMOD_SHIFT);
299 qr = ficlLongDiv(ud, (FICL_UNS)base);
300 result.lo = qr.quot << UMOD_SHIFT;
302 ud.lo = (qr.rem << UMOD_SHIFT) | (pUD->lo & UMOD_MASK);
303 qr = ficlLongDiv(ud, (FICL_UNS)base);
304 result.lo |= qr.quot & UMOD_MASK;
308 return (UNS16)(qr.rem);
312 /**************************************************************************
314 ** Michael A. Gauland gaulandm@mdhost.cse.tek.com
315 **************************************************************************/
316 #if PORTABLE_LONGMULDIV != 0
317 /**************************************************************************
320 **************************************************************************/
321 DPUNS m64Add(DPUNS x, DPUNS y)
326 result.hi = x.hi + y.hi;
327 result.lo = x.lo + y.lo;
330 carry = ((x.lo | y.lo) & CELL_HI_BIT) && !(result.lo & CELL_HI_BIT);
331 carry |= ((x.lo & y.lo) & CELL_HI_BIT);
342 /**************************************************************************
345 **************************************************************************/
346 DPUNS m64Sub(DPUNS x, DPUNS y)
350 result.hi = x.hi - y.hi;
351 result.lo = x.lo - y.lo;
362 /**************************************************************************
365 **************************************************************************/
366 DPUNS m64ASL( DPUNS x )
370 result.hi = x.hi << 1;
371 if (x.lo & CELL_HI_BIT)
376 result.lo = x.lo << 1;
382 /**************************************************************************
384 ** 64 bit right shift (unsigned - no sign extend)
385 **************************************************************************/
386 DPUNS m64ASR( DPUNS x )
390 result.lo = x.lo >> 1;
393 result.lo |= CELL_HI_BIT;
396 result.hi = x.hi >> 1;
401 /**************************************************************************
404 **************************************************************************/
405 DPUNS m64Or( DPUNS x, DPUNS y )
409 result.hi = x.hi | y.hi;
410 result.lo = x.lo | y.lo;
416 /**************************************************************************
418 ** Return -1 if x < y; 0 if x==y, and 1 if x > y.
419 **************************************************************************/
420 int m64Compare(DPUNS x, DPUNS y)
428 else if (x.hi < y.hi)
434 /* High parts are equal */
439 else if (x.lo < y.lo)
453 /**************************************************************************
454 f i c l L o n g M u l
455 ** Portable versions of ficlLongMul and ficlLongDiv in C
457 ** Michael A. Gauland gaulandm@mdhost.cse.tek.com
458 **************************************************************************/
459 DPUNS ficlLongMul(FICL_UNS x, FICL_UNS y)
461 DPUNS result = { 0, 0 };
465 addend.hi = 0; /* No sign extension--arguments are unsigned */
471 result = m64Add(result, addend);
474 addend = m64ASL(addend);
480 /**************************************************************************
481 f i c l L o n g D i v
482 ** Portable versions of ficlLongMul and ficlLongDiv in C
484 ** Michael A. Gauland gaulandm@mdhost.cse.tek.com
485 **************************************************************************/
486 UNSQR ficlLongDiv(DPUNS q, FICL_UNS y)
502 while ((m64Compare(subtrahend, q) < 0) &&
503 (subtrahend.hi & CELL_HI_BIT) == 0)
506 subtrahend = m64ASL(subtrahend);
509 while (mask.lo != 0 || mask.hi != 0)
511 if (m64Compare(subtrahend, q) <= 0)
513 q = m64Sub( q, subtrahend);
514 quotient = m64Or(quotient, mask);
517 subtrahend = m64ASR(subtrahend);
520 result.quot = quotient.lo;