Rev 2 | Blame | Compare with Previous | Last modification | View Log | RSS feed
/*
Crown Copyright (c) 1997, 1998
This TenDRA(r) Computer Program is subject to Copyright
owned by the United Kingdom Secretary of State for Defence
acting through the Defence Evaluation and Research Agency
(DERA). It is made available to Recipients with a
royalty-free licence for its use, reproduction, transfer
to other parties and amendment for any purpose not excluding
product development provided that any such use et cetera
shall be deemed to be acceptance of the following conditions:-
(1) Its Recipients shall ensure that this Notice is
reproduced upon any copies or amended versions of it;
(2) Any amended version of it shall be clearly marked to
show both the nature of and the organisation responsible
for the relevant amendment or amendments;
(3) Its onward transfer from a recipient to another
party shall be deemed to be that party's acceptance of
these conditions;
(4) DERA gives no warranty or assurance as to its
quality or suitability for any purpose and DERA accepts
no liability whatsoever in relation to any use to which
it may be put.
*/
/* INT64liba.c
** -----------
**
** This file contains functions which perform 64-bit
** arithmetic on 32-bit machines.
**
** The functions all use abstract types which refer
** to some characteristic of that type, so that INT32
** is a 32-bit signed integer, and UINT64 is a 32-bit
** unsigned integer. These types are defined in the
** file 'INT64_types.h'.
*/
#include <stdio.h>
#include "ossg.h"
#if defined (__TenDRA__)
#include "abstract.h"
#else
#include "concrete.h"
#endif /* defined(__TenDRA__) */
#undef STATIC
#ifndef DEBUG
#define STATIC static
#else
#define STATIC
#endif /* ndef DEBUG */
#define UINT32_const(X) ((UINT32) (X))
/* Error Treatments handling */
int __TDFerror;
#define OVERFLOW_ERROR (__TDFerror = -1)
#if USE_SEPARATE_DIV_ZERO_ERROR
#define DIV_ZERO_ERROR (__TDFerror = 0)
#else
#define DIV_ZERO_ERROR OVERFLOW_ERROR
#endif
#define CLEAR_ERRORS (__TDFerror = 1)
/* Declarations of functions used internally */
static TDF_INT64 TDFUplus PROTO_S ((TDF_INT64, TDF_INT64));
static TDF_INT64 TDFUminus PROTO_S ((TDF_INT64, TDF_INT64));
static TDF_INT64 TDFUmult PROTO_S ((TDF_INT64, TDF_INT64));
static TDF_INT64 TDFUdiv_rem PROTO_S ((TDF_INT64, TDF_INT64, UINT32));
static TDF_INT64 TDFUshr PROTO_S ((TDF_INT64, UINT32));
/* Declarations of DEBUG functions */
#if DEBUG
INT64 make_INT64 PROTO_S ((INT32, UINT32));
UINT64 make_UINT64 PROTO_S ((UINT32, UINT32));
void INT64_print PROTO_S ((char *, INT64, char *));
void UINT64_print PROTO_S ((char *, UINT64, char *));
#endif
/* Forward declarations */
INT64 __TDFUs_plus PROTO_S ((INT64, INT64));
INT64 __TDFUs_minus PROTO_S ((INT64, INT64));
INT64 __TDFUs_mult PROTO_S ((INT64, INT64));
INT64 __TDFUs_div1 PROTO_S ((INT64, INT64));
INT64 __TDFUs_div2 PROTO_S ((INT64, INT64));
INT64 __TDFUs_rem1 PROTO_S ((INT64, INT64));
INT64 __TDFUs_rem2 PROTO_S ((INT64, INT64));
INT64 __TDFUneg PROTO_S ((INT64));
INT64 __TDFUabs PROTO_S ((INT64));
INT64 __TDFUsswiden PROTO_S ((INT32));
INT64 __TDFUuswiden PROTO_S ((UINT32));
INT32 __TDFUssshorten PROTO_S ((INT64));
INT32 __TDFUusshorten PROTO_S ((UINT64));
INT64 __TDFUu642s64 PROTO_S ((UINT64));
INT64 __TDFUs_max PROTO_S ((INT64, INT64));
INT64 __TDFUs_min PROTO_S ((INT64, INT64));
int __TDFUs_test PROTO_S ((INT64, INT64));
UINT64 __TDFUu_plus PROTO_S ((UINT64, UINT64));
UINT64 __TDFUu_minus PROTO_S ((UINT64, UINT64));
UINT64 __TDFUu_mult PROTO_S ((UINT64, UINT64));
UINT64 __TDFUu_div2 PROTO_S ((UINT64, UINT64));
UINT64 __TDFUu_rem2 PROTO_S ((UINT64, UINT64));
UINT64 __TDFUu_shl PROTO_S ((UINT64, UINT32));
UINT64 __TDFUu_shr PROTO_S ((UINT64, UINT32));
UINT64 __TDFUuuwiden PROTO_S ((UINT32));
UINT64 __TDFUsuwiden PROTO_S ((INT32));
UINT32 __TDFUsushorten PROTO_S ((INT64));
UINT32 __TDFUuushorten PROTO_S ((UINT64));
UINT64 __TDFUs642u64 PROTO_S ((INT64));
UINT64 __TDFUu_max PROTO_S ((UINT64, UINT64));
UINT64 __TDFUu_min PROTO_S ((UINT64, UINT64));
int __TDFUu_test PROTO_S ((UINT64, UINT64));
UINT64 __TDFUand PROTO_S ((UINT64, UINT64));
UINT64 __TDFUor PROTO_S ((UINT64, UINT64));
UINT64 __TDFUxor PROTO_S ((UINT64, UINT64));
UINT64 __TDFUnot PROTO_S ((UINT64));
#define TDFUis_positive(X) ((hi_32(X) >= 0))
#define TDFUis_negative(X) ((hi_32(X) < 0))
#define TDFUis_zero(X) ((hi_32(X) == 0) && (lo_32(X) == 0))
#define TDFUis_nonzero(X) ((hi_32(X) != 0) || (lo_32(X) != 0))
/*
** TDFUshl:
**
** Simply masks out the necessary bits as appropriate
** for shifts of less than 32 and those greater than 32.
**
** No error checking - should have already been done.
**
** Defined here as a macro, which is implemented as a
** TDF token. This token is then used in the PL_TDF
** file which implements the remaining functions in the
** 64-bit arithmetic library.
**
** The token must contain IF, but TDFC only allows this
** to be done with "procedure tokens". If non-TenDRA
** compilation is to be possible, this macro cannot be
** used directly since constants will produce illegal
** shift values, so a function has to be defined. A
** function cannot have the desired side-effect of updating
** the first parameter (without making it into a pointer),
** so another macro is used (which calls the function
** defined using the macro which produces the definition
** of the TDF token !!! Got it ???
*/
#define _TDFUshl(shifted_a,a,n) \
{ \
if (n > 32) /* assuming n <= 64 */ \
{ \
hi_u32 (shifted_a) = lo_u32(a) << (n -32); \
lo_u32 (shifted_a) = (UINT32) 0x00000000; \
} \
else /* (0 < n <= 32) */ \
{ \
hi_u32 (shifted_a) = (lo_u32(a) >> (32-n)) | (hi_u32(a) << n); \
lo_u32 (shifted_a) = lo_u32(a) << n; \
} \
}
#ifdef __TenDRA__
/* Want to use this value in the .pl file */
#pragma token EXP rvalue : int : BIGEND_INT64 #
#ifndef BIGENDIAN_INT64
#define BIGEND_INT64 0
#else
#define BIGEND_INT64 BIGENDIAN_INT64
#endif
/* Use a "procedure token" to implement TDFUshl */
#pragma token PROC (EXP lvalue : TDF_INT64 : , \
EXP rvalue : TDF_INT64 : , \
EXP rvalue : UINT32 : ) \
STATEMENT TDFUshl #
#define TDFUshl(shifted_a,a,n) _TDFUshl((shifted_a),(a),(n))
#else
/* Use a macro (which calls a function) to implement TDFUshl */
TDF_INT64 __TDFUshl PROTO_S ((TDF_INT64, UINT32));
TDF_INT64 __TDFUshl PROTO_N ((shifted_a, a, n))
PROTO_T (TDF_INT64 a X UINT32 n)
{
TDF_INT64 tmp;
_TDFUshl(tmp, a, n);
return tmp;
}
#define TDFUshl(shifted_a,a,n) { shifted_a = __TDFUshl(a,n); }
#endif /* __TenDRA__ */
/* Function definitions */
/*
** TDFUplus:
**
** Generic function to add two 64-bit integers,
** interpretting each as an unsigned value.
*/
static TDF_INT64 TDFUplus PROTO_N ((a, b))
PROTO_T (TDF_INT64 a X TDF_INT64 b)
{
TDF_INT64 sum;
lo_u32 (sum) = lo_u32(a) + lo_u32(b);
if (lo_u32(sum) < lo_u32(a)) {
hi_u32 (sum) = hi_u32(a) + hi_u32(b) + 1;
}
else {
hi_u32 (sum) = hi_u32(a) + hi_u32(b);
}
return sum;
}
/*
** __TDFUs_plus:
**
** Interpret signed numbers as unsigned, add them
** as unsigned numbers, then reinterpret as signed.
** Error handling if two positives produce a negative
** or two negative produce a positive, else alright.
*/
INT64 __TDFUs_plus PROTO_N ((param_a, param_b))
PROTO_T (INT64 param_a X INT64 param_b)
{
TDF_INT64 sum, a, b;
PARAM(a) = param_a;
PARAM(b) = param_b;
CLEAR_ERRORS;
sum = TDFUplus (a, b); /* add as unsigned numbers */
if (TDFUis_positive(a) && /* a >= 0 */
TDFUis_positive(b) && /* b >= 0 */
TDFUis_negative(sum)) /* a+b < 0 */
{
OVERFLOW_ERROR;
}
if (TDFUis_negative(a) && /* a < 0 */
TDFUis_negative(b) && /* b < 0 */
TDFUis_positive(sum)) /* a+b >= 0 */
{
OVERFLOW_ERROR;
}
return PARAM(sum);
}
/*
** __TDFUu_plus:
**
** Adds arguments, and tests to see if sum is less
** than the first argument for error detection.
*/
UINT64 __TDFUu_plus PROTO_N ((param_a, param_b))
PROTO_T (UINT64 param_a X UINT64 param_b)
{
TDF_INT64 sum, a, b;
UPARAM(a) = param_a;
UPARAM(b) = param_b;
CLEAR_ERRORS;
sum = TDFUplus (a, b);
if ((hi_u32(sum) <= hi_u32(a)) &&
(lo_u32(sum) < lo_u32(a))) /* sum < a */
{
OVERFLOW_ERROR;
}
return UPARAM(sum);
}
/*
** TDFUminus:
**
** Generic function to subtracting two 64-bit
** integers, interpretting each as an unsigned value.
*/
static TDF_INT64 TDFUminus PROTO_N ((a, b))
PROTO_T (TDF_INT64 a X TDF_INT64 b)
{
TDF_INT64 sum;
lo_u32 (sum) = lo_u32(a) - lo_u32(b);
if (lo_u32(sum) > lo_u32(a)) {
hi_u32 (sum) = hi_u32(a) - hi_u32(b) - 1;
}
else {
hi_u32 (sum) = hi_u32(a) - hi_u32(b);
}
return sum;
}
/*
** __TDFUs_minus:
**
** Interpret signed numbers as unsigned, add them
** as unsigned numbers, then reinterpret as signed.
** Error handling if two positives produce a negative
** or two negative produce a positive, else alright.
*/
INT64 __TDFUs_minus PROTO_N ((param_a, param_b))
PROTO_T (INT64 param_a X INT64 param_b)
{
TDF_INT64 diff, a, b;
PARAM(a) = param_a;
PARAM(b) = param_b;
CLEAR_ERRORS;
diff = TDFUminus (a, b); /* subtract as unsigned numbers */
if (TDFUis_positive(a) && /* a >= 0 */
TDFUis_negative(b) && /* b < 0 */
TDFUis_negative(diff)) /* a-b < 0 */
{
OVERFLOW_ERROR;
}
if (TDFUis_negative(a) && /* a < 0 */
TDFUis_positive(b) && /* b >= 0 */
TDFUis_positive(diff)) /* a-b >= 0 */
{
OVERFLOW_ERROR;
}
return PARAM(diff);
}
/*
** __TDFUu_minus:
**
** Adds arguments, and tests to see if sum is less
** than the first argument for error detection.
*/
UINT64 __TDFUu_minus PROTO_N ((param_a, param_b))
PROTO_T (UINT64 param_a X UINT64 param_b)
{
TDF_INT64 diff, a, b;
UPARAM(a) = param_a;
UPARAM(b) = param_b;
CLEAR_ERRORS;
diff = TDFUminus (a, b);
if ((hi_u32(diff) >= hi_u32(a)) &&
(lo_u32(diff) > lo_u32(a))) /* diff > a */
{
OVERFLOW_ERROR;
}
return UPARAM(diff);
}
/*
** a3 a2 a1 a0
**
** X b3 b2 b1 b0
** --------------------------
** ----a0 X b0---- (STAGE1)
**
** ----a1 X b0---- (STAGE2)
** ----a0 X b1----
**
** ----a2 X b0---- (STAGE3)
** ----a1 X b1----
** ----a0 X b2----
**
** ----a3 X b0---- (STAGE4)
** ----a2 X b1----
** ----a1 X b2----
** ----a0 X b3----
**
**
** These are the only terms necessary to produce the answer.
** The other terms must all be used at error checking to
** ensure that overflow does not occur.
**
** By adding the contributions in this order, it is possible
** to control the size of the sum as each stage, i.e. only
** certain terms need to be considered, reducing the number
** of steps in the addition.
**
** If the parameter 'error_check' is non-zero, error-checking
** is done: its value is reset to zero if non occur.
*/
#define hi_u16(X) ((X) >> 16)
#define lo_u16(X) ((X) & ((UINT32) 0xffff))
static TDF_INT64 TDFUmult PROTO_N ((a, b))
PROTO_T (TDF_INT64 a X TDF_INT64 b)
{
TDF_INT64 prod;
UINT32 a0, a1, a2, a3;
UINT32 b0, b1, b2, b3;
UINT32 work1, work2;
a0 = lo_u16 (lo_u32(a));
a1 = hi_u16 (lo_u32(a));
a2 = lo_u16 (hi_u32(a));
a3 = hi_u16 (hi_u32(a));
b0 = lo_u16 (lo_u32(b));
b1 = hi_u16 (lo_u32(b));
b2 = lo_u16 (hi_u32(b));
b3 = hi_u16 (hi_u32(b));
if ((a1 == 0) && (a2 == 0) && (a3 == 0) &&
(b1 == 0) && (b2 == 0) && (b3 == 0))
{
hi_u32(prod) = 0; /* result fits into 32 bits */
lo_u32(prod) = a0 * b0;
return prod;
}
/* Start STAGE1 */
lo_u32(prod) = a0 * b0;
/* Start STAGE2 */
work1 = a1 * b0;
work2 = work1 + (a0 * b1);
if (work2 < work1) {
hi_u32(prod) = ((UINT32) 0x10000); /* set carry in result */
}
else {
hi_u32(prod) = 0; /* must initialise somewhere */
}
work1 = lo_u16(work2); /* lo-16 bits of sum */
work2 = hi_u16(work2); /* hi-16 bits of sum */
lo_u32(prod) += (work1 << 16);
if ((work1 << 16) > lo_u32(prod)) { /* wrap */
hi_u32(prod) += (1 + work2);
}
else {
hi_u32(prod) += work2;
}
/* Start STAGE3 */
work1 = a2 * b0;
work2 = work1 + (a1 * b1);
if (work1 > work2) {
OVERFLOW_ERROR;
}
work1 = work2 + (a0 * b2);
if (work2 > work1) {
OVERFLOW_ERROR;
}
hi_u32(prod) += work1;
if (work1 > hi_u32(prod)) {
OVERFLOW_ERROR;
}
/* Start STAGE4 */
work1 = a3 * b0;
work2 = a2 * b1;
if ((work1 > ((UINT32) 0xffff)) ||
(work2 > ((UINT32) 0xffff)))
{
OVERFLOW_ERROR;
}
work1 = work1 + work2;
work2 = a1 * b2;
if (work2 > ((UINT32) 0xffff)) {
OVERFLOW_ERROR;
}
work1 = work1 + work2;
work2 = a0 * b3;
work1 = work1 + work2;
if ((work1 > ((UINT32) 0xffff)) ||
(work2 > ((UINT32) 0xffff)))
{
OVERFLOW_ERROR;
}
work2 = work1 << 16; /* We've made sure this will work */
hi_u32(prod) += work2;
if ((work2 > hi_u32(prod)) || /* This mustn't overflow */
(a3 * b1 != 0) ||
(a2 * b2 != 0) || /* Remaining contributions */
(a1 * b3 != 0) || /* must all be zero */
(a3 * b2 != 0) ||
(a2 * b3 != 0) ||
(a3 * b3 != 0))
{
OVERFLOW_ERROR;
}
return prod;
}
/*
** __TDFUs_mult:
**
** Implements long multiplication in binary,
** checking for errors as it goes along.
*/
INT64 __TDFUs_mult PROTO_N ((param_a, param_b))
PROTO_T (INT64 param_a X INT64 param_b)
{
TDF_INT64 prod, a, b;
int sign;
PARAM(a) = param_a;
PARAM(b) = param_b;
CLEAR_ERRORS;
sign = ((hi_32(a) ^ hi_32(b)) < 0); /* sign of result */
PARAM(a) = __TDFUabs(param_a);
PARAM(b) = __TDFUabs(param_b);
prod = TDFUmult (a, b); /* Any overflow will be flagged */
if (hi_32(prod) < 0) { /* definitely okay if this is false */
if ((sign == 0) || /* positive int too big for INT_64 */
(hi_u32(prod) > ((UINT32) 0x80000000)) || /* signed int too big for INT_64 */
(lo_u32(prod) > ((UINT32) 0x00000000)))
{
OVERFLOW_ERROR;
}
}
if ((sign == 0) || /* result is unsigned */
((hi_u32(prod) == ((UINT32) 0x80000000)) && /* - this number has the same */
(lo_u32(prod) != ((UINT32) 0x00000000)))) /* representation as its negation */
{
return PARAM(prod);
}
return __TDFUneg(PARAM(prod));
}
/*
** __TDFUu_mult:
**
** Implements long multiplication in binary,
** checking for errors as it goes along.
*/
#if 0
UINT64 __TDFUu_mult PROTO_N ((param_a, param_b))
PROTO_T (UINT64 param_a X UINT64 param_b)
{
TDF_INT64 prod, a, b;
if (__TDFUu_test(param_b, UPARAM(const_u0)) == 0) return UPARAM(const_u0);
UPARAM(a) = param_a;
UPARAM(b) = param_b;
prod = const_u0;
while (__TDFUu_test(UPARAM(b), UPARAM(const_u1)) != 0)
{
if ((lo_u32(b) & 1) != 0) {
prod = __TDFUu_plus (UPARAM(prod), UPARAM(a)); /* Any overflow will be flagged */
}
PARAM(a) = __TDFUu_shl (UPARAM(a), (UINT32) 1);
PARAM(b) = __TDFUu_shr (UPARAM(b), (UINT32) 1);
}
return UPARAM (__TDFUu_plus (prod, a));
}
#else
UINT64 __TDFUu_mult PROTO_N ((param_a, param_b))
PROTO_T (UINT64 param_a X UINT64 param_b)
{
TDF_INT64 prod, a, b;
UPARAM(a) = param_a;
UPARAM(b) = param_b;
CLEAR_ERRORS;
prod = TDFUmult (a, b); /* Any overflow will be flagged */
return UPARAM(prod);
}
#endif
/*
** Implementation of div0, div1, rem0 and rem1.
** ===========================================
**
** Clearly there are no problems for unsigned numbers.
** I have written a generic function to divide two
** unsigned 64-bit integers which produces both the
** result of the divide and the remainder.
**
** For signed numbers, a and b, it is possible to work
** out the values of m and n s.t.:
**
** |a| = n * |b| + m, (0 <= m < |b|)
**
** From this information, I believe the correct values
** for the various functions can be deduced from the
** following table:
**
**
** a b class | div rem
** ======================================================
** + + 1 | n m
** + + 2 | n m
** + - 1 | -n or -(n+1) 0 or (b+m)
** + - 2 | -n m
** - + 1 | -n or -(n+1) 0 or (b-m)
** - + 2 | -n -m
** - - 1 | n -m
** - - 2 | n -m
*/
/*
** TDFUdiv_rem:
**
** Generic function to divide two 64-bit integers,
** interpretting each as an unsigned value. Implements
** the basic 'shift, compare and subtract' algorithm.
**
** Since the one function produces both the result and
** the remainder, it is passed a parameter which tells
** it which result is expected. The S_DIV_0_1 flags is
** used for a signed-div0 call for which the arguments
** represent integers of different signs; in this case,
** it is the result depends on the remainder (and so
** is adjusted according while this information is still
** freely available.
**
*/
#define S_DIV1 ((UINT32) 0)
#define S_DIV2 ((UINT32) 2)
#define U_DIV2 ((UINT32) 3)
#define S_REM1 ((UINT32) 4)
#define S_REM2 ((UINT32) 6)
#define U_REM2 ((UINT32) 7)
#define S_DIV1_0_1 ((UINT32) 8)
#define is_signed(X) (((X) & 1) == 0)
#define is_class1(X) (((X) & 2) == 0)
#define is_div(X) (((X) & 4) == 0)
static TDF_INT64 TDFUdiv_rem PROTO_N ((a, b, flags))
PROTO_T (TDF_INT64 a X TDF_INT64 b X UINT32 flags)
{
TDF_INT64 new_int, a_upper;
UINT32 i;
new_int = const_u0;
a_upper = const_u0;
if (hi_u32(a) == 0) {
if (hi_u32(b) == 0) { /* both a and b fit into 32 bits */
lo_u32(new_int) = lo_u32(a) / lo_u32(b);
lo_u32(a_upper) = lo_u32(a) % lo_u32(b);
}
else { /* b > a */
lo_u32(new_int) = 0;
a_upper = a;
}
}
else {
for (i = 64; i > 0; i--)
{
TDFUshl (new_int, new_int, (UINT32) 1);
TDFUshl (a_upper, a_upper, (UINT32) 1);
if ((hi_u32(a) >> 31) != 0)
{
hi_u32(a) &= ((UINT32) 0x7fffffff); /* mask out for shift */
lo_u32(a_upper) |= 1; /* do carry */
}
TDFUshl (a, a, (UINT32) 1);
if (__TDFUu_test(UPARAM(a_upper), UPARAM(b)) >= 0) {
UPARAM(a_upper) = __TDFUu_minus (UPARAM(a_upper), UPARAM(b));
lo_u32(new_int) |= 1;
}
}
}
if (is_div(flags)) /* result of 'div' */
{
if ((flags == S_DIV1_0_1) &&
((lo_32(a_upper) != 0) ||
(hi_32(a_upper) != 0)))
{
PARAM(new_int) = __TDFUs_plus(PARAM(new_int), PARAM(const_1));
}
return new_int;
}
return a_upper; /* result of 'rem' */
}
/*
** __TDFUs_div1:
**
** Division-by-zero is the only possible error.
*/
INT64 __TDFUs_div1 PROTO_N ((param_a, param_b))
PROTO_T (INT64 param_a X INT64 param_b)
{
TDF_INT64 quot, a, b;
int is_neg;
PARAM(b) = param_b;
if (TDFUis_zero(b))
{
DIV_ZERO_ERROR; /* Stop now - return anything */
return param_b;
}
PARAM(a) = param_a;
is_neg = ((hi_32(a) ^ hi_32(b)) < 0); /* is result negative ? */
PARAM(a) = __TDFUabs(param_a);
PARAM(b) = __TDFUabs(param_b);
if (is_neg) {
quot = TDFUdiv_rem (a, b, S_DIV1_0_1);
CLEAR_ERRORS; /* May have been set by __TDFUabs */
hi_32(quot) = hi_32(quot) ^ ( (INT32)
((UINT32) 0xffffffff)); /* Avoid errors, don't call __TDFUneg */
lo_32(quot) = lo_32(quot) ^ UINT32_const(0xffffffff);
return PARAM (TDFUplus (quot, const_1));
}
quot = TDFUdiv_rem (a, b, S_DIV1);
CLEAR_ERRORS; /* May have been set by __TDFUabs */
if (TDFUis_negative(quot)) /* Only happens for INT_MAX+1 */
{
OVERFLOW_ERROR;
}
return PARAM(quot);
}
/*
** __TDFUs_div2:
**
** Division-by-zero is the only possible error.
*/
INT64 __TDFUs_div2 PROTO_N ((param_a, param_b))
PROTO_T (INT64 param_a X INT64 param_b)
{
TDF_INT64 quot, a, b;
int is_neg;
PARAM(b) = param_b;
if (TDFUis_zero(b))
{
DIV_ZERO_ERROR; /* Stop now - return anything */
return param_b;
}
PARAM(a) = param_a;
is_neg = ((hi_32(a) ^ hi_32(b)) < 0); /* is result negative ? */
PARAM(a) = __TDFUabs(param_a);
PARAM(b) = __TDFUabs(param_b);
quot = TDFUdiv_rem (a, b, S_DIV2);
CLEAR_ERRORS; /* May have been set by __TDFUabs */
if (is_neg) {
hi_32(quot) = hi_32(quot) ^
( (INT32) ((UINT32) 0xffffffff)); /* Avoid errors, don't call __TDFUneg */
lo_32(quot) = lo_32(quot) ^ ((UINT32) 0xffffffff);
return PARAM(TDFUplus (quot, const_1));
}
if (TDFUis_negative(quot)) /* Only happens for INT_MAX+1 */
{
OVERFLOW_ERROR;
}
return PARAM(quot);
}
/*
** __TDFUu_div2:
**
** Division-by-zero is the only possible error.
*/
UINT64 __TDFUu_div2 PROTO_N ((param_a, param_b))
PROTO_T (UINT64 param_a X UINT64 param_b)
{
TDF_INT64 a, b;
CLEAR_ERRORS;
UPARAM(b) = param_b;
if (TDFUis_zero(b))
{
DIV_ZERO_ERROR; /* Stop now - return anything */
return param_b;
}
UPARAM(a) = param_a;
return UPARAM (TDFUdiv_rem (a, b, U_DIV2));
}
/*
** __TDFUs_rem1:
**
** Modulo-zero is the only possible error.
*/
INT64 __TDFUs_rem1 PROTO_N ((param_a, param_b))
PROTO_T (INT64 param_a X INT64 param_b)
{
TDF_INT64 rem, a, b, abs_a, abs_b;
int is_neg;
PARAM(b) = param_b;
if (TDFUis_zero(b))
{
DIV_ZERO_ERROR; /* Stop now - return anything */
return param_b;
}
PARAM(a) = param_a;
PARAM(abs_a) = __TDFUabs(param_a);
PARAM(abs_b) = __TDFUabs(param_b);
is_neg = TDFUis_negative(b); /* is result negative ? */
rem = TDFUdiv_rem (abs_a, abs_b, S_REM1);
CLEAR_ERRORS; /* May have been set by __TDFUabs */
if (TDFUis_positive(a) &&
TDFUis_negative(b) &&
TDFUis_nonzero(rem))
{
return __TDFUs_plus (param_b, PARAM(rem));
}
if (TDFUis_negative(a) &&
TDFUis_positive(b) &&
TDFUis_nonzero(rem))
{
return __TDFUs_minus (param_b, PARAM(rem));
}
if (is_neg) return __TDFUneg(PARAM(rem));
return PARAM(rem);
}
/*
** __TDFUs_rem2:
**
** Modulo-zero is the only possible error.
*/
INT64 __TDFUs_rem2 PROTO_N ((param_a, param_b))
PROTO_T (INT64 param_a X INT64 param_b)
{
TDF_INT64 rem, a, b;
int is_neg;
PARAM(b) = param_b;
if (TDFUis_zero(b))
{
DIV_ZERO_ERROR; /* Stop now - return anything */
return param_b;
}
PARAM(a) = param_a;
is_neg = TDFUis_negative(a); /* is result negative ? */
PARAM(a) = __TDFUabs(param_a);
PARAM(b) = __TDFUabs(param_b);
rem = TDFUdiv_rem (a, b, S_REM2);
CLEAR_ERRORS; /* May have been set by __TDFUabs */
if (is_neg) return __TDFUneg(PARAM(rem));
return PARAM(rem);
}
/*
** __TDFUu_rem2:
**
** Modulo-zero is the only possible error.
*/
UINT64 __TDFUu_rem2 PROTO_N ((param_a, param_b))
PROTO_T (UINT64 param_a X UINT64 param_b)
{
TDF_INT64 a, b;
UPARAM(b) = param_b;
if (TDFUis_zero(b))
{
DIV_ZERO_ERROR; /* Stop now - return anything */
return param_b;
}
UPARAM(a) = param_a;
CLEAR_ERRORS;
return UPARAM (TDFUdiv_rem (a, b, U_REM2));
}
/*
** __TDFUneg:
**
** Flip all bits and add 1, as for 2's complement.
** Overflow error if original value was INT64_MIN.
*/
INT64 __TDFUneg PROTO_N ((param_a))
PROTO_T (INT64 param_a)
{
TDF_INT64 a;
PARAM(a) = param_a;
hi_32(a) = hi_32(a) ^ ((INT32) ((UINT32) 0xffffffff));
lo_32(a) = lo_32(a) ^ ((UINT32) 0xffffffff);
return __TDFUs_plus (PARAM(a), PARAM(const_1)); /* Should handle any errors */
}
/*
** __TDFUs_shl:
**
** Best implemented in PL_TDF.
*/
/*
** __TDFUu_shl:
**
** Does error checking. If all is okay, returns the value.
**
*/
UINT64 __TDFUu_shl PROTO_N ((param_a, n))
PROTO_T (UINT64 param_a X UINT32 n)
{
TDF_INT64 a;
CLEAR_ERRORS;
if (n > 63) {
OVERFLOW_ERROR;
return UPARAM(const_u0);
}
if (n == 0) return param_a;
/* check that the result won't overflow */
UPARAM(a) = param_a;
if (n > 32) {
if (((lo_u32(a) >> (64-n)) != 0) ||
(hi_u32(a) != 0))
{
OVERFLOW_ERROR;
}
}
else if ((hi_u32(a) >> (32-n)) != 0)
{
OVERFLOW_ERROR;
}
TDFUshl (a, a, n);
return UPARAM(a);
}
/*
** __TDFUu_shr:
**
** 2 cases: n > 32 - copy high 32-bits across and shift.
** n <= 32 - mask out two contributions and OR together.
*/
static TDF_INT64 TDFUshr PROTO_N ((a, n))
PROTO_T (TDF_INT64 a X UINT32 n)
{
TDF_INT64 shifted_a;
CLEAR_ERRORS;
if (n >= 32) /* 32 <= n < 64 */
{
lo_u32 (shifted_a) = hi_u32(a) >> (n -32);
hi_u32 (shifted_a) = (UINT32) 0x00000000;
}
else /* (0 < n <= 32) */
{
lo_u32 (shifted_a) = (hi_u32(a) << (32-n)) | (lo_u32(a) >> n);
hi_u32 (shifted_a) = hi_u32(a) >> n;
}
return shifted_a;
}
/*
** __TDFUs_shr:
**
** Best implemented in PL_TDF
*/
/*
** __TDFUu_shr:
**
** Error occurs if n > 64 since the result is undefined.
*/
UINT64 __TDFUu_shr PROTO_N ((param_a, n))
PROTO_T (UINT64 param_a X UINT32 n)
{
TDF_INT64 a;
CLEAR_ERRORS;
if (n > 63) { /* This is undefined */
OVERFLOW_ERROR;
return UPARAM(const_u0);
}
if (n == 0) return param_a;
UPARAM(a) = param_a;
return UPARAM (TDFUshr (a, n));
}
/*
** __TDFUabs:
**
** Checks for a negative quantity, for which it
** calls '__TDFUneg'. Overflow for, as for __TDFUneg,
** occurs when the value is INT32_MIN.
**
*/
INT64 __TDFUabs PROTO_N ((param_a))
PROTO_T (INT64 param_a)
{
TDF_INT64 a;
PARAM(a) = param_a;
if (TDFUis_negative(a)) {
return __TDFUneg (param_a); /* This handles any errors */
}
CLEAR_ERRORS;
return param_a;
}
/*
** __TDFUsswiden:
**
** Extends the sign bit, and returns the new int.
**
*/
INT64 __TDFUsswiden PROTO_N ((a))
PROTO_T (INT32 a)
{
TDF_INT64 ext_int;
CLEAR_ERRORS;
hi_32(ext_int) = (INT32) ((a < 0) ? -1 : 0); /* sign-extend a */
lo_32(ext_int) = (UINT32) a;
return PARAM(ext_int);
}
/*
** __TDFUsuwiden:
**
** Error if a is negative.
** Otherwise extends the sign bit (just to be safe)
*/
UINT64 __TDFUsuwiden PROTO_N ((a))
PROTO_T (INT32 a)
{
TDF_INT64 ext_int;
CLEAR_ERRORS;
if (a < 0)
{
OVERFLOW_ERROR;
}
hi_u32(ext_int) = (UINT32) ((a < 0) ? 0xffffffff : 0);
/* sign-extend a */
lo_u32(ext_int) = (UINT32) a;
return UPARAM(ext_int);
}
/*
** __TDFUuuwiden:
**
** Puts zero in bits 32-63 and returns
*/
UINT64 __TDFUuuwiden PROTO_N ((a))
PROTO_T (UINT32 a)
{
TDF_INT64 ext_int;
CLEAR_ERRORS;
hi_u32(ext_int) = 0;
lo_u32(ext_int) = a;
return UPARAM(ext_int);
}
/*
** __TDFUuswiden:
**
** Same as __TDFUuuswiden.
**
*/
INT64 __TDFUuswiden PROTO_N ((a))
PROTO_T (UINT32 a)
{
TDF_INT64 ext_int;
CLEAR_ERRORS;
hi_32(ext_int) = 0;
lo_32(ext_int) = a;
return PARAM(ext_int);
}
/*
** __TDFUssshorten:
**
** Check that bits 31-63 are all set/unset,
** otherwise signal overflow. Mask sign
** bit into bit-31.
*/
INT32 __TDFUssshorten PROTO_N ((param_a))
PROTO_T (INT64 param_a)
{
TDF_INT64 a;
PARAM(a) = param_a;
CLEAR_ERRORS;
switch ((int) hi_32(a)) {
case 0:
if ((lo_32(a) & ((UINT32) 0x80000000)) != 0) {
OVERFLOW_ERROR;
}
break;
case -1:
if ((lo_32(a) & ((UINT32) 0x80000000)) == 0) {
OVERFLOW_ERROR;
}
break;
default:
OVERFLOW_ERROR;
}
return ((((INT32) lo_32(a)) & ((INT32) 0x7fffffff)) |
(hi_32(a) & ((INT32) ((UINT32) 0x80000000))));
}
/*
** __TDFUsushorten:
**
** Check that bits 32-63 are all zero
** otherwise signal overflow. Answer
** is in bits 0-31.
*/
UINT32 __TDFUsushorten PROTO_N ((param_a))
PROTO_T (INT64 param_a)
{
TDF_INT64 a;
PARAM(a) = param_a;
CLEAR_ERRORS;
if (hi_32(a) != 0)
{
OVERFLOW_ERROR;
}
return lo_32(a);
}
/*
** __TDFUuushorten:
**
** Error if bits 32-63 are not all zero.
** Return bits 0-31.
*/
UINT32 __TDFUuushorten PROTO_N ((param_a))
PROTO_T (UINT64 param_a)
{
TDF_INT64 a;
UPARAM(a) = param_a;
CLEAR_ERRORS;
if (hi_u32(a) != 0)
{
OVERFLOW_ERROR;
}
return lo_u32(a);
}
/*
** __TDFUusshorten:
**
** Error if bits 31-63 are not all zero.
** Return bits 0-31.
*/
INT32 __TDFUusshorten PROTO_N ((param_a))
PROTO_T (UINT64 param_a)
{
TDF_INT64 a;
UPARAM(a) = param_a;
CLEAR_ERRORS;
if ((hi_u32(a) != 0) ||
(((INT32) lo_u32(a)) < 0)) /* Must be an error - started off unsigned */
{
OVERFLOW_ERROR;
}
return (INT32) lo_u32(a);
}
/*
** __TDFUu642s64:
**
** Error only if MSB of "a" is set.
** Return bits 0-31.
*/
INT64 __TDFUu642s64 PROTO_N ((param_a))
PROTO_T (UINT64 param_a)
{
TDF_INT64 a;
UPARAM(a) = param_a;
CLEAR_ERRORS;
if (TDFUis_negative(a)) /* Cast and check */
{
OVERFLOW_ERROR;
}
return PARAM(a);
}
/*
** __TDFUs642u64:
**
** Error only if "a" < 0.
** Return bits 0-31.
*/
UINT64 __TDFUs642u64 PROTO_N ((param_a))
PROTO_T (INT64 param_a)
{
TDF_INT64 a;
PARAM(a) = param_a;
CLEAR_ERRORS;
if (TDFUis_negative(a)) /* Check and cast */
{
OVERFLOW_ERROR;
}
return UPARAM(a);
}
/*
** __TDFUs_max:
**
** Uses: (a>b) ? a : b No errors.
**
*/
INT64 __TDFUs_max PROTO_N ((param_a, param_b))
PROTO_T (INT64 param_a X INT64 param_b)
{
CLEAR_ERRORS;
if (__TDFUs_test(param_a, param_b) > 0) {
return param_a; /* a = max(a,b) */
}
return param_b; /* b = max(a,b) */
}
/*
** __TDFUu_max:
**
** Uses: (a>b) ? a : b No errors.
**
*/
UINT64 __TDFUu_max PROTO_N ((param_a, param_b))
PROTO_T (UINT64 param_a X UINT64 param_b)
{
if (__TDFUu_test(param_a, param_b) > 0) {
return param_a; /* a = max(a,b) */
}
return param_b; /* b = max(a,b) */
}
/*
** __TDFUs_min:
**
** Uses: (a<b) ? a : b No errors.
**
*/
INT64 __TDFUs_min PROTO_N ((param_a, param_b))
PROTO_T (INT64 param_a X INT64 param_b)
{
if (__TDFUs_test(param_a, param_b) < 0) {
return param_a; /* a = min(a,b) */
}
return param_b; /* b = min(a,b) */
}
/*
** __TDFUu_min:
**
** Uses: (a<b) ? a : b No errors.
**
*/
UINT64 __TDFUu_min PROTO_N ((param_a, param_b))
PROTO_T (UINT64 param_a X UINT64 param_b)
{
if (__TDFUu_test(param_a, param_b) < 0) {
return param_a; /* a = min(a,b) */
}
return param_b; /* b = min(a,b) */
}
/*
** TDFs_test:
**
** Returns -1 if a < b; This gives: a op b <=> TDFs_test(a,b) op 0
** 0 if a = b;
** 1 if a > b.
**
*/
int __TDFUs_test PROTO_N ((param_a, param_b))
PROTO_T (INT64 param_a X INT64 param_b)
{
TDF_INT64 a, b;
PARAM(a) = param_a;
PARAM(b) = param_b;
CLEAR_ERRORS;
if (hi_32(a) < hi_32(b)) return -1;
if (hi_32(a) > hi_32(b)) return 1;
if (lo_32(a) < lo_32(b)) return -1;
if (lo_32(a) > lo_32(b)) return 1;
return 0;
}
/*
** __TDFUu_test: as for __TDFUs_test, but for unsigned types.
*/
int __TDFUu_test PROTO_N ((param_a, param_b))
PROTO_T (UINT64 param_a X UINT64 param_b)
{
TDF_INT64 a, b;
UPARAM(a) = param_a;
UPARAM(b) = param_b;
CLEAR_ERRORS;
if (hi_u32(a) < hi_u32(b)) return -1;
if (hi_u32(a) > hi_u32(b)) return 1;
if (lo_u32(a) < lo_u32(b)) return -1;
if (lo_u32(a) > lo_u32(b)) return 1;
return 0;
}
/*
** __TDFUand
**
** ANDs the hi- and low-words together, and returns.
*/
UINT64 __TDFUand PROTO_N ((param_a, param_b))
PROTO_T (UINT64 param_a X UINT64 param_b)
{
TDF_INT64 a, b;
UPARAM(a) = param_a;
UPARAM(b) = param_b;
CLEAR_ERRORS;
hi_u32(a) = (hi_u32(a) & hi_u32(b));
lo_u32(a) = (lo_u32(a) & lo_u32(b));
return UPARAM(a);
}
/*
** __TDFUor
**
** ORs the hi- and low-words together, and returns.
*/
UINT64 __TDFUor PROTO_N ((param_a, param_b))
PROTO_T (UINT64 param_a X UINT64 param_b)
{
TDF_INT64 a, b;
UPARAM(a) = param_a;
UPARAM(b) = param_b;
CLEAR_ERRORS;
hi_u32(a) = (hi_u32(a) | hi_u32(b));
lo_u32(a) = (lo_u32(a) | lo_u32(b));
return UPARAM(a);
}
/*
** __TDFUxor
**
** ORs the hi- and low-words together, and returns.
*/
UINT64 __TDFUxor PROTO_N ((param_a, param_b))
PROTO_T (UINT64 param_a X UINT64 param_b)
{
TDF_INT64 a, b;
UPARAM(a) = param_a;
UPARAM(b) = param_b;
CLEAR_ERRORS;
hi_u32(a) = (hi_u32(a) ^ hi_u32(b));
lo_u32(a) = (lo_u32(a) ^ lo_u32(b));
return UPARAM(a);
}
/*
** __TDFUnot
**
** XORs the hi- and low-words each with 0xffffffff
*/
UINT64 __TDFUnot PROTO_N ((param_a))
PROTO_T (UINT64 param_a)
{
TDF_INT64 a;
UPARAM(a) = param_a;
CLEAR_ERRORS;
hi_u32(a) = hi_u32(a) ^ ((UINT32) 0xffffffff);
lo_u32(a) = lo_u32(a) ^ ((UINT32) 0xffffffff);
return UPARAM(a);
}
/* Functions only used for debug purposes - normally hidden */
#if DEBUG
INT64 make_INT64 PROTO_N ((new_hi, new_lo))
PROTO_T (INT32 new_hi X UINT32 new_lo)
{
TDF_INT64 new_int;
hi_32 (new_int) = new_hi;
lo_32 (new_int) = new_lo;
return PARAM(new_int);
}
UINT64 make_UINT64 PROTO_N ((new_hi, new_lo))
PROTO_T (UINT32 new_hi X UINT32 new_lo)
{
TDF_INT64 new_int;
hi_u32 (new_int) = new_hi;
lo_u32 (new_int) = new_lo;
return UPARAM(new_int);
}
void INT64_print PROTO_N ((t1, a, t2))
PROTO_T (char * t1 X INT64 a X char *t2)
{
IGNORE printf ("%s(%d,%u)%s", t1, (int) a.hi32, (unsigned) a.lo32, t2);
return;
}
void UINT64_print PROTO_N ((t1, a, t2))
PROTO_T (char * t1 X UINT64 a X char * t2)
{
IGNORE printf ("%s(%u,%u)%s", t1, (unsigned) a.hi32, (unsigned) a.lo32, t2);
return;
}
#endif /* DEBUG */