/***********************************************************************************
           Copyright 1998, NEC Electronics Inc. All rights reserved.  

Information and source code samples contained herein are provided "as-is", 
without representations or warranties, and are subject to change without notice.
NEC Electronics Inc. cannot support modifications or derivative works created from
the sample source code provided. Developers may use, reproduce, and modify portions 
or entire sections of the sample source code for the purposes of creating Dreamcast 
applications.  Developers may only commercially distribute the executable or binary 
portions of the sample source code.  Sample source code contained herein may not be 
appropriate for use under all circumstances.  Please consult Developer documentation
provided along with your Dreamcast unit for additional information and suggestions.
************************************************************************************/


/***********************************************************************************
** File:     DrawText.c
** Purpose:  Simple text-output to Kamui frame buffers
** Author:   Kevin Kralian
** Created:  November 31, 1998
**
** Version:  1.01
** 
** Description:
**    This module provides printf() capability to the front/back display buffer.
**    It is intended as a development/diagnostic tool, as a means of instantaneous
**    output during events such as crashes, exceptions, assertion failures, etc. 
**    It works with all resolutions and screen color modes (1555, 888, 8888, etc).
**
**    Warning! This is not designed for high-performance use! The included print
**    functions cause stalls waiting for rendering and page-flips to complete, so 
**    don't profile your app while printing to screen. 
**
**    These functions do not require any explicit setup. The only requirement is
**    that the framebuffer is initialized and visible. Once you are that far, you
**    can simply say:
**                          diagPrintf( "Hello World!\n" );
**
**    By default, printing is directed to the backbuffer, so you will ideally want
**    to issue the print commands between the kmRender() and kmFlip() commands.
**    There are a series of functions in DrawText.h that can modify text states.
**    Also, be aware that the font is an 8x8 ANSI character set. This will be 
**    difficult to read on some NTSC/PAL monitors in interlace modes.
**
***********************************************************************************/ 

#include <kamui.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>

#include "DrawText.h"
#include "ScrnInfo.h"			/* for frame-buffer info */

/*=========================================================================*/

/* DEFINES */

#define MAX_STRING 2048							/* max length for printf conversions */

/* Initial text settings- these can be changed with the functions below */

#define INITIAL_TEXT_MARGIN			4			/* In character units */
#define INITIAL_TEXT_POSX			0			/* relative to top-left margin */
#define INITIAL_TEXT_POSY			0			/* relative to top-left margin */
#define INITIAL_TEXT_FG_RGB			255,255,255 /* foreground RGB color */
#define INITIAL_TEXT_BG_RGB			0,0,0		/* background RGB color */
#define INITIAL_TEXT_USEBLOCKMODE	1			/* bool- solid block mode? (else transparent background) */
#define INITIAL_TEXT_USEBACKBUFFER	1			/* bool- use backbuffer? (else use frontbuffer) */
#define INITIAL_PIXELDOUBLE_X		0			/* bool- Horizontal pixel-doubling? (must be 0 or 1) */
#define INITIAL_PIXELDOUBLE_Y		0			/* bool- Vertical pixel-doubling? */

/*=========================================================================*/

/* private functions */

static void plotCharacter32( int x, int y, char c );
static void plotString32( char *string );
static void constrainXY();

/*=========================================================================*/
/* CHARACTER SET */

#define FIRST_CHAR		' '		/* SPACE */
#define LAST_CHAR		127		/* DEL */
#define DEFAULT_CHAR	127		/* DEL - gray block */

