Initial revision
[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
6    This program is free software; you can redistribute it and/or modify it
7    under the terms of the GNU General Public License as published by the
8    Free Software Foundation; either version 2, or (at your option) any
9    later version.
10    
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15    
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19    
20    In other words, you are welcome to use, share and improve this program.
21    You are forbidden to forbid anyone else to use, share and improve
22    what you give them.   Help stamp out software-hoarding!  
23 -------------------------------------------------------------------------*/
24
25 #include <stdarg.h>
26 #include <string.h>
27 #include <ctype.h>
28 #include <stdio.h>
29
30 extern void putchar(const char);
31
32 #define PTR value.p 
33
34 /****************************************************************************/
35
36 typedef char _generic *ptr_t;
37
38 //#define toupper(c) ((c)&=~0x20)
39 #define toupper(c) ((c)&=0xDF)
40
41 typedef union
42 {
43   unsigned char  byte[5];
44   long           l;
45   unsigned long  ul;
46   char _generic *p;
47 } value_t;
48
49
50 static code char memory_id[] = "IXCP-";
51
52 ptr_t output_ptr;
53 bit   output_to_string;
54 bit   lower_case;
55 bit   lsd;
56
57 data value_t        value;
58
59      unsigned short radix;
60
61 /****************************************************************************/
62
63 static void output_char( char c ) reentrant
64 {
65   if (output_to_string)
66   {
67     *output_ptr++ = c;
68   }
69   else
70   {
71     putchar( c );
72   }
73 }
74
75 /*--------------------------------------------------------------------------*/
76
77 static void output_digit( unsigned char n ) reentrant
78 {
79   output_char( n <= 9 ? '0'+n : (lower_case ? n+(char)('a'-10) : n+(char)('A'-10)) );
80 }
81
82 /*--------------------------------------------------------------------------*/
83
84 static void output_2digits( unsigned char b ) reentrant
85 {
86   output_digit( b>>4 );
87   output_digit( b&0x0F );
88 }
89         
90 /*--------------------------------------------------------------------------*/
91
92 static void calculate_digit( void )
93 {
94   unsigned char i;
95
96   for( i = 32; i != 0; i-- )
97   {
98 _asm
99   clr  c
100   mov  a,_value+0
101   rlc  a
102   mov  _value+0,a
103   mov  a,_value+1
104   rlc  a
105   mov  _value+1,a
106   mov  a,_value+2
107   rlc  a
108   mov  _value+2,a
109   mov  a,_value+3
110   rlc  a
111   mov  _value+3,a
112   mov  a,_value+4
113   rlc  a
114   mov  _value+4,a
115 _endasm;
116
117     if (radix <= value.byte[4] )
118     {
119       value.byte[4] -= radix;
120       value.byte[0]++;
121     }
122   }
123 }
124
125 /*--------------------------------------------------------------------------*/
126
127 int vsprintf (const char *buf, const char *format, va_list ap)
128 {
129   bit            left_justify;
130   bit            zero_padding;
131   bit            prefix_sign;
132   bit            prefix_space;
133   bit            signed_argument;
134   bit            char_argument;
135   bit            long_argument;
136
137   unsigned char  width;
138   unsigned char  length;
139   char           c;
140
141   output_ptr = buf;
142   if ( !buf )
143   {
144     output_to_string = 0;
145   }
146   else
147   {
148     output_to_string = 1;
149   }
150
151   while( c=*format++ )
152   {
153     if ( c == '%' )
154     {
155       left_justify    = 0;
156       zero_padding    = 0;
157       prefix_sign     = 0;
158       prefix_space    = 0;
159       signed_argument = 0;
160       radix           = 0;
161       char_argument   = 0;
162       long_argument   = 0;
163       width           = 0;
164
165 get_conversion_spec:
166
167       c = *format++;
168
169       if (isdigit(c))
170       {
171         width = 10*width + (c - '0');
172
173         if (width == 0)
174         {
175           /* first character of width is a zero */
176           zero_padding = 1;
177         }
178         goto get_conversion_spec;
179       }
180
181       lower_case = islower(c);
182       if (lower_case)
183       {
184         c = toupper(c);
185       }
186
187       switch( c )
188       {
189       case '-':
190         left_justify = 1;
191         goto get_conversion_spec;
192       case '+':
193         prefix_sign = 1;
194         goto get_conversion_spec;
195       case ' ':
196         prefix_space = 1;
197         goto get_conversion_spec;
198       case 'B':
199         char_argument = 1;
200         goto get_conversion_spec;
201       case 'L':
202         long_argument = 1;
203         goto get_conversion_spec;
204
205       case 'C':
206         output_char( va_arg(ap,unsigned char) );
207         break;
208
209       case 'S':
210         PTR = va_arg(ap,ptr_t);
211
212         length = strlen(PTR);
213         if ( ( !left_justify ) && (length < width) )
214         {
215           width -= length;
216           while( width-- != 0 )
217           {
218             output_char( ' ' );
219           }
220         }
221
222         while ( *PTR )
223           output_char( *PTR++ );
224
225         if ( left_justify && (length < width))
226         {
227           width -= length;
228           while( width-- != 0 )
229           {
230             output_char( ' ' );
231           }
232         }
233         break;
234
235       case 'P':
236         PTR = va_arg(ap,ptr_t);
237
238         output_char( memory_id[(value.byte[2] > 3) ? 4 : value.byte[2]] );
239         output_char( ':' );
240         if ((value.byte[2] != 0x00) && (value.byte[2] != 0x03))
241           output_2digits( value.byte[1] );
242         output_2digits( value.byte[0] );
243         break;
244
245       case 'D':
246       case 'I':
247         signed_argument = 1;
248         radix = 10;
249         break;
250
251       case 'O':
252         radix = 8;
253         break;
254
255       case 'U':
256         radix = 10;
257         break;
258
259       case 'X':
260         radix = 16;
261         break;
262
263       default:
264         // nothing special, just output the character
265         output_char( c );
266         break;
267       }
268
269       if (radix != 0)
270       {
271         // Apperently we have to output an integral type
272         // with radix "radix"
273
274         // store value in byte[0] (LSB) ... byte[3] (MSB)
275         if (char_argument)
276         {
277           value.l = va_arg(ap,char);
278           if (!signed_argument)
279           {
280             value.byte[1] = 0x00;
281             value.byte[2] = 0x00;
282             value.byte[3] = 0x00;
283           }
284         }
285         else if (long_argument)
286         {
287           value.l = va_arg(ap,long);
288         }
289         else
290         {
291           value.l = va_arg(ap,int);
292           if (!signed_argument)
293           {
294             value.byte[2] = 0x00;
295             value.byte[3] = 0x00;
296           }
297         }
298
299         if ( signed_argument )
300         {
301           if (value.l < 0)
302             value.l = -value.l;
303           else
304             signed_argument = 0;
305         }
306
307         length=0;
308         lsd = 1;
309         while( (value.byte[0] != 0) || (value.byte[1] != 0) ||
310                (value.byte[2] != 0) || (value.byte[3] != 0) )
311         {
312           value.byte[4] = 0;
313           calculate_digit();
314
315 _asm
316   jb   _lsd,1$
317   pop  b                ; b = <lsd>
318   mov  a,_value+4       ; a = <msd>
319   swap a
320   orl  b,a              ; b = <msd><lsd>
321   push b
322   sjmp 2$
323 1$:
324   mov  a,_value+4       ; a = <lsd>
325   push acc
326 2$:
327 _endasm;
328
329           length++;
330           lsd = ~lsd;
331         }
332
333         if (width == 0)
334         {
335           // default width. We set it to 1 to output
336           // at least one character is case the value itself
337           // is zero (i.e. length==0)
338           width=1;
339         }
340
341         /* prepend spaces if needed */
342         if (!zero_padding)
343         {
344           while ( width > length+1 )
345           {
346             output_char( ' ' );
347             width--;
348           }
349         }
350
351         if (signed_argument) // this now means the original value was negative
352         {
353           output_char( '-' );
354           // adjust width to compensate for this character
355           width--;
356         }
357         else if (length != 0)
358         {
359           // value > 0
360           if (prefix_sign)
361           {
362             output_char( '+' );
363             // adjust width to compensate for this character
364             width--;
365           }
366           else if (prefix_space)
367           {
368             output_char( ' ' );
369             // adjust width to compensate for this character
370             width--;
371           }
372         }
373
374         /* prepend zeroes/spaces if needed */
375         while ( width-- > length )
376         {
377           output_char( zero_padding ? '0' : ' ' );
378         }
379
380         /* output the digits */
381         while( length-- )
382         {
383           lsd = ~lsd;
384
385 _asm
386   jb   _lsd,3$
387   pop  acc              ; a = <msd><lsd>
388   nop                   ; to disable the "optimizer"
389   push acc
390   swap a
391   anl  a,#0x0F          ; a = <msd>
392   sjmp 4$
393 3$:
394   pop  acc
395   anl  a,#0x0F          ; a = <lsd>
396 4$:
397   mov  _value+4,a
398 _endasm;
399
400           output_digit( value.byte[4] );
401         }
402       }
403     }
404     else
405     {
406       // nothing special, just output the character
407       output_char( c );
408     }
409   }
410        
411   // Copy \0 to the end of buf
412   // Modified by JB 17/12/99
413   if (output_to_string) output_char(0);
414 }
415
416 /*--------------------------------------------------------------------------*/
417
418 int vprintf (const char *format, va_list ap)
419 {
420   return vsprintf( 0, format, ap );
421 }