* as/xa51/xa_version.h,
[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->byte[0]++;
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.byte[0]++;
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.byte[1] = 0x00;
654             value.byte[2] = 0x00;
655             value.byte[3] = 0x00;
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.byte[2] = 0x00;
668             value.byte[3] = 0x00;
669           }
670         }
671
672         if ( signed_argument )
673         {
674           if (value.l < 0)
675             value.l = -value.l;
676           else
677             signed_argument = 0;
678         }
679
680         length=0;
681         lsd = 1;
682
683         do {
684           value.byte[4] = 0;
685 #if defined SDCC_STACK_AUTO
686           calculate_digit(&value, radix);
687 #else
688           calculate_digit(radix);
689 #endif
690 #if defined ASM_ALLOWED
691 _asm
692   jb   _lsd,1$
693   pop  b                ; b = <lsd>
694   mov  a,_value+4       ; a = <msd>
695   swap a
696   orl  b,a              ; b = <msd><lsd>
697   push b
698   sjmp 2$
699 1$:
700   mov  a,_value+4       ; a = <lsd>
701   push acc
702 2$:
703 _endasm;
704 #else
705           if (!lsd)
706           {
707             *pstore = (value.byte[4] << 4) | (value.byte[4] >> 4) | *pstore;
708             pstore--;
709           }
710           else
711           {
712             *pstore = value.byte[4];
713           }
714 #endif
715           length++;
716           lsd = !lsd;
717         } while( (value.byte[0] != 0) || (value.byte[1] != 0) ||
718                  (value.byte[2] != 0) || (value.byte[3] != 0) );
719
720         if (width == 0)
721         {
722           // default width. We set it to 1 to output
723           // at least one character in case the value itself
724           // is zero (i.e. length==0)
725           width=1;
726         }
727
728         /* prepend spaces if needed */
729         if (!zero_padding && !left_justify)
730         {
731           while ( width > (unsigned char) (length+1) )
732           {
733             output_char( ' ', p );
734             charsOutputted++;
735             width--;
736           }
737         }
738
739         if (signed_argument) // this now means the original value was negative
740         {
741           output_char( '-', p );
742           charsOutputted++;
743           // adjust width to compensate for this character
744           width--;
745         }
746         else if (length != 0)
747         {
748           // value > 0
749           if (prefix_sign)
750           {
751             output_char( '+', p );
752             charsOutputted++;
753             // adjust width to compensate for this character
754             width--;
755           }
756           else if (prefix_space)
757           {
758             output_char( ' ', p );
759             charsOutputted++;
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             charsOutputted++;
771           }
772         else
773         {
774           /* spaces are appended after the digits */
775           if (width > length)
776             width -= length;
777           else
778             width = 0;
779         }
780
781         /* output the digits */
782         while( length-- )
783         {
784           lsd = !lsd;
785 #ifdef ASM_ALLOWED
786 _asm
787   jb   _lsd,3$
788   pop  acc              ; a = <msd><lsd>
789   nop                   ; to disable the "optimizer"
790   push acc
791   swap a
792   anl  a,#0x0F          ; a = <msd>
793   sjmp 4$
794 3$:
795   pop  acc
796   anl  a,#0x0F          ; a = <lsd>
797 4$:
798   mov  _value+4,a
799 _endasm;
800 #else
801           if (!lsd)
802           {
803             pstore++;
804             value.byte[4] = *pstore >> 4;
805           }
806           else
807           {
808             value.byte[4] = *pstore & 0x0F;
809           }
810 #endif
811 #ifdef SDCC_STACK_AUTO
812           output_digit( value.byte[4], lower_case, output_char, p );
813 #else
814           output_digit( value.byte[4] );
815 #endif
816           charsOutputted++;
817         }
818         if (left_justify)
819           while (width-- > 0)
820           {
821             output_char(' ', p);
822             charsOutputted++;
823           }
824       }
825     }
826     else
827     {
828       // nothing special, just output the character
829       output_char( c, p );
830       charsOutputted++;
831     }
832   }
833
834   return charsOutputted;
835 }
836
837 /****************************************************************************/