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