* device/lib/printf_large.c: fixed bug #2656821: bug in printf
[fw/sdcc] / device / lib / printf_large.c
index ba43fd546cb52ccbf93a8765cf46e7d3c0b022e5..fba74ff0298f76c5d2d3f2d2f073f1b9be7bf034 100644 (file)
@@ -24,7 +24,7 @@
    what you give them.   Help stamp out software-hoarding!
 -------------------------------------------------------------------------*/
 
-#if defined(__ds390)
+#if defined (SDCC_ds390)
 #define USE_FLOATS 1
 #endif
 
 #define NULL_STRING_LENGTH 6
 #endif
 
+#if defined (SDCC_mcs51) && defined (SDCC_MODEL_SMALL) && !defined (SDCC_STACK_AUTO)
+# define MEM_SPACE_BUF __idata
+# define MEM_SPACE_BUF_PP __idata
+#else
+# define MEM_SPACE_BUF
+# define MEM_SPACE_BUF_PP _AUTOMEM
+#endif
+
 /****************************************************************************/
 
 //typedef char * ptr_t;
 #ifdef toupper
 #undef toupper
 #endif
+#ifdef tolower
+#undef tolower
+#endif
+#ifdef islower
+#undef islower
+#endif
+#ifdef isdigit
+#undef isdigit
+#endif
 
 //#define toupper(c) ((c)&=~0x20)
 #define toupper(c) ((c)&=0xDF)
+#define tolower(c) ((c)|=0x20)
+#define islower(c) ((unsigned char)c >= (unsigned char)'a' && (unsigned char)c <= (unsigned char)'z')
+#define isdigit(c) ((unsigned char)c >= (unsigned char)'0' && (unsigned char)c <= (unsigned char)'9')
 
 typedef union
 {
@@ -63,38 +83,68 @@ typedef union
   char           *ptr;
 } value_t;
 
-static const char memory_id[] = "IXCP-";
-
 #ifndef SDCC_STACK_AUTO
   static BOOL lower_case;
   static pfn_outputchar output_char;
   static void* p;
   static value_t value;
+  static int charsOutputted;
 #endif
 
 /****************************************************************************/
 
+#ifdef SDCC_STACK_AUTO
+  #define OUTPUT_CHAR(c, p) { output_char (c, p); charsOutputted++; }
+#else
+  #define OUTPUT_CHAR(c, p) _output_char (c)
+  static void _output_char( unsigned char c )
+  {
+    output_char( c, p );
+    charsOutputted++;
+  }
+#endif
+
+/*--------------------------------------------------------------------------*/
+
 #ifdef SDCC_STACK_AUTO
   static void output_digit( unsigned char n, BOOL lower_case, pfn_outputchar output_char, void* p )
+  {
+    register unsigned char c = n + (unsigned char)'0';
+
+    if (c > (unsigned char)'9')
+    {
+      c += (unsigned char)('A' - '0' - 10);
+      if (lower_case)
+         c += (unsigned char)('a' - 'A');
+    }
+    output_char( c, p );
+  }
 #else
   static void output_digit( unsigned char n )
-#endif
   {
-    output_char( n <= 9 ? '0'+n :
-               (lower_case ? n+(char)('a'-10) : n+(char)('A'-10)), p );
+    register unsigned char c = n + (unsigned char)'0';
+
+    if (c > (unsigned char)'9')
+    {
+      c += (unsigned char)('A' - '0' - 10);
+      if (lower_case)
+         c = tolower(c);
+    }
+    _output_char( c );
   }
+#endif
 
 /*--------------------------------------------------------------------------*/
 
 #ifdef SDCC_STACK_AUTO
-  #define OUTPUT_2DIGITS( B )  output_2digits( B, lower_case, output_char, p )
+  #define OUTPUT_2DIGITS( B )   { output_2digits( B, lower_case, output_char, p ); charsOutputted += 2; }
   static void output_2digits( unsigned char b, BOOL lower_case, pfn_outputchar output_char, void* p )
   {
     output_digit( b>>4,   lower_case, output_char, p );
     output_digit( b&0x0F, lower_case, output_char, p );
   }
 #else
