/* ** emfloat.c ** Source for emulated floating-point routines. ** BYTEmark (tm) ** BYTE's Native Mode Benchmarks ** Rick Grehan, BYTE Magazine. ** ** Created: ** Last update: 3/95 ** ** DISCLAIMER ** The source, executable, and documentation files that comprise ** the BYTEmark benchmarks are made available on an "as is" basis. ** This means that we at BYTE Magazine have made every reasonable ** effort to verify that the there are no errors in the source and ** executable code. We cannot, however, guarantee that the programs ** are error-free. Consequently, McGraw-HIll and BYTE Magazine make ** no claims in regard to the fitness of the source code, executable ** code, and documentation of the BYTEmark. ** Furthermore, BYTE Magazine, McGraw-Hill, and all employees ** of McGraw-Hill cannot be held responsible for any damages resulting ** from the use of this code or the results obtained from using ** this code. */ #include "../pub/libvex_basictypes.h" static HWord (*serviceFn)(HWord,HWord) = 0; ///////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////// static char* my_strcpy ( char* dest, const char* src ) { char* dest_orig = dest; while (*src) *dest++ = *src++; *dest = 0; return dest_orig; } static void* my_memcpy ( void *dest, const void *src, int sz ) { const char *s = (const char *)src; char *d = (char *)dest; while (sz--) *d++ = *s++; return dest; } static void* my_memmove( void *dst, const void *src, unsigned int len ) { register char *d; register char *s; if ( dst > src ) { d = (char *)dst + len - 1; s = (char *)src + len - 1; while ( len >= 4 ) { *d-- = *s--; *d-- = *s--; *d-- = *s--; *d-- = *s--; len -= 4; } while ( len-- ) { *d-- = *s--; } } else if ( dst < src ) { d = (char *)dst; s = (char *)src; while ( len >= 4 ) { *d++ = *s++; *d++ = *s++; *d++ = *s++; *d++ = *s++; len -= 4; } while ( len-- ) { *d++ = *s++; } } return dst; } ///////////////////////////////////////////////////////////////////// static void vexxx_log_bytes ( char* p, int n ) { int i; for (i = 0; i < n; i++) (*serviceFn)( 1, (int)p[i] ); } /*---------------------------------------------------------*/ /*--- vexxx_printf ---*/ /*---------------------------------------------------------*/ /* This should be the only <...> include in the entire VEXXX library. New code for vexxx_util.c should go above this point. */ #include <stdarg.h> static HChar vexxx_toupper ( HChar c ) { if (c >= 'a' && c <= 'z') return toHChar(c + ('A' - 'a')); else return c; } static Int vexxx_strlen ( const HChar* str ) { Int i = 0; while (str[i] != 0) i++; return i; } Bool vexxx_streq ( const HChar* s1, const HChar* s2 ) { while (True) { if (*s1 == 0 && *s2 == 0) return True; if (*s1 != *s2) return False; s1++; s2++; } } /* Some flags. */ #define VG_MSG_SIGNED 1 /* The value is signed. */ #define VG_MSG_ZJUSTIFY 2 /* Must justify with '0'. */ #define VG_MSG_LJUSTIFY 4 /* Must justify on the left. */ #define VG_MSG_PAREN 8 /* Parenthesize if present (for %y) */ #define VG_MSG_COMMA 16 /* Add commas to numbers (for %d, %u) */ /* Copy a string into the buffer. */ static UInt myvprintf_str ( void(*send)(HChar), Int flags, Int width, HChar* str, Bool capitalise ) { # define MAYBE_TOUPPER(ch) toHChar(capitalise ? vexxx_toupper(ch) : (ch)) UInt ret = 0; Int i, extra; Int len = vexxx_strlen(str); if (width == 0) { ret += len; for (i = 0; i < len; i++) send(MAYBE_TOUPPER(str[i])); return ret; } if (len > width) { ret += width; for (i = 0; i < width; i++) send(MAYBE_TOUPPER(str[i])); return ret; } extra = width - len; if (flags & VG_MSG_LJUSTIFY) { ret += extra; for (i = 0; i < extra; i++) send(' '); } ret += len; for (i = 0; i < len; i++) send(MAYBE_TOUPPER(str[i])); if (!(flags & VG_MSG_LJUSTIFY)) { ret += extra; for (i = 0; i < extra; i++) send(' '); } # undef MAYBE_TOUPPER return ret; } /* Write P into the buffer according to these args: * If SIGN is true, p is a signed. * BASE is the base. * If WITH_ZERO is true, '0' must be added. * WIDTH is the width of the field. */ static UInt myvprintf_int64 ( void(*send)(HChar), Int flags, Int base, Int width, ULong pL) { HChar buf[40]; Int ind = 0; Int i, nc = 0; Bool neg = False; HChar *digits = "0123456789ABCDEF"; UInt ret = 0; UInt p = (UInt)pL; if (base < 2 || base > 16) return ret; if ((flags & VG_MSG_SIGNED) && (Int)p < 0) { p = - (Int)p; neg = True; } if (p == 0) buf[ind++] = '0'; else { while (p > 0) { if ((flags & VG_MSG_COMMA) && 10 == base && 0 == (ind-nc) % 3 && 0 != ind) { buf[ind++] = ','; nc++; } buf[ind++] = digits[p % base]; p /= base; } } if (neg) buf[ind++] = '-'; if (width > 0 && !(flags & VG_MSG_LJUSTIFY)) { for(; ind < width; ind++) { //vassert(ind < 39); buf[ind] = toHChar((flags & VG_MSG_ZJUSTIFY) ? '0': ' '); } } /* Reverse copy to buffer. */ ret += ind; for (i = ind -1; i >= 0; i--) { send(buf[i]); } if (width > 0 && (flags & VG_MSG_LJUSTIFY)) { for(; ind < width; ind++) { ret++; send(' '); // Never pad with zeroes on RHS -- changes the value! } } return ret; } /* A simple vprintf(). */ static UInt vprintf_wrk ( void(*send)(HChar), const HChar *format, va_list vargs ) { UInt ret = 0; int i; int flags; int width; Bool is_long; /* We assume that vargs has already been initialised by the caller, using va_start, and that the caller will similarly clean up with va_end. */ for (i = 0; format[i] != 0; i++) { if (format[i] != '%') { send(format[i]); ret++; continue; } i++; /* A '%' has been found. Ignore a trailing %. */ if (format[i] == 0) break; if (format[i] == '%') { /* `%%' is replaced by `%'. */ send('%'); ret++; continue; } flags = 0; is_long = False; width = 0; /* length of the field. */ if (format[i] == '(') { flags |= VG_MSG_PAREN; i++; } /* If ',' follows '%', commas will be inserted. */ if (format[i] == ',') { flags |= VG_MSG_COMMA; i++; } /* If '-' follows '%', justify on the left. */ if (format[i] == '-') { flags |= VG_MSG_LJUSTIFY; i++; } /* If '0' follows '%', pads will be inserted. */ if (format[i] == '0') { flags |= VG_MSG_ZJUSTIFY; i++; } /* Compute the field length. */ while (format[i] >= '0' && format[i] <= '9') { width *= 10; width += format[i++] - '0'; } while (format[i] == 'l') { i++; is_long = True; } switch (format[i]) { case 'd': /* %d */ flags |= VG_MSG_SIGNED; if (is_long) ret += myvprintf_int64(send, flags, 10, width, (ULong)(va_arg (vargs, Long))); else ret += myvprintf_int64(send, flags, 10, width, (ULong)(va_arg (vargs, Int))); break; case 'u': /* %u */ if (is_long) ret += myvprintf_int64(send, flags, 10, width, (ULong)(va_arg (vargs, ULong))); else ret += myvprintf_int64(send, flags, 10, width, (ULong)(va_arg (vargs, UInt))); break; case 'p': /* %p */ ret += 2; send('0'); send('x'); ret += myvprintf_int64(send, flags, 16, width, (ULong)((HWord)va_arg (vargs, void *))); break; case 'x': /* %x */ if (is_long) ret += myvprintf_int64(send, flags, 16, width, (ULong)(va_arg (vargs, ULong))); else ret += myvprintf_int64(send, flags, 16, width, (ULong)(va_arg (vargs, UInt))); break; case 'c': /* %c */ ret++; send(toHChar(va_arg (vargs, int))); break; case 's': case 'S': { /* %s */ char *str = va_arg (vargs, char *); if (str == (char*) 0) str = "(null)"; ret += myvprintf_str(send, flags, width, str, toBool(format[i]=='S')); break; } # if 0 case 'y': { /* %y - print symbol */ Addr a = va_arg(vargs, Addr); HChar *name; if (VG_(get_fnname_w_offset)(a, &name)) { HChar buf[1 + VG_strlen(name) + 1 + 1]; if (flags & VG_MSG_PAREN) { VG_(sprintf)(str, "(%s)", name): } else { VG_(sprintf)(str, "%s", name): } ret += myvprintf_str(send, flags, width, buf, 0); } break; } # endif default: break; } } return ret; } /* A general replacement for printf(). Note that only low-level debugging info should be sent via here. The official route is to to use vg_message(). This interface is deprecated. */ static HChar myprintf_buf[1000]; static Int n_myprintf_buf; static void add_to_myprintf_buf ( HChar c ) { if (c == '\n' || n_myprintf_buf >= 1000-10 /*paranoia*/ ) { (*vexxx_log_bytes)( myprintf_buf, vexxx_strlen(myprintf_buf) ); n_myprintf_buf = 0; myprintf_buf[n_myprintf_buf] = 0; } myprintf_buf[n_myprintf_buf++] = c; myprintf_buf[n_myprintf_buf] = 0; } static UInt vexxx_printf ( const char *format, ... ) { UInt ret; va_list vargs; va_start(vargs,format); n_myprintf_buf = 0; myprintf_buf[n_myprintf_buf] = 0; ret = vprintf_wrk ( add_to_myprintf_buf, format, vargs ); if (n_myprintf_buf > 0) { (*vexxx_log_bytes)( myprintf_buf, n_myprintf_buf ); } va_end(vargs); return ret; } /*---------------------------------------------------------------*/ /*--- end vexxx_util.c ---*/ /*---------------------------------------------------------------*/ ///////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////// //#include <stdio.h> //#include <string.h> //#include <malloc.h> typedef unsigned char uchar; typedef unsigned int uint; typedef unsigned short ushort; typedef unsigned long ulong; typedef int int32; /* Signed 32 bit integer */ #define INTERNAL_FPF_PRECISION 4 #define CPUEMFLOATLOOPMAX 500000L #define EMFARRAYSIZE 3000L typedef struct { int adjust; /* Set adjust code */ ulong request_secs; /* # of seconds requested */ ulong arraysize; /* Size of array */ ulong loops; /* Loops per iterations */ double emflops; /* Results */ } EmFloatStruct; /* Is this a 64 bit architecture? If so, this will define LONG64 */ /* Uwe F. Mayer 15 November 1997 */ // #include "pointer.h" #define u8 unsigned char #define u16 unsigned short #ifdef LONG64 #define u32 unsigned int #else #define u32 unsigned long #endif #define uchar unsigned char #define ulong unsigned long #define MAX_EXP 32767L #define MIN_EXP (-32767L) #define IFPF_IS_ZERO 0 #define IFPF_IS_SUBNORMAL 1 #define IFPF_IS_NORMAL 2 #define IFPF_IS_INFINITY 3 #define IFPF_IS_NAN 4 #define IFPF_TYPE_COUNT 5 #define ZERO_ZERO 0 #define ZERO_SUBNORMAL 1 #define ZERO_NORMAL 2 #define ZERO_INFINITY 3 #define ZERO_NAN 4 #define SUBNORMAL_ZERO 5 #define SUBNORMAL_SUBNORMAL 6 #define SUBNORMAL_NORMAL 7 #define SUBNORMAL_INFINITY 8 #define SUBNORMAL_NAN 9 #define NORMAL_ZERO 10 #define NORMAL_SUBNORMAL 11 #define NORMAL_NORMAL 12 #define NORMAL_INFINITY 13 #define NORMAL_NAN 14 #define INFINITY_ZERO 15 #define INFINITY_SUBNORMAL 16 #define INFINITY_NORMAL 17 #define INFINITY_INFINITY 18 #define INFINITY_NAN 19 #define NAN_ZERO 20 #define NAN_SUBNORMAL 21 #define NAN_NORMAL 22 #define NAN_INFINITY 23 #define NAN_NAN 24 #define OPERAND_ZERO 0 #define OPERAND_SUBNORMAL 1 #define OPERAND_NORMAL 2 #define OPERAND_INFINITY 3 #define OPERAND_NAN 4 typedef struct { u8 type; /* Indicates, NORMAL, SUBNORMAL, etc. */ u8 sign; /* Mantissa sign */ short exp; /* Signed exponent...no bias */ u16 mantissa[INTERNAL_FPF_PRECISION]; } InternalFPF; static void SetupCPUEmFloatArrays(InternalFPF *abase, InternalFPF *bbase, InternalFPF *cbase, ulong arraysize); static ulong DoEmFloatIteration(InternalFPF *abase, InternalFPF *bbase, InternalFPF *cbase, ulong arraysize, ulong loops); static void SetInternalFPFZero(InternalFPF *dest, uchar sign); static void SetInternalFPFInfinity(InternalFPF *dest, uchar sign); static void SetInternalFPFNaN(InternalFPF *dest); static int IsMantissaZero(u16 *mant); static void Add16Bits(u16 *carry,u16 *a,u16 b,u16 c); static void Sub16Bits(u16 *borrow,u16 *a,u16 b,u16 c); static void ShiftMantLeft1(u16 *carry,u16 *mantissa); static void ShiftMantRight1(u16 *carry,u16 *mantissa); static void StickyShiftRightMant(InternalFPF *ptr,int amount); static void normalize(InternalFPF *ptr); static void denormalize(InternalFPF *ptr,int minimum_exponent); static void RoundInternalFPF(InternalFPF *ptr); static void choose_nan(InternalFPF *x,InternalFPF *y,InternalFPF *z, int intel_flag); static void AddSubInternalFPF(uchar operation,InternalFPF *x, InternalFPF *y,InternalFPF *z); static void MultiplyInternalFPF(InternalFPF *x,InternalFPF *y, InternalFPF *z); static void DivideInternalFPF(InternalFPF *x,InternalFPF *y, InternalFPF *z); static void Int32ToInternalFPF(int32 mylong, InternalFPF *dest); static int InternalFPFToString(char *dest, InternalFPF *src); static int32 randnum(int32 lngval); static int32 randwc(int32 num) { return(randnum((int32)0)%num); } static int32 randw[2] = { (int32)13 , (int32)117 }; static int32 randnum(int32 lngval) { register int32 interm; if (lngval!=(int32)0) { randw[0]=(int32)13; randw[1]=(int32)117; } interm=(randw[0]*(int32)254754+randw[1]*(int32)529562)%(int32)999563; randw[1]=randw[0]; randw[0]=interm; return(interm); } static void SetupCPUEmFloatArrays(InternalFPF *abase, InternalFPF *bbase, InternalFPF *cbase, ulong arraysize) { ulong i; InternalFPF locFPF1,locFPF2; randnum((int32)13); for(i=0;i<arraysize;i++) {/* LongToInternalFPF(randwc(50000L),&locFPF1); */ Int32ToInternalFPF(randwc((int32)50000),&locFPF1); /* LongToInternalFPF(randwc(50000L)+1L,&locFPF2); */ Int32ToInternalFPF(randwc((int32)50000)+(int32)1,&locFPF2); DivideInternalFPF(&locFPF1,&locFPF2,abase+i); /* LongToInternalFPF(randwc(50000L)+1L,&locFPF2); */ Int32ToInternalFPF(randwc((int32)50000)+(int32)1,&locFPF2); DivideInternalFPF(&locFPF1,&locFPF2,bbase+i); } return; } static char* str1 = "loops %d\n"; static ulong DoEmFloatIteration(InternalFPF *abase, InternalFPF *bbase, InternalFPF *cbase, ulong arraysize, ulong loops) { static uchar jtable[16] = {0,0,0,0,1,1,1,1,2,2,2,2,2,3,3,3}; ulong i; int number_of_loops; loops = 100; number_of_loops=loops-1; /* the index of the first loop we run */ vexxx_printf(str1, (int)loops); /* ** Each pass through the array performs operations in ** the followingratios: ** 4 adds, 4 subtracts, 5 multiplies, 3 divides ** (adds and subtracts being nearly the same operation) */ { for(i=0;i<arraysize;i++) switch(jtable[i % 16]) { case 0: /* Add */ AddSubInternalFPF(0,abase+i, bbase+i, cbase+i); break; case 1: /* Subtract */ AddSubInternalFPF(1,abase+i, bbase+i, cbase+i); break; case 2: /* Multiply */ MultiplyInternalFPF(abase+i, bbase+i, cbase+i); break; case 3: /* Divide */ DivideInternalFPF(abase+i, bbase+i, cbase+i); break; } { ulong j[8]; /* we test 8 entries */ int k; ulong i; char buffer[1024]; if (100==loops) /* the first loop */ { j[0]=(ulong)2; j[1]=(ulong)6; j[2]=(ulong)10; j[3]=(ulong)14; j[4]=(ulong)(arraysize-14); j[5]=(ulong)(arraysize-10); j[6]=(ulong)(arraysize-6); j[7]=(ulong)(arraysize-2); for(k=0;k<8;k++){ i=j[k]; InternalFPFToString(buffer,abase+i); vexxx_printf("%6d: (%s) ",i,buffer); switch(jtable[i % 16]) { case 0: my_strcpy(buffer,"+"); break; case 1: my_strcpy(buffer,"-"); break; case 2: my_strcpy(buffer,"*"); break; case 3: my_strcpy(buffer,"/"); break; } vexxx_printf("%s ",buffer); InternalFPFToString(buffer,bbase+i); vexxx_printf("(%s) = ",buffer); InternalFPFToString(buffer,cbase+i); vexxx_printf("%s\n",buffer); } return 0; } } } return 0; } /*********************** ** SetInternalFPFZero ** ************************ ** Set an internal floating-point-format number to zero. ** sign determines the sign of the zero. */ static void SetInternalFPFZero(InternalFPF *dest, uchar sign) { int i; /* Index */ dest->type=IFPF_IS_ZERO; dest->sign=sign; dest->exp=MIN_EXP; for(i=0;i<INTERNAL_FPF_PRECISION;i++) dest->mantissa[i]=0; return; } /*************************** ** SetInternalFPFInfinity ** **************************** ** Set an internal floating-point-format number to infinity. ** This can happen if the exponent exceeds MAX_EXP. ** As above, sign picks the sign of infinity. */ static void SetInternalFPFInfinity(InternalFPF *dest, uchar sign) { int i; /* Index */ dest->type=IFPF_IS_INFINITY; dest->sign=sign; dest->exp=MIN_EXP; for(i=0;i<INTERNAL_FPF_PRECISION;i++) dest->mantissa[i]=0; return; } /********************** ** SetInternalFPFNaN ** *********************** ** Set an internal floating-point-format number to Nan ** (not a number). Note that we "emulate" an 80x87 as far ** as the mantissa bits go. */ static void SetInternalFPFNaN(InternalFPF *dest) { int i; /* Index */ dest->type=IFPF_IS_NAN; dest->exp=MAX_EXP; dest->sign=1; dest->mantissa[0]=0x4000; for(i=1;i<INTERNAL_FPF_PRECISION;i++) dest->mantissa[i]=0; return; } /******************* ** IsMantissaZero ** ******************** ** Pass this routine a pointer to an internal floating point format ** number's mantissa. It checks for an all-zero mantissa. ** Returns 0 if it is NOT all zeros, !=0 otherwise. */ static int IsMantissaZero(u16 *mant) { int i; /* Index */ int n; /* Return value */ n=0; for(i=0;i<INTERNAL_FPF_PRECISION;i++) n|=mant[i]; return(!n); } /************** ** Add16Bits ** *************** ** Add b, c, and carry. Retult in a. New carry in carry. */ static void Add16Bits(u16 *carry, u16 *a, u16 b, u16 c) { u32 accum; /* Accumulator */ /* ** Do the work in the 32-bit accumulator so we can return ** the carry. */ accum=(u32)b; accum+=(u32)c; accum+=(u32)*carry; *carry=(u16)((accum & 0x00010000) ? 1 : 0); /* New carry */ *a=(u16)(accum & 0xFFFF); /* Result is lo 16 bits */ return; } /************** ** Sub16Bits ** *************** ** Additive inverse of above. */ static void Sub16Bits(u16 *borrow, u16 *a, u16 b, u16 c) { u32 accum; /* Accumulator */ accum=(u32)b; accum-=(u32)c; accum-=(u32)*borrow; *borrow=(u32)((accum & 0x00010000) ? 1 : 0); /* New borrow */ *a=(u16)(accum & 0xFFFF); return; } /******************* ** ShiftMantLeft1 ** ******************** ** Shift a vector of 16-bit numbers left 1 bit. Also provides ** a carry bit, which is shifted in at the beginning, and ** shifted out at the end. */ static void ShiftMantLeft1(u16 *carry, u16 *mantissa) { int i; /* Index */ int new_carry; u16 accum; /* Temporary holding placed */ for(i=INTERNAL_FPF_PRECISION-1;i>=0;i--) { accum=mantissa[i]; new_carry=accum & 0x8000; /* Get new carry */ accum=accum<<1; /* Do the shift */ if(*carry) accum|=1; /* Insert previous carry */ *carry=new_carry; mantissa[i]=accum; /* Return shifted value */ } return; } /******************** ** ShiftMantRight1 ** ********************* ** Shift a mantissa right by 1 bit. Provides carry, as ** above */ static void ShiftMantRight1(u16 *carry, u16 *mantissa) { int i; /* Index */ int new_carry; u16 accum; for(i=0;i<INTERNAL_FPF_PRECISION;i++) { accum=mantissa[i]; new_carry=accum & 1; /* Get new carry */ accum=accum>>1; if(*carry) accum|=0x8000; *carry=new_carry; mantissa[i]=accum; } return; } /***************************** ** StickyShiftMantRight ** ****************************** ** This is a shift right of the mantissa with a "sticky bit". ** I.E., if a carry of 1 is shifted out of the least significant ** bit, the least significant bit is set to 1. */ static void StickyShiftRightMant(InternalFPF *ptr, int amount) { int i; /* Index */ u16 carry; /* Self-explanatory */ u16 *mantissa; mantissa=ptr->mantissa; if(ptr->type!=IFPF_IS_ZERO) /* Don't bother shifting a zero */ { /* ** If the amount of shifting will shift everyting ** out of existence, then just clear the whole mantissa ** and set the lowmost bit to 1. */ if(amount>=INTERNAL_FPF_PRECISION * 16) { for(i=0;i<INTERNAL_FPF_PRECISION-1;i++) mantissa[i]=0; mantissa[INTERNAL_FPF_PRECISION-1]=1; } else for(i=0;i<amount;i++) { carry=0; ShiftMantRight1(&carry,mantissa); if(carry) mantissa[INTERNAL_FPF_PRECISION-1] |= 1; } } return; } /************************************************** ** POST ARITHMETIC PROCESSING ** ** (NORMALIZE, ROUND, OVERFLOW, AND UNDERFLOW) ** **************************************************/ /************** ** normalize ** *************** ** Normalize an internal-representation number. Normalization ** discards empty most-significant bits. */ static void normalize(InternalFPF *ptr) { u16 carry; /* ** As long as there's a highmost 0 bit, shift the significand ** left 1 bit. Each time you do this, though, you've ** gotta decrement the exponent. */ while ((ptr->mantissa[0] & 0x8000) == 0) { carry = 0; ShiftMantLeft1(&carry, ptr->mantissa); ptr->exp--; } return; } /**************** ** denormalize ** ***************** ** Denormalize an internal-representation number. This means ** shifting it right until its exponent is equivalent to ** minimum_exponent. (You have to do this often in order ** to perform additions and subtractions). */ static void denormalize(InternalFPF *ptr, int minimum_exponent) { long exponent_difference; if (IsMantissaZero(ptr->mantissa)) { vexxx_printf("Error: zero significand in denormalize\n"); } exponent_difference = ptr->exp-minimum_exponent; if (exponent_difference < 0) { /* ** The number is subnormal */ exponent_difference = -exponent_difference; if (exponent_difference >= (INTERNAL_FPF_PRECISION * 16)) { /* Underflow */ SetInternalFPFZero(ptr, ptr->sign); } else { ptr->exp+=exponent_difference; StickyShiftRightMant(ptr, exponent_difference); } } return; } /********************* ** RoundInternalFPF ** ********************** ** Round an internal-representation number. ** The kind of rounding we do here is simplest...referred to as ** "chop". "Extraneous" rightmost bits are simply hacked off. */ void RoundInternalFPF(InternalFPF *ptr) { /* int i; */ if (ptr->type == IFPF_IS_NORMAL || ptr->type == IFPF_IS_SUBNORMAL) { denormalize(ptr, MIN_EXP); if (ptr->type != IFPF_IS_ZERO) { /* clear the extraneous bits */ ptr->mantissa[3] &= 0xfff8; /* for (i=4; i<INTERNAL_FPF_PRECISION; i++) { ptr->mantissa[i] = 0; } */ /* ** Check for overflow */ /* Does not do anything as ptr->exp is a short and MAX_EXP=37268 if (ptr->exp > MAX_EXP) { SetInternalFPFInfinity(ptr, ptr->sign); } */ } } return; } /******************************************************* ** ARITHMETIC OPERATIONS ON INTERNAL REPRESENTATION ** *******************************************************/ /*************** ** choose_nan ** **************** ** Called by routines that are forced to perform math on ** a pair of NaN's. This routine "selects" which NaN is ** to be returned. */ static void choose_nan(InternalFPF *x, InternalFPF *y, InternalFPF *z, int intel_flag) { int i; /* ** Compare the two mantissas, ** return the larger. Note that we will be emulating ** an 80387 in this operation. */ for (i=0; i<INTERNAL_FPF_PRECISION; i++) { if (x->mantissa[i] > y->mantissa[i]) { my_memmove((void *)x,(void *)z,sizeof(InternalFPF)); return; } if (x->mantissa[i] < y->mantissa[i]) { my_memmove((void *)y,(void *)z,sizeof(InternalFPF)); return; } } /* ** They are equal */ if (!intel_flag) /* if the operation is addition */ my_memmove((void *)x,(void *)z,sizeof(InternalFPF)); else /* if the operation is multiplication */ my_memmove((void *)y,(void *)z,sizeof(InternalFPF)); return; } /********************** ** AddSubInternalFPF ** *********************** ** Adding or subtracting internal-representation numbers. ** Internal-representation numbers pointed to by x and y are ** added/subtracted and the result returned in z. */ static void AddSubInternalFPF(uchar operation, InternalFPF *x, InternalFPF *y, InternalFPF *z) { int exponent_difference; u16 borrow; u16 carry; int i; InternalFPF locx,locy; /* Needed since we alter them */ /* ** Following big switch statement handles the ** various combinations of operand types. */ switch ((x->type * IFPF_TYPE_COUNT) + y->type) { case ZERO_ZERO: my_memmove((void *)x,(void *)z,sizeof(InternalFPF)); if (x->sign ^ y->sign ^ operation) { z->sign = 0; /* positive */ } break; case NAN_ZERO: case NAN_SUBNORMAL: case NAN_NORMAL: case NAN_INFINITY: case SUBNORMAL_ZERO: case NORMAL_ZERO: case INFINITY_ZERO: case INFINITY_SUBNORMAL: case INFINITY_NORMAL: my_memmove((void *)x,(void *)z,sizeof(InternalFPF)); break; case ZERO_NAN: case SUBNORMAL_NAN: case NORMAL_NAN: case INFINITY_NAN: my_memmove((void *)y,(void *)z,sizeof(InternalFPF)); break; case ZERO_SUBNORMAL: case ZERO_NORMAL: case ZERO_INFINITY: case SUBNORMAL_INFINITY: case NORMAL_INFINITY: my_memmove((void *)y,(void *)z,sizeof(InternalFPF)); z->sign ^= operation; break; case SUBNORMAL_SUBNORMAL: case SUBNORMAL_NORMAL: case NORMAL_SUBNORMAL: case NORMAL_NORMAL: /* ** Copy x and y to locals, since we may have ** to alter them. */ my_memmove((void *)&locx,(void *)x,sizeof(InternalFPF)); my_memmove((void *)&locy,(void *)y,sizeof(InternalFPF)); /* compute sum/difference */ exponent_difference = locx.exp-locy.exp; if (exponent_difference == 0) { /* ** locx.exp == locy.exp ** so, no shifting required */ if (locx.type == IFPF_IS_SUBNORMAL || locy.type == IFPF_IS_SUBNORMAL) z->type = IFPF_IS_SUBNORMAL; else z->type = IFPF_IS_NORMAL; /* ** Assume that locx.mantissa > locy.mantissa */ z->sign = locx.sign; z->exp= locx.exp; } else if (exponent_difference > 0) { /* ** locx.exp > locy.exp */ StickyShiftRightMant(&locy, exponent_difference); z->type = locx.type; z->sign = locx.sign; z->exp = locx.exp; } else /* if (exponent_difference < 0) */ { /* ** locx.exp < locy.exp */ StickyShiftRightMant(&locx, -exponent_difference); z->type = locy.type; z->sign = locy.sign ^ operation; z->exp = locy.exp; } if (locx.sign ^ locy.sign ^ operation) { /* ** Signs are different, subtract mantissas */ borrow = 0; for (i=(INTERNAL_FPF_PRECISION-1); i>=0; i--) Sub16Bits(&borrow, &z->mantissa[i], locx.mantissa[i], locy.mantissa[i]); if (borrow) { /* The y->mantissa was larger than the ** x->mantissa leaving a negative ** result. Change the result back to ** an unsigned number and flip the ** sign flag. */ z->sign = locy.sign ^ operation; borrow = 0; for (i=(INTERNAL_FPF_PRECISION-1); i>=0; i--) { Sub16Bits(&borrow, &z->mantissa[i], 0, z->mantissa[i]); } } else { /* The assumption made above ** (i.e. x->mantissa >= y->mantissa) ** was correct. Therefore, do nothing. ** z->sign = x->sign; */ } if (IsMantissaZero(z->mantissa)) { z->type = IFPF_IS_ZERO; z->sign = 0; /* positive */ } else if (locx.type == IFPF_IS_NORMAL || locy.type == IFPF_IS_NORMAL) { normalize(z); } } else { /* signs are the same, add mantissas */ carry = 0; for (i=(INTERNAL_FPF_PRECISION-1); i>=0; i--) { Add16Bits(&carry, &z->mantissa[i], locx.mantissa[i], locy.mantissa[i]); } if (carry) { z->exp++; carry=0; ShiftMantRight1(&carry,z->mantissa); z->mantissa[0] |= 0x8000; z->type = IFPF_IS_NORMAL; } else if (z->mantissa[0] & 0x8000) z->type = IFPF_IS_NORMAL; } break; case INFINITY_INFINITY: SetInternalFPFNaN(z); break; case NAN_NAN: choose_nan(x, y, z, 1); break; } /* ** All the math is done; time to round. */ RoundInternalFPF(z); return; } /************************ ** MultiplyInternalFPF ** ************************* ** Two internal-representation numbers x and y are multiplied; the ** result is returned in z. */ static void MultiplyInternalFPF(InternalFPF *x, InternalFPF *y, InternalFPF *z) { int i; int j; u16 carry; u16 extra_bits[INTERNAL_FPF_PRECISION]; InternalFPF locy; /* Needed since this will be altered */ /* ** As in the preceding function, this large switch ** statement selects among the many combinations ** of operands. */ switch ((x->type * IFPF_TYPE_COUNT) + y->type) { case INFINITY_SUBNORMAL: case INFINITY_NORMAL: case INFINITY_INFINITY: case ZERO_ZERO: case ZERO_SUBNORMAL: case ZERO_NORMAL: my_memmove((void *)x,(void *)z,sizeof(InternalFPF)); z->sign ^= y->sign; break; case SUBNORMAL_INFINITY: case NORMAL_INFINITY: case SUBNORMAL_ZERO: case NORMAL_ZERO: my_memmove((void *)y,(void *)z,sizeof(InternalFPF)); z->sign ^= x->sign; break; case ZERO_INFINITY: case INFINITY_ZERO: SetInternalFPFNaN(z); break; case NAN_ZERO: case NAN_SUBNORMAL: case NAN_NORMAL: case NAN_INFINITY: my_memmove((void *)x,(void *)z,sizeof(InternalFPF)); break; case ZERO_NAN: case SUBNORMAL_NAN: case NORMAL_NAN: case INFINITY_NAN: my_memmove((void *)y,(void *)z,sizeof(InternalFPF)); break; case SUBNORMAL_SUBNORMAL: case SUBNORMAL_NORMAL: case NORMAL_SUBNORMAL: case NORMAL_NORMAL: /* ** Make a local copy of the y number, since we will be ** altering it in the process of multiplying. */ my_memmove((void *)&locy,(void *)y,sizeof(InternalFPF)); /* ** Check for unnormal zero arguments */ if (IsMantissaZero(x->mantissa) || IsMantissaZero(y->mantissa)) SetInternalFPFInfinity(z, 0); /* ** Initialize the result */ if (x->type == IFPF_IS_SUBNORMAL || y->type == IFPF_IS_SUBNORMAL) z->type = IFPF_IS_SUBNORMAL; else z->type = IFPF_IS_NORMAL; z->sign = x->sign ^ y->sign; z->exp = x->exp + y->exp ; for (i=0; i<INTERNAL_FPF_PRECISION; i++) { z->mantissa[i] = 0; extra_bits[i] = 0; } for (i=0; i<(INTERNAL_FPF_PRECISION*16); i++) { /* ** Get rightmost bit of the multiplier */ carry = 0; ShiftMantRight1(&carry, locy.mantissa); if (carry) { /* ** Add the multiplicand to the product */ carry = 0; for (j=(INTERNAL_FPF_PRECISION-1); j>=0; j--) Add16Bits(&carry, &z->mantissa[j], z->mantissa[j], x->mantissa[j]); } else { carry = 0; } /* ** Shift the product right. Overflow bits get ** shifted into extra_bits. We'll use it later ** to help with the "sticky" bit. */ ShiftMantRight1(&carry, z->mantissa); ShiftMantRight1(&carry, extra_bits); } /* ** Normalize ** Note that we use a "special" normalization routine ** because we need to use the extra bits. (These are ** bits that may have been shifted off the bottom that ** we want to reclaim...if we can. */ while ((z->mantissa[0] & 0x8000) == 0) { carry = 0; ShiftMantLeft1(&carry, extra_bits); ShiftMantLeft1(&carry, z->mantissa); z->exp--; } /* ** Set the sticky bit if any bits set in extra bits. */ if (IsMantissaZero(extra_bits)) { z->mantissa[INTERNAL_FPF_PRECISION-1] |= 1; } break; case NAN_NAN: choose_nan(x, y, z, 0); break; } /* ** All math done...do rounding. */ RoundInternalFPF(z); return; } /********************** ** DivideInternalFPF ** *********************** ** Divide internal FPF number x by y. Return result in z. */ static void DivideInternalFPF(InternalFPF *x, InternalFPF *y, InternalFPF *z) { int i; int j; u16 carry; u16 extra_bits[INTERNAL_FPF_PRECISION]; InternalFPF locx; /* Local for x number */ /* ** As with preceding function, the following switch ** statement selects among the various possible ** operands. */ switch ((x->type * IFPF_TYPE_COUNT) + y->type) { case ZERO_ZERO: case INFINITY_INFINITY: SetInternalFPFNaN(z); break; case ZERO_SUBNORMAL: case ZERO_NORMAL: if (IsMantissaZero(y->mantissa)) { SetInternalFPFNaN(z); break; } case ZERO_INFINITY: case SUBNORMAL_INFINITY: case NORMAL_INFINITY: SetInternalFPFZero(z, x->sign ^ y->sign); break; case SUBNORMAL_ZERO: case NORMAL_ZERO: if (IsMantissaZero(x->mantissa)) { SetInternalFPFNaN(z); break; } case INFINITY_ZERO: case INFINITY_SUBNORMAL: case INFINITY_NORMAL: SetInternalFPFInfinity(z, 0); z->sign = x->sign ^ y->sign; break; case NAN_ZERO: case NAN_SUBNORMAL: case NAN_NORMAL: case NAN_INFINITY: my_memmove((void *)x,(void *)z,sizeof(InternalFPF)); break; case ZERO_NAN: case SUBNORMAL_NAN: case NORMAL_NAN: case INFINITY_NAN: my_memmove((void *)y,(void *)z,sizeof(InternalFPF)); break; case SUBNORMAL_SUBNORMAL: case NORMAL_SUBNORMAL: case SUBNORMAL_NORMAL: case NORMAL_NORMAL: /* ** Make local copy of x number, since we'll be ** altering it in the process of dividing. */ my_memmove((void *)&locx,(void *)x,sizeof(InternalFPF)); /* ** Check for unnormal zero arguments */ if (IsMantissaZero(locx.mantissa)) { if (IsMantissaZero(y->mantissa)) SetInternalFPFNaN(z); else SetInternalFPFZero(z, 0); break; } if (IsMantissaZero(y->mantissa)) { SetInternalFPFInfinity(z, 0); break; } /* ** Initialize the result */ z->type = x->type; z->sign = x->sign ^ y->sign; z->exp = x->exp - y->exp + ((INTERNAL_FPF_PRECISION * 16 * 2)); for (i=0; i<INTERNAL_FPF_PRECISION; i++) { z->mantissa[i] = 0; extra_bits[i] = 0; } while ((z->mantissa[0] & 0x8000) == 0) { carry = 0; ShiftMantLeft1(&carry, locx.mantissa); ShiftMantLeft1(&carry, extra_bits); /* ** Time to subtract yet? */ if (carry == 0) for (j=0; j<INTERNAL_FPF_PRECISION; j++) { if (y->mantissa[j] > extra_bits[j]) { carry = 0; goto no_subtract; } if (y->mantissa[j] < extra_bits[j]) break; } /* ** Divisor (y) <= dividend (x), subtract */ carry = 0; for (j=(INTERNAL_FPF_PRECISION-1); j>=0; j--) Sub16Bits(&carry, &extra_bits[j], extra_bits[j], y->mantissa[j]); carry = 1; /* 1 shifted into quotient */ no_subtract: ShiftMantLeft1(&carry, z->mantissa); z->exp--; } break; case NAN_NAN: choose_nan(x, y, z, 0); break; } /* ** Math complete...do rounding */ RoundInternalFPF(z); } /********************** ** LongToInternalFPF ** ** Int32ToInternalFPF ** *********************** ** Convert a signed (long) 32-bit integer into an internal FPF number. */ /* static void LongToInternalFPF(long mylong, */ static void Int32ToInternalFPF(int32 mylong, InternalFPF *dest) { int i; /* Index */ u16 myword; /* Used to hold converted stuff */ /* ** Save the sign and get the absolute value. This will help us ** with 64-bit machines, since we use only the lower 32 ** bits just in case. (No longer necessary after we use int32.) */ /* if(mylong<0L) */ if(mylong<(int32)0) { dest->sign=1; mylong=(int32)0-mylong; } else dest->sign=0; /* ** Prepare the destination floating point number */ dest->type=IFPF_IS_NORMAL; for(i=0;i<INTERNAL_FPF_PRECISION;i++) dest->mantissa[i]=0; /* ** See if we've got a zero. If so, make the resultant FP ** number a true zero and go home. */ if(mylong==0) { dest->type=IFPF_IS_ZERO; dest->exp=0; return; } /* ** Not a true zero. Set the exponent to 32 (internal FPFs have ** no bias) and load the low and high words into their proper ** locations in the mantissa. Then normalize. The action of ** normalizing slides the mantissa bits into place and sets ** up the exponent properly. */ dest->exp=32; myword=(u16)((mylong >> 16) & 0xFFFFL); dest->mantissa[0]=myword; myword=(u16)(mylong & 0xFFFFL); dest->mantissa[1]=myword; normalize(dest); return; } #if 1 /************************ ** InternalFPFToString ** ************************* ** FOR DEBUG PURPOSES ** This routine converts an internal floating point representation ** number to a string. Used in debugging the package. ** Returns length of converted number. ** NOTE: dest must point to a buffer big enough to hold the ** result. Also, this routine does append a null (an effect ** of using the sprintf() function). It also returns ** a length count. ** NOTE: This routine returns 5 significant digits. Thats ** about all I feel safe with, given the method of ** conversion. It should be more than enough for programmers ** to determine whether the package is properly ported. */ static int InternalFPFToString(char *dest, InternalFPF *src) { InternalFPF locFPFNum; /* Local for src (will be altered) */ InternalFPF IFPF10; /* Floating-point 10 */ InternalFPF IFPFComp; /* For doing comparisons */ int msign; /* Holding for mantissa sign */ int expcount; /* Exponent counter */ int ccount; /* Character counter */ int i,j,k; /* Index */ u16 carryaccum; /* Carry accumulator */ u16 mycarry; /* Local for carry */ /* ** Check first for the simple things...Nan, Infinity, Zero. ** If found, copy the proper string in and go home. */ switch(src->type) { case IFPF_IS_NAN: my_memcpy(dest,"NaN",3); return(3); case IFPF_IS_INFINITY: if(src->sign==0) my_memcpy(dest,"+Inf",4); else my_memcpy(dest,"-Inf",4); return(4); case IFPF_IS_ZERO: if(src->sign==0) my_memcpy(dest,"+0",2); else my_memcpy(dest,"-0",2); return(2); } /* ** Move the internal number into our local holding area, since ** we'll be altering it to print it out. */ my_memcpy((void *)&locFPFNum,(void *)src,sizeof(InternalFPF)); /* ** Set up a floating-point 10...which we'll use a lot in a minute. */ /* LongToInternalFPF(10L,&IFPF10); */ Int32ToInternalFPF((int32)10,&IFPF10); /* ** Save the mantissa sign and make it positive. */ msign=src->sign; /* src->sign=0 */ /* bug, fixed Nov. 13, 1997 */ (&locFPFNum)->sign=0; expcount=0; /* Init exponent counter */ /* ** See if the number is less than 10. If so, multiply ** the number repeatedly by 10 until it's not. For each ** multiplication, decrement a counter so we can keep track ** of the exponent. */ while(1) { AddSubInternalFPF(1,&locFPFNum,&IFPF10,&IFPFComp); if(IFPFComp.sign==0) break; MultiplyInternalFPF(&locFPFNum,&IFPF10,&IFPFComp); expcount--; my_memcpy((void *)&locFPFNum,(void *)&IFPFComp,sizeof(InternalFPF)); } /* ** Do the reverse of the above. As long as the number is ** greater than or equal to 10, divide it by 10. Increment the ** exponent counter for each multiplication. */ while(1) { AddSubInternalFPF(1,&locFPFNum,&IFPF10,&IFPFComp); if(IFPFComp.sign!=0) break; DivideInternalFPF(&locFPFNum,&IFPF10,&IFPFComp); expcount++; my_memcpy((void *)&locFPFNum,(void *)&IFPFComp,sizeof(InternalFPF)); } /* ** About time to start storing things. First, store the ** mantissa sign. */ ccount=1; /* Init character counter */ if(msign==0) *dest++='+'; else *dest++='-'; /* ** At this point we know that the number is in the range ** 10 > n >=1. We need to "strip digits" out of the ** mantissa. We do this by treating the mantissa as ** an integer and multiplying by 10. (Not a floating-point ** 10, but an integer 10. Since this is debug code and we ** could care less about speed, we'll do it the stupid ** way and simply add the number to itself 10 times. ** Anything that makes it to the left of the implied binary point ** gets stripped off and emitted. We'll do this for ** 5 significant digits (which should be enough to ** verify things). */ /* ** Re-position radix point */ carryaccum=0; while(locFPFNum.exp>0) { mycarry=0; ShiftMantLeft1(&mycarry,locFPFNum.mantissa); carryaccum=(carryaccum<<1); if(mycarry) carryaccum++; locFPFNum.exp--; } while(locFPFNum.exp<0) { mycarry=0; ShiftMantRight1(&mycarry,locFPFNum.mantissa); locFPFNum.exp++; } for(i=0;i<6;i++) if(i==1) { /* Emit decimal point */ *dest++='.'; ccount++; } else { /* Emit a digit */ *dest++=('0'+carryaccum); ccount++; carryaccum=0; my_memcpy((void *)&IFPF10, (void *)&locFPFNum, sizeof(InternalFPF)); /* Do multiply via repeated adds */ for(j=0;j<9;j++) { mycarry=0; for(k=(INTERNAL_FPF_PRECISION-1);k>=0;k--) Add16Bits(&mycarry,&(IFPFComp.mantissa[k]), locFPFNum.mantissa[k], IFPF10.mantissa[k]); carryaccum+=mycarry ? 1 : 0; my_memcpy((void *)&locFPFNum, (void *)&IFPFComp, sizeof(InternalFPF)); } } /* ** Now move the 'E', the exponent sign, and the exponent ** into the string. */ *dest++='E'; /* sprint is supposed to return an integer, but it caused problems on SunOS * with the native cc. Hence we force it. * Uwe F. Mayer */ if (expcount < 0) { *dest++ = '-'; expcount =- expcount; } else *dest++ = ' '; *dest++ = (char)(expcount + '0'); *dest++ = 0; ccount += 3; /* ** All done, go home. */ return(ccount); } #endif //////////////////////////////////////////////////////////////////////// static void* AllocateMemory ( unsigned long n, int* p ) { *p = 0; void* r = (void*) (*serviceFn)(2,n); return r; } static void FreeMemory ( void* p, int* zz ) { *zz = 0; // free(p); } /************** ** DoEmFloat ** *************** ** Perform the floating-point emulation routines portion of the ** CPU benchmark. Returns the operations per second. */ static void DoEmFloat(void) { EmFloatStruct *locemfloatstruct; /* Local structure */ InternalFPF *abase; /* Base of A array */ InternalFPF *bbase; /* Base of B array */ InternalFPF *cbase; /* Base of C array */ ulong tickcount; /* # of ticks */ char *errorcontext; /* Error context string pointer */ int systemerror; /* For holding error code */ ulong loops; /* # of loops */ /* ** Link to global structure */ EmFloatStruct global_emfloatstruct; global_emfloatstruct.adjust = 0; global_emfloatstruct.request_secs = 0; global_emfloatstruct.arraysize = 100; global_emfloatstruct.loops = 1; global_emfloatstruct.emflops = 0.0; locemfloatstruct=&global_emfloatstruct; /* ** Set the error context */ errorcontext="CPU:Floating Emulation"; abase=(InternalFPF *)AllocateMemory(locemfloatstruct->arraysize*sizeof(InternalFPF), &systemerror); bbase=(InternalFPF *)AllocateMemory(locemfloatstruct->arraysize*sizeof(InternalFPF), &systemerror); cbase=(InternalFPF *)AllocateMemory(locemfloatstruct->arraysize*sizeof(InternalFPF), &systemerror); /* ** Set up the arrays */ SetupCPUEmFloatArrays(abase,bbase,cbase,locemfloatstruct->arraysize); loops=100; tickcount=DoEmFloatIteration(abase,bbase,cbase, locemfloatstruct->arraysize, loops); FreeMemory((void *)abase,&systemerror); FreeMemory((void *)bbase,&systemerror); FreeMemory((void *)cbase,&systemerror); return; } ////////////////// void entry ( HWord(*f)(HWord,HWord) ) { serviceFn = f; vexxx_printf("starting\n"); DoEmFloat(); (*serviceFn)(0,0); }