unsigned char characterSet[][8] =  				/* 8x8 (1 bit) ANSI character set */
{
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,	/* ' '  ASCII #0x20 */
	0x30,0x30,0x30,0x30,0x30,0x00,0x30,0x00,	/* '!'  ASCII #0x21 */
	0xcc,0xcc,0x00,0x00,0x00,0x00,0x00,0x00,	/* '"'  ASCII #0x22 */
	0x6c,0x6c,0xfe,0x6c,0xfe,0x6c,0x6c,0x00,	/* '#'  ASCII #0x23 */
	0x10,0x7c,0xd0,0x7c,0x16,0x7c,0x10,0x00,	/* '$'  ASCII #0x24 */
	0x00,0xc6,0xcc,0x18,0x30,0x66,0xc6,0x00,	/* '%'  ASCII #0x25 */
	0x78,0xcc,0x78,0xf0,0xda,0xcc,0x76,0x00,	/* '&'  ASCII #0x26 */
	0x18,0x18,0x00,0x00,0x00,0x00,0x00,0x00,	/* '''  ASCII #0x27 */
	0x18,0x30,0x60,0x60,0x60,0x30,0x18,0x00,	/* '('  ASCII #0x28 */
	0x60,0x30,0x18,0x18,0x18,0x30,0x60,0x00,	/* ')'  ASCII #0x29 */
	0x00,0x48,0x30,0xfc,0x30,0x48,0x00,0x00,	/* '*'  ASCII #0x2a */
	0x00,0x30,0x30,0xfc,0x30,0x30,0x00,0x00,	/* '+'  ASCII #0x2b */
	0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x30,	/* ','  ASCII #0x2c */
	0x00,0x00,0x00,0x7c,0x00,0x00,0x00,0x00,	/* '-'  ASCII #0x2d */
	0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00,	/* '.'  ASCII #0x2e */
	0x00,0x06,0x0c,0x18,0x30,0x60,0xc0,0x00,	/* '/'  ASCII #0x2f */
	0x7c,0xc6,0xce,0xd6,0xe6,0xc6,0x7c,0x00,	/* '0'  ASCII #0x30 */
	0x30,0x70,0x30,0x30,0x30,0x30,0x78,0x00,	/* '1'  ASCII #0x31 */
	0x78,0xcc,0x0c,0x18,0x30,0x60,0xfc,0x00,	/* '2'  ASCII #0x32 */
	0x78,0xcc,0x0c,0x38,0x0c,0xcc,0x78,0x00,	/* '3'  ASCII #0x33 */
	0xcc,0xcc,0xcc,0x7c,0x0c,0x0c,0x0c,0x00,	/* '4'  ASCII #0x34 */
	0xfc,0xc0,0xc0,0x78,0x0c,0xcc,0x78,0x00,	/* '5'  ASCII #0x35 */
	0x78,0xc0,0xc0,0xf8,0xcc,0xcc,0x78,0x00,	/* '6'  ASCII #0x36 */
	0xfc,0x0c,0x0c,0x18,0x30,0x30,0x30,0x00,	/* '7'  ASCII #0x37 */
	0x78,0xcc,0xcc,0x78,0xcc,0xcc,0x78,0x00,	/* '8'  ASCII #0x38 */
	0x78,0xcc,0xcc,0x7c,0x0c,0x0c,0x0c,0x00,	/* '9'  ASCII #0x39 */
	0x00,0x30,0x30,0x00,0x30,0x30,0x00,0x00,	/* ':'  ASCII #0x3a */
	0x00,0x30,0x30,0x00,0x30,0x30,0x60,0x00,	/* ';'  ASCII #0x3b */
	0x18,0x30,0x60,0xc0,0x60,0x30,0x18,0x00,	/* '<'  ASCII #0x3c */
	0x00,0x00,0xfc,0x00,0xfc,0x00,0x00,0x00,	/* '='  ASCII #0x3d */
	0x60,0x30,0x18,0x0c,0x18,0x30,0x60,0x00,	/* '>'  ASCII #0x3e */
	0x78,0xcc,0x18,0x30,0x30,0x00,0x30,0x00,	/* '?'  ASCII #0x3f */
	0x7c,0xc6,0xd6,0xde,0xcc,0xc0,0x7c,0x00,	/* '@'  ASCII #0x40 */
	0x30,0x78,0xcc,0xcc,0xfc,0xcc,0xcc,0x00,	/* 'A'  ASCII #0x41 */
	0xf8,0xcc,0xcc,0xf8,0xcc,0xcc,0xf8,0x00,	/* 'B'  ASCII #0x42 */
	0x78,0xcc,0xc0,0xc0,0xc0,0xcc,0x78,0x00,	/* 'C'  ASCII #0x43 */
	0xf0,0xd8,0xcc,0xcc,0xcc,0xd8,0xf0,0x00,	/* 'D'  ASCII #0x44 */
	0xfc,0xc0,0xc0,0xf0,0xc0,0xc0,0xfc,0x00,	/* 'E'  ASCII #0x45 */
	0xfc,0xc0,0xc0,0xf0,0xc0,0xc0,0xc0,0x00,	/* 'F'  ASCII #0x46 */
	0x78,0xcc,0xc0,0xc0,0xdc,0xcc,0x78,0x00,	/* 'G'  ASCII #0x47 */
	0xcc,0xcc,0xcc,0xfc,0xcc,0xcc,0xcc,0x00,	/* 'H'  ASCII #0x48 */
	0x78,0x30,0x30,0x30,0x30,0x30,0x78,0x00,	/* 'I'  ASCII #0x49 */
	0x3c,0x18,0x18,0x18,0x18,0xd8,0x70,0x00,	/* 'J'  ASCII #0x4a */
	0xcc,0xd8,0xf0,0xe0,0xf0,0xd8,0xcc,0x00,	/* 'K'  ASCII #0x4b */
	0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xfc,0x00,	/* 'L'  ASCII #0x4c */
	0x6c,0x6c,0x6c,0xfe,0xd6,0xd6,0xd6,0x00,	/* 'M'  ASCII #0x4d */
	0xcc,0xcc,0xec,0xfc,0xdc,0xcc,0xcc,0x00,	/* 'N'  ASCII #0x4e */
	0x78,0xcc,0xcc,0xcc,0xcc,0xcc,0x78,0x00,	/* 'O'  ASCII #0x4f */
	0xf8,0xcc,0xcc,0xf8,0xc0,0xc0,0xc0,0x00,	/* 'P'  ASCII #0x50 */
	0x7c,0xc6,0xc6,0xc6,0xd6,0xcc,0x76,0x00,	/* 'Q'  ASCII #0x51 */
	0xf8,0xcc,0xcc,0xf8,0xf0,0xd8,0xcc,0x00,	/* 'R'  ASCII #0x52 */
	0x78,0xcc,0xc0,0x78,0x0c,0xcc,0x78,0x00,	/* 'S'  ASCII #0x53 */
	0xfc,0x30,0x30,0x30,0x30,0x30,0x30,0x00,	/* 'T'  ASCII #0x54 */
	0xcc,0xcc,0xcc,0xcc,0xcc,0xcc,0x78,0x00,	/* 'U'  ASCII #0x55 */
	0xcc,0xcc,0xcc,0x48,0x78,0x30,0x30,0x00,	/* 'V'  ASCII #0x56 */
	0xd6,0xd6,0xd6,0xd6,0x7c,0x6c,0x6c,0x00,	/* 'W'  ASCII #0x57 */
	0xcc,0xcc,0x78,0x30,0x78,0xcc,0xcc,0x00,	/* 'X'  ASCII #0x58 */
	0xcc,0xcc,0xcc,0x78,0x30,0x30,0x30,0x00,	/* 'Y'  ASCII #0x59 */
	0xfc,0x0c,0x18,0x30,0x60,0xc0,0xfc,0x00,	/* 'Z'  ASCII #0x5a */
	0x7c,0x60,0x60,0x60,0x60,0x60,0x7c,0x00,	/* '['  ASCII #0x5b */
	0x00,0xc0,0x60,0x30,0x18,0x0c,0x06,0x00,	/* '\'  ASCII #0x5c */
	0xf8,0x18,0x18,0x18,0x18,0x18,0xf8,0x00,	/* ']'  ASCII #0x5d */
	0x10,0x38,0x6c,0x00,0x00,0x00,0x00,0x00,	/* '^'  ASCII #0x5e */
	0x00,0x00,0x00,0x00,0x00,0x00,0xfe,0x00,	/* '_'  ASCII #0x5f */
	0x30,0x30,0x00,0x00,0x00,0x00,0x00,0x00,	/* '`'  ASCII #0x60 */
	0x00,0x00,0x78,0x0c,0x7c,0xcc,0x7a,0x00,	/* 'a'  ASCII #0x61 */
	0xc0,0xc0,0xc0,0xf8,0xcc,0xcc,0xf8,0x00,	/* 'b'  ASCII #0x62 */
	0x00,0x00,0x78,0xcc,0xc0,0xcc,0x78,0x00,	/* 'c'  ASCII #0x63 */
	0x0c,0x0c,0x0c,0x7c,0xcc,0xcc,0x7c,0x00,	/* 'd'  ASCII #0x64 */
	0x00,0x00,0x78,0xcc,0xf8,0xc0,0x78,0x00,	/* 'e'  ASCII #0x65 */
	0x38,0x6c,0x60,0xf0,0x60,0x60,0x60,0x00,	/* 'f'  ASCII #0x66 */
	0x00,0x00,0x74,0xcc,0xcc,0x7c,0x0c,0x78,	/* 'g'  ASCII #0x67 */
	0xc0,0xc0,0xc0,0xf8,0xcc,0xcc,0xcc,0x00,	/* 'h'  ASCII #0x68 */
	0x30,0x00,0x30,0x30,0x30,0x30,0x30,0x00,	/* 'i'  ASCII #0x69 */
	0x18,0x00,0x18,0x18,0x18,0x18,0x18,0x70,	/* 'j'  ASCII #0x6a */
	0xc0,0xc0,0xcc,0xd8,0xf0,0xd8,0xcc,0x00,	/* 'k'  ASCII #0x6b */
	0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x00,	/* 'l'  ASCII #0x6c */
	0x00,0x00,0x6c,0x6c,0xfe,0xd6,0xd6,0x00,	/* 'm'  ASCII #0x6d */
	0x00,0x00,0xd8,0xfc,0xcc,0xcc,0xcc,0x00,	/* 'n'  ASCII #0x6e */
	0x00,0x00,0x78,0xcc,0xcc,0xcc,0x78,0x00,	/* 'o'  ASCII #0x6f */
	0x00,0x00,0xf8,0xcc,0xcc,0xf8,0xc0,0xc0,	/* 'p'  ASCII #0x70 */
	0x00,0x00,0x74,0xcc,0xcc,0x7c,0x0c,0x0e,	/* 'q'  ASCII #0x71 */
	0x00,0x00,0xb8,0xec,0xc0,0xc0,0xc0,0x00,	/* 'r'  ASCII #0x72 */
	0x00,0x00,0x78,0xc0,0x78,0x0c,0x78,0x00,	/* 's'  ASCII #0x73 */
	0x30,0x30,0xfc,0x30,0x30,0x30,0x1c,0x00,	/* 't'  ASCII #0x74 */
	0x00,0x00,0xcc,0xcc,0xcc,0xcc,0x76,0x00,	/* 'u'  ASCII #0x75 */
	0x00,0x00,0xcc,0xcc,0xcc,0x78,0x30,0x00,	/* 'v'  ASCII #0x76 */
	0x00,0x00,0xd6,0xd6,0xfe,0x6c,0x6c,0x00,	/* 'w'  ASCII #0x77 */
	0x00,0x00,0xcc,0x78,0x30,0x78,0xcc,0x00,	/* 'x'  ASCII #0x78 */
	0x00,0x00,0xcc,0xcc,0xcc,0x7c,0x0c,0x78,	/* 'y'  ASCII #0x79 */
	0x00,0x00,0xfc,0x18,0x30,0x60,0xfc,0x00,	/* 'z'  ASCII #0x7a */
	0x38,0x60,0x60,0xc0,0x60,0x60,0x38,0x00,	/* '{'  ASCII #0x7b */
	0x30,0x30,0x30,0x00,0x30,0x30,0x30,0x00,	/* '|'  ASCII #0x7c */
	0x70,0x18,0x18,0x0c,0x18,0x18,0x70,0x00,	/* '}'  ASCII #0x7d */
	0x00,0x66,0xd8,0x00,0x00,0x00,0x00,0x00,	/* '~'  ASCII #0x7e */
	0xaa,0x54,0xaa,0x54,0xaa,0x54,0xaa,0x00,	/* ' '  ASCII #0x7f */
};

