gertk
Posts: 52
Joined: Mon Aug 29, 2011 9:08 am

Simple printf and sprintf

Tue Nov 06, 2012 10:24 pm

I added some crude divide and module functions to this but it works fine.
A simple printf and sprintf function by Georges Menie

Code: Select all

/*
	Copyright 2001, 2002 Georges Menie (http://www.menie.org)

    This program 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 of the License, or
    (at your option) any later version.

    This program 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 Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

/*
	putchar is the only external dependency for this file,
	if you have a working putchar, just remove the following
	define. If the function should be called something else,
	replace serial_putchar(c) by your own function call.
*/

// define your default character out function here
#define putchar(c) serial_putchar(c)

// crude divide and modulo function here

// integer modulo v with d
int mymod(int value, int divisor)
{
	int cc=0;
	
	// division by zero
	if (divisor==0) return 0xffffffff;

	while (value >= divisor){
		value -= divisor;
		cc++;
	}
	return value;
}

// integer divide v with d
int mydiv(int value, int divisor)
{
	int cc=0;
	
	// division by zero..
	if (divisor==0) return 0xffffffff;
	
	while (value >= divisor){
		value -= divisor;
		cc++;
	}
	return cc;
}


static void printchar(char **str, int c)
{
	extern int putchar(int c);
	if (str) {
		**str = c;
		++(*str);
	}
	else (void)putchar(c);
}

#define PAD_RIGHT 1
#define PAD_ZERO 2

static int prints(char **out, const char *string, int width, int pad)
{
	register int pc = 0, padchar = ' ';

	if (width > 0) {
		register int len = 0;
		register const char *ptr;
		for (ptr = string; *ptr; ++ptr) ++len;
		if (len >= width) width = 0;
		else width -= len;
		if (pad & PAD_ZERO) padchar = '0';
	}
	if (!(pad & PAD_RIGHT)) {
		for ( ; width > 0; --width) {
			printchar (out, padchar);
			++pc;
		}
	}
	for ( ; *string ; ++string) {
		printchar (out, *string);
		++pc;
	}
	for ( ; width > 0; --width) {
		printchar (out, padchar);
		++pc;
	}

	return pc;
}

/* the following should be enough for 32 bit int */
#define PRINT_BUF_LEN 12

static int printi(char **out, int i, int b, int sg, int width, int pad, int letbase)
{
	char print_buf[PRINT_BUF_LEN];
	register char *s;
	register int t, neg = 0, pc = 0;
	register unsigned int u = i;

	if (i == 0) {
		print_buf[0] = '0';
		print_buf[1] = '\0';
		return prints (out, print_buf, width, pad);
	}

	if (sg && b == 10 && i < 0) {
		neg = 1;
		u = -i;
	}

	s = print_buf + PRINT_BUF_LEN-1;
	*s = '\0';

	while (u) {
		t = mymod(u,b);
		if( t >= 10 )
			t += letbase - '0' - 10;
		*--s = t + '0';
		// u /= b
		u = mydiv(u,b);
	}

	if (neg) {
		if( width && (pad & PAD_ZERO) ) {
			printchar (out, '-');
			++pc;
			--width;
		}
		else {
			*--s = '-';
		}
	}

	return pc + prints (out, s, width, pad);
}

static int print(char **out, int *varg)
{
	register int width, pad;
	register int pc = 0;
	register char *format = (char *)(*varg++);
	char scr[2];

	for (; *format != 0; ++format) {
		if (*format == '%') {
			++format;
			width = pad = 0;
			if (*format == '\0') break;
			if (*format == '%') goto out;
			if (*format == '-') {
				++format;
				pad = PAD_RIGHT;
			}
			while (*format == '0') {
				++format;
				pad |= PAD_ZERO;
			}
			for ( ; *format >= '0' && *format <= '9'; ++format) {
				width *= 10;
				width += *format - '0';
			}
			if( *format == 's' ) {
				register char *s = *((char **)varg++);
				pc += prints (out, s?s:"(null)", width, pad);
				continue;
			}
			if( *format == 'd' ) {
				pc += printi (out, *varg++, 10, 1, width, pad, 'a');
				continue;
			}
			if( *format == 'x' ) {
				pc += printi (out, *varg++, 16, 0, width, pad, 'a');
				continue;
			}
			if( *format == 'X' ) {
				pc += printi (out, *varg++, 16, 0, width, pad, 'A');
				continue;
			}
			if( *format == 'u' ) {
				pc += printi (out, *varg++, 10, 0, width, pad, 'a');
				continue;
			}
			if( *format == 'c' ) {
				/* char are converted to int then pushed on the stack */
				scr[0] = *varg++;
				scr[1] = '\0';
				pc += prints (out, scr, width, pad);
				continue;
			}
		}
		else {
		out:
			printchar (out, *format);
			++pc;
		}
	}
	if (out) **out = '\0';
	return pc;
}

