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