/*=========================================================================*/

/* The print routines are basically state based. The states track things 
** such as cursor position, screen margin, foreground & background colors,
** print mode (solid or cut-out), and the target (front or back buffer).
** The states are initialized to the #defines listed at the top of this file.
** They can be changed later with the provided functions. The entire state 
** can also be backed-up and restored; mainly so diagnostic messages can be
** drawn at any time without destroying the text-state of other messages.
*/

typedef struct
{
	int posX, posY;
	int minX, minY, maxX, maxY;					/* [min,max) character units */
	struct { unsigned char r,g,b; } FGColor;	/* foreground color */
	struct { unsigned char r,g,b; } BGColor;	/* background color */
	int bSolid;									/* use solid block-drawing? (else punch-through) */
	int bPrintToBackBuffer;						/* draw to back? (else draw to front) */
	int bPixelDoubleX;							/* double horizontal pixels? (must be 0 or 1) */
	int bPixelDoubleY;							/* double vertical pixels? */
	int nMargin;								/* in character units */
} TEXT_STATE;

/* default text states are defined at the top of this file */

static TEXT_STATE state = 
{
	INITIAL_TEXT_MARGIN + INITIAL_TEXT_POSX,
	INITIAL_TEXT_MARGIN + INITIAL_TEXT_POSY,
	INITIAL_TEXT_MARGIN,
	INITIAL_TEXT_MARGIN,
	80-INITIAL_TEXT_MARGIN,
	60-INITIAL_TEXT_MARGIN,
	{INITIAL_TEXT_FG_RGB},
	{INITIAL_TEXT_BG_RGB},
	INITIAL_TEXT_USEBLOCKMODE, 
	INITIAL_TEXT_USEBACKBUFFER,
	INITIAL_PIXELDOUBLE_X,
	INITIAL_PIXELDOUBLE_Y,
	INITIAL_TEXT_MARGIN
};

