Usermode/libc - FP printf (partial)
authorJohn Hodge <[email protected]>
Thu, 7 Feb 2013 10:43:06 +0000 (18:43 +0800)
committerJohn Hodge <[email protected]>
Thu, 7 Feb 2013 10:43:06 +0000 (18:43 +0800)
Makefile.cfg
Usermode/Libraries/libc.so_src/Makefile
Usermode/Libraries/libc.so_src/printf.c

index c030d47..3ec9975 100644 (file)
@@ -7,6 +7,7 @@ ACESSDIR := $(shell cd $(ACESSDIR) && pwd)
 
 # Install destination configuration
 DISTROOT := -i $(ACESSDIR)/Acess2.img ::/Acess2
+NCC := $(CC)
 xCP := mcopy -D o
 xMKDIR := mmd -D s
 
index 0013408..cd4afa3 100644 (file)
@@ -20,3 +20,9 @@ BIN = libc.so
 \r
 include ../Makefile.tpl\r
 \r
+%.no: %.c\r
+       $(NCC) -c $< -o $@\r
+\r
+TEST_%: TEST_%.no %.no\r
+       $(NCC) -o $@ TEST_$*.no $*.no\r
+\r
index 1c946c2..223518f 100644 (file)
@@ -9,12 +9,21 @@
 #include <stdio.h>
 #include <stdarg.h>
 #include <string.h>
+#include <ctype.h>     // toupper
+#include <assert.h>    // assert() 
 
 // === TYPES ===
 typedef void   (*printf_putch_t)(void *h, char ch);
+enum eFPN {
+       FPN_STD,
+       FPN_SCI,
+       FPN_SHORTEST,
+};
 
 // === PROTOTYPES ===
 void   itoa(char *buf, uint64_t num, size_t base, int minLength, char pad, int bSigned);
+size_t _printf_itoa(printf_putch_t putch_cb, void *putch_h, uint64_t num, size_t base, int minLength, char pad, int bSigned);
+size_t _printf_ftoa(printf_putch_t putch_cb, void *putch_h, long double num, size_t Base, enum eFPN Notation, int Precision, int bForcePoint, int bForceSign, int bCapitals);
 
 // === CODE ===
 /**
@@ -32,7 +41,9 @@ EXPORT int _vcprintf_int(printf_putch_t putch_cb, void *putch_h, const char *for
        char    *p;
        char    pad;
        uint64_t        arg;
-        int    bLongLong, bPadLeft;
+       long double     arg_f;
+//     char    cPositiveChar;
+        int    bLongLong, bPadLeft, bLong;
 
        #define _addchar(ch) do { \
                putch_cb(putch_h, ch); \
@@ -57,32 +68,55 @@ EXPORT int _vcprintf_int(printf_putch_t putch_cb, void *putch_h, const char *for
                }
                
                bPadLeft = 0;
+               bLong = 0;
                bLongLong = 0;
                minSize = 0;
                precision = -1;
+//             cPositiveChar = '\0';
                pad = ' ';
                
-               // Padding Character
+               // - Flags
+               // Alternate form (0<oct>, 0x<hex>, 123.)
+               if(c == '#') {
+                       // TODO:
+                       c = *format++;
+               }
+               // Padding with '0'
                if(c == '0') {
                        pad = '0';
                        c = *format++;
                }
+               // Pad on left
+               if(c == '-') {
+                       bPadLeft = 1;
+                       c = *format++;
+               }
+               // Include space for positive sign
+               if(c == ' ') {
+                       // TODO:
+//                     cPositiveChar = ' ';
+                       c = *format++;
+               }
+               // Always include sign
+               if(c == '+') {
+//                     cPositiveChar = '+';
+                       c = *format++;
+               }
+               
                // Padding length
                if( c == '*' ) {
                        // Variable length
                        minSize = va_arg(args, size_t);
                        c = *format++;
                }
-               else {
-                       if('1' <= c && c <= '9')
+               else if('1' <= c && c <= '9')
+               {
+                       minSize = 0;
+                       while('0' <= c && c <= '9')
                        {
-                               minSize = 0;
-                               while('0' <= c && c <= '9')
-                               {
-                                       minSize *= 10;
-                                       minSize += c - '0';
-                                       c = *format++;
-                               }
+                               minSize *= 10;
+                               minSize += c - '0';
+                               c = *format++;
                        }
                }
 
@@ -106,12 +140,13 @@ EXPORT int _vcprintf_int(printf_putch_t putch_cb, void *putch_h, const char *for
                }
        
                // Check for long long
-               bLongLong = 0;
                if(c == 'l')
                {
+                       bLong = 1;
                        c = *format++;
                        if(c == 'l') {
                                bLongLong = 1;
+                               c = *format++;
                        }
                }
                
@@ -172,6 +207,29 @@ EXPORT int _vcprintf_int(printf_putch_t putch_cb, void *putch_h, const char *for
                        precision = -1;
                        goto sprintf_puts;
 
+               // Standard float
+               case 'f':
+                       if(bLong)       arg_f = va_arg(args, long double);
+                       else    arg_f = va_arg(args, double);
+                       pos += _printf_ftoa(putch_cb, putch_h, arg_f, 10, FPN_STD, precision, 0, bPadLeft, 0);
+                       break;
+               case 'F':
+                       if(bLong)       arg_f = va_arg(args, long double);
+                       else    arg_f = va_arg(args, double);
+                       pos += _printf_ftoa(putch_cb, putch_h, arg_f, 10, FPN_STD, precision, 0, bPadLeft, 1);
+                       break;
+               // Scientific Float
+               case 'e':
+                       if(bLong)       arg_f = va_arg(args, long double);
+                       else    arg_f = va_arg(args, double);
+                       pos += _printf_ftoa(putch_cb, putch_h, arg_f, 10, FPN_SCI, precision, 0, bPadLeft, 0);
+                       break;
+               case 'E':
+                       if(bLong)       arg_f = va_arg(args, long double);
+                       else    arg_f = va_arg(args, double);
+                       pos += _printf_ftoa(putch_cb, putch_h, arg_f, 10, FPN_SCI, precision, 0, bPadLeft, 1);
+                       break;
+
                // String
                case 's':
                        p = va_arg(args, char*);
@@ -292,7 +350,15 @@ EXPORT int printf(const char *format, ...)
        return ret;
 }
 
-const char cUCDIGITS[] = "0123456789ABCDEF";
+void itoa(char *buf, uint64_t num, size_t base, int minLength, char pad, int bSigned)
+{
+       struct s_sprintf_info   info = {buf, 0, 1024};
+       if(!buf)        return;
+       _printf_itoa(_vsnprintf_putch, &info, num, base, minLength, pad, bSigned);
+       buf[info.ofs] = '\0';
+}
+
+const char cDIGITS[] = "0123456789abcdef";
 /**
  * \brief Convert an integer into a character string
  * \param buf  Destination Buffer
@@ -302,15 +368,14 @@ const char cUCDIGITS[] = "0123456789ABCDEF";
  * \param pad  Padding used to ensure minLength
  * \param bSigned      Signed number output?
  */
