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