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