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