From 2103edc7b67600818930793851b57e3583f73954 Mon Sep 17 00:00:00 2001 From: John Hodge Date: Thu, 7 Feb 2013 18:43:06 +0800 Subject: [PATCH] Usermode/libc - FP printf (partial) --- Makefile.cfg | 1 + Usermode/Libraries/libc.so_src/Makefile | 6 + Usermode/Libraries/libc.so_src/printf.c | 322 ++++++++++++++++++++++-- 3 files changed, 305 insertions(+), 24 deletions(-) diff --git a/Makefile.cfg b/Makefile.cfg index c030d475..3ec9975e 100644 --- a/Makefile.cfg +++ b/Makefile.cfg @@ -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 diff --git a/Usermode/Libraries/libc.so_src/Makefile b/Usermode/Libraries/libc.so_src/Makefile index 00134088..cd4afa36 100644 --- a/Usermode/Libraries/libc.so_src/Makefile +++ b/Usermode/Libraries/libc.so_src/Makefile @@ -20,3 +20,9 @@ BIN = libc.so include ../Makefile.tpl +%.no: %.c + $(NCC) -c $< -o $@ + +TEST_%: TEST_%.no %.no + $(NCC) -o $@ TEST_$*.no $*.no + diff --git a/Usermode/Libraries/libc.so_src/printf.c b/Usermode/Libraries/libc.so_src/printf.c index 1c946c21..223518f1 100644 --- a/Usermode/Libraries/libc.so_src/printf.c +++ b/Usermode/Libraries/libc.so_src/printf.c @@ -9,12 +9,21 @@ #include #include #include +#include // toupper +#include // 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, 0x, 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*)# + + *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; } -- 2.20.1