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