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 $ */
11 /* $DragonFly: src/sys/boot/ficl/math64.c,v 1.2 2003/06/17 04:28:17 dillon Exp $ */
17 /**************************************************************************
19 ** Returns the absolute value of an DPINT
20 **************************************************************************/
30 /**************************************************************************
31 m 6 4 F l o o r e d D i v I
33 ** FROM THE FORTH ANS...
34 ** Floored division is integer division in which the remainder carries
35 ** the sign of the divisor or is zero, and the quotient is rounded to
36 ** its arithmetic floor. Symmetric division is integer division in which
37 ** the remainder carries the sign of the dividend or is zero and the
38 ** quotient is the mathematical quotient rounded towards zero or
39 ** truncated. Examples of each are shown in tables 3.3 and 3.4.
41 ** Table 3.3 - Floored Division Example
42 ** Dividend Divisor Remainder Quotient
43 ** -------- ------- --------- --------
50 ** Table 3.4 - Symmetric Division Example
51 ** Dividend Divisor Remainder Quotient
52 ** -------- ------- --------- --------
57 **************************************************************************/
58 INTQR m64FlooredDivI(DPINT num, FICL_INT den)
65 if (m64IsNegative(num))
78 uqr = ficlLongDiv(m64CastIU(num), (FICL_UNS)den);
79 qr = m64CastQRUI(uqr);
86 qr.rem = den - qr.rem;
97 /**************************************************************************
98 m 6 4 I s N e g a t i v e
99 ** Returns TRUE if the specified DPINT has its sign bit set.
100 **************************************************************************/
101 int m64IsNegative(DPINT x)
107 /**************************************************************************
109 ** Mixed precision multiply and accumulate primitive for number building.
110 ** Multiplies DPUNS u by FICL_UNS mul and adds FICL_UNS add. Mul is typically
111 ** the numeric base, and add represents a digit to be appended to the
113 ** Returns the result of the operation
114 **************************************************************************/
115 DPUNS m64Mac(DPUNS u, FICL_UNS mul, FICL_UNS add)
117 DPUNS resultLo = ficlLongMul(u.lo, mul);
118 DPUNS resultHi = ficlLongMul(u.hi, mul);
119 resultLo.hi += resultHi.lo;
120 resultHi.lo = resultLo.lo + add;
122 if (resultHi.lo < resultLo.lo)
125 resultLo.lo = resultHi.lo;
131 /**************************************************************************
133 ** Multiplies a pair of FICL_INTs and returns an DPINT result.
134 **************************************************************************/
135 DPINT m64MulI(FICL_INT x, FICL_INT y)
152 prod = ficlLongMul(x, y);
154 return m64CastUI(prod);
156 return m64Negate(m64CastUI(prod));
160 /**************************************************************************
162 ** Negates an DPINT by complementing and incrementing.
163 **************************************************************************/
164 DPINT m64Negate(DPINT x)
176 /**************************************************************************
178 ** Push an DPINT onto the specified stack in the order required
179 ** by ANS Forth (most significant cell on top)
180 ** These should probably be macros...
181 **************************************************************************/
182 void i64Push(FICL_STACK *pStack, DPINT i64)
184 stackPushINT(pStack, i64.lo);
185 stackPushINT(pStack, i64.hi);
189 void u64Push(FICL_STACK *pStack, DPUNS u64)
191 stackPushINT(pStack, u64.lo);
192 stackPushINT(pStack, u64.hi);
197 /**************************************************************************
199 ** Pops an DPINT off the stack in the order required by ANS Forth
200 ** (most significant cell on top)
201 ** These should probably be macros...
202 **************************************************************************/
203 DPINT i64Pop(FICL_STACK *pStack)
206 ret.hi = stackPopINT(pStack);
207 ret.lo = stackPopINT(pStack);
211 DPUNS u64Pop(FICL_STACK *pStack)
214 ret.hi = stackPopINT(pStack);
215 ret.lo = stackPopINT(pStack);
220 /**************************************************************************
221 m 6 4 S y m m e t r i c D i v
222 ** Divide an DPINT by a FICL_INT and return a FICL_INT quotient and a
223 ** FICL_INT remainder. The absolute values of quotient and remainder are not
224 ** affected by the signs of the numerator and denominator (the operation
225 ** is symmetric on the number line)
226 **************************************************************************/
227 INTQR m64SymmetricDivI(DPINT num, FICL_INT den)
234 if (m64IsNegative(num))
236 num = m64Negate(num);
238 signQuot = -signQuot;
244 signQuot = -signQuot;
247 uqr = ficlLongDiv(m64CastIU(num), (FICL_UNS)den);
248 qr = m64CastQRUI(uqr);
259 /**************************************************************************
261 ** Divides a DPUNS by base (an UNS16) and returns an UNS16 remainder.
262 ** Writes the quotient back to the original DPUNS as a side effect.
263 ** This operation is typically used to convert an DPUNS to a text string
264 ** in any base. See words.c:numberSignS, for example.
265 ** Mechanics: performs 4 ficlLongDivs, each of which produces 16 bits
266 ** of the quotient. C does not provide a way to divide an FICL_UNS by an
267 ** UNS16 and get an FICL_UNS quotient (ldiv is closest, but it's signed,
268 ** unfortunately), so I've used ficlLongDiv.
269 **************************************************************************/
270 #if (BITS_PER_CELL == 32)
272 #define UMOD_SHIFT 16
273 #define UMOD_MASK 0x0000ffff
275 #elif (BITS_PER_CELL == 64)
277 #define UMOD_SHIFT 32
278 #define UMOD_MASK 0x00000000ffffffff
282 UNS16 m64UMod(DPUNS *pUD, UNS16 base)
288 result.hi = result.lo = 0;
291 ud.lo = pUD->hi >> UMOD_SHIFT;
292 qr = ficlLongDiv(ud, (FICL_UNS)base);
293 result.hi = qr.quot << UMOD_SHIFT;
295 ud.lo = (qr.rem << UMOD_SHIFT) | (pUD->hi & UMOD_MASK);
296 qr = ficlLongDiv(ud, (FICL_UNS)base);
297 result.hi |= qr.quot & UMOD_MASK;
299 ud.lo = (qr.rem << UMOD_SHIFT) | (pUD->lo >> UMOD_SHIFT);
300 qr = ficlLongDiv(ud, (FICL_UNS)base);
301 result.lo = qr.quot << UMOD_SHIFT;
303 ud.lo = (qr.rem << UMOD_SHIFT) | (pUD->lo & UMOD_MASK);
304 qr = ficlLongDiv(ud, (FICL_UNS)base);
305 result.lo |= qr.quot & UMOD_MASK;
309 return (UNS16)(qr.rem);
313 /**************************************************************************
315 ** Michael A. Gauland gaulandm@mdhost.cse.tek.com
316 **************************************************************************/
317 #if PORTABLE_LONGMULDIV != 0
318 /**************************************************************************
321 **************************************************************************/
322 DPUNS m64Add(DPUNS x, DPUNS y)
327 result.hi = x.hi + y.hi;
328 result.lo = x.lo + y.lo;
331 carry = ((x.lo | y.lo) & CELL_HI_BIT) && !(result.lo & CELL_HI_BIT);
332 carry |= ((x.lo & y.lo) & CELL_HI_BIT);
343 /**************************************************************************
346 **************************************************************************/
347 DPUNS m64Sub(DPUNS x, DPUNS y)
351 result.hi = x.hi - y.hi;
352 result.lo = x.lo - y.lo;
363 /**************************************************************************
366 **************************************************************************/
367 DPUNS m64ASL( DPUNS x )
371 result.hi = x.hi << 1;
372 if (x.lo & CELL_HI_BIT)
377 result.lo = x.lo << 1;
383 /**************************************************************************
385 ** 64 bit right shift (unsigned - no sign extend)
386 **************************************************************************/
387 DPUNS m64ASR( DPUNS x )
391 result.lo = x.lo >> 1;
394 result.lo |= CELL_HI_BIT;
397 result.hi = x.hi >> 1;
402 /**************************************************************************
405 **************************************************************************/
406 DPUNS m64Or( DPUNS x, DPUNS y )
410 result.hi = x.hi | y.hi;
411 result.lo = x.lo | y.lo;
417 /**************************************************************************
419 ** Return -1 if x < y; 0 if x==y, and 1 if x > y.
420 **************************************************************************/
421 int m64Compare(DPUNS x, DPUNS y)
429 else if (x.hi < y.hi)
435 /* High parts are equal */
440 else if (x.lo < y.lo)
454 /**************************************************************************
455 f i c l L o n g M u l
456 ** Portable versions of ficlLongMul and ficlLongDiv in C
458 ** Michael A. Gauland gaulandm@mdhost.cse.tek.com
459 **************************************************************************/
460 DPUNS ficlLongMul(FICL_UNS x, FICL_UNS y)
462 DPUNS result = { 0, 0 };
466 addend.hi = 0; /* No sign extension--arguments are unsigned */
472 result = m64Add(result, addend);
475 addend = m64ASL(addend);
481 /**************************************************************************
482 f i c l L o n g D i v
483 ** Portable versions of ficlLongMul and ficlLongDiv in C
485 ** Michael A. Gauland gaulandm@mdhost.cse.tek.com
486 **************************************************************************/
487 UNSQR ficlLongDiv(DPUNS q, FICL_UNS y)
503 while ((m64Compare(subtrahend, q) < 0) &&
504 (subtrahend.hi & CELL_HI_BIT) == 0)
507 subtrahend = m64ASL(subtrahend);
510 while (mask.lo != 0 || mask.hi != 0)
512 if (m64Compare(subtrahend, q) <= 0)
514 q = m64Sub( q, subtrahend);
515 quotient = m64Or(quotient, mask);
518 subtrahend = m64ASR(subtrahend);
521 result.quot = quotient.lo;