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