cleaned up the mess I left behind
[fw/sdcc] / device / lib / vprintf.c
index 9977d8abbc827dfffb1ec9e23fbdafd27935158c..6c277e84af4f2bfa2e425600def93bf1b1f5996e 100644 (file)
@@ -1,39 +1,53 @@
 /*-------------------------------------------------------------------------
   vprintf.c - formatted output conversion
 /*-------------------------------------------------------------------------
   vprintf.c - formatted output conversion
+
              Written By - Martijn van Balen aed@iae.nl (1999)
              Written By - Martijn van Balen aed@iae.nl (1999)
+            Added %f By - johan.knol@iduna.nl (2000)
 
    This program is free software; you can redistribute it and/or modify it
    under the terms of the GNU General Public License as published by the
    Free Software Foundation; either version 2, or (at your option) any
    later version.
 
    This program is free software; you can redistribute it and/or modify it
    under the terms of the GNU General Public License as published by the
    Free Software Foundation; either version 2, or (at your option) any
    later version.
-   
+
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
-   
+
    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-   
+
    In other words, you are welcome to use, share and improve this program.
    You are forbidden to forbid anyone else to use, share and improve
    In other words, you are welcome to use, share and improve this program.
    You are forbidden to forbid anyone else to use, share and improve
-   what you give them.   Help stamp out software-hoarding!  
+   what you give them.   Help stamp out software-hoarding!
 -------------------------------------------------------------------------*/
 
 -------------------------------------------------------------------------*/
 
+/* this module uses some global variables instead function parameters, so: */
+#ifdef SDCC_STACK_AUTO
+#warning "this module cannot yet be use as a reentrant one"
+#endif
+
+#if defined(__ds390)
+#define USE_FLOATS 1
+#endif
+
 #include <stdarg.h>
 #include <string.h>
 #include <ctype.h>
 #include <stdio.h>
 
 #include <stdarg.h>
 #include <string.h>
 #include <ctype.h>
 #include <stdio.h>
 
-extern void putchar(const char);
+#define PTR value.p
 
 
-#define PTR value.p 
+#ifdef SDCC_ds390
+#define NULL_STRING "<NULL>"
+#define NULL_STRING_LENGTH 6
+#endif
 
 /****************************************************************************/
 
 
 /****************************************************************************/
 
-typedef char _generic *ptr_t;
+//typedef char * ptr_t;
+#define ptr_t char *
 
 #ifdef toupper
 #undef toupper
 
 #ifdef toupper
 #undef toupper
@@ -47,20 +61,25 @@ typedef union
   unsigned char  byte[5];
   long           l;
   unsigned long  ul;
   unsigned char  byte[5];
   long           l;
   unsigned long  ul;
-  char _generic *p;
+  float          f;
+  char           *p;
 } value_t;
 
 
 static code char memory_id[] = "IXCP-";
 
 } value_t;
 
 
 static code char memory_id[] = "IXCP-";
 
-ptr_t output_ptr;
-bit   output_to_string;
-bit   lower_case;
-bit   lsd;
+static ptr_t output_ptr;
+static bit   output_to_string;
+static bit   lower_case;
+static bit   lsd;
 
 
-data value_t        value;
+/* this one NEEDS to be in data */
+static data value_t value;
 
 
-     unsigned short radix;
+static unsigned char radix;
+
+// this makes the whole dammed thing nonreentrent
+static int charsOutputted;
 
 /****************************************************************************/
 
 
 /****************************************************************************/
 
@@ -74,13 +93,15 @@ static void output_char( char c ) reentrant
   {
     putchar( c );
   }
   {
     putchar( c );
   }
+  charsOutputted++;
 }
 
 /*--------------------------------------------------------------------------*/
 
 static void output_digit( unsigned char n ) reentrant
 {
 }
 
 /*--------------------------------------------------------------------------*/
 
 static void output_digit( unsigned char n ) reentrant
 {
-  output_char( n <= 9 ? '0'+n : (lower_case ? n+(char)('a'-10) : n+(char)('A'-10)) );
+  output_char( n <= 9 ? '0'+n : 
+              (lower_case ? n+(char)('a'-10) : n+(char)('A'-10)) );
 }
 
 /*--------------------------------------------------------------------------*/
 }
 
 /*--------------------------------------------------------------------------*/
