* device/lib/printf_large.c (output_float): removed recursion,
[fw/sdcc] / device / lib / printf_large.c
1 /*-------------------------------------------------------------------------
2   printf_large.c - formatted output conversion
3
4              Written By - Martijn van Balen aed@iae.nl (1999)
5              Added %f By - johan.knol@iduna.nl (2000)
6              Refactored by - Maarten Brock (2004)
7
8    This library is free software; you can redistribute it and/or
9    modify it under the terms of the GNU Lesser General Public
10    License as published by the Free Software Foundation; either
11    version 2.1 of the License, or (at your option) any later version.
12
13    This library is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16    Lesser General Public License for more details.
17
18    You should have received a copy of the GNU Lesser General Public
19    License along with this library; if not, write to the Free Software
20    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
21
22    In other words, you are welcome to use, share and improve this program.
23    You are forbidden to forbid anyone else to use, share and improve
24    what you give them.   Help stamp out software-hoarding!
25 -------------------------------------------------------------------------*/
26
27 #if defined (SDCC_ds390)
28 #define USE_FLOATS 1
29 #endif
30
31 #include <stdarg.h>
32 #include <string.h>
33 #include <ctype.h>
34 #include <stdio.h>
35 #include <stdbool.h>
36 #include <sdcc-lib.h>
37
38 #define PTR value.ptr
39
40 #ifdef SDCC_ds390
41 #define NULL_STRING "<NULL>"
42 #define NULL_STRING_LENGTH 6
43 #endif
44
45 /****************************************************************************/
46
47 //typedef char * ptr_t;
48 #define ptr_t char *
49
50 #ifdef toupper
51 #undef toupper
52 #endif
53 #ifdef tolower
54 #undef tolower
55 #endif
56 #ifdef islower
57 #undef islower
58 #endif
59 #ifdef isdigit
60 #undef isdigit
61 #endif
62
63 //#define toupper(c) ((c)&=~0x20)
64 #define toupper(c) ((c)&=0xDF)
65 #define tolower(c) ((c)|=0x20)
66 #define islower(c) ((unsigned char)c >= (unsigned char)'a' && (unsigned char)c <= (unsigned char)'z')
67 #define isdigit(c) ((unsigned char)c >= (unsigned char)'0' && (unsigned char)c <= (unsigned char)'9')
68
69 typedef union
70 {
71   unsigned char  byte[5];
72   long           l;
73   unsigned long  ul;
74   float          f;
75   char           *ptr;
76 } value_t;
77
78 #ifndef SDCC_STACK_AUTO
79   static BOOL lower_case;
80   static pfn_outputchar output_char;
81   static void* p;
82   static value_t value;
83   static int charsOutputted;
84 #endif
85
86 /****************************************************************************/
87
88 #ifdef SDCC_STACK_AUTO
89   #define OUTPUT_CHAR(c, p) { output_char (c, p); charsOutputted++; }
90 #else
91   #define OUTPUT_CHAR(c, p) _output_char (c)
92   static void _output_char( unsigned char c )
93   {
94     output_char( c, p );
95     charsOutputted++;
96   }
97 #endif
98
99 /*--------------------------------------------------------------------------*/
100
101 #ifdef SDCC_STACK_AUTO
102   static void output_digit( unsigned char n, BOOL lower_case, pfn_outputchar output_char, void* p )
103   {
104     register unsigned char c = n + (unsigned char)'0';
105
106     if (c > (unsigned char)'9')
107     {
108       c += (unsigned char)('A' - '0' - 10);
109       if (lower_case)
110          c += (unsigned char)('a' - 'A');
111     }
112     output_char( c, p );
113   }
114 #else
115   static void output_digit( unsigned char n )
116   {
117     register unsigned char c = n + (unsigned char)'0';
118
119     if (c > (unsigned char)'9')
120     {
121       c += (unsigned char)('A' - '0' - 10);
122       if (lower_case)
123          c = tolower(c);
124     }
125     _output_char( c );
126   }
127 #endif
128
129 /*--------------------------------------------------------------------------*/
130
131 #ifdef SDCC_STACK_AUTO
132   #define OUTPUT_2DIGITS( B )   { output_2digits( B, lower_case, output_char, p ); charsOutputted += 2; }
133   static void output_2digits( unsigned char b, BOOL lower_case, pfn_outputchar output_char, void* p )
134   {
135     output_digit( b>>4,   lower_case, output_char, p );
136     output_digit( b&0x0F, lower_case, output_char, p );
137   }
138 #else
139   #define OUTPUT_2DIGITS( B )   output_2digits( B )
140   static void output_2digits( unsigned char b )
141   {
142     output_digit( b>>4   );
143     output_digit( b&0x0F );
144   }
145 #endif
146
147 /*--------------------------------------------------------------------------*/
148
149 #if defined SDCC_STACK_AUTO
150 static void calculate_digit( value_t _AUTOMEM * value, unsigned char radix )
151 {
152   unsigned long ul = value->ul;
153   unsigned char _AUTOMEM * pb4 = &value->byte[4];
154   unsigned char i = 32;
155
156   do
157   {
158     *pb4 = (*pb4 << 1) | ((ul >> 31) & 0x01);
159     ul <<= 1;
160
161     if (radix <= *pb4 )
162     {
163       *pb4 -= radix;
164       ul |= 1;
165     }
166   } while (--i);
167   value->ul = ul;
168 }
169 #else
170 static void calculate_digit( unsigned char radix )
171 {
172   register unsigned long ul = value.ul;
173   register unsigned char b4 = value.byte[4];
174   register unsigned char i = 32;
175
176   do
177   {
178     b4 = (b4 << 1);
179     b4 |= (ul >> 31) & 0x01;
180     ul <<= 1;
181
182     if (radix <= b4 )
183     {
184       b4 -= radix;
185       ul |= 1;
186     }
187   } while (--i);
188   value.ul = ul;
189   value.byte[4] = b4;
190 }
191 #endif
192
193 #if USE_FLOATS
194
195 /* This is a very inefficient but direct approach, since we have no math
196    library yet (e.g. log()).
197    It does most of the modifiers, but has some restrictions. E.g. the
198    abs(float) shouldn't be bigger than an unsigned long (that's
199    about 4294967295), but still makes it usefull for most real-life
200    applications.
201 */
202
203 #define DEFAULT_FLOAT_PRECISION 6
204
205 #ifdef SDCC_STACK_AUTO
206 #define OUTPUT_FLOAT(F, W, D, L, Z, S, P)       output_float(F, W, D, L, Z, S, P, output_char, p)
207 static unsigned char
208 output_float (float f, unsigned char reqWidth,
209               signed char reqDecimals,
210               BOOL left, BOOL zero, BOOL sign, BOOL space,
211               pfn_outputchar output_char, void* p)
212 {
213   unsigned char charsOutputted = 0;
214  #if defined (SDCC_mcs51)
215   char fpBuffer[16];      //mcs51 has only a small stack
216  #else
217   char fpBuffer[128];
218  #endif
219 #else
220 #define OUTPUT_FLOAT(F, W, D, L, Z, S, P)       output_float(F, W, D, L, Z, S, P)
221 static void
222 output_float (float f, unsigned char reqWidth,
223               signed char reqDecimals,
224               BOOL left, BOOL zero, BOOL sign, BOOL space)
225 {
226   xdata char fpBuffer[128];
227 #endif //SDCC_STACK_AUTO
228   BOOL negative = 0;
229   unsigned long integerPart;
230   float rounding;
231   float decimalPart;
232   char fpBI=0, fpBD;
233   unsigned char minWidth, i;
234   signed char exp = -128;
235
236   // save the sign
237   if (f<0) {
238     negative=1;
239     f=-f;
240   }
241
242   if (f>0x00ffffff) {
243     // this part is from Frank van der Hulst
244
245     for (exp = 0; f >= 10.0; exp++) f /=10.0;
246     for (       ; f < 1.0;   exp--) f *=10.0;
247
248     if (negative) {
249       OUTPUT_CHAR ('-', p);
250     } else {
251       if (sign) {
252         OUTPUT_CHAR ('+', p);
253       }
254     }
255     reqWidth = 0;
256     left = 0;
257     zero = 0;
258     sign = 0;
259     space = 0;
260   }
261
262   // display some decimals as default
263   if (reqDecimals==-1)
264     reqDecimals=DEFAULT_FLOAT_PRECISION;
265
266   // round the float
267   rounding = 0.5;
268   for (i=reqDecimals; i>0; i--) {
269       rounding /= 10.0;
270   }
271   f += rounding;
272
273   // split the float
274   integerPart = f;
275   decimalPart = f - integerPart;
276
277   // fill the buffer with the integerPart (in reversed order!)
278   while (integerPart) {
279     fpBuffer[fpBI++]='0' + integerPart%10;
280     integerPart /= 10;
281   }
282   if (!fpBI) {
283     // we need at least a 0
284     fpBuffer[fpBI++]='0';
285   }
286
287   // fill buffer with the decimalPart (in normal order)
288   fpBD=fpBI;
289
290   for (i=reqDecimals; i>0; i--) {
291       decimalPart *= 10.0;
292       // truncate the float
293       integerPart = decimalPart;
294       fpBuffer[fpBD++] = '0' + integerPart;
295       decimalPart -= integerPart;
296   }
297
298   minWidth=fpBI; // we need at least these
299   minWidth+=reqDecimals?reqDecimals+1:0; // maybe these
300   if (negative || sign || space)
301     minWidth++; // and maybe even this :)
302
303   if (!left && reqWidth>i) {
304     if (zero) {
305       if (negative)
306       {
307         OUTPUT_CHAR('-', p);
308       }
309       else if (sign)
310       {
311         OUTPUT_CHAR('+', p);
312       }
313       else if (space)
314       {
315         OUTPUT_CHAR(' ', p);
316       }
317       while (reqWidth-->minWidth)
318       {
319         OUTPUT_CHAR('0', p);
320       }
321     } else {
322       while (reqWidth-->minWidth)
323       {
324         OUTPUT_CHAR(' ', p);
325       }
326       if (negative)
327       {
328         OUTPUT_CHAR('-', p);
329       }
330       else if (sign)
331       {
332         OUTPUT_CHAR('+', p);
333       }
334       else if (space)
335       {
336         OUTPUT_CHAR(' ', p);
337       }
338     }
339   } else {
340     if (negative)
341     {
342       OUTPUT_CHAR('-', p);
343     }
344     else if (sign)
345     {
346       OUTPUT_CHAR('+', p);
347     }
348     else if (space)
349     {
350       OUTPUT_CHAR(' ', p);
351     }
352   }
353
354   // output the integer part
355   i=fpBI-1;
356   do {
357     OUTPUT_CHAR (fpBuffer[i], p);
358   } while (i--);
359
360   // ouput the decimal part
361   if (reqDecimals) {
362     OUTPUT_CHAR ('.', p);
363     i=fpBI;
364     while (reqDecimals--)
365     {
366       OUTPUT_CHAR (fpBuffer[i++], p);
367     }
368   }
369
370   if (left && reqWidth>minWidth) {
371     while (reqWidth-->minWidth)
372     {
373       OUTPUT_CHAR(' ', p);
374     }
375   }
376
377   if (exp != -128) {
378     OUTPUT_CHAR ('e', p);
379     if (exp<0) {
380       OUTPUT_CHAR ('-', p);
381       exp = -exp;
382     }
383     OUTPUT_CHAR ('0'+exp/10, p);
384     OUTPUT_CHAR ('0'+exp%10, p);
385   }
386 #ifdef SDCC_STACK_AUTO
387   return charsOutputted;
388 #else
389   return;
390 #endif //SDCC_STACK_AUTO
391 }
392 #endif //USE_FLOATS
393
394 int _print_format (pfn_outputchar pfn, void* pvoid, const char *format, va_list ap)
395 {
396   BOOL   left_justify;
397   BOOL   zero_padding;
398   BOOL   prefix_sign;
399   BOOL   prefix_space;
400   BOOL   signed_argument;
401   BOOL   char_argument;
402   BOOL   long_argument;
403   BOOL   float_argument;
404 #ifdef SDCC_STACK_AUTO
405   BOOL   lower_case;
406   value_t value;
407   int charsOutputted;
408 #endif
409   BOOL   lsd;
410
411   unsigned char radix;
412   unsigned char  width;
413   signed char decimals;
414   unsigned char  length;
415   char           c;
416
417 #ifdef SDCC_STACK_AUTO
418   #define output_char   pfn
419   #define p             pvoid
420 #else
421   output_char = pfn;
422   p = pvoid;
423 #endif
424
425   // reset output chars
426   charsOutputted = 0;
427
428 #ifdef SDCC_ds390
429   if (format==0) {
430     format=NULL_STRING;
431   }
432 #endif
433
434   while( c=*format++ )
435   {
436     if ( c=='%' )
437     {
438       left_justify    = 0;
439       zero_padding    = 0;
440       prefix_sign     = 0;
441       prefix_space    = 0;
442       signed_argument = 0;
443       char_argument   = 0;
444       long_argument   = 0;
445       float_argument  = 0;
446       radix           = 0;
447       width           = 0;
448       decimals        = -1;
449
450 get_conversion_spec:
451
452       c = *format++;
453
454       if (c=='%') {
455         OUTPUT_CHAR(c, p);
456         continue;
457       }
458
459       if (isdigit(c)) {
460         if (decimals==-1) {
461           width = 10*width + (c - '0');
462           if (width == 0) {
463             /* first character of width is a zero */
464             zero_padding = 1;
465           }
466         } else {
467           decimals = 10*decimals + (c-'0');
468         }
469         goto get_conversion_spec;
470       }
471
472       if (c=='.') {
473         if (decimals=-1) decimals=0;
474         else
475           ; // duplicate, ignore
476         goto get_conversion_spec;
477       }
478
479       if (islower(c))
480       {
481         c = toupper(c);
482         lower_case = 1;
483       }
484       else
485         lower_case = 0;
486
487       switch( c )
488       {
489       case '-':
490         left_justify = 1;
491         goto get_conversion_spec;
492       case '+':
493         prefix_sign = 1;
494         goto get_conversion_spec;
495       case ' ':
496         prefix_space = 1;
497         goto get_conversion_spec;
498       case 'B':
499         char_argument = 1;
500         goto get_conversion_spec;
501       case 'L':
502         long_argument = 1;
503         goto get_conversion_spec;
504
505       case 'C':
506         if( char_argument )
507           c = va_arg(ap,char);
508         else
509           c = va_arg(ap,int);
510         OUTPUT_CHAR( c, p );
511         break;
512
513       case 'S':
514         PTR = va_arg(ap,ptr_t);
515
516 #ifdef SDCC_ds390
517         if (PTR==0) {
518           PTR=NULL_STRING;
519           length=NULL_STRING_LENGTH;
520         } else {
521           length = strlen(PTR);
522         }
523 #else
524         length = strlen(PTR);
525 #endif
526         if ( decimals == -1 )
527         {
528           decimals = length;
529         }
530         if ( ( !left_justify ) && (length < width) )
531         {
532           width -= length;
533           while( width-- != 0 )
534           {
535             OUTPUT_CHAR( ' ', p );
536           }
537         }
538
539         while ( (c = *PTR)  && (decimals-- > 0))
540         {
541           OUTPUT_CHAR( c, p );
542           PTR++;
543         }
544
545         if ( left_justify && (length < width))
546         {
547           width -= length;
548           while( width-- != 0 )
549           {
550             OUTPUT_CHAR( ' ', p );
551           }
552         }
553         break;
554
555       case 'P':
556         PTR = va_arg(ap,ptr_t);
557
558 #if defined (SDCC_ds390)
559         {
560           unsigned char memtype = value.byte[3];
561           if (memtype > 0x80)
562             c = 'C';
563           else if (memtype > 0x60)
564             c = 'P';
565           else if (memtype > 0x40)
566             c = 'I';
567           else
568             c = 'X';
569         }
570         OUTPUT_CHAR(c, p);
571         OUTPUT_CHAR(':', p);
572         OUTPUT_CHAR('0', p);
573         OUTPUT_CHAR('x', p);
574         OUTPUT_2DIGITS( value.byte[2] );
575         OUTPUT_2DIGITS( value.byte[1] );
576         OUTPUT_2DIGITS( value.byte[0] );
577 #elif defined (SDCC_mcs51)
578         {
579           unsigned char memtype = value.byte[2];
580           if (memtype > 0x80)
581             c = 'C';
582           else if (memtype > 0x60)
583             c = 'P';
584           else if (memtype > 0x40)
585             c = 'I';
586           else
587             c = 'X';
588         }
589         OUTPUT_CHAR(c, p);
590         OUTPUT_CHAR(':', p);
591         OUTPUT_CHAR('0', p);
592         OUTPUT_CHAR('x', p);
593         if ((c != 'I' /* idata */) &&
594             (c != 'P' /* pdata */))
595         {
596           OUTPUT_2DIGITS( value.byte[1] );
597         }
598         OUTPUT_2DIGITS( value.byte[0] );
599 #else
600         OUTPUT_CHAR('0', p);
601         OUTPUT_CHAR('x', p);
602         OUTPUT_2DIGITS( value.byte[1] );
603         OUTPUT_2DIGITS( value.byte[0] );
604 #endif
605         break;
606
607       case 'D':
608       case 'I':
609         signed_argument = 1;
610         radix = 10;
611         break;
612
613       case 'O':
614         radix = 8;
615         break;
616
617       case 'U':
618         radix = 10;
619         break;
620
621       case 'X':
622         radix = 16;
623         break;
624
625       case 'F':
626         float_argument=1;
627         break;
628
629       default:
630         // nothing special, just output the character
631         OUTPUT_CHAR( c, p );
632         break;
633       }
634
635       if (float_argument) {
636         value.f=va_arg(ap,float);
637 #if !USE_FLOATS
638         PTR="<NO FLOAT>";
639         while (c=*PTR++)
640         {
641           OUTPUT_CHAR (c, p);
642         }
643         // treat as long hex
644         //radix=16;
645         //long_argument=1;
646         //zero_padding=1;
647         //width=8;
648 #else
649         // ignore b and l conversion spec for now
650 #ifdef SDCC_STACK_AUTO
651         charsOutputted += OUTPUT_FLOAT(value.f, width, decimals, left_justify,
652                                      zero_padding, prefix_sign, prefix_space);
653 #else
654         OUTPUT_FLOAT(value.f, width, decimals, left_justify,
655                      zero_padding, prefix_sign, prefix_space);
656 #endif //SDCC_STACK_AUTO
657 #endif //USE_FLOATS
658       } else if (radix != 0)
659       {
660         // Apperently we have to output an integral type
661         // with radix "radix"
662         unsigned char store[6];
663         unsigned char _AUTOMEM *pstore = &store[5];
664
665         // store value in byte[0] (LSB) ... byte[3] (MSB)
666         if (char_argument)
667         {
668           value.l = va_arg(ap,char);
669           if (!signed_argument)
670           {
671             value.l &= 0xFF;
672           }
673         }
674         else if (long_argument)
675         {
676           value.l = va_arg(ap,long);
677         }
678         else // must be int
679         {
680           value.l = va_arg(ap,int);
681           if (!signed_argument)
682           {
683             value.l &= 0xFFFF;
684           }
685         }
686
687         if ( signed_argument )
688         {
689           if (value.l < 0)
690             value.l = -value.l;
691           else
692             signed_argument = 0;
693         }
694
695         length=0;
696         lsd = 1;
697
698         do {
699           value.byte[4] = 0;
700 #if defined SDCC_STACK_AUTO
701           calculate_digit(&value, radix);
702 #else
703           calculate_digit(radix);
704 #endif
705           if (!lsd)
706           {
707             *pstore = (value.byte[4] << 4) | (value.byte[4] >> 4) | *pstore;
708             pstore--;
709           }
710           else
711           {
712             *pstore = value.byte[4];
713           }
714           length++;
715           lsd = !lsd;
716         } while( value.ul );
717
718         if (width == 0)
719         {
720           // default width. We set it to 1 to output
721           // at least one character in case the value itself
722           // is zero (i.e. length==0)
723           width=1;
724         }
725
726         /* prepend spaces if needed */
727         if (!zero_padding && !left_justify)
728         {
729           while ( width > (unsigned char) (length+1) )
730           {
731             OUTPUT_CHAR( ' ', p );
732             width--;
733           }
734         }
735
736         if (signed_argument) // this now means the original value was negative
737         {
738           OUTPUT_CHAR( '-', p );
739           // adjust width to compensate for this character
740           width--;
741         }
742         else if (length != 0)
743         {
744           // value > 0
745           if (prefix_sign)
746           {
747             OUTPUT_CHAR( '+', p );
748             // adjust width to compensate for this character
749             width--;
750           }
751           else if (prefix_space)
752           {
753             OUTPUT_CHAR( ' ', p );
754             // adjust width to compensate for this character
755             width--;
756           }
757         }
758
759         /* prepend zeroes/spaces if needed */
760         if (!left_justify)
761           while ( width-- > length )
762           {
763             OUTPUT_CHAR( zero_padding ? '0' : ' ', p );
764           }
765         else
766         {
767           /* spaces are appended after the digits */
768           if (width > length)
769             width -= length;
770           else
771             width = 0;
772         }
773
774         /* output the digits */
775         while( length-- )
776         {
777           lsd = !lsd;
778           if (!lsd)
779           {
780             pstore++;
781             value.byte[4] = *pstore >> 4;
782           }
783           else
784           {
785             value.byte[4] = *pstore & 0x0F;
786           }
787 #ifdef SDCC_STACK_AUTO
788           output_digit( value.byte[4], lower_case, output_char, p );
789           charsOutputted++;
790 #else
791           output_digit( value.byte[4] );
792 #endif
793         }
794         if (left_justify)
795           while (width-- > 0)
796           {
797             OUTPUT_CHAR(' ', p);
798           }
799       }
800     }
801     else
802     {
803       // nothing special, just output the character
804       OUTPUT_CHAR( c, p );
805     }
806   }
807
808   return charsOutputted;
809 }
810
811 /****************************************************************************/