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