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