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