1 /*-------------------------------------------------------------------------
2 printf_large.c - formatted output conversion
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)
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.
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.
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
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 -------------------------------------------------------------------------*/
27 #ifdef SDCC_STACK_AUTO
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. */
37 #if defined(SDCC_mcs51)
38 #if defined(SDCC_STACK_AUTO)
39 #if defined(SDCC_USE_XSTACK)
42 //strange enough "idata" doesn't work
45 #elif defined(SDCC_MODEL_LARGE)
67 #define NULL_STRING "<NULL>"
68 #define NULL_STRING_LENGTH 6
71 /****************************************************************************/
73 //typedef char * ptr_t;
80 //#define toupper(c) ((c)&=~0x20)
81 #define toupper(c) ((c)&=0xDF)
85 unsigned char byte[5];
92 static const char memory_id[] = "IXCP-";
94 #ifndef SDCC_STACK_AUTO
95 static BOOL lower_case;
96 static pfn_outputchar output_char;
102 /* this one NEEDS to be in data */
103 static data value_t value;
105 static value_t value;
109 /****************************************************************************/
111 #ifdef SDCC_STACK_AUTO
112 static void output_digit( unsigned char n, BOOL lower_case, pfn_outputchar output_char, void* p )
114 static void output_digit( unsigned char n )
117 output_char( n <= 9 ? '0'+n :
118 (lower_case ? n+(char)('a'-10) : n+(char)('A'-10)), p );
121 /*--------------------------------------------------------------------------*/
123 #ifdef SDCC_STACK_AUTO
124 #define OUTPUT_2DIGITS( B ) output_2digits( B, lower_case, output_char, p )
125 static void output_2digits( unsigned char b, BOOL lower_case, pfn_outputchar output_char, void* p )
127 output_digit( b>>4, lower_case, output_char, p );
128 output_digit( b&0x0F, lower_case, output_char, p );
131 #define OUTPUT_2DIGITS( B ) output_2digits( B )
132 static void output_2digits( unsigned char b )
134 output_digit( b>>4 );
135 output_digit( b&0x0F );
139 /*--------------------------------------------------------------------------*/
141 #if defined ASM_ALLOWED
142 static void calculate_digit( unsigned char radix )
146 for( i = 32; i != 0; i-- )
166 if (radix <= value.byte[4] )
168 value.byte[4] -= radix;
173 #elif defined SDCC_STACK_AUTO
174 static void calculate_digit( value_t* value, unsigned char radix )
178 for( i = 32; i != 0; i-- )
180 value->byte[4] = (value->byte[4] << 1) | ((value->ul >> 31) & 0x01);
183 if (radix <= value->byte[4] )
185 value->byte[4] -= radix;
191 static void calculate_digit( unsigned char radix )
195 for( i = 32; i != 0; i-- )
197 value.byte[4] = (value.byte[4] << 1) | ((value.ul >> 31) & 0x01);
200 if (radix <= value.byte[4] )
202 value.byte[4] -= radix;
211 /* This is a very inefficient but direct approach, since we have no math
212 library yet (e.g. log()).
213 It does most of the modifiers, but has some restrictions. E.g. the
214 abs(float) shouldn't be bigger than an unsigned long (that's
215 about 4294967295), but still makes it usefull for most real-life
219 #define DEFAULT_FLOAT_PRECISION 6
221 #ifdef SDCC_STACK_AUTO
222 #define OUTPUT_FLOAT(F, W, D, L, Z, S, P) output_float(F, W, D, L, Z, S, P, output_char, p)
223 static int output_float (float f, unsigned char reqWidth,
224 signed char reqDecimals,
225 BOOL left, BOOL zero, BOOL sign, BOOL space,
226 pfn_outputchar output_char, void* p)
228 #define OUTPUT_FLOAT(F, W, D, L, Z, S, P) output_float(F, W, D, L, Z, S, P)
229 static int output_float (float f, unsigned char reqWidth,
230 signed char reqDecimals,
231 BOOL left, BOOL zero, BOOL sign, BOOL space)
235 unsigned long integerPart;
239 unsigned char minWidth, i;
240 int charsOutputted=0;
249 // this part is from Frank van der Hulst
252 for (exp = 0; f >= 10.0; exp++) f /=10.0;
253 for ( ; f < 1.0; exp--) f *=10.0;
256 output_char ('-', p);
260 output_char ('+', p);
264 charsOutputted += OUTPUT_FLOAT(f, 0, reqDecimals, 0, 0, 0, 0);
265 output_char ('e', p);
268 output_char ('-', p);
272 output_char ('0'+exp/10, p);
273 output_char ('0'+exp%10, p);
275 return charsOutputted;
280 decimalPart=f-integerPart;
282 // fill the buffer with the integerPart (in reversed order!)
283 while (integerPart) {
284 fpBuffer[fpBI++]='0' + integerPart%10;
288 // we need at least a 0
289 fpBuffer[fpBI++]='0';
292 // display some decimals as default
294 reqDecimals=DEFAULT_FLOAT_PRECISION;
296 // fill buffer with the decimalPart (in normal order)
298 if (i=reqDecimals /* that's an assignment */) {
301 // truncate the float
302 integerPart=decimalPart;
303 fpBuffer[fpBD++]='0' + integerPart;
304 decimalPart-=integerPart;
308 minWidth=fpBI; // we need at least these
309 minWidth+=reqDecimals?reqDecimals+1:0; // maybe these
310 if (negative || sign || space)
311 minWidth++; // and maybe even this :)
313 if (!left && reqWidth>i) {
330 while (reqWidth-->minWidth)
336 while (reqWidth-->minWidth)
375 // output the integer part
378 output_char (fpBuffer[i], p);
382 // ouput the decimal part
384 output_char ('.', p);
387 while (reqDecimals--)
389 output_char (fpBuffer[i++], p);
394 if (left && reqWidth>minWidth) {
395 while (reqWidth-->minWidth)
401 return charsOutputted;
405 int _print_format (pfn_outputchar pfn, void* pvoid, const char *format, va_list ap)
411 BOOL signed_argument;
415 #ifdef SDCC_STACK_AUTO
426 signed char decimals;
427 unsigned char length;
430 #ifdef SDCC_STACK_AUTO
431 #define output_char pfn
438 // reset output chars
475 width = 10*width + (c - '0');
477 /* first character of width is a zero */
481 decimals = 10*decimals + (c-'0');
483 goto get_conversion_spec;
487 if (decimals=-1) decimals=0;
489 ; // duplicate, ignore
490 goto get_conversion_spec;
493 lower_case = islower(c);
503 goto get_conversion_spec;
506 goto get_conversion_spec;
509 goto get_conversion_spec;
512 goto get_conversion_spec;
515 goto get_conversion_spec;
518 output_char( va_arg(ap,int), p );
523 PTR = va_arg(ap,ptr_t);
528 length=NULL_STRING_LENGTH;
530 length = strlen(PTR);
533 length = strlen(PTR);
535 if ( ( !left_justify ) && (length < width) )
538 while( width-- != 0 )
540 output_char( ' ', p );
547 output_char( *PTR++, p );
551 if ( left_justify && (length < width))
554 while( width-- != 0 )
556 output_char( ' ', p );
563 PTR = va_arg(ap,ptr_t);
566 output_char(memory_id[(value.byte[3] > 3) ? 4 : value.byte[3]], p );
570 OUTPUT_2DIGITS( value.byte[2] );
571 OUTPUT_2DIGITS( value.byte[1] );
572 OUTPUT_2DIGITS( value.byte[0] );
573 charsOutputted += 10;
575 output_char( memory_id[(value.byte[2] > 3) ? 4 : value.byte[2]], p );
579 if ((value.byte[2] != 0x00 /* DSEG */) &&
580 (value.byte[2] != 0x03 /* SSEG */))
582 OUTPUT_2DIGITS( value.byte[1] );
585 OUTPUT_2DIGITS( value.byte[0] );
613 // nothing special, just output the character
619 if (float_argument) {
620 value.f=va_arg(ap,float);
634 // ignore b and l conversion spec for now
635 charsOutputted += OUTPUT_FLOAT(value.f, width, decimals, left_justify,
636 zero_padding, prefix_sign, prefix_space);
638 } else if (radix != 0)
640 // Apperently we have to output an integral type
641 // with radix "radix"
643 unsigned char store[6];
644 unsigned char NEAR *pstore = &store[5];
647 // store value in byte[0] (LSB) ... byte[3] (MSB)
650 value.l = va_arg(ap,char);
652 else if (long_argument)
654 value.l = va_arg(ap,long);
658 value.l = va_arg(ap,int);
661 if ( signed_argument )
674 #if defined SDCC_STACK_AUTO
675 calculate_digit(&value, radix);
677 calculate_digit(radix);
679 #if defined ASM_ALLOWED
683 mov a,_value+4 ; a = <msd>
685 orl b,a ; b = <msd><lsd>
689 mov a,_value+4 ; a = <lsd>
696 *pstore = (value.byte[4] << 4) | (value.byte[4] >> 4) | *pstore;
701 *pstore = value.byte[4];
706 } while( (value.byte[0] != 0) || (value.byte[1] != 0) ||
707 (value.byte[2] != 0) || (value.byte[3] != 0) );
711 // default width. We set it to 1 to output
712 // at least one character in case the value itself
713 // is zero (i.e. length==0)
717 /* prepend spaces if needed */
718 if (!zero_padding && !left_justify)
720 while ( width > (unsigned char) (length+1) )
722 output_char( ' ', p );
728 if (signed_argument) // this now means the original value was negative
730 output_char( '-', p );
732 // adjust width to compensate for this character
735 else if (length != 0)
740 output_char( '+', p );
742 // adjust width to compensate for this character
745 else if (prefix_space)
747 output_char( ' ', p );
749 // adjust width to compensate for this character
754 /* prepend zeroes/spaces if needed */
756 while ( width-- > length )
758 output_char( zero_padding ? '0' : ' ', p );
763 /* spaces are appended after the digits */
770 /* output the digits */
777 pop acc ; a = <msd><lsd>
778 nop ; to disable the "optimizer"
781 anl a,#0x0F ; a = <msd>
785 anl a,#0x0F ; a = <lsd>
793 value.byte[4] = *pstore >> 4;
797 value.byte[4] = *pstore & 0x0F;
800 #ifdef SDCC_STACK_AUTO
801 output_digit( value.byte[4], lower_case, output_char, p );
803 output_digit( value.byte[4] );
817 // nothing special, just output the character
823 return charsOutputted;
826 /****************************************************************************/