-  #define OUTPUT_2DIGITS( B )  output_2digits( B )
+  #define OUTPUT_2DIGITS( B )   output_2digits( B )
   static void output_2digits( unsigned char b )
   {
     output_digit( b>>4   );
@@ -105,38 +155,46 @@ static const char memory_id[] = "IXCP-";
 /*--------------------------------------------------------------------------*/
 
 #if defined SDCC_STACK_AUTO
-static void calculate_digit( value_t* value, unsigned char radix )
+static void calculate_digit( value_t _AUTOMEM * value, unsigned char radix )
 {
-  unsigned char i;
+  unsigned long ul = value->ul;
+  unsigned char _AUTOMEM * pb4 = &value->byte[4];
+  unsigned char i = 32;
 
-  for( i = 32; i != 0; i-- )
+  do
   {
-    value->byte[4] = (value->byte[4] << 1) | ((value->ul >> 31) & 0x01);
-    value->ul <<= 1;
+    *pb4 = (*pb4 << 1) | ((ul >> 31) & 0x01);
+    ul <<= 1;
 
-    if (radix <= value->byte[4] )
+    if (radix <= *pb4 )
     {
-      value->byte[4] -= radix;
-      value->ul |= 1;
+      *pb4 -= radix;
+      ul |= 1;
     }
-  }
+  } while (--i);
+  value->ul = ul;
 }
 #else
 static void calculate_digit( unsigned char radix )
 {
-  unsigned char i;
+  register unsigned long ul = value.ul;
+  register unsigned char b4 = value.byte[4];
+  register unsigned char i = 32;
 
-  for( i = 32; i != 0; i-- )
+  do
   {
-    value.byte[4] = (value.byte[4] << 1) | ((value.ul >> 31) & 0x01);
-    value.ul <<= 1;
+    b4 = (b4 << 1);
+    b4 |= (ul >> 31) & 0x01;
+    ul <<= 1;
 
-    if (radix <= value.byte[4] )
+    if (radix <= b4 )
     {
-      value.byte[4] -= radix;
-      value.ul |= 1;
+      b4 -= radix;
+      ul |= 1;
     }
-  }
+  } while (--i);
+  value.ul = ul;
+  value.byte[4] = b4;
 }
 #endif
 
@@ -153,25 +211,35 @@ static void calculate_digit( unsigned char radix )
 #define DEFAULT_FLOAT_PRECISION 6
 
 #ifdef SDCC_STACK_AUTO
