/* vim: set filetype=c:
 *
 * bprintf.c86  -  printf() routine for bootrom
 *
 * Copyright (C) 2003-2007 Gero Kuhlmann   <gero@gkminix.han.de>
 *
 *  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 of the License, or
 *  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 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, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * $Id: bprintf.c86,v 1.5 2007/01/06 18:30:51 gkminix Exp $
 */


/* Include target system C headers */
#include <common.h86>
#include <i386/stdarg.h86>
#include <i386/string.h86>
#include <i386/ctype.h86>
#include <i386/conio.h86>



/*
 * This printf() routine is a special version for the bootrom. It implements
 * only the functionality actually needed by the bootrom, and also avoids
 * using cputc() so that the conio module doesn't have to get included into
 * the bootrom.
 */
static void doprint(char c)
{
  if (c != '\n')
	charout(c);
  else
	crlfout();
}



/*
 * bprintf() is like printf() but it always prints to the console. This is
 * a special version for the bootrom code.
 */
size_t bprintf(const char *fmt, ...)
{
  char c, *cp;
  size_t len, count = 0;
  bool_t sign, lval;
  unsigned short base;
  long val;
  va_list ap;

  va_start(ap, fmt);

  while ((c = *fmt++) != '\0') {
	/* If the character is not %, just print it */
	if (c != '%') {
		doprint(c);
		count++;
		continue;
	}

	/* Prepare for format handling */
	sign = FALSE;
	lval = FALSE;

	c = *fmt++;
	if (c == 'l') {
		c = *fmt++;
		lval = TRUE;
	}
	if (c == '\0')
		break;

	/* Determine type of argument */
	switch(c) {
		case 'x': base = 16; goto printunsigned;
		case 'o': base =  8; goto printunsigned;
		case 'u': base = 10; goto printunsigned;
		case 'd': base = 10; goto printsigned;
		case 's':
			cp = va_arg(ap, char *);
			if (lval)
				len = (short)va_arg(ap, unsigned int);
			else
				len = _strlen(cp);
			goto printstr;
		case 'c':
			doprint((char)va_arg(ap, int));
			break;
		default:
			doprint(c);
			break;
	}
	continue;

	/* Print a signed numeric value */
printsigned:
	if (lval)
		val = (long)va_arg(ap, long);
	else
		val = (short)va_arg(ap, int);
	if ((sign = (val < 0)))
		val = -val;
	goto printnum;

	/* Print an unsigned numeric value */
printunsigned:
	if (lval)
		val = (unsigned long)va_arg(ap, unsigned long);
	else
		val = (unsigned short)va_arg(ap, unsigned int);

	/* Convert numeric value into string */
printnum:
	cp = __numcvt(val, base);
	len = _strlen(cp);

	/* Print a string value */
printstr:
	count--;
	if (sign) {
		doprint('-');
		count++;
	}
	while (*cp && len-- > 0) {
		doprint(*cp++);
		count++;
	}
  }

  va_end(ap);
  return(count);
}

