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