/* assuming sizeof(void *) == sizeof(int) */

int printf(const char *format, ...)
{
	register int *varg = (int *)(&format);
	return print(0, varg);
}

int sprintf(char *out, const char *format, ...)
{
	register int *varg = (int *)(&format);
	return print(&out, varg);
}

User avatar
jojopi
Posts: 3078
Joined: Tue Oct 11, 2011 8:38 pm

Re: Simple printf and sprintf

Wed Nov 07, 2012 9:37 am

gertk wrote: while (value >= divisor){
value -= divisor;
cc++;
}
Over a second of cpu to print a single integer! And it does not handle negative or unsigned quantities correctly.

The correct naïve way to do integer division is a bit at a time using shifts. But I strongly suggest you work out how to use the optimized implementations supplied with gcc instead. Not only are these likely much better, but gcc is also smart enough to eliminate divisions entirely where the divisor is known. (For instance, (x/10) is implemented as something like (((long long)x*1717986919>>34)+(x<0)).)

gertk
Posts: 52
Joined: Mon Aug 29, 2011 9:08 am

Re: Simple printf and sprintf

Wed Nov 07, 2012 5:29 pm

Well, thanks for nothing. I did say it has a crude div and mod function. For Bare Metal simple debugging without including huge stdlib stuff it works fine for me. Just replace mydiv and mymod by the routines of your choice.
Instead of ranting why not put an improved version here yourself ? :?:

gertk
Posts: 52
Joined: Mon Aug 29, 2011 9:08 am

Re: Simple printf and sprintf

Wed Nov 07, 2012 9:29 pm

How is this, converted to fixed number of shifts so speed should be improved now.

Tested on gcc (Ubuntu):

A=100 B=3 A div B=33
A=100 B=3 A mod B=1
A=-100 B=3 A div B=-33
A=-100 B=3 A mod B=-1
A=100 B=-3 A div B=-33
A=100 B=-3 A mod B=1
A=-100 B=-3 A div B=33
A=-100 B=-3 A mod B=-1


tested mymod and mydiv on the Pi (without stdlib)
A=100 B=3 A div B=33
A=100 B=3 A mod B=1
A=-100 B=3 A div B=-33
A=-100 B=3 A mod B=-1
A=100 B=-3 A div B=-33
A=100 B=-3 A mod B=1
A=-100 B=-3 A div B=33
A=-100 B=-3 A mod B=-1

Code: Select all

/*
	Copyright 2001, 2002 Georges Menie (www.menie.org)

    This program 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 of the License, or
    (at your option) any later version.

    This program 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 Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

/*
	putchar is the only external dependency for this file,
	if you have a working putchar, just remove the following
	define. If the function should be called something else,
	replace serial_putchar(c) by your own function call.
*/

// define your default character out function here
#define putchar(c) serial_putchar(c)

// integer division
int mydiv(int value, int divisor)
{
	int quotient=0;
	int remainder=0;
	int bitcount;
	int value_sign=1;
	int divisor_sign=1;
	
	if (value<0)
	{
		value = -value;
		value_sign=-1;
	}
	
	if (divisor<0)
	{
		divisor=-divisor;
		divisor_sign=-1;
	}
	
	if (divisor == 0) return 0xffffffff;

	for (bitcount = 31; bitcount>=0; bitcount--)
	{
		remainder <<= 1;   	

  		if (value & (1<<bitcount)) remainder|=1;
  	
		if (remainder >= divisor)
		{
    			remainder -= divisor;
			quotient |= (1<<bitcount);
		}
	}
	return (quotient * value_sign * divisor_sign);
}

// integer modulo
int mymod(int value, int divisor)
{
	int quotient=0;
	int remainder=0;
	int bitcount;
	int value_sign=1;
	int divisor_sign=1;
	
	if (value<0)
	{
		value = -value;
		value_sign=-1;
	}
	
	if (divisor<0)
	{
		divisor=-divisor;
		divisor_sign=-1;
	}
	
	if (divisor == 0) return 0xffffffff;

	for (bitcount = 31; bitcount>=0; bitcount--)
	{
		remainder <<= 1;   	

  		if (value & (1<<bitcount)) remainder|=1;
  	
		if (remainder >= divisor)
		{
    			remainder -= divisor;
			quotient |= (1<<bitcount);
		}
	}
	// ignore divisor sign on mod function (according to gcc)
	return (remainder * value_sign);
}



static void printchar(char **str, int c)
{
	extern int putchar(int c);
	if (str) {
		**str = c;
		++(*str);
	}
	else (void)putchar(c);
}