-#define OUTPUT_FLOAT(F, W, D, L, Z, S, P)      output_float(F, W, D, L, Z, S, P, output_char, p)
-static int output_float (float f, unsigned char reqWidth,
-                         signed char reqDecimals,
-                         BOOL left, BOOL zero, BOOL sign, BOOL space,
-                         pfn_outputchar output_char, void* p)
+#define OUTPUT_FLOAT(F, W, D, L, Z, S, P)       output_float(F, W, D, L, Z, S, P, output_char, p)
+static unsigned char
+output_float (float f, unsigned char reqWidth,
+              signed char reqDecimals,
+              BOOL left, BOOL zero, BOOL sign, BOOL space,
+              pfn_outputchar output_char, void* p)
+{
+  unsigned char charsOutputted = 0;
+ #if defined (SDCC_mcs51)
+  char fpBuffer[16];      //mcs51 has only a small stack
+ #else
+  char fpBuffer[128];
+ #endif
 #else
-#define OUTPUT_FLOAT(F, W, D, L, Z, S, P)      output_float(F, W, D, L, Z, S, P)
-static int output_float (float f, unsigned char reqWidth,
-                         signed char reqDecimals,
-                         BOOL left, BOOL zero, BOOL sign, BOOL space)
-#endif
+#define OUTPUT_FLOAT(F, W, D, L, Z, S, P)       output_float(F, W, D, L, Z, S, P)
+static void
+output_float (float f, unsigned char reqWidth,
+              signed char reqDecimals,
+              BOOL left, BOOL zero, BOOL sign, BOOL space)
 {
-  BOOL negative=0;
+  __xdata char fpBuffer[128];
+#endif //SDCC_STACK_AUTO
+  BOOL negative = 0;
   unsigned long integerPart;
+  float rounding;
   float decimalPart;
-  char fpBuffer[128];
   char fpBI=0, fpBD;
   unsigned char minWidth, i;
-  int charsOutputted=0;
+  signed char exp = -128;
 
   // save the sign
   if (f<0) {
@@ -181,37 +249,38 @@ static int output_float (float f, unsigned char reqWidth,
 
   if (f>0x00ffffff) {
     // this part is from Frank van der Hulst
-    signed char exp;
 
     for (exp = 0; f >= 10.0; exp++) f /=10.0;
     for (       ; f < 1.0;   exp--) f *=10.0;
 
     if (negative) {
-      output_char ('-', p);
-      charsOutputted++;
+      OUTPUT_CHAR ('-', p);
     } else {
       if (sign) {
-        output_char ('+', p);
-        charsOutputted++;
+        OUTPUT_CHAR ('+', p);
       }
     }
-    charsOutputted += OUTPUT_FLOAT(f, 0, reqDecimals, 0, 0, 0, 0);
-    output_char ('e', p);
-    charsOutputted++;
-    if (exp<0) {
-      output_char ('-', p);
-      charsOutputted++;
-      exp = -exp;
-    }
-    output_char ('0'+exp/10, p);
-    output_char ('0'+exp%10, p);
-    charsOutputted += 2;
-    return charsOutputted;
+    reqWidth = 0;
+    left = 0;
+    zero = 0;
+    sign = 0;
+    space = 0;
+  }
+
+  // display some decimals as default
+  if (reqDecimals==-1)
+    reqDecimals=DEFAULT_FLOAT_PRECISION;
+
+  // round the float
+  rounding = 0.5;
+  for (i=reqDecimals; i>0; i--) {
+      rounding /= 10.0;
   }
+  f += rounding;
 
   // split the float
-  integerPart=f;
-  decimalPart=f-integerPart;
+  integerPart = f;
+  decimalPart = f - integerPart;
 
   // fill the buffer with the integerPart (in reversed order!)
   while (integerPart) {
@@ -223,25 +292,15 @@ static int output_float (float f, unsigned char reqWidth,
     fpBuffer[fpBI++]='0';
   }
 
-  // display some decimals as default
-  if (reqDecimals==-1)
-    reqDecimals=DEFAULT_FLOAT_PRECISION;
-
   // fill buffer with the decimalPart (in normal order)
   fpBD=fpBI;
 
-  for (i=reqDecimals; i>1; i--) {
+  for (i=reqDecimals; i>0; i--) {
       decimalPart *= 10.0;
       // truncate the float
-      integerPart=decimalPart;
-      fpBuffer[fpBD++]='0' + integerPart;
-      decimalPart-=integerPart;
-  }
-  if (i) {
-    decimalPart *= 10.0;
-    // truncate the float
-    integerPart = decimalPart + 0.5;
-    fpBuffer[fpBD++] = '0' + integerPart;
+      integerPart = decimalPart;
+      fpBuffer[fpBD++] = '0' + integerPart;
+      decimalPart -= integerPart;
   }
 
   minWidth=fpBI; // we need at least these
@@ -253,93 +312,92 @@ static int output_float (float f, unsigned char reqWidth,
     if (zero) {
       if (negative)
       {
-        output_char('-', p);
-        charsOutputted++;
+        OUTPUT_CHAR('-', p);
       }
       else if (sign)
       {
-        output_char('+', p);
-        charsOutputted++;
+        OUTPUT_CHAR('+', p);
       }
       else if (space)
       {
-        output_char(' ', p);
-        charsOutputted++;
+        OUTPUT_CHAR(' ', p);
       }
       while (reqWidth-->minWidth)
       {
-        output_char('0', p);
-        charsOutputted++;
+        OUTPUT_CHAR('0', p);
       }
     } else {
       while (reqWidth-->minWidth)
       {
-        output_char(' ', p);
-        charsOutputted++;
+        OUTPUT_CHAR(' ', p);
       }
       if (negative)
       {
-        output_char('-', p);
-        charsOutputted++;
+        OUTPUT_CHAR('-', p);
       }
       else if (sign)
       {
-        output_char('+', p);
-        charsOutputted++;
+        OUTPUT_CHAR('+', p);
       }
       else if (space)
       {
-        output_char(' ', p);
-        charsOutputted++;
+        OUTPUT_CHAR(' ', p);
       }
     }
   } else {
     if (negative)
     {
-      output_char('-', p);
-      charsOutputted++;
+      OUTPUT_CHAR('-', p);
     }
     else if (sign)
     {
-      output_char('+', p);
-      charsOutputted++;
+      OUTPUT_CHAR('+', p);
     }
     else if (space)
     {
-      output_char(' ', p);
-      charsOutputted++;
+      OUTPUT_CHAR(' ', p);
     }
   }
 
   // output the integer part
   i=fpBI-1;
   do {
-    output_char (fpBuffer[i], p);
-    charsOutputted++;
+    OUTPUT_CHAR (fpBuffer[i], p);
   } while (i--);
 
   // ouput the decimal part
   if (reqDecimals) {
-    output_char ('.', p);
-    charsOutputted++;
+    OUTPUT_CHAR ('.', p);
     i=fpBI;
     while (reqDecimals--)
     {
-      output_char (fpBuffer[i++], p);
-      charsOutputted++;
+      OUTPUT_CHAR (fpBuffer[i++], p);
     }
   }
 
   if (left && reqWidth>minWidth) {
     while (reqWidth-->minWidth)
     {
-      output_char(' ', p);
-      charsOutputted++;
+      OUTPUT_CHAR(' ', p);
     }
   }
+
+  if (exp != -128) {
+    OUTPUT_CHAR ('e', p);
+    if (exp<0) {
+      OUTPUT_CHAR ('-', p);
+      exp = -exp;
+    }
+    OUTPUT_CHAR ('0'+exp/10, p);
+    OUTPUT_CHAR ('0'+exp%10, p);
+  }
+#ifdef SDCC_STACK_AUTO
   return charsOutputted;
+#else
+  return;
+#endif //SDCC_STACK_AUTO
 }
-#endif
+#endif //USE_FLOATS
 
 int _print_format (pfn_outputchar pfn, void* pvoid, const char *format, va_list ap)
 {
@@ -354,11 +412,11 @@ int _print_format (pfn_outputchar pfn, void* pvoid, const char *format, va_list
 #ifdef SDCC_STACK_AUTO
   BOOL   lower_case;
   value_t value;
+  int charsOutputted;
 #endif
   BOOL   lsd;
 
   unsigned char radix;
-  int charsOutputted;
   unsigned char  width;
   signed char decimals;
   unsigned char  length;
@@ -373,7 +431,7 @@ int _print_format (pfn_outputchar pfn, void* pvoid, const char *format, va_list
 #endif
 
   // reset output chars
-  charsOutputted=0;
+  charsOutputted = 0;
 
 #ifdef SDCC_ds390
   if (format==0) {
@@ -390,10 +448,10 @@ int _print_format (pfn_outputchar pfn, void* pvoid, const char *format, va_list
       prefix_sign     = 0;
       prefix_space    = 0;
       signed_argument = 0;
-      radix           = 0;
       char_argument   = 0;
       long_argument   = 0;
       float_argument  = 0;
+      radix           = 0;
       width           = 0;
       decimals        = -1;
 
@@ -402,8 +460,7 @@ get_conversion_spec:
       c = *format++;
 
       if (c=='%') {
-        output_char(c, p);
-        charsOutputted++;
+        OUTPUT_CHAR(c, p);
         continue;
       }
 
@@ -421,17 +478,19 @@ get_conversion_spec:
       }
 
       if (c=='.') {
-        if (decimals=-1) decimals=0;
+        if (decimals==-1) decimals=0;
         else
           ; // duplicate, ignore
         goto get_conversion_spec;
       }
 
-      lower_case = islower(c);
-      if (lower_case)
+      if (islower(c))
       {
         c = toupper(c);
+        lower_case = 1;
       }
+      else
+        lower_case = 0;
 
       switch( c )
       {
@@ -452,8 +511,11 @@ get_conversion_spec:
         goto get_conversion_spec;
 
       case 'C':
-        output_char( va_arg(ap,int), p );
-        charsOutputted++;
+        if( char_argument )
+          c = va_arg(ap,char);
+        else
+          c = va_arg(ap,int);
+        OUTPUT_CHAR( c, p );
         break;
 
       case 'S':
@@ -478,15 +540,14 @@ get_conversion_spec:
           width -= length;
           while( width-- != 0 )
           {
-            output_char( ' ', p );
-            charsOutputted++;
+            OUTPUT_CHAR( ' ', p );
           }
         }
 
-        while ( *PTR  && (decimals-- > 0))
+        while ( (c = *PTR)  && (decimals-- > 0))
         {
-          output_char( *PTR++, p );
-          charsOutputted++;
+          OUTPUT_CHAR( c, p );
+          PTR++;
         }
 
         if ( left_justify && (length < width))
@@ -494,8 +555,7 @@ get_conversion_spec:
           width -= length;
           while( width-- != 0 )
           {
-            output_char( ' ', p );
-            charsOutputted++;
+            OUTPUT_CHAR( ' ', p );
           }
         }
         break;
@@ -503,28 +563,52 @@ get_conversion_spec:
       case 'P':
         PTR = va_arg(ap,ptr_t);
 
-#ifdef SDCC_ds390
-        output_char(memory_id[(value.byte[3] > 3) ? 4 : value.byte[3]], p );
-        output_char(':', p);
-        output_char('0', p);
-        output_char('x', p);
+#if defined (SDCC_ds390)
+        {
+          unsigned char memtype = value.byte[3];
+          if (memtype >= 0x80)
+            c = 'C';
+          else if (memtype >= 0x60)
+            c = 'P';
+          else if (memtype >= 0x40)
+            c = 'I';
+          else
+            c = 'X';
+        }
+        OUTPUT_CHAR(c, p);
+        OUTPUT_CHAR(':', p);
+        OUTPUT_CHAR('0', p);
+        OUTPUT_CHAR('x', p);
         OUTPUT_2DIGITS( value.byte[2] );
         OUTPUT_2DIGITS( value.byte[1] );
         OUTPUT_2DIGITS( value.byte[0] );
-        charsOutputted += 10;
-#else
-        output_char( memory_id[(value.byte[2] > 3) ? 4 : value.byte[2]], p );
-        output_char(':', p);
-        output_char('0', p);
-        output_char('x', p);
-        if ((value.byte[2] != 0x00 /* DSEG */) &&
-            (value.byte[2] != 0x03 /* SSEG */))
+#elif defined (SDCC_mcs51)
+        {
+          unsigned char memtype = value.byte[2];
+          if (memtype >= 0x80)
+            c = 'C';
+          else if (memtype >= 0x60)
+            c = 'P';
+          else if (memtype >= 0x40)
+            c = 'I';
+          else
+            c = 'X';
+        }
+        OUTPUT_CHAR(c, p);
+        OUTPUT_CHAR(':', p);
+        OUTPUT_CHAR('0', p);
+        OUTPUT_CHAR('x', p);
+        if ((c != 'I' /* idata */) &&
+            (c != 'P' /* pdata */))
         {
           OUTPUT_2DIGITS( value.byte[1] );
-          charsOutputted += 2;
         }
         OUTPUT_2DIGITS( value.byte[0] );
-        charsOutputted += 6;
+#else
+        OUTPUT_CHAR('0', p);
+        OUTPUT_CHAR('x', p);
+        OUTPUT_2DIGITS( value.byte[1] );
+        OUTPUT_2DIGITS( value.byte[0] );
 #endif
         break;
 
@@ -552,8 +636,7 @@ get_conversion_spec:
 
       default:
         // nothing special, just output the character
-        output_char( c, p );
-        charsOutputted++;
+        OUTPUT_CHAR( c, p );
         break;
       }
 
@@ -563,8 +646,7 @@ get_conversion_spec:
         PTR="<NO FLOAT>";
         while (c=*PTR++)
         {
-          output_char (c, p);
-          charsOutputted++;
+          OUTPUT_CHAR (c, p);
         }
         // treat as long hex
         //radix=16;
@@ -573,15 +655,20 @@ get_conversion_spec:
         //width=8;
 #else
         // ignore b and l conversion spec for now
+#ifdef SDCC_STACK_AUTO
         charsOutputted += OUTPUT_FLOAT(value.f, width, decimals, left_justify,
                                      zero_padding, prefix_sign, prefix_space);
-#endif
+#else
+        OUTPUT_FLOAT(value.f, width, decimals, left_justify,
+                     zero_padding, prefix_sign, prefix_space);
+#endif //SDCC_STACK_AUTO
+#endif //USE_FLOATS
       } else if (radix != 0)
       {
-        // Apperently we have to output an integral type
+        // Apparently we have to output an integral type
         // with radix "radix"
-        unsigned char store[6];
-        unsigned char _AUTOMEM *pstore = &store[5];
+        unsigned char MEM_SPACE_BUF store[6];
+        unsigned char MEM_SPACE_BUF_PP *pstore = &store[5];
 
         // store value in byte[0] (LSB) ... byte[3] (MSB)
         if (char_argument)
@@ -649,16 +736,14 @@ get_conversion_spec:
         {
           while ( width > (unsigned char) (length+1) )
           {
-            output_char( ' ', p );
-            charsOutputted++;
+            OUTPUT_CHAR( ' ', p );
             width--;
           }
         }
 
         if (signed_argument) // this now means the original value was negative
         {
-          output_char( '-', p );
-          charsOutputted++;
+          OUTPUT_CHAR( '-', p );
           // adjust width to compensate for this character
           width--;
         }
@@ -667,15 +752,13 @@ get_conversion_spec:
           // value > 0
           if (prefix_sign)
           {
-            output_char( '+', p );
-            charsOutputted++;
+            OUTPUT_CHAR( '+', p );
             // adjust width to compensate for this character
             width--;
           }
           else if (prefix_space)
           {
-            output_char( ' ', p );
-            charsOutputted++;
+            OUTPUT_CHAR( ' ', p );
             // adjust width to compensate for this character
             width--;
           }
@@ -685,8 +768,7 @@ get_conversion_spec:
         if (!left_justify)
           while ( width-- > length )
           {
-            output_char( zero_padding ? '0' : ' ', p );
-            charsOutputted++;
+            OUTPUT_CHAR( zero_padding ? '0' : ' ', p );
           }
         else
         {
@@ -712,24 +794,22 @@ get_conversion_spec:
           }
 #ifdef SDCC_STACK_AUTO
           output_digit( value.byte[4], lower_case, output_char, p );
+          charsOutputted++;
 #else
           output_digit( value.byte[4] );
 #endif
-          charsOutputted++;
         }
         if (left_justify)
           while (width-- > 0)
           {
-            output_char(' ', p);
-            charsOutputted++;
+            OUTPUT_CHAR(' ', p);
           }
       }
     }
     else
     {
       // nothing special, just output the character
-      output_char( c, p );
-      charsOutputted++;
+      OUTPUT_CHAR( c, p );
     }
   }