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