#define PAD_RIGHT 1
#define PAD_ZERO 2

static int prints(char **out, const char *string, int width, int pad)
{
	register int pc = 0, padchar = ' ';

	if (width > 0) {
		register int len = 0;
		register const char *ptr;
		for (ptr = string; *ptr; ++ptr) ++len;
		if (len >= width) width = 0;
		else width -= len;
		if (pad & PAD_ZERO) padchar = '0';
	}
	if (!(pad & PAD_RIGHT)) {
		for ( ; width > 0; --width) {
			printchar (out, padchar);
			++pc;
		}
	}
	for ( ; *string ; ++string) {
		printchar (out, *string);
		++pc;
	}
	for ( ; width > 0; --width) {
		printchar (out, padchar);
		++pc;
	}

	return pc;
}

/* the following should be enough for 32 bit int */
#define PRINT_BUF_LEN 12

static int printi(char **out, int i, int b, int sg, int width, int pad, int letbase)
{
	char print_buf[PRINT_BUF_LEN];
	register char *s;
	register int t, neg = 0, pc = 0;
	register unsigned int u = i;

	if (i == 0) {
		print_buf[0] = '0';
		print_buf[1] = '\0';
		return prints (out, print_buf, width, pad);
	}

	if (sg && b == 10 && i < 0) {
		neg = 1;
		u = -i;
	}

	s = print_buf + PRINT_BUF_LEN-1;
	*s = '\0';

	while (u) {
		// t = u % b ;
		t = mymod(u,b);
		if( t >= 10 )
			t += letbase - '0' - 10;
		*--s = t + '0';
		// u /= b
		u = mydiv(u,b);
	}

	if (neg) {
		if( width && (pad & PAD_ZERO) ) {
			printchar (out, '-');
			++pc;
			--width;
		}
		else {
			*--s = '-';
		}
	}

	return pc + prints (out, s, width, pad);
}

static int print(char **out, int *varg)
{
	register int width, pad;
	register int pc = 0;
	register char *format = (char *)(*varg++);
	char scr[2];

	for (; *format != 0; ++format) {
		if (*format == '%') {
			++format;
			width = pad = 0;
			if (*format == '\0') break;
			if (*format == '%') goto out;
			if (*format == '-') {
				++format;
				pad = PAD_RIGHT;
			}
			while (*format == '0') {
				++format;
				pad |= PAD_ZERO;
			}
			for ( ; *format >= '0' && *format <= '9'; ++format) {
				width *= 10;
				width += *format - '0';
			}
			if( *format == 's' ) {
				register char *s = *((char **)varg++);
				pc += prints (out, s?s:"(null)", width, pad);
				continue;
			}
			if( *format == 'd' ) {
				pc += printi (out, *varg++, 10, 1, width, pad, 'a');
				continue;
			}
			if( *format == 'x' ) {
				pc += printi (out, *varg++, 16, 0, width, pad, 'a');
				continue;
			}
			if( *format == 'X' ) {
				pc += printi (out, *varg++, 16, 0, width, pad, 'A');
				continue;
			}
			if( *format == 'u' ) {
				pc += printi (out, *varg++, 10, 0, width, pad, 'a');
				continue;
			}
			if( *format == 'c' ) {
				/* char are converted to int then pushed on the stack */
				scr[0] = *varg++;
				scr[1] = '\0';
				pc += prints (out, scr, width, pad);
				continue;
			}
		}
		else {
		out:
			printchar (out, *format);
			++pc;
		}
	}
	if (out) **out = '\0';
	return pc;
}

/* assuming sizeof(void *) == sizeof(int) */

int printf(const char *format, ...)
{
	register int *varg = (int *)(&format);
	return print(0, varg);
}

int sprintf(char *out, const char *format, ...)
{
	register int *varg = (int *)(&format);
	return print(&out, varg);
}

Neil
Posts: 98
Joined: Thu Sep 29, 2011 7:10 am
Contact: Website

Re: Simple printf and sprintf

Thu Nov 08, 2012 3:59 pm

For a full-featured yet small code size (s)printf library I can recommend format. Doesn't do any floating point yet, as I haven't yet had time to work on that part.

gertk
Posts: 52
Joined: Mon Aug 29, 2011 9:08 am

Re: Simple printf and sprintf

Thu Nov 08, 2012 5:40 pm

Neil wrote:For a full-featured yet small code size (s)printf library I can recommend format. Doesn't do any floating point yet, as I haven't yet had time to work on that part.
Looks good also, floating point is no direct priority for me, just needed to have a sprintf and for debugging printf is very handy. :)

Return to “Bare metal, Assembly language”