/*-------------------------------------------------------------------------
vprintf.c - formatted output conversion
-
+
Written By - Martijn van Balen aed@iae.nl (1999)
- Added %f By - johan.knol@iduna.nl (2000)
+ Refactored by - Maarten Brock (2004)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
- This program is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License as published by the
- Free Software Foundation; either version 2, or (at your option) any
- later version.
-
- This program is distributed in the hope that it will be useful,
+ This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
In other words, you are welcome to use, share and improve this program.
You are forbidden to forbid anyone else to use, share and improve
- what you give them. Help stamp out software-hoarding!
+ what you give them. Help stamp out software-hoarding!
-------------------------------------------------------------------------*/
-#define USE_FLOATS 1
#include <stdarg.h>
-#include <string.h>
-#include <ctype.h>
#include <stdio.h>
-#define PTR value.p
-
-/****************************************************************************/
-
-typedef char _generic *ptr_t;
-
-#ifdef toupper
-#undef toupper
-#endif
-
-//#define toupper(c) ((c)&=~0x20)
-#define toupper(c) ((c)&=0xDF)
-
-typedef union
-{
- unsigned char byte[5];
- long l;
- unsigned long ul;
- float f;
- char _generic *p;
-} value_t;
-
-
-static code char memory_id[] = "IXCP-";
-
-ptr_t output_ptr;
-bit output_to_string;
-bit lower_case;
-bit lsd;
-
-data value_t value;
-
-unsigned short radix;
-
-/****************************************************************************/
-
-static void output_char( char c ) reentrant
-{
- if (output_to_string)
- {
- *output_ptr++ = c;
- }
- else
- {
- putchar( c );
- }
-}
-
-/*--------------------------------------------------------------------------*/
-
-static void output_digit( unsigned char n ) reentrant
-{
- output_char( n <= 9 ? '0'+n : (lower_case ? n+(char)('a'-10) : n+(char)('A'-10)) );
-}
-
-/*--------------------------------------------------------------------------*/
-
-static void output_2digits( unsigned char b ) reentrant
+static void put_char_to_stdout( char c, void* p ) _REENTRANT
{
- output_digit( b>>4 );
- output_digit( b&0x0F );
+ p; //make compiler happy
+ putchar( c );
}
-
-/*--------------------------------------------------------------------------*/
-static void calculate_digit( void )
+int vprintf (const char *format, va_list ap)
{
- unsigned char i;
-
- for( i = 32; i != 0; i-- )
- {
-_asm
- clr c
- mov a,_value+0
- rlc a
- mov _value+0,a
- mov a,_value+1
- rlc a
- mov _value+1,a
- mov a,_value+2
- rlc a
- mov _value+2,a
- mov a,_value+3
- rlc a
- mov _value+3,a
- mov a,_value+4
- rlc a
- mov _value+4,a
-_endasm;
-
- if (radix <= value.byte[4] )
- {
- value.byte[4] -= radix;
- value.byte[0]++;
- }
- }
+ return _print_format( put_char_to_stdout, NULL, format, ap );
}
-#if USE_FLOATS
-
-/* This is a very inefficient but direct approach, since we have no math
- library yet (e.g. log()).
- It does most of the modifiers, but has some restrictions. E.g. the
- abs(float) shouldn't be bigger than an unsigned long (that's
- about 4294967295), but still makes it usefull for most real-life
- applications.
-*/
-
-#define DEFAULT_FLOAT_PRECISION 6
-
-static void output_float (float f, unsigned char reqWidth,
- signed char reqDecimals,
- bit left, bit zero, bit sign, bit space)
+int printf (const char *format, ...)
{
- char negative=0;
- long integerPart;
- float decimalPart;
- char fpBuffer[128];
- char fpBI=0, fpBD;
- unsigned char minWidth, i;
-
- // save the sign
- if (f<0) {
- negative=1;
- f=-f;
- }
-
- // split the float
- integerPart=f;
- decimalPart=f-integerPart;
-
- // fill the buffer with the integerPart (in reversed order!)
- while (integerPart) {
- fpBuffer[fpBI++]='0' + integerPart%10;
- integerPart /= 10;
- }
- if (!fpBI) {
- // we need at least a 0
- fpBuffer[fpBI++]='0';
- }
-
- // display some decimals as default
- if (reqDecimals==-1)
- reqDecimals=DEFAULT_FLOAT_PRECISION;
-
- // fill buffer with the decimalPart (in normal order)
- fpBD=fpBI;
- if (i=reqDecimals /* that's an assignment */) {
- do {
- decimalPart *= 10.0;
- // truncate the float
- integerPart=decimalPart;
- fpBuffer[fpBD++]='0' + integerPart;
- decimalPart-=integerPart;
- } while (--i);
- }
-
- minWidth=fpBI; // we need at least these
- minWidth+=reqDecimals?reqDecimals+1:0; // maybe these
- if (negative || sign || space)
- minWidth++; // and maybe even this :)
-
- if (!left && reqWidth>i) {
- if (zero) {
- if (negative) output_char('-');
- else if (sign) output_char('+');
- else if (space) output_char(' ');
- while (reqWidth-->minWidth)
- output_char ('0');
- } else {
- while (reqWidth-->minWidth)
- output_char (' ');
- if (negative) output_char('-');
- else if (sign) output_char('+');
- else if (space) output_char (' ');
- }
- } else {
- if (negative) output_char('-');
- else if (sign) output_char('+');
- else if (space) output_char(' ');
- }
+ va_list arg;
+ int i;
- // output the integer part
- i=fpBI-1;
- do {
- output_char (fpBuffer[i]);
- } while (i--);
-
- // ouput the decimal part
- if (reqDecimals) {
- output_char ('.');
- i=fpBI;
- while (reqDecimals--)
- output_char (fpBuffer[i++]);
- }
-
- if (left && reqWidth>minWidth) {
- while (reqWidth-->minWidth)
- output_char(' ');
- }
-}
-#endif
-
-/*--------------------------------------------------------------------------*/
-
-int vsprintf (const char *buf, const char *format, va_list ap)
-{
- bit left_justify;
- bit zero_padding;
- bit prefix_sign;
- bit prefix_space;
- bit signed_argument;
- bit char_argument;
- bit long_argument;
- bit float_argument;
+ va_start (arg, format);
+ i = _print_format( put_char_to_stdout, NULL, format, arg );
+ va_end (arg);
- unsigned char width;
- signed char decimals;
- unsigned char length;
- char c;
-
- output_ptr = buf;
- if ( !buf )
- {
- output_to_string = 0;
- }
- else
- {
- output_to_string = 1;
- }
-
- while( c=*format++ )
- {
- if ( c=='%' )
- {
- left_justify = 0;
- zero_padding = 0;
- prefix_sign = 0;
- prefix_space = 0;
- signed_argument = 0;
- radix = 0;
- char_argument = 0;
- long_argument = 0;
- float_argument = 0;
- width = 0;
- decimals = -1;
-
-get_conversion_spec:
-
- c = *format++;
-
- if (c=='%') {
- output_char(c);
- continue;
- }
-
- if (isdigit(c)) {
- if (decimals==-1) {
- width = 10*width + (c - '0');
- if (width == 0) {
- /* first character of width is a zero */
- zero_padding = 1;
- }
- } else {
- decimals = 10*decimals + (c-'0');
- }
- goto get_conversion_spec;
- }
-
- if (c=='.') {
- if (decimals=-1) decimals=0;
- else
- ; // duplicate, ignore
- goto get_conversion_spec;
- }
-
- lower_case = islower(c);
- if (lower_case)
- {
- c = toupper(c);
- }
-
- switch( c )
- {
- case '-':
- left_justify = 1;
- goto get_conversion_spec;
- case '+':
- prefix_sign = 1;
- goto get_conversion_spec;
- case ' ':
- prefix_space = 1;
- goto get_conversion_spec;
- case 'B':
- char_argument = 1;
- goto get_conversion_spec;
- case 'L':
- long_argument = 1;
- goto get_conversion_spec;
-
- case 'C':
- output_char( va_arg(ap,unsigned char) );
- break;
-
- case 'S':
- PTR = va_arg(ap,ptr_t);
-
- length = strlen(PTR);
- if ( ( !left_justify ) && (length < width) )
- {
- width -= length;
- while( width-- != 0 )
- {
- output_char( ' ' );
- }
- }
-
- while ( *PTR )
- output_char( *PTR++ );
-
- if ( left_justify && (length < width))
- {
- width -= length;
- while( width-- != 0 )
- {
- output_char( ' ' );
- }
- }
- break;
-
- case 'P':
- PTR = va_arg(ap,ptr_t);
-
-#ifdef SDCC_MODEL_FLAT24
- output_char(memory_id[(value.byte[3] > 3) ? 4 : value.byte[3]] );
- output_char(':');
- output_char('0');
- output_char('x');
- output_2digits(value.byte[2]);
- output_2digits(value.byte[1]);
- output_2digits(value.byte[0]);
-#else
- output_char( memory_id[(value.byte[2] > 3) ? 4 : value.byte[2]] );
- output_char(':');
- output_char('0');
- output_char('x');
- if ((value.byte[2] != 0x00 /* DSEG */) && (value.byte[2] != 0x03 /* SSEG */))
- output_2digits( value.byte[1] );
- output_2digits( value.byte[0] );
-#endif
- break;
-
- case 'D':
- case 'I':
- signed_argument = 1;
- radix = 10;
- break;
-
- case 'O':
- radix = 8;
- break;
-
- case 'U':
- radix = 10;
- break;
-
- case 'X':
- radix = 16;
- break;
-
- case 'F':
- float_argument=1;
- break;
-
- default:
- // nothing special, just output the character
- output_char( c );
- break;
- }
-
- if (float_argument) {
- value.f=va_arg(ap,float);
-#if !USE_FLOATS
- // treat as long hex
- radix=16;
- long_argument=1;
- zero_padding=1;
- width=8;
-#else
- // ignore b and l conversion spec for now
- output_float(value.f, width, decimals, left_justify, zero_padding,
- prefix_sign, prefix_space);
-#endif
- } else if (radix != 0)
- {
- // Apperently we have to output an integral type
- // with radix "radix"
-
- // store value in byte[0] (LSB) ... byte[3] (MSB)
- if (char_argument)
- {
- value.l = va_arg(ap,char);
- if (!signed_argument)
- {
- value.byte[1] = 0x00;
- value.byte[2] = 0x00;
- value.byte[3] = 0x00;
- }
- }
- else if (long_argument)
- {
- value.l = va_arg(ap,long);
- }
- else // must be int
- {
- value.l = va_arg(ap,int);
- if (!signed_argument)
- {
- value.byte[2] = 0x00;
- value.byte[3] = 0x00;
- }
- }
-
- if ( signed_argument )
- {
- if (value.l < 0)
- value.l = -value.l;
- else
- signed_argument = 0;
- }
-
- length=0;
- lsd = 1;
-
- //jwk20000814: do this at least once, e.g.: printf ("%d", (int)0);
- do {
- value.byte[4] = 0;
- calculate_digit();
-
-_asm
- jb _lsd,1$
- pop b ; b = <lsd>
- mov a,_value+4 ; a = <msd>
- swap a
- orl b,a ; b = <msd><lsd>
- push b
- sjmp 2$
-1$:
- mov a,_value+4 ; a = <lsd>
- push acc
-2$:
-_endasm;
-
- length++;
- lsd = ~lsd;
- } while( (value.byte[0] != 0) || (value.byte[1] != 0) ||
- (value.byte[2] != 0) || (value.byte[3] != 0) );
-
- if (width == 0)
- {
- // default width. We set it to 1 to output
- // at least one character is case the value itself
- // is zero (i.e. length==0)
- width=1;
- }
-
- /* prepend spaces if needed */
- if (!zero_padding)
- {
- while ( width > length+1 )
- {
- output_char( ' ' );
- width--;
- }
- }
-
- if (signed_argument) // this now means the original value was negative
- {
- output_char( '-' );
- // adjust width to compensate for this character
- width--;
- }
- else if (length != 0)
- {
- // value > 0
- if (prefix_sign)
- {
- output_char( '+' );
- // adjust width to compensate for this character
- width--;
- }
- else if (prefix_space)
- {
- output_char( ' ' );
- // adjust width to compensate for this character
- width--;
- }
- }
-
- /* prepend zeroes/spaces if needed */
- while ( width-- > length )
- {
- output_char( zero_padding ? '0' : ' ' );
- }
-
- /* output the digits */
- while( length-- )
- {
- lsd = ~lsd;
-
-_asm
- jb _lsd,3$
- pop acc ; a = <msd><lsd>
- nop ; to disable the "optimizer"
- push acc
- swap a
- anl a,#0x0F ; a = <msd>
- sjmp 4$
-3$:
- pop acc
- anl a,#0x0F ; a = <lsd>
-4$:
- mov _value+4,a
-_endasm;
-
- output_digit( value.byte[4] );
- }
- }
- }
- else
- {
- // nothing special, just output the character
- output_char( c );
- }
- }
-
- // Copy \0 to the end of buf
- // Modified by JB 17/12/99
- if (output_to_string) output_char(0);
-}
-
-/*--------------------------------------------------------------------------*/
-
-int vprintf (const char *format, va_list ap)
-{
- return vsprintf( 0, format, ap );
+ return i;
}