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