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