static TEXT_STATE backupState;

/* The 'pack24' union is for use with the 24-bit frame buffer. It is a simple 
** 16-pixel buffer that allows us to queue up red, green, and blue components;
** and then lets us write them out as 12 aligned DWORDS. This takes care of the
** alignment problems we would otherwise have (no frame buffer byte access).
*/

union {
	struct { char b,g,r; } pixels[16];
	unsigned long longs[12];
} pack24;


/*=========================================================================*/
/* STATE MANAGEMENT FUNCTIONS */


/* Normally, you'll write to the backbuffer after finishing a 3D render */
void diagUseBackBuffer()
{
	state.bPrintToBackBuffer = TRUE;	
}

/* The main reason for drawing to front buffer is for instantaneous error messages */
void diagUseFrontBuffer()
{
	state.bPrintToBackBuffer = FALSE;
}

void diagSetBGColor( unsigned char r, unsigned char g, unsigned char b )
{
	state.BGColor.r = r; state.BGColor.g = g; state.BGColor.b = b;
}

void diagSetFGColor( unsigned char r, unsigned char g, unsigned char b )
{
	state.FGColor.r = r; state.FGColor.g = g; state.FGColor.b = b;
}

/* block draw- text foreground & background drawn; obliterates background graphics */
void diagUseSolidBackground()
{
	state.bSolid = TRUE;
}

