short (8 bit) replaced by char
[fw/sdcc] / device / lib / vprintf.c
1 /*-------------------------------------------------------------------------
2   vprintf.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
7    This program is free software; you can redistribute it and/or modify it
8    under the terms of the GNU General Public License as published by the
9    Free Software Foundation; either version 2, or (at your option) any
10    later version.
11    
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20    
21    In other words, you are welcome to use, share and improve this program.
22    You are forbidden to forbid anyone else to use, share and improve
23    what you give them.   Help stamp out software-hoarding!  
24 -------------------------------------------------------------------------*/
25 #ifdef __ds390
26 #define USE_FLOATS 1
27 #endif
28
29 #include <stdarg.h>
30 #include <string.h>
31 #include <ctype.h>
32 #include <stdio.h>
33
34 #define PTR value.p 
35
36 #ifdef SDCC_ds390
37 #define NULL_STRING "<NULL>"
38 #define NULL_STRING_LENGTH 6
39 #endif
40
41 /* XSPEC is defined in stdio.h and used here to place
42    auto variables in XSEG */
43
44 /****************************************************************************/
45
46 typedef char _generic *ptr_t;
47
48 #ifdef toupper
49 #undef toupper
50 #endif
51
52 //#define toupper(c) ((c)&=~0x20)
53 #define toupper(c) ((c)&=0xDF)
54
55 typedef union
56 {
57   unsigned char  byte[5];
58   long           l;
59   unsigned long  ul;
60   float          f;
61   char _generic *p;
62 } value_t;
63
64
65 static code char memory_id[] = "IXCP-";
66
67 static ptr_t output_ptr;
68 static bit   output_to_string;
69 static bit   lower_case;
70 static bit   lsd;
71
72 /* this one NEEDS to be in data */
73 static data value_t value;
74
75 static unsigned char radix;
76
77 // jwk: TODO: this makes the whole dammed thing nonreentrent
78 static int charsOutputted;
79
80 /****************************************************************************/
81
82 static void output_char( char c ) reentrant
83 {
84   if (output_to_string)
85   {
86     *output_ptr++ = c;
87   }
88   else
89   {
90     putchar( c );
91   }
92   charsOutputted++;
93 }
94
95 /*--------------------------------------------------------------------------*/
96
97 static void output_digit( unsigned char n ) reentrant
98 {
99   output_char( n <= 9 ? '0'+n : 
100                (lower_case ? n+(char)('a'-10) : n+(char)('A'-10)) );
101 }
102
103 /*--------------------------------------------------------------------------*/
104
105 static void output_2digits( unsigned char b ) reentrant
106 {
107   output_digit( b>>4 );
108   output_digit( b&0x0F );
109 }
110         
111 /*--------------------------------------------------------------------------*/
112
113 static void calculate_digit( void )
114 {
115   unsigned char i;
116
117   for( i = 32; i != 0; i-- )
118   {
119 _asm
120   clr  c
121   mov  a,_value+0  
122   rlc  a
123   mov  _value+0,a
124   mov  a,_value+1
125   rlc  a
126   mov  _value+1,a
127   mov  a,_value+2
128   rlc  a
129   mov  _value+2,a
130   mov  a,_value+3
131   rlc  a
132   mov  _value+3,a
133   mov  a,_value+4
134   rlc  a
135   mov  _value+4,a
136 _endasm;
137
138     if (radix <= value.byte[4] )
139     {
140       value.byte[4] -= radix;
141       value.byte[0]++;
142     }
143   }
144 }
145
146 #if USE_FLOATS
147
148 /* This is a very inefficient but direct approach, since we have no math
149    library yet (e.g. log()).
150    It does most of the modifiers, but has some restrictions. E.g. the 
151    abs(float) shouldn't be bigger than an unsigned long (that's 
152    about 4294967295), but still makes it usefull for most real-life
153    applications.
154 */
155
156 #define DEFAULT_FLOAT_PRECISION 6
157
158 static void output_float (float f, unsigned char reqWidth, 
159                           signed char reqDecimals,
160                           bit left, bit zero, bit sign, bit space)
161 {
162   char negative=0;
163   long integerPart;
164   float decimalPart;
165   char fpBuffer[128];
166   char fpBI=0, fpBD;
167   unsigned char minWidth, i;
168
169   // save the sign
170   if (f<0) {
171     negative=1;
172     f=-f;
173   }
174
175   // split the float
176   integerPart=f;
177   decimalPart=f-integerPart;
178
179   // fill the buffer with the integerPart (in reversed order!)
180   while (integerPart) {
181     fpBuffer[fpBI++]='0' + integerPart%10;
182     integerPart /= 10;
183   }
184   if (!fpBI) {
185     // we need at least a 0
186     fpBuffer[fpBI++]='0';
187   }
188
189   // display some decimals as default
190   if (reqDecimals==-1)
191     reqDecimals=DEFAULT_FLOAT_PRECISION;
192   
193   // fill buffer with the decimalPart (in normal order)
194   fpBD=fpBI;
195   if (i=reqDecimals /* that's an assignment */) {
196     do {
197       decimalPart *= 10.0;
198       // truncate the float
199       integerPart=decimalPart;
200       fpBuffer[fpBD++]='0' + integerPart;
201       decimalPart-=integerPart;
202     } while (--i);
203   }
204   
205   minWidth=fpBI; // we need at least these
206   minWidth+=reqDecimals?reqDecimals+1:0; // maybe these
207   if (negative || sign || space)
208     minWidth++; // and maybe even this :)
209   
210   if (!left && reqWidth>i) {
211     if (zero) {
212       if (negative) output_char('-');
213       else if (sign) output_char('+');
214       else if (space) output_char(' ');
215       while (reqWidth-->minWidth)
216         output_char ('0');
217     } else {
218       while (reqWidth-->minWidth)
219         output_char (' ');
220       if (negative) output_char('-');
221       else if (sign) output_char('+');
222       else if (space) output_char (' ');
223     }
224   } else {
225     if (negative) output_char('-');
226     else if (sign) output_char('+');
227     else if (space) output_char(' ');
228   }
229
230   // output the integer part
231   i=fpBI-1;
232   do {
233     output_char (fpBuffer[i]);
234   } while (i--);
235   
236   // ouput the decimal part
237   if (reqDecimals) {
238     output_char ('.');
239     i=fpBI;
240     while (reqDecimals--)
241       output_char (fpBuffer[i++]);
242   }
243
244   if (left && reqWidth>minWidth) {
245     while (reqWidth-->minWidth)
246       output_char(' ');
247   }
248 }
249 #endif
250
251 /*--------------------------------------------------------------------------*/
252
253 int vsprintf (const char *buf, const char *format, va_list ap)
254 {
255   bit            left_justify;
256   bit            zero_padding;
257   bit            prefix_sign;
258   bit            prefix_space;
259   bit            signed_argument;
260   bit            char_argument;
261   bit            long_argument;
262   bit            float_argument;
263
264   unsigned char  width;
265   signed char decimals;
266   unsigned char  length;
267   char           c;
268
269   // reset output chars
270   charsOutputted=0;
271
272   output_ptr = buf;
273   if ( !buf )
274   {
275     output_to_string = 0;
276   }
277   else
278   {
279     output_to_string = 1;
280   }
281
282 #ifdef SDCC_ds390
283   if (format==0) {
284     format=NULL_STRING;
285   }
286 #endif
287  
288   while( c=*format++ )
289   {
290     if ( c=='%' )
291     {
292       left_justify    = 0;
293       zero_padding    = 0;
294       prefix_sign     = 0;
295       prefix_space    = 0;
296       signed_argument = 0;
297       radix           = 0;
298       char_argument   = 0;
299       long_argument   = 0;
300       float_argument  = 0;
301       width           = 0;
302       decimals        = -1;
303
304 get_conversion_spec:
305
306       c = *format++;
307
308       if (c=='%') {
309         output_char(c);
310         continue;
311       }
312
313       if (isdigit(c)) {
314         if (decimals==-1) {
315           width = 10*width + (c - '0');
316           if (width == 0) {
317             /* first character of width is a zero */
318             zero_padding = 1;
319           }
320         } else {
321           decimals = 10*decimals + (c-'0');
322         }
323         goto get_conversion_spec;
324       }
325
326       if (c=='.') {
327         if (decimals=-1) decimals=0;
328         else 
329           ; // duplicate, ignore
330         goto get_conversion_spec;
331       }
332
333       lower_case = islower(c);
334       if (lower_case)
335       {
336         c = toupper(c);
337       }
338
339       switch( c )
340       {
341       case '-':
342         left_justify = 1;
343         goto get_conversion_spec;
344       case '+':
345         prefix_sign = 1;
346         goto get_conversion_spec;
347       case ' ':
348         prefix_space = 1;
349         goto get_conversion_spec;
350       case 'B':
351         char_argument = 1;
352         goto get_conversion_spec;
353       case 'L':
354         long_argument = 1;
355         goto get_conversion_spec;
356
357       case 'C':
358         output_char( va_arg(ap,int) );
359         break;
360
361       case 'S':
362         PTR = va_arg(ap,ptr_t);
363
364 #ifdef SDCC_ds390
365         if (PTR==0) {
366           PTR=NULL_STRING;
367           length=NULL_STRING_LENGTH;
368         } else {
369           length = strlen(PTR);
370         }
371 #else
372         length = strlen(PTR);
373 #endif
374         if ( ( !left_justify ) && (length < width) )
375         {
376           width -= length;
377           while( width-- != 0 )
378           {
379             output_char( ' ' );
380           }
381         }
382
383         while ( *PTR )
384           output_char( *PTR++ );
385
386         if ( left_justify && (length < width))
387         {
388           width -= length;
389           while( width-- != 0 )
390           {
391             output_char( ' ' );
392           }
393         }
394         break;
395
396       case 'P':
397         PTR = va_arg(ap,ptr_t);
398
399 #ifdef SDCC_ds390
400         output_char(memory_id[(value.byte[3] > 3) ? 4 : value.byte[3]] );
401         output_char(':');
402         output_char('0');
403         output_char('x');
404         output_2digits(value.byte[2]);
405         output_2digits(value.byte[1]);
406         output_2digits(value.byte[0]);
407 #else
408         output_char( memory_id[(value.byte[2] > 3) ? 4 : value.byte[2]] );
409         output_char(':');
410         output_char('0');
411         output_char('x');
412         if ((value.byte[2] != 0x00 /* DSEG */) && 
413             (value.byte[2] != 0x03 /* SSEG */))
414           output_2digits( value.byte[1] );
415         output_2digits( value.byte[0] );
416 #endif
417         break;
418
419       case 'D':
420       case 'I':
421         signed_argument = 1;
422         radix = 10;
423         break;
424
425       case 'O':
426         radix = 8;
427         break;
428
429       case 'U':
430         radix = 10;
431         break;
432
433       case 'X':
434         radix = 16;
435         break;
436
437       case 'F':
438         float_argument=1;
439         break;
440         
441       default:
442         // nothing special, just output the character
443         output_char( c );
444         break;
445       }
446
447       if (float_argument) {
448         value.f=va_arg(ap,float);
449 #if !USE_FLOATS
450         PTR="<NO FLOAT>";
451         while (c=*PTR++)
452           output_char (c);
453         // treat as long hex
454         //radix=16;
455         //long_argument=1;
456         //zero_padding=1;
457         //width=8;
458 #else
459         // ignore b and l conversion spec for now
460         output_float(value.f, width, decimals, left_justify, zero_padding, 
461                      prefix_sign, prefix_space);
462 #endif
463       } else if (radix != 0)
464       {
465         // Apperently we have to output an integral type
466         // with radix "radix"
467
468         // store value in byte[0] (LSB) ... byte[3] (MSB)
469         if (char_argument)
470         {
471           value.l = va_arg(ap,char);
472           if (!signed_argument)
473           {
474             value.byte[1] = 0x00;
475             value.byte[2] = 0x00;
476             value.byte[3] = 0x00;
477           }
478         }
479         else if (long_argument)
480         {
481           value.l = va_arg(ap,long);
482         }
483         else // must be int
484         {
485           value.l = va_arg(ap,int);
486           if (!signed_argument)
487           {
488             value.byte[2] = 0x00;
489             value.byte[3] = 0x00;
490           }
491         }
492
493         if ( signed_argument )
494         {
495           if (value.l < 0)
496             value.l = -value.l;
497           else
498             signed_argument = 0;
499         }
500
501         length=0;
502         lsd = 1;
503
504         //jwk20000814: do this at least once, e.g.: printf ("%d", (int)0);
505         do {
506           value.byte[4] = 0;
507           calculate_digit();
508
509 _asm
510   jb   _lsd,1$
511   pop  b                ; b = <lsd>
512   mov  a,_value+4       ; a = <msd>
513   swap a
514   orl  b,a              ; b = <msd><lsd>
515   push b
516   sjmp 2$
517 1$:
518   mov  a,_value+4       ; a = <lsd>
519   push acc
520 2$:
521 _endasm;
522
523           length++;
524           lsd = ~lsd;
525         } while( (value.byte[0] != 0) || (value.byte[1] != 0) ||
526                  (value.byte[2] != 0) || (value.byte[3] != 0) );
527         
528         if (width == 0)
529         {
530           // default width. We set it to 1 to output
531           // at least one character is case the value itself
532           // is zero (i.e. length==0)
533           width=1;
534         }
535
536         /* prepend spaces if needed */
537         if (!zero_padding)
538         {
539           while ( width > length+1 )
540           {
541             output_char( ' ' );
542             width--;
543           }
544         }
545
546         if (signed_argument) // this now means the original value was negative
547         {
548           output_char( '-' );
549           // adjust width to compensate for this character
550           width--;
551         }
552         else if (length != 0)
553         {
554           // value > 0
555           if (prefix_sign)
556           {
557             output_char( '+' );
558             // adjust width to compensate for this character
559             width--;
560           }
561           else if (prefix_space)
562           {
563             output_char( ' ' );
564             // adjust width to compensate for this character
565             width--;
566           }
567         }
568
569         /* prepend zeroes/spaces if needed */
570         while ( width-- > length )
571         {
572           output_char( zero_padding ? '0' : ' ' );
573         }
574
575         /* output the digits */
576         while( length-- )
577         {
578           lsd = ~lsd;
579
580 _asm
581   jb   _lsd,3$
582   pop  acc              ; a = <msd><lsd>
583   nop                   ; to disable the "optimizer"
584   push acc
585   swap a
586   anl  a,#0x0F          ; a = <msd>
587   sjmp 4$
588 3$:
589   pop  acc
590   anl  a,#0x0F          ; a = <lsd>
591 4$:
592   mov  _value+4,a
593 _endasm;
594
595           output_digit( value.byte[4] );
596         }
597       }
598     }
599     else
600     {
601       // nothing special, just output the character
602       output_char( c );
603     }
604   }
605        
606   // Copy \0 to the end of buf
607   // Modified by JB 17/12/99
608   if (output_to_string) {
609     output_char(0);
610     return charsOutputted-1;
611   } else {
612     return charsOutputted;
613   }
614 }
615
616 /*--------------------------------------------------------------------------*/
617
618 int vprintf (const char *format, va_list ap)
619 {
620   return vsprintf( 0, format, ap );
621 }