-EXPORT void itoa(char *buf, uint64_t num, size_t base, int minLength, char pad, int bSigned)
+size_t _printf_itoa(printf_putch_t putch_cb, void *putch_h, uint64_t num, size_t base, int minLength, char pad, int bSigned)
 {
        char    tmpBuf[64];
-        int    pos=0, i;
+        int    pos=0;
+       size_t ret = 0;
 
-       if(!buf)        return;
        if(base > 16 || base < 2) {
-               buf[0] = 0;
-               return;
+               return 0;
        }
        
        if(bSigned && (int64_t)num < 0)
@@ -322,16 +387,225 @@ EXPORT void itoa(char *buf, uint64_t num, size_t base, int minLength, char pad,
        
        // Encode into reversed string
        while(num > base-1) {
-               tmpBuf[pos++] = cUCDIGITS[ num % base ];
+               tmpBuf[pos++] = cDIGITS[ num % base ];
                num = (uint64_t) num / (uint64_t)base;          // Shift {number} right 1 digit
        }
 
-       tmpBuf[pos++] = cUCDIGITS[ num % base ];                // Last digit of {number}
+       tmpBuf[pos++] = cDIGITS[ num % base ];          // Last digit of {number}
        if(bSigned)     tmpBuf[pos++] = '-';    // Append sign symbol if needed
        
-       i = 0;
        minLength -= pos;
-       while(minLength-- > 0)  buf[i++] = pad;
-       while(pos-- > 0)                buf[i++] = tmpBuf[pos]; // Reverse the order of characters
-       buf[i] = 0;
+       while(minLength-- > 0) {
+               putch_cb(putch_h, pad);
+               ret ++;
+       }
+       while(pos-- > 0) {
+               putch_cb(putch_h, tmpBuf[pos]); // Reverse the order of characters
+               ret ++;
+       }
+       
+       return ret;
+}
+
+int expand_double(double num, uint64_t *Significand, int16_t *Exponent, int *SignIsNeg)
+{
+       // IEEE 754 binary64
+       #if 0
+       {
+               uint64_t test_exp = 0xC000000000000000;
+               double test_value = -2.0f;
+               assert( *((uint64_t*)&test_value) == test_exp );
+       }
+       #endif
+       
+       const uint64_t  *bit_rep = (void*)&num;
+       
+       *SignIsNeg = *bit_rep >> 63;
+       *Exponent = ((*bit_rep >> 52) & 0x7FF) - 1023;
+       *Significand = (*bit_rep & ((1ULL << 52)-1)) << (64-52);
+
+//     printf("%llx %i %i %llx\n", *bit_rep, (int)*SignIsNeg, (int)*Exponent, *Significand);
+
+       // Subnormals
+       if( *Exponent == -1023 && *Significand != 0 )
+               return 1;
+       // Infinity
+       if( *Exponent == 0x800 && *Significand == 0)
+               return 2;
+       // NaNs
+       if( *Exponent == 0x800 && *Significand != 0)
+               return 3;
+
+       return 0;
+}
+
+/**
+ * Internal function
+ * \return Remainder
+ */
+double _longdiv(double num, double den, int *quot)
+{
+       assert(num >= 0);
+       assert(den > 0);
+//     printf("%llu / %llu\n", (long long int)num, (long long int)den);
+       
+       *quot = 0;
+       assert(num < den*10);
+       while(num >= den)
+       {
+               num -= den;
+               (*quot) ++;
+               assert( *quot < 10 );
+       }
+//     printf(" %i\n", *quot);
+       return num;
+}
+
+size_t _printf_ftoa(printf_putch_t putch_cb, void *putch_h, long double num, size_t Base, enum eFPN Notation, int Precision, int bForcePoint, int bForceSign, int bCapitals)
+{
+       uint64_t        significand;
+       int16_t exponent;
+        int    signisneg;
+        int    rv;
+       size_t  ret = 0;
+
+       #define _putch(_ch) do{\
+               if(bCapitals)\
+                       putch_cb(putch_h, toupper(_ch));\
+               else\
+                       putch_cb(putch_h, _ch);\
+               ret ++;\
+       }while(0)
+
+       if( Base <= 1 || Base > 16 )
+               return 0;
+
+       rv = expand_double(num, &significand, &exponent, &signisneg);
+       switch(rv)
+       {
+       // 0: No errors, nothing special
+       case 0:
+               break;
+       // 1: Subnormals
+       case 1:
+               // TODO: Subnormal = 0?
+               break;
+       // 2: Infinity
+       case 2:
+               _putch('i');
+               _putch('n');
+               _putch('f');
+               return 3;
+       case 3:
+               _putch('N');
+               _putch('a');
+               _putch('N');
+               return 3;
+       }
+
+       // - Used as 0/1 bools in arithmatic later on
+       bForcePoint = !!bForcePoint;
+       bForceSign = !!bForceSign;
+
+       // Apply default to precision
+       if( Precision == -1 )
+               Precision = 6;
+
+       if( num < 0 )
+               num = -num;
+
+       // Select optimum type
+       if( Notation == FPN_SHORTEST )
+       {
+               //TODO:
+               //int   first_set_sig = BSL(significand);
+       //      TODO: if( num > pos(Base, 2+Precision+2+log_base(exponent) )
+               Notation = FPN_SCI;
+       }
+
+       double precision_max = 1;
+       while(Precision--)
+               precision_max /= Base;
+
+       // Determine scientific's exponent and starting denominator
+       double den = 1;
+        int    sci_exponent = 0;
+       if( Notation == FPN_SCI )
+       {
+               if( num < 1 )
+               {
+                       while(num < 1)
+                       {
+                               num *= Base;
+                               sci_exponent ++;
+                       }
+               }
+               else if( num >= Base )
+               {
+                       while(num >= Base)
+                       {
+                               num /= Base;
+                               sci_exponent ++;
+                       }
+               }
+               else
+               {
+                       // no exponent
+               }
+               den = 1;
+       }
+       else
+       {
+               while( den < num )
+                       den *= Base;
+               den /= Base;
+       }
+
+       // Leading sign
+       if( signisneg )
+               _putch('-');
+       else if( bForceSign )
+               _putch('+');
+       else {
+       }
+
+        int    value;
+       // Whole section
+       do
+       {
+               num = _longdiv(num, den, &value);
+               _putch(cDIGITS[value]);
+               den /= Base;
+       } while( den >= 1 );
+
+       // Decimal point (if needed/forced)     
+       if( den >= precision_max || bForcePoint )
+               _putch('.');
+       // Decimal section
+       while( den >= precision_max )
+       {
+               num = _longdiv(num, den, &value);
+               _putch(cDIGITS[value]);
+               den /= Base;
+       }
+
+       if( Notation == FPN_SCI )
+       {
+               if( Base == 16 )
+                       _putch('p');
+               else
+                       _putch('e');
+               if(sci_exponent < 0)
+               {
+                       _putch('-');
+                       sci_exponent = -sci_exponent;
+               }
+               else
+                       _putch('+');
+               _printf_itoa(putch_cb, putch_h, sci_exponent, Base, 0, 0, 0);
+       }       
+
+       #undef _putch
+
+       return ret;
 }

UCC git Repository :: git.ucc.asn.au