/* only text foreground drawn; background graphics visible through open spaces in characters */
void diagUseTransparentBackground()
{
	state.bSolid = FALSE;
}

/* store x,y,colors,mode,etc for later restoration */
void diagPreserveState()
{
	backupState = state;
}

/* restore previously saved state */
void diagRestoreState()
{
	state = backupState;
}


/* sets the X Y cursor position relative to the border (if any) */

void diagSetXY( int x, int y )
{
	state.posX = x + state.minX;
	state.posY = y + state.minY;
	constrainXY();
}


/*
Bring in text border by n characters to prevent text from going into TV's obscured overscan area.
We actually have control of the top/left/bottom/right margins; for simplicity, this function
simply takes a single count that is applied to all 4 sides (use 0 for no margins).
*/

void diagSetScreenMargin( int nCharacters )
{
	state.nMargin = nCharacters;
	state.minX = nCharacters; 
	state.minY = nCharacters;
	state.maxX = getScreenWidth() / (8 << state.bPixelDoubleX) - nCharacters - 1;
	state.maxY = getScreenHeight() / (8 << state.bPixelDoubleY) - nCharacters - 1;

	/* sanity check- if illegal window size, reset to full screen size */
	if (state.minX >= state.maxX  ||  state.minY >= state.maxY)
	{
		state.minX = state.minY = 0;
		state.maxX = getScreenWidth()/(8 << state.bPixelDoubleX) - 1;
		state.maxX = getScreenHeight()/(8 << state.bPixelDoubleY) - 1;
	}
	constrainXY();
}

