* device/lib/printf_large.c: slightly smaller
[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 #else
215 #define OUTPUT_FLOAT(F, W, D, L, Z, S, P)       output_float(F, W, D, L, Z, S, P)
216 static void
217 output_float (float f, unsigned char reqWidth,
218               signed char reqDecimals,
219               BOOL left, BOOL zero, BOOL sign, BOOL space)
220 {
221 #endif //SDCC_STACK_AUTO
222   BOOL negative = 0;
223   unsigned long integerPart;
224   float decimalPart;
225   char fpBuffer[128];
226   char fpBI=0, fpBD;
227   unsigned char minWidth, i;
228
229   // save the sign
230   if (f<0) {
231     negative=1;
232     f=-f;
233   }
234
235   if (f>0x00ffffff) {
236     // this part is from Frank van der Hulst
237     signed char exp;
238
239     for (exp = 0; f >= 10.0; exp++) f /=10.0;
240     for (       ; f < 1.0;   exp--) f *=10.0;
241
242     if (negative) {
243       OUTPUT_CHAR ('-', p);
244     } else {
245       if (sign) {
246         OUTPUT_CHAR ('+', p);
247       }
248     }
249 #ifdef SDCC_STACK_AUTO
250     charsOutputted += OUTPUT_FLOAT(f, 0, reqDecimals, 0, 0, 0, 0);
251 #else
252     OUTPUT_FLOAT(f, 0, reqDecimals, 0, 0, 0, 0);
253 #endif
254     OUTPUT_CHAR ('e', p);
255     if (exp<0) {
256       OUTPUT_CHAR ('-', p);
257       exp = -exp;
258     }
259     OUTPUT_CHAR ('0'+exp/10, p);
260     OUTPUT_CHAR ('0'+exp%10, p);
261 #ifdef SDCC_STACK_AUTO
262     return charsOutputted;
263 #else
264     return;
265 #endif //SDCC_STACK_AUTO
266   }
267
268   // split the float
269   integerPart=f;
270   decimalPart=f-integerPart;
271
272   // fill the buffer with the integerPart (in reversed order!)
273   while (integerPart) {
274     fpBuffer[fpBI++]='0' + integerPart%10;
275     integerPart /= 10;
276   }
277   if (!fpBI) {
278     // we need at least a 0
279     fpBuffer[fpBI++]='0';
280   }
281
282   // display some decimals as default
283   if (reqDecimals==-1)
284     reqDecimals=DEFAULT_FLOAT_PRECISION;
285
286   // fill buffer with the decimalPart (in normal order)
287   fpBD=fpBI;
288
289   for (i=reqDecimals; i>1; i--) {
290       decimalPart *= 10.0;
291       // truncate the float
292       integerPart=decimalPart;
293       fpBuffer[fpBD++]='0' + integerPart;
294       decimalPart-=integerPart;
295   }
296   if (i) {
297     decimalPart *= 10.0;
298     // truncate the float
299     integerPart = decimalPart + 0.5;
300     fpBuffer[fpBD++] = '0' + integerPart;
301   }
302
303   minWidth=fpBI; // we need at least these
304   minWidth+=reqDecimals?reqDecimals+1:0; // maybe these
305   if (negative || sign || space)
306     minWidth++; // and maybe even this :)
307
308   if (!left && reqWidth>i) {
309     if (zero) {
310       if (negative)
311       {
312         OUTPUT_CHAR('-', p);
313       }
314       else if (sign)
315       {
316         OUTPUT_CHAR('+', p);
317       }
318       else if (space)
319       {
320         OUTPUT_CHAR(' ', p);
321       }
322       while (reqWidth-->minWidth)
323       {
324         OUTPUT_CHAR('0', p);
325       }
326     } else {
327       while (reqWidth-->minWidth)
328       {
329         OUTPUT_CHAR(' ', p);
330       }
331       if (negative)
332       {
333         OUTPUT_CHAR('-', p);
334       }
335       else if (sign)
336       {
337         OUTPUT_CHAR('+', p);
338       }
339       else if (space)
340       {
341         OUTPUT_CHAR(' ', p);
342       }
343     }
344   } else {
345     if (negative)
346     {
347       OUTPUT_CHAR('-', p);
348     }
349     else if (sign)
350     {
351       OUTPUT_CHAR('+', p);
352     }
353     else if (space)
354     {
355       OUTPUT_CHAR(' ', p);
356     }
357   }
358
359   // output the integer part
360   i=fpBI-1;
361   do {
362     OUTPUT_CHAR (fpBuffer[i], p);
363   } while (i--);
364
365   // ouput the decimal part
366   if (reqDecimals) {
367     OUTPUT_CHAR ('.', p);
368     i=fpBI;
369     while (reqDecimals--)
370     {
371       OUTPUT_CHAR (fpBuffer[i++], p);
372     }
373   }
374
375   if (left && reqWidth>minWidth) {
376     while (reqWidth-->minWidth)
377     {
378       OUTPUT_CHAR(' ', p);
379     }
380   }
381 #ifdef SDCC_STACK_AUTO
382   return charsOutputted;
383 #else
384   return;
385 #endif //SDCC_STACK_AUTO
386 }
387 #endif //USE_FLOATS
388
389 int _print_format (pfn_outputchar pfn, void* pvoid, const char *format, va_list ap)
390 {
391   BOOL   left_justify;
392   BOOL   zero_padding;
393   BOOL   prefix_sign;
394   BOOL   prefix_space;
395   BOOL   signed_argument;
396   BOOL   char_argument;
397   BOOL   long_argument;
398   BOOL   float_argument;
399 #ifdef SDCC_STACK_AUTO
400   BOOL   lower_case;
401   value_t value;
402   int charsOutputted;
403 #endif
404   BOOL   lsd;
405
406   unsigned char radix;
407   unsigned char  width;
408   signed char decimals;
409   unsigned char  length;
410   char           c;
411
412 #ifdef SDCC_STACK_AUTO
413   #define output_char   pfn
414   #define p             pvoid
415 #else
416   output_char = pfn;
417   p = pvoid;
418 #endif
419
420   // reset output chars
421   charsOutputted = 0;
422
423 #ifdef SDCC_ds390
424   if (format==0) {
425     format=NULL_STRING;
426   }
427 #endif
428
429   while( c=*format++ )
430   {
431     if ( c=='%' )
432     {
433       left_justify    = 0;
434       zero_padding    = 0;
435       prefix_sign     = 0;
436       prefix_space    = 0;
437       signed_argument = 0;
438       char_argument   = 0;
439       long_argument   = 0;
440       float_argument  = 0;
441       radix           = 0;
442       width           = 0;
443       decimals        = -1;
444
445 get_conversion_spec:
446
447       c = *format++;
448
449       if (c=='%') {
450         OUTPUT_CHAR(c, p);
451         continue;
452       }
453
454       if (isdigit(c)) {
455         if (decimals==-1) {
456           width = 10*width + (c - '0');
457           if (width == 0) {
458             /* first character of width is a zero */
459             zero_padding = 1;
460           }
461         } else {
462           decimals = 10*decimals + (c-'0');
463         }
464         goto get_conversion_spec;
465       }
466
467       if (c=='.') {
468         if (decimals=-1) decimals=0;
469         else
470           ; // duplicate, ignore
471         goto get_conversion_spec;
472       }
473
474       if (islower(c))
475       {
476         c = toupper(c);
477         lower_case = 1;
478       }
479       else
480         lower_case = 0;
481
482       switch( c )
483       {
484       case '-':
485         left_justify = 1;
486         goto get_conversion_spec;
487       case '+':
488         prefix_sign = 1;
489         goto get_conversion_spec;
490       case ' ':
491         prefix_space = 1;
492         goto get_conversion_spec;
493       case 'B':
494         char_argument = 1;
495         goto get_conversion_spec;
496       case 'L':
497         long_argument = 1;
498         goto get_conversion_spec;
499
500       case 'C':
501         OUTPUT_CHAR( va_arg(ap,int), p );
502         break;
503
504       case 'S':
505         PTR = va_arg(ap,ptr_t);
506
507 #ifdef SDCC_ds390
508         if (PTR==0) {
509           PTR=NULL_STRING;
510           length=NULL_STRING_LENGTH;
511         } else {
512           length = strlen(PTR);
513         }
514 #else
515         length = strlen(PTR);
516 #endif
517         if ( decimals == -1 )
518         {
519           decimals = length;
520         }
521         if ( ( !left_justify ) && (length < width) )
522         {
523           width -= length;
524           while( width-- != 0 )
525           {
526             OUTPUT_CHAR( ' ', p );
527           }
528         }
529
530         while ( (c = *PTR)  && (decimals-- > 0))
531         {
532           OUTPUT_CHAR( c, p );
533           PTR++;
534         }
535
536         if ( left_justify && (length < width))
537         {
538           width -= length;
539           while( width-- != 0 )
540           {
541             OUTPUT_CHAR( ' ', p );
542           }
543         }
544         break;
545
546       case 'P':
547         PTR = va_arg(ap,ptr_t);
548
549 #if defined (SDCC_ds390)
550         {
551           unsigned char memtype = value.byte[3];
552           if (memtype > 0x80)
553             c = 'C';
554           else if (memtype > 0x60)
555             c = 'P';
556           else if (memtype > 0x40)
557             c = 'I';
558           else
559             c = 'X';
560         }
561         OUTPUT_CHAR(c, p);
562         OUTPUT_CHAR(':', p);
563         OUTPUT_CHAR('0', p);
564         OUTPUT_CHAR('x', p);
565         OUTPUT_2DIGITS( value.byte[2] );
566         OUTPUT_2DIGITS( value.byte[1] );
567         OUTPUT_2DIGITS( value.byte[0] );
568 #elif defined (SDCC_mcs51)
569         {
570           unsigned char memtype = value.byte[2];
571           if (memtype > 0x80)
572             c = 'C';
573           else if (memtype > 0x60)
574             c = 'P';
575           else if (memtype > 0x40)
576             c = 'I';
577           else
578             c = 'X';
579         }
580         OUTPUT_CHAR(c, p);
581         OUTPUT_CHAR(':', p);
582         OUTPUT_CHAR('0', p);
583         OUTPUT_CHAR('x', p);
584         if ((c != 'I' /* idata */) &&
585             (c != 'P' /* pdata */))
586         {
587           OUTPUT_2DIGITS( value.byte[1] );
588         }
589         OUTPUT_2DIGITS( value.byte[0] );
590 #else
591         OUTPUT_CHAR('0', p);
592         OUTPUT_CHAR('x', p);
593         OUTPUT_2DIGITS( value.byte[1] );
594         OUTPUT_2DIGITS( value.byte[0] );
595 #endif
596         break;
597
598       case 'D':
599       case 'I':
600         signed_argument = 1;
601         radix = 10;
602         break;
603
604       case 'O':
605         radix = 8;
606         break;
607
608       case 'U':
609         radix = 10;
610         break;
611
612       case 'X':
613         radix = 16;
614         break;
615
616       case 'F':
617         float_argument=1;
618         break;
619
620       default:
621         // nothing special, just output the character
622         OUTPUT_CHAR( c, p );
623         break;
624       }
625
626       if (float_argument) {
627         value.f=va_arg(ap,float);
628 #if !USE_FLOATS
629         PTR="<NO FLOAT>";
630         while (c=*PTR++)
631         {
632           OUTPUT_CHAR (c, p);
633         }
634         // treat as long hex
635         //radix=16;
636         //long_argument=1;
637         //zero_padding=1;
638         //width=8;
639 #else
640         // ignore b and l conversion spec for now
641 #ifdef SDCC_STACK_AUTO
642         charsOutputted += OUTPUT_FLOAT(value.f, width, decimals, left_justify,
643                                      zero_padding, prefix_sign, prefix_space);
644 #else
645         OUTPUT_FLOAT(value.f, width, decimals, left_justify,
646                      zero_padding, prefix_sign, prefix_space);
647 #endif //SDCC_STACK_AUTO
648 #endif //USE_FLOATS
649       } else if (radix != 0)
650       {
651         // Apperently we have to output an integral type
652         // with radix "radix"
653         unsigned char store[6];
654         unsigned char _AUTOMEM *pstore = &store[5];
655
656         // store value in byte[0] (LSB) ... byte[3] (MSB)
657         if (char_argument)
658         {
659           value.l = va_arg(ap,char);
660           if (!signed_argument)
661           {
662             value.l &= 0xFF;
663           }
664         }
665         else if (long_argument)
666         {
667           value.l = va_arg(ap,long);
668         }
669         else // must be int
670         {
671           value.l = va_arg(ap,int);
672           if (!signed_argument)
673           {
674             value.l &= 0xFFFF;
675           }
676         }
677
678         if ( signed_argument )
679         {
680           if (value.l < 0)
681             value.l = -value.l;
682           else
683             signed_argument = 0;
684         }
685
686         length=0;
687         lsd = 1;
688
689         do {
690           value.byte[4] = 0;
691 #if defined SDCC_STACK_AUTO
692           calculate_digit(&value, radix);
693 #else
694           calculate_digit(radix);
695 #endif
696           if (!lsd)
697           {
698             *pstore = (value.byte[4] << 4) | (value.byte[4] >> 4) | *pstore;
699             pstore--;
700           }
701           else
702           {
703             *pstore = value.byte[4];
704           }
705           length++;
706           lsd = !lsd;
707         } while( value.ul );
708
709         if (width == 0)
710         {
711           // default width. We set it to 1 to output
712           // at least one character in case the value itself
713           // is zero (i.e. length==0)
714           width=1;
715         }
716
717         /* prepend spaces if needed */
718         if (!zero_padding && !left_justify)
719         {
720           while ( width > (unsigned char) (length+1) )
721           {
722             OUTPUT_CHAR( ' ', p );
723             width--;
724           }
725         }
726
727         if (signed_argument) // this now means the original value was negative
728         {
729           OUTPUT_CHAR( '-', p );
730           // adjust width to compensate for this character
731           width--;
732         }
733         else if (length != 0)
734         {
735           // value > 0
736           if (prefix_sign)
737           {
738             OUTPUT_CHAR( '+', p );
739             // adjust width to compensate for this character
740             width--;
741           }
742           else if (prefix_space)
743           {
744             OUTPUT_CHAR( ' ', p );
745             // adjust width to compensate for this character
746             width--;
747           }
748         }
749
750         /* prepend zeroes/spaces if needed */
751         if (!left_justify)
752           while ( width-- > length )
753           {
754             OUTPUT_CHAR( zero_padding ? '0' : ' ', p );
755           }
756         else
757         {
758           /* spaces are appended after the digits */
759           if (width > length)
760             width -= length;
761           else
762             width = 0;
763         }
764
765         /* output the digits */
766         while( length-- )
767         {
768           lsd = !lsd;
769           if (!lsd)
770           {
771             pstore++;
772             value.byte[4] = *pstore >> 4;
773           }
774           else
775           {
776             value.byte[4] = *pstore & 0x0F;
777           }
778 #ifdef SDCC_STACK_AUTO
779           output_digit( value.byte[4], lower_case, output_char, p );
780           charsOutputted++;
781 #else
782           output_digit( value.byte[4] );
783 #endif
784         }
785         if (left_justify)
786           while (width-- > 0)
787           {
788             OUTPUT_CHAR(' ', p);
789           }
790       }
791     }
792     else
793     {
794       // nothing special, just output the character
795       OUTPUT_CHAR( c, p );
796     }
797   }
798
799   return charsOutputted;
800 }
801
802 /****************************************************************************/