@@ -101,7 +122,7 @@ static void calculate_digit( void )
   {
 _asm
   clr  c
   {
 _asm
   clr  c
-  mov  a,_value+0
+  mov  a,_value+0  
   rlc  a
   mov  _value+0,a
   mov  a,_value+1
   rlc  a
   mov  _value+0,a
   mov  a,_value+1
@@ -126,22 +147,157 @@ _endasm;
   }
 }
 
   }
 }
 
+#if USE_FLOATS
+
+/* This is a very inefficient but direct approach, since we have no math
+   library yet (e.g. log()).
+   It does most of the modifiers, but has some restrictions. E.g. the 
+   abs(float) shouldn't be bigger than an unsigned long (that's 
+   about 4294967295), but still makes it usefull for most real-life
+   applications.
+*/
+
+#define DEFAULT_FLOAT_PRECISION 6
+
+static void output_float (float f, unsigned char reqWidth, 
+                         signed char reqDecimals,
+                         bit left, bit zero, bit sign, bit space)
+{
+  char negative=0;
+  unsigned long integerPart;
+  float decimalPart;
+  char fpBuffer[128];
+  char fpBI=0, fpBD;
+  unsigned char minWidth, i;
+
+  // save the sign
+  if (f<0) {
+    negative=1;
+    f=-f;
+  }
+
+  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) {
+      putchar ('-');
+    } else {
+      if (sign) {
+       putchar ('+');
+      }
+    }
+    output_float(f, 0, reqDecimals, 0, 0, 0, 0);
+    putchar ('e');
+    if (exp<0) {
+      putchar ('-');
+      exp = -exp;
+    }
+    putchar ('0'+exp/10);
+    putchar ('0'+exp%10);
+    return;
+  }
+
+  // split the float
+  integerPart=f;
+  decimalPart=f-integerPart;
+
+  // fill the buffer with the integerPart (in reversed order!)
+  while (integerPart) {
+    fpBuffer[fpBI++]='0' + integerPart%10;
+    integerPart /= 10;
+  }
+  if (!fpBI) {
+    // we need at least a 0
+    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;
+  if (i=reqDecimals /* that's an assignment */) {
+    do {
+      decimalPart *= 10.0;
+      // truncate the float
+      integerPart=decimalPart;
+      fpBuffer[fpBD++]='0' + integerPart;
+      decimalPart-=integerPart;
+    } while (--i);
+  }
+  
+  minWidth=fpBI; // we need at least these
+  minWidth+=reqDecimals?reqDecimals+1:0; // maybe these
+  if (negative || sign || space)
+    minWidth++; // and maybe even this :)
+  
+  if (!left && reqWidth>i) {
+    if (zero) {
+      if (negative) output_char('-');
+      else if (sign) output_char('+');
+      else if (space) output_char(' ');
+      while (reqWidth-->minWidth)
+       output_char ('0');
+    } else {
+      while (reqWidth-->minWidth)
+       output_char (' ');
+      if (negative) output_char('-');
+      else if (sign) output_char('+');
+      else if (space) output_char (' ');
+    }
+  } else {
+    if (negative) output_char('-');
+    else if (sign) output_char('+');
+    else if (space) output_char(' ');
+  }
+
+  // output the integer part
+  i=fpBI-1;
+  do {
+    output_char (fpBuffer[i]);
+  } while (i--);
+  
+  // ouput the decimal part
+  if (reqDecimals) {
+    output_char ('.');
+    i=fpBI;
+    while (reqDecimals--)
+      output_char (fpBuffer[i++]);
+  }
+
+  if (left && reqWidth>minWidth) {
+    while (reqWidth-->minWidth)
+      output_char(' ');
+  }
+}
+#endif
+
 /*--------------------------------------------------------------------------*/
 
 int vsprintf (const char *buf, const char *format, va_list ap)
 {
 /*--------------------------------------------------------------------------*/
 
 int vsprintf (const char *buf, const char *format, va_list ap)
 {
-  bit            left_justify;
-  bit            zero_padding;
-  bit            prefix_sign;
-  bit            prefix_space;
-  bit            signed_argument;
-  bit            char_argument;
-  bit            long_argument;
+  static bit            left_justify;
+  static bit            zero_padding;
+  static bit            prefix_sign;
+  static bit            prefix_space;
+  static bit            signed_argument;
+  static bit            char_argument;
+  static bit            long_argument;
+  static bit            float_argument;
 
   unsigned char  width;
 
   unsigned char  width;
+  signed char decimals;
   unsigned char  length;
   char           c;
 
   unsigned char  length;
   char           c;
 
+  // reset output chars
+  charsOutputted=0;
+
   output_ptr = buf;
   if ( !buf )
   {
   output_ptr = buf;
   if ( !buf )
   {
@@ -152,9 +308,15 @@ int vsprintf (const char *buf, const char *format, va_list ap)
     output_to_string = 1;
   }
 
     output_to_string = 1;
   }
 
+#ifdef SDCC_ds390
+  if (format==0) {
+    format=NULL_STRING;
+  }
+#endif
   while( c=*format++ )
   {
   while( c=*format++ )
   {
-    if ( c == '%' )
+    if ( c=='%' )
     {
       left_justify    = 0;
       zero_padding    = 0;
     {
       left_justify    = 0;
       zero_padding    = 0;
@@ -164,22 +326,37 @@ int vsprintf (const char *buf, const char *format, va_list ap)
       radix           = 0;
       char_argument   = 0;
       long_argument   = 0;
       radix           = 0;
       char_argument   = 0;
       long_argument   = 0;
+      float_argument  = 0;
       width           = 0;
       width           = 0;
+      decimals        = -1;
 
 get_conversion_spec:
 
       c = *format++;
 
 
 get_conversion_spec:
 
       c = *format++;
 
-      if (isdigit(c))
-      {
-        width = 10*width + (c - '0');
+      if (c=='%') {
+       output_char(c);
+       continue;
+      }
 
 
-       if (width == 0)
-       {
-         /* first character of width is a zero */
-         zero_padding = 1;
+      if (isdigit(c)) {
+       if (decimals==-1) {
+         width = 10*width + (c - '0');
+         if (width == 0) {
+           /* first character of width is a zero */
+           zero_padding = 1;
+         }
+       } else {
+         decimals = 10*decimals + (c-'0');
        }
        }
-        goto get_conversion_spec;
+       goto get_conversion_spec;
+      }
+
+      if (c=='.') {
+       if (decimals=-1) decimals=0;
+       else 
+         ; // duplicate, ignore
+       goto get_conversion_spec;
       }
 
       lower_case = islower(c);
       }
 
       lower_case = islower(c);
@@ -207,13 +384,22 @@ get_conversion_spec:
        goto get_conversion_spec;
 
       case 'C':
        goto get_conversion_spec;
 
       case 'C':
-               output_char( va_arg(ap,unsigned char) );
+               output_char( va_arg(ap,int) );
        break;
 
       case 'S':
        PTR = va_arg(ap,ptr_t);
 
        break;
 
       case 'S':
        PTR = va_arg(ap,ptr_t);
 
+#ifdef SDCC_ds390
+       if (PTR==0) {
+         PTR=NULL_STRING;
+         length=NULL_STRING_LENGTH;
+       } else {
+         length = strlen(PTR);
+       }
+#else
        length = strlen(PTR);
        length = strlen(PTR);
+#endif
        if ( ( !left_justify ) && (length < width) )
        {
          width -= length;
        if ( ( !left_justify ) && (length < width) )
        {
          width -= length;
@@ -239,7 +425,7 @@ get_conversion_spec:
       case 'P':
        PTR = va_arg(ap,ptr_t);
 
       case 'P':
        PTR = va_arg(ap,ptr_t);
 
-#ifdef SDCC_MODEL_FLAT24
+#ifdef SDCC_ds390
        output_char(memory_id[(value.byte[3] > 3) ? 4 : value.byte[3]] );
        output_char(':');
        output_char('0');
        output_char(memory_id[(value.byte[3] > 3) ? 4 : value.byte[3]] );
        output_char(':');
        output_char('0');
@@ -252,7 +438,8 @@ get_conversion_spec:
        output_char(':');
        output_char('0');
        output_char('x');
        output_char(':');
        output_char('0');
        output_char('x');
-       if ((value.byte[2] != 0x00 /* DSEG */) && (value.byte[2] != 0x03 /* SSEG */))
+       if ((value.byte[2] != 0x00 /* DSEG */) &&
+           (value.byte[2] != 0x03 /* SSEG */))
          output_2digits( value.byte[1] );
        output_2digits( value.byte[0] );
 #endif
          output_2digits( value.byte[1] );
        output_2digits( value.byte[0] );
 #endif
@@ -276,13 +463,33 @@ get_conversion_spec:
        radix = 16;
        break;
 
        radix = 16;
        break;
 
+      case 'F':
+       float_argument=1;
+       break;
+       
       default:
        // nothing special, just output the character
        output_char( c );
        break;
       }
 
       default:
        // nothing special, just output the character
        output_char( c );
        break;
       }
 
-      if (radix != 0)
+      if (float_argument) {
+       value.f=va_arg(ap,float);
+#if !USE_FLOATS
+       PTR="<NO FLOAT>";
+       while (c=*PTR++)
+         output_char (c);
+       // treat as long hex
+       //radix=16;
+       //long_argument=1;
+       //zero_padding=1;
+       //width=8;
+#else
+       // ignore b and l conversion spec for now
+       output_float(value.f, width, decimals, left_justify, zero_padding, 
+                    prefix_sign, prefix_space);
+#endif
+      } else if (radix != 0)
       {
        // Apperently we have to output an integral type
         // with radix "radix"
       {
        // Apperently we have to output an integral type
         // with radix "radix"
@@ -302,7 +509,7 @@ get_conversion_spec:
        {
          value.l = va_arg(ap,long);
        }
        {
          value.l = va_arg(ap,long);
        }
-       else
+       else // must be int
        {
          value.l = va_arg(ap,int);
          if (!signed_argument)
        {
          value.l = va_arg(ap,int);
          if (!signed_argument)
@@ -323,7 +530,6 @@ get_conversion_spec:
        length=0;
         lsd = 1;
 
        length=0;
         lsd = 1;
 
-       //jwk20000814: do this at least once, e.g.: printf ("%d", (int)0);
        do {
           value.byte[4] = 0;
          calculate_digit();
        do {
           value.byte[4] = 0;
          calculate_digit();
@@ -346,7 +552,7 @@ _endasm;
           lsd = ~lsd;
        } while( (value.byte[0] != 0) || (value.byte[1] != 0) ||
                 (value.byte[2] != 0) || (value.byte[3] != 0) );
           lsd = ~lsd;
        } while( (value.byte[0] != 0) || (value.byte[1] != 0) ||
                 (value.byte[2] != 0) || (value.byte[3] != 0) );
-       
+
        if (width == 0)
        {
          // default width. We set it to 1 to output
        if (width == 0)
        {
          // default width. We set it to 1 to output
@@ -356,7 +562,7 @@ _endasm;
        }
 
        /* prepend spaces if needed */
        }
 
        /* prepend spaces if needed */
-       if (!zero_padding)
+       if (!zero_padding && !left_justify)
        {
          while ( width > length+1 )
          {
        {
          while ( width > length+1 )
          {
@@ -389,9 +595,18 @@ _endasm;
        }
 
        /* prepend zeroes/spaces if needed */
        }
 
        /* prepend zeroes/spaces if needed */
-       while ( width-- > length )
+       if (!left_justify)
+         while ( width-- > length )
+         {
+           output_char( zero_padding ? '0' : ' ' );
+         }
+       else
        {
        {
-         output_char( zero_padding ? '0' : ' ' );
+         /* spaces are appended after the digits */
+         if (width > length)
+           width -= length;
+         else
+           width = 0;
        }
 
        /* output the digits */
        }
 
        /* output the digits */
@@ -416,6 +631,9 @@ _endasm;
 
          output_digit( value.byte[4] );
        }
 
          output_digit( value.byte[4] );
        }
+       if (left_justify)
+         while (width-- > 0)
+           output_char(' ');
       }
     }
     else
       }
     }
     else
@@ -424,10 +642,15 @@ _endasm;
       output_char( c );
     }
   }
       output_char( c );
     }
   }
-       
+
   // Copy \0 to the end of buf
   // Modified by JB 17/12/99
   // Copy \0 to the end of buf
   // Modified by JB 17/12/99
-  if (output_to_string) output_char(0);
+  if (output_to_string) {
+    output_char(0);
+    return charsOutputted-1;
+  } else {
+    return charsOutputted;
+  }
 }
 
 /*--------------------------------------------------------------------------*/
 }
 
 /*--------------------------------------------------------------------------*/