/*
This function is used to enable horizontal and/or vertical pixel doubling.
This lets you draw 8x8, 8x16, 16x8, or 16x16 characters. This exists so the same
8x8 font can be readable in resolutions ranging from 320x240 to 640x480.
*/

void diagSetPixelDoubling( int bDoubleX, int bDoubleY )
{
	/* attempt to maintain onscreen XY cursor position */
	if (bDoubleX && !state.bPixelDoubleX) state.posX = (state.posX+1)/2;
	if (!bDoubleX && state.bPixelDoubleX) state.posX *= 2;
	if (bDoubleY && !state.bPixelDoubleY) state.posY = (state.posY+1)/2;
	if (!bDoubleY && state.bPixelDoubleY) state.posY *= 2;

	state.bPixelDoubleX = bDoubleX ? 1 : 0;
	state.bPixelDoubleY = bDoubleY ? 1 : 0;

	/* need to recompute max X & Y with new character size */
	diagSetScreenMargin( state.nMargin );
	constrainXY();
}


/* Replace a single character in the character set */
void diagReplaceCharacter( char c, unsigned char character[8] )
{
	int row;

	if ( c < FIRST_CHAR  ||  c > LAST_CHAR )
		return;
	for (row=0; row < 8; row++)
		characterSet[c-FIRST_CHAR][row] = character[row];
}


/*=========================================================================*/

/* PRINT ROUTINES */


