/*-------------------------------------------------------------------------
- printf.c - formatted output conversion
-
+ printf_large.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!
-------------------------------------------------------------------------*/
+#if defined (SDCC_ds390)
+#define USE_FLOATS 1
+#endif
+
#include <stdarg.h>
+#include <string.h>
+#include <ctype.h>
#include <stdio.h>
+#include <stdbool.h>
+#include <sdcc-lib.h>
+
+#define PTR value.ptr
+
+#ifdef SDCC_ds390
+#define NULL_STRING "<NULL>"
+#define NULL_STRING_LENGTH 6
+#endif
+
+/****************************************************************************/
+
+//typedef char * ptr_t;
+#define ptr_t char *
+
+#ifdef toupper
+#undef toupper
+#endif
+#ifdef tolower
+#undef tolower
+#endif
+#ifdef islower
+#undef islower
+#endif
+#ifdef isdigit
+#undef isdigit
+#endif
+
+//#define toupper(c) ((c)&=~0x20)
+#define toupper(c) ((c)&=0xDF)
+#define tolower(c) ((c)|=0x20)
+#define islower(c) ((unsigned char)c >= (unsigned char)'a' && (unsigned char)c <= (unsigned char)'z')
+#define isdigit(c) ((unsigned char)c >= (unsigned char)'0' && (unsigned char)c <= (unsigned char)'9')
+
+typedef union
+{
+ unsigned char byte[5];
+ long l;
+ unsigned long ul;
+ float f;
+ char *ptr;
+} value_t;
+
+#ifndef SDCC_STACK_AUTO
+ static BOOL lower_case;
+ static pfn_outputchar output_char;
+ static void* p;
+ static value_t value;
+ static int charsOutputted;
+#endif
+
+/****************************************************************************/
+
+#ifdef SDCC_STACK_AUTO
+ #define OUTPUT_CHAR(c, p) { output_char (c, p); charsOutputted++; }
+#else
+ #define OUTPUT_CHAR(c, p) _output_char (c)
+ static void _output_char( unsigned char c )
+ {
+ output_char( c, p );
+ charsOutputted++;
+ }
+#endif
+
+/*--------------------------------------------------------------------------*/
+
+#ifdef SDCC_STACK_AUTO
+ static void output_digit( unsigned char n, BOOL lower_case, pfn_outputchar output_char, void* p )
+ {
+ register unsigned char c = n + (unsigned char)'0';
+
+ if (c > (unsigned char)'9')
+ {
+ c += (unsigned char)('A' - '0' - 10);
+ if (lower_case)
+ c += (unsigned char)('a' - 'A');
+ }
+ output_char( c, p );
+ }
+#else
+ static void output_digit( unsigned char n )
+ {
+ register unsigned char c = n + (unsigned char)'0';
+
+ if (c > (unsigned char)'9')
+ {
+ c += (unsigned char)('A' - '0' - 10);
+ if (lower_case)
+ c = tolower(c);
+ }
+ _output_char( c );
+ }
+#endif
+
+/*--------------------------------------------------------------------------*/
-int printf (const char *format, ...) reentrant
+#ifdef SDCC_STACK_AUTO
+ #define OUTPUT_2DIGITS( B ) { output_2digits( B, lower_case, output_char, p ); charsOutputted += 2; }
+ static void output_2digits( unsigned char b, BOOL lower_case, pfn_outputchar output_char, void* p )
+ {
+ output_digit( b>>4, lower_case, output_char, p );
+ output_digit( b&0x0F, lower_case, output_char, p );
+ }
+#else
+ #define OUTPUT_2DIGITS( B ) output_2digits( B )
+ static void output_2digits( unsigned char b )
+ {
+ output_digit( b>>4 );
+ output_digit( b&0x0F );
+ }
+#endif
+
+/*--------------------------------------------------------------------------*/
+
+#if defined SDCC_STACK_AUTO
+static void calculate_digit( value_t _AUTOMEM * value, unsigned char radix )
+{
+ unsigned long ul = value->ul;
+ unsigned char _AUTOMEM * pb4 = &value->byte[4];
+ unsigned char i = 32;
+
+ do
+ {
+ *pb4 = (*pb4 << 1) | ((ul >> 31) & 0x01);
+ ul <<= 1;
+
+ if (radix <= *pb4 )
+ {
+ *pb4 -= radix;
+ ul |= 1;
+ }
+ } while (--i);
+ value->ul = ul;
+}
+#else
+static void calculate_digit( unsigned char radix )
{
- va_list arg;
- int done;
+ register unsigned long ul = value.ul;
+ register unsigned char b4 = value.byte[4];
+ register unsigned char i = 32;
- va_start (arg, format);
- done = vprintf ((char *)0,format, arg);
- va_end (arg);
+ do
+ {
+ b4 = (b4 << 1);
+ b4 |= (ul >> 31) & 0x01;
+ ul <<= 1;
- return done;
+ if (radix <= b4 )
+ {
+ b4 -= radix;
+ ul |= 1;
+ }
+ } while (--i);
+ value.ul = ul;
+ value.byte[4] = b4;
}
+#endif
-int sprintf (const char *buf, const char *format, ...) reentrant
+#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
+
+#ifdef SDCC_STACK_AUTO
+#define OUTPUT_FLOAT(F, W, D, L, Z, S, P) output_float(F, W, D, L, Z, S, P, output_char, p)
+static unsigned char
+output_float (float f, unsigned char reqWidth,
+ signed char reqDecimals,
+ BOOL left, BOOL zero, BOOL sign, BOOL space,
+ pfn_outputchar output_char, void* p)
{
- va_list arg;
- int done;
+ unsigned char charsOutputted = 0;
+ char fpBuffer[128];
+#else
+#define OUTPUT_FLOAT(F, W, D, L, Z, S, P) output_float(F, W, D, L, Z, S, P)
+static void
+output_float (float f, unsigned char reqWidth,
+ signed char reqDecimals,
+ BOOL left, BOOL zero, BOOL sign, BOOL space)
+{
+ xdata char fpBuffer[128];
+#endif //SDCC_STACK_AUTO
+ BOOL negative = 0;
+ unsigned long integerPart;
+ float decimalPart;
+ char fpBI=0, fpBD;
+ unsigned char minWidth, i;
+
+ // save the sign
+ if (f<0) {
+ negative=1;
+ f=-f;
+ }
+
+ if (f>0x00ffffff) {
+ // this part is from Frank van der Hulst
+ signed char exp;
+
+ for (exp = 0; f >= 10.0; exp++) f /=10.0;
+ for ( ; f < 1.0; exp--) f *=10.0;
+
+ if (negative) {
+ OUTPUT_CHAR ('-', p);
+ } else {
+ if (sign) {
+ OUTPUT_CHAR ('+', p);
+ }
+ }
+#ifdef SDCC_STACK_AUTO
+ charsOutputted += OUTPUT_FLOAT(f, 0, reqDecimals, 0, 0, 0, 0);
+#else
+ OUTPUT_FLOAT(f, 0, reqDecimals, 0, 0, 0, 0);
+#endif
+ OUTPUT_CHAR ('e', p);
+ if (exp<0) {
+ OUTPUT_CHAR ('-', p);
+ exp = -exp;
+ }
+ OUTPUT_CHAR ('0'+exp/10, p);
+ OUTPUT_CHAR ('0'+exp%10, p);
+#ifdef SDCC_STACK_AUTO
+ return charsOutputted;
+#else
+ return;
+#endif //SDCC_STACK_AUTO
+ }
+
+ // 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;
+
+ for (i=reqDecimals; i>1; i--) {
+ decimalPart *= 10.0;
+ // truncate the float
+ integerPart=decimalPart;
+ fpBuffer[fpBD++]='0' + integerPart;
+ decimalPart-=integerPart;
+ }
+ if (i) {
+ decimalPart *= 10.0;
+ // truncate the float
+ integerPart = decimalPart + 0.5;
+ fpBuffer[fpBD++] = '0' + integerPart;
+ }
+
+ 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('-', p);
+ }
+ else if (sign)
+ {
+ OUTPUT_CHAR('+', p);
+ }
+ else if (space)
+ {
+ OUTPUT_CHAR(' ', p);
+ }
+ while (reqWidth-->minWidth)
+ {
+ OUTPUT_CHAR('0', p);
+ }
+ } else {
+ while (reqWidth-->minWidth)
+ {
+ OUTPUT_CHAR(' ', p);
+ }
+ if (negative)
+ {
+ OUTPUT_CHAR('-', p);
+ }
+ else if (sign)
+ {
+ OUTPUT_CHAR('+', p);
+ }
+ else if (space)
+ {
+ OUTPUT_CHAR(' ', p);
+ }
+ }
+ } else {
+ if (negative)
+ {
+ OUTPUT_CHAR('-', p);
+ }
+ else if (sign)
+ {
+ OUTPUT_CHAR('+', p);
+ }
+ else if (space)
+ {
+ OUTPUT_CHAR(' ', p);
+ }
+ }
- va_start (arg, format);
- done = vsprintf (buf, format, arg);
- va_end (arg);
+ // output the integer part
+ i=fpBI-1;
+ do {
+ OUTPUT_CHAR (fpBuffer[i], p);
+ } while (i--);
- return done;
+ // ouput the decimal part
+ if (reqDecimals) {
+ OUTPUT_CHAR ('.', p);
+ i=fpBI;
+ while (reqDecimals--)
+ {
+ OUTPUT_CHAR (fpBuffer[i++], p);
+ }
+ }
+
+ if (left && reqWidth>minWidth) {
+ while (reqWidth-->minWidth)
+ {
+ OUTPUT_CHAR(' ', p);
+ }
+ }
+#ifdef SDCC_STACK_AUTO
+ return charsOutputted;
+#else
+ return;
+#endif //SDCC_STACK_AUTO
}
+#endif //USE_FLOATS
+
+int _print_format (pfn_outputchar pfn, void* pvoid, const char *format, va_list ap)
+{
+ BOOL left_justify;
+ BOOL zero_padding;
+ BOOL prefix_sign;
+ BOOL prefix_space;
+ BOOL signed_argument;
+ BOOL char_argument;
+ BOOL long_argument;
+ BOOL float_argument;
+#ifdef SDCC_STACK_AUTO
+ BOOL lower_case;
+ value_t value;
+ int charsOutputted;
+#endif
+ BOOL lsd;
+
+ unsigned char radix;
+ unsigned char width;
+ signed char decimals;
+ unsigned char length;
+ char c;
+
+#ifdef SDCC_STACK_AUTO
+ #define output_char pfn
+ #define p pvoid
+#else
+ output_char = pfn;
+ p = pvoid;
+#endif
+
+ // reset output chars
+ charsOutputted = 0;
+
+#ifdef SDCC_ds390
+ if (format==0) {
+ format=NULL_STRING;
+ }
+#endif
+
+ while( c=*format++ )
+ {
+ if ( c=='%' )
+ {
+ left_justify = 0;
+ zero_padding = 0;
+ prefix_sign = 0;
+ prefix_space = 0;
+ signed_argument = 0;
+ char_argument = 0;
+ long_argument = 0;
+ float_argument = 0;
+ radix = 0;
+ width = 0;
+ decimals = -1;
+
+get_conversion_spec:
+
+ c = *format++;
+
+ if (c=='%') {
+ OUTPUT_CHAR(c, p);
+ 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;
+ }
+
+ if (islower(c))
+ {
+ c = toupper(c);
+ lower_case = 1;
+ }
+ else
+ lower_case = 0;
+
+ 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':
+ if( char_argument )
+ c = va_arg(ap,char);
+ else
+ c = va_arg(ap,int);
+ OUTPUT_CHAR( c, p );
+ break;
+
+ case 'S':
+ PTR = va_arg(ap,ptr_t);
+
+#ifdef SDCC_ds390
+ if (PTR==0) {
+ PTR=NULL_STRING;
+ length=NULL_STRING_LENGTH;
+ } else {
+ length = strlen(PTR);
+ }
+#else
+ length = strlen(PTR);
+#endif
+ if ( decimals == -1 )
+ {
+ decimals = length;
+ }
+ if ( ( !left_justify ) && (length < width) )
+ {
+ width -= length;
+ while( width-- != 0 )
+ {
+ OUTPUT_CHAR( ' ', p );
+ }
+ }
+
+ while ( (c = *PTR) && (decimals-- > 0))
+ {
+ OUTPUT_CHAR( c, p );
+ PTR++;
+ }
+
+ if ( left_justify && (length < width))
+ {
+ width -= length;
+ while( width-- != 0 )
+ {
+ OUTPUT_CHAR( ' ', p );
+ }
+ }
+ break;
+
+ case 'P':
+ PTR = va_arg(ap,ptr_t);
+
+#if defined (SDCC_ds390)
+ {
+ unsigned char memtype = value.byte[3];
+ if (memtype > 0x80)
+ c = 'C';
+ else if (memtype > 0x60)
+ c = 'P';
+ else if (memtype > 0x40)
+ c = 'I';
+ else
+ c = 'X';
+ }
+ OUTPUT_CHAR(c, p);
+ OUTPUT_CHAR(':', p);
+ OUTPUT_CHAR('0', p);
+ OUTPUT_CHAR('x', p);
+ OUTPUT_2DIGITS( value.byte[2] );
+ OUTPUT_2DIGITS( value.byte[1] );
+ OUTPUT_2DIGITS( value.byte[0] );
+#elif defined (SDCC_mcs51)
+ {
+ unsigned char memtype = value.byte[2];
+ if (memtype > 0x80)
+ c = 'C';
+ else if (memtype > 0x60)
+ c = 'P';
+ else if (memtype > 0x40)
+ c = 'I';
+ else
+ c = 'X';
+ }
+ OUTPUT_CHAR(c, p);
+ OUTPUT_CHAR(':', p);
+ OUTPUT_CHAR('0', p);
+ OUTPUT_CHAR('x', p);
+ if ((c != 'I' /* idata */) &&
+ (c != 'P' /* pdata */))
+ {
+ OUTPUT_2DIGITS( value.byte[1] );
+ }
+ OUTPUT_2DIGITS( value.byte[0] );
+#else
+ OUTPUT_CHAR('0', p);
+ OUTPUT_CHAR('x', p);
+ 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, p );
+ break;
+ }
+
+ if (float_argument) {
+ value.f=va_arg(ap,float);
+#if !USE_FLOATS
+ PTR="<NO FLOAT>";
+ while (c=*PTR++)
+ {
+ OUTPUT_CHAR (c, p);
+ }
+ // treat as long hex
+ //radix=16;
+ //long_argument=1;
+ //zero_padding=1;
+ //width=8;
+#else
+ // ignore b and l conversion spec for now
+#ifdef SDCC_STACK_AUTO
+ charsOutputted += OUTPUT_FLOAT(value.f, width, decimals, left_justify,
+ zero_padding, prefix_sign, prefix_space);
+#else
+ OUTPUT_FLOAT(value.f, width, decimals, left_justify,
+ zero_padding, prefix_sign, prefix_space);
+#endif //SDCC_STACK_AUTO
+#endif //USE_FLOATS
+ } else if (radix != 0)
+ {
+ // Apperently we have to output an integral type
+ // with radix "radix"
+ unsigned char store[6];
+ unsigned char _AUTOMEM *pstore = &store[5];
+
+ // store value in byte[0] (LSB) ... byte[3] (MSB)
+ if (char_argument)
+ {
+ value.l = va_arg(ap,char);
+ if (!signed_argument)
+ {
+ value.l &= 0xFF;
+ }
+ }
+ else if (long_argument)
+ {
+ value.l = va_arg(ap,long);
+ }
+ else // must be int
+ {
+ value.l = va_arg(ap,int);
+ if (!signed_argument)
+ {
+ value.l &= 0xFFFF;
+ }
+ }
+
+ if ( signed_argument )
+ {
+ if (value.l < 0)
+ value.l = -value.l;
+ else
+ signed_argument = 0;
+ }
+
+ length=0;
+ lsd = 1;
+
+ do {
+ value.byte[4] = 0;
+#if defined SDCC_STACK_AUTO
+ calculate_digit(&value, radix);
+#else
+ calculate_digit(radix);
+#endif
+ if (!lsd)
+ {
+ *pstore = (value.byte[4] << 4) | (value.byte[4] >> 4) | *pstore;
+ pstore--;
+ }
+ else
+ {
+ *pstore = value.byte[4];
+ }
+ length++;
+ lsd = !lsd;
+ } while( value.ul );
+
+ if (width == 0)
+ {
+ // default width. We set it to 1 to output
+ // at least one character in case the value itself
+ // is zero (i.e. length==0)
+ width=1;
+ }
+
+ /* prepend spaces if needed */
+ if (!zero_padding && !left_justify)
+ {
+ while ( width > (unsigned char) (length+1) )
+ {
+ OUTPUT_CHAR( ' ', p );
+ width--;
+ }
+ }
+
+ if (signed_argument) // this now means the original value was negative
+ {
+ OUTPUT_CHAR( '-', p );
+ // adjust width to compensate for this character
+ width--;
+ }
+ else if (length != 0)
+ {
+ // value > 0
+ if (prefix_sign)
+ {
+ OUTPUT_CHAR( '+', p );
+ // adjust width to compensate for this character
+ width--;
+ }
+ else if (prefix_space)
+ {
+ OUTPUT_CHAR( ' ', p );
+ // adjust width to compensate for this character
+ width--;
+ }
+ }
+
+ /* prepend zeroes/spaces if needed */
+ if (!left_justify)
+ while ( width-- > length )
+ {
+ OUTPUT_CHAR( zero_padding ? '0' : ' ', p );
+ }
+ else
+ {
+ /* spaces are appended after the digits */
+ if (width > length)
+ width -= length;
+ else
+ width = 0;
+ }
+
+ /* output the digits */
+ while( length-- )
+ {
+ lsd = !lsd;
+ if (!lsd)
+ {
+ pstore++;
+ value.byte[4] = *pstore >> 4;
+ }
+ else
+ {
+ value.byte[4] = *pstore & 0x0F;
+ }
+#ifdef SDCC_STACK_AUTO
+ output_digit( value.byte[4], lower_case, output_char, p );
+ charsOutputted++;
+#else
+ output_digit( value.byte[4] );
+#endif
+ }
+ if (left_justify)
+ while (width-- > 0)
+ {
+ OUTPUT_CHAR(' ', p);
+ }
+ }
+ }
+ else
+ {
+ // nothing special, just output the character
+ OUTPUT_CHAR( c, p );
+ }
+ }
+
+ return charsOutputted;
+}
+
+/****************************************************************************/