/* Draws a text character into the appropriate frame buffer at character position (x,y) */
void plotCharacter32( int x, int y, char c )
{
	int row, col, nLongs, nByteAdvance, fgColor, bgColor, doubleX, doubleY;

	union {
		unsigned int   *u32;
		unsigned short *u16;
		unsigned char  *u8;
	} pFrameBuffer;		/* generic pointer to anything */

	waitForEndOfRender();

	/* compute line advancement (stride - 8 pixels) */
	switch ( getScreenColorMode() )
	{
	case KM_DSPBPP_ARGB8888:
		fgColor = (state.FGColor.r << 16 ) | (state.FGColor.g << 8 ) | (state.FGColor.b);
		bgColor = (state.BGColor.r << 16 ) | (state.BGColor.g << 8 ) | (state.BGColor.b);
		break;
	case KM_DSPBPP_RGB565:
		fgColor = (state.FGColor.r << 8 & 0xf800 ) | (state.FGColor.g << 3 & 0x07e0 ) | (state.FGColor.b >> 3 & 0x001f );
		bgColor = (state.BGColor.r << 8 & 0xf800 ) | (state.BGColor.g << 3 & 0x07e0 ) | (state.BGColor.b >> 3 & 0x001f );
		break;
	case KM_DSPBPP_RGB555:		
	case KM_DSPBPP_ARGB1555:
		fgColor = (state.FGColor.r << 7 & 0x7c00 ) | (state.FGColor.g << 2 & 0x03e0 ) | (state.FGColor.b >> 3 & 0x001f );
		bgColor = (state.BGColor.r << 7 & 0x7c00 ) | (state.BGColor.g << 2 & 0x03e0 ) | (state.BGColor.b >> 3 & 0x001f );
		break;
	}
	nByteAdvance = getScreenStride() - (8*getScreenBytesPerPixel() << state.bPixelDoubleX);

	/* compute pixel location based on character X & Y position */
	x *= 8 << state.bPixelDoubleX;
	y *= 8 << state.bPixelDoubleY;

	if ( c < FIRST_CHAR  ||  c > LAST_CHAR )
		c = DEFAULT_CHAR;

	if (state.bPrintToBackBuffer)
		pFrameBuffer.u8 = (KMBYTE*)getBackBufferAddr() + getScreenStride()*y + getScreenBytesPerPixel()*x;
	else
		pFrameBuffer.u8 = (KMBYTE*)getFrontBufferAddr() + getScreenStride()*y + getScreenBytesPerPixel()*x;


	switch ( getScreenColorMode() )
	{
	case KM_DSPBPP_RGB888:		
		/* uses the pack24 union to buffer up pixels and write out as 32-bit aligned */
		for (row=0; row < 8; row++)
		{
			for (doubleY=0; doubleY <= state.bPixelDoubleY; doubleY++)
			{
				int nPixel=0;

				/* when preserving background, read existing framebuffer first */
				if (!state.bSolid)
					for (nLongs = 0; nLongs < (6 << state.bPixelDoubleX); nLongs++)
						pack24.longs[nLongs] = pFrameBuffer.u32[nLongs];
	
				for (col=0, nPixel=0; col < 8; col++)
				{
					for (doubleX=0; doubleX <= state.bPixelDoubleX; doubleX++)
					{
						if ( characterSet[c - FIRST_CHAR][row] & (0x80 >> col) )
						{
							pack24.pixels[nPixel].r = state.FGColor.r;
							pack24.pixels[nPixel].g = state.FGColor.g;
							pack24.pixels[nPixel++].b = state.FGColor.b;
						}
						else if (state.bSolid)
						{
							pack24.pixels[nPixel].r = state.BGColor.r;
							pack24.pixels[nPixel].g = state.BGColor.g;
							pack24.pixels[nPixel++].b = state.BGColor.b;
						}
						else
							nPixel++;
					}
				}
				for (nLongs = 0; nLongs < (6 << state.bPixelDoubleX); nLongs++)
					*pFrameBuffer.u32++ = pack24.longs[nLongs];
				pFrameBuffer.u8 += nByteAdvance;
			}
		}
		return;

	case KM_DSPBPP_ARGB8888:	
		for (row=0; row < 8; row++)
		{
			for (doubleY=0; doubleY <= state.bPixelDoubleY; doubleY++)
			{
				for (col=0; col < 8; col++)
				{
					for (doubleX=0; doubleX <= state.bPixelDoubleX; doubleX++)
					{
						if ( characterSet[c - FIRST_CHAR][row] & (0x80 >> col) )
							*pFrameBuffer.u32 = fgColor;
						else if (state.bSolid)
							*pFrameBuffer.u32 = bgColor;
						pFrameBuffer.u32++;
					}
				}
				pFrameBuffer.u8 += nByteAdvance;
			}
		}
		return;

	case KM_DSPBPP_RGB565:		
	case KM_DSPBPP_RGB555:		
	case KM_DSPBPP_ARGB1555:
		for (row=0; row < 8; row++)
		{
			for (doubleY=0; doubleY <= state.bPixelDoubleY; doubleY++)
			{
				for (col=0; col < 8; col++)
				{
					for (doubleX=0; doubleX <= state.bPixelDoubleX; doubleX++)
					{
						if ( characterSet[c - FIRST_CHAR][row] & (0x80 >> col) )
							*pFrameBuffer.u16 = fgColor;
						else if (state.bSolid)
							*pFrameBuffer.u16 = bgColor;
						pFrameBuffer.u16++;
					}
				}
				pFrameBuffer.u8 += nByteAdvance;
			}
		}
		return;
	} /* switch */
}



void diagPrintString( char *string )
{
	int i=0;

	while ( string[i] )
	{
		if ( string[i] == '\n' )
		{
			state.posX = 0;
			state.posY++;
		}
		else
		{
			plotCharacter32( state.posX, state.posY, string[i] );
			state.posX++;
		}
		constrainXY();
		i++;
	}
}

void diagPrintStringAt( int xpos, int ypos, char *string )
{
	diagSetXY( xpos, ypos );
	diagPrintString( string );
}


void diagPrintf( char *format, ... )
{
	char output[MAX_STRING];
	va_list argptr;

	va_start( argptr, format );
	vsprintf( output, format, argptr );
	diagPrintString( output );
}


/*
Internal function that is called after altering the X & Y cursor positions.
It will clamp top or left side underflows, and wrap bottom or right side overflows.
*/
void constrainXY()
{
	/* clamp X to left edge */
	if (state.posX < state.minX )
		state.posX = state.minX;

	/* if X goes off right side, bump down to next line */
	if (state.posX > state.maxX)
	{
		state.posX = state.minX;
		state.posY++;
	}

	/* clamp Y to left edge */
	if (state.posY < state.minY )
		state.posY = state.minY;

	/* if Y goes off bottom, wrap it to top */
	if (state.posY > state.maxY)
		state.posY = state.minY; 
}


/*=========================================================================*/

