/*=================================< Graphics.c >=============================*/
//
//    Title:      GRAPHICS UNIT
//    Saved date: 09 Jun 2003 19:24:38
//    Written by: JAG
//    Project:    RMS101 SBC 
//    Target:     LH79520
//    Compiler:   GNU GCC for ARM
//		Copyright:	Subject to the Revely Open Source License (see readme.txt)
//
// The Graphics.c unit is a collection of functions for displaying text and
// graphics. Call GraphInit to initialize before calling other functions.
//
// The color palette system is based on the standardized 8-bit Windows/Mac 
// method where the first 40 colors are system colors (we're defining just 
// 17 here) and the last 216 colors are common to all platforms.
// 
// The sprite functions allow movement of a 16x16 colored sprite.  At this 
// time they do not preserve the background.  OK so they're not REAL sprites...
//
// Refer to Video.c for more information regarding implemented graphics modes.
//
// Copyright Revely Microsystems - Refer documentation for legalese
//
// History:
//
// 1.00	12-10-02 JAG	Initial release
// 1.01	06-07-03 JAG	Add 216 color support functions
//
/*============================================================================*/
 
#include <stddefs.h>
#include <video.h>
#include <graphics.h>
#include <regs.h>
#include <fonts.h>

/*------------------------------< Unit Constants >----------------------------*/ 

// Define a simple 16 color palette (color 17 is the background)
const HALF Palette[][3] = {

 	 {0,0,0},            /* BLACK, */
    {31,31,31},       	/* WHITE, */
    {16,16,16},       	/* DARKGREY, */
    {24,24,24},       	/* GREY, */
    {31,0,0},           /* RED, */
    {16,0,0},           /* BROWN, */
    {31,31,0},         	/* YELLOW, */
    {24,24,0},         	/* DARKYELLOW, */
    {0,31,0},           /* GREEN, */
    {0,16,0},           /* DARKGREEN, */
    {0,31,31},         	/* CYAN, */
    {0,20,20},         	/* DARKCYAN, */
    {0,0,31},           /* BLUE, */
    {0,0,20},           /* DARKBLUE, */
    {31,0,31},         	/* PURPLE, */
    {20,0,20}};         /* DARK PURPLE */ 

// Increment values for 216 color palette
const WORD PalInc[] = { 0, 6, 13, 19, 26, 31 };

/*---------------------------------< Defines >--------------------------------*/
#define CHARSPACEX	0		// 2 pixels between characters in x direction
#define CHARSPACEY	2		// 2 pixels between characters in y direction
#define PALETTEBACK	8  	// Palette location for background color
#define VGAPIXELS		307200	// Number of pixels in 640x480 image
// Sprite Stuff
#define MAXSPRITES	8
#define SPR_XSIZE		16
#define SPR_YSIZE		16

/*-----------------------------< Local Variables >----------------------------*/

PBYTE pVram;		// Pointer to start of Main Video Memory
PBYTE pVram2;		// Pointer to start of Alternate Video Memory
BYTE bVideoOut;	// Remember current video output mode
BYTE bVideoRes;	// Remember current resolution
BYTE bDrawScr;		// Currently active draw to screen (0 or 1)
BYTE bViewScr;		// Currently active view screen (0 or 1)

struct Sprite {	WORD xpos; 
						WORD ypos; 
						PBYTE pImage; 
						BYTE active; } SpriteRec[MAXSPRITES]; 

/*--------------------------------< GraphInit >-------------------------------*/
void GraphInit(BYTE bRes, BYTE bOut)
{
	WORD wI;
	WORD wVR1, wVR2;
	
	VideoInitDAC(bRes, bOut);
	VideoInit(&wVR1, &wVR2, bRes, bOut);
	pVram = (PBYTE)wVR1;
	pVram2 = (PBYTE)wVR2;
	bVideoOut = bOut;
	bVideoRes = bRes;
	bViewScr=0;
	bDrawScr=0;
	
	// Init Sprite Table
	for (wI=0; wI < MAXSPRITES; wI++)
		SpriteRec[wI].active = FALSE;
}

/*--------------------------------< GraphDraw >-------------------------------*/
void GraphDraw(HALF x, HALF y, BYTE c)
{
	WORD index;
	
	if (bVideoRes == VRES_VGA)
		index = y * 640 + x;
	else
		index = y * 800 + x;
	
	*(pVram + index) = c;
}

/*------------------------------< GraphTextXY >------------------------------*/
void GraphTextXY(PCHAR sStr, HALF x, HALF y, BYTE c, BYTE bStyle)
{
	// Co-ordinates define upper left corner of text to be displayed
	// Each character is 8 pixels wide, by 14 pixels high
	
	WORD index, wPixLine;
	BYTE bRowData, bRow, bPix, bChars=0;
	PBYTE pPix;
	
	if (bVideoRes == VRES_VGA)
		wPixLine = 640;
	else
		wPixLine = 800;
	
	index = y * wPixLine + x;
	
	while (*sStr != NULL)
	{
		// Bounds checking on character
		if ((*sStr > '}') || (*sStr < ' '))
			*sStr = 0x7e;
		// Calculate starting point for this character
		pPix = pVram + index + ((CHARSPACEX + 8) * bChars);
		
		for (bRow =0; bRow < 14; bRow++)
		{
			// Get row of pixel data from table
			if (bStyle == BOLD)
				bRowData = FontBoldTable[bRow + 14 * (*sStr - 0x1f)];
			else
				bRowData = FontTable[bRow + 14 * (*sStr - 0x1f)];
			for (bPix=0; bPix < 8; bPix++)
			{
				if (bRowData & 0x80)
					pPix[bPix] = c;
				bRowData <<= 1;
			}
			pPix += wPixLine;
		}
		bChars++;
		sStr++;
	}
}

/*-------------------------------< GraphLine  >------------------------------*/
void GraphLine(HALF x1, HALF y1, HALF x2, HALF y2, BYTE bColor)
{
  	HALF	dx;			// Change in x.                   
  	HALF	dy;         // Change in y.                   
  	HALF i;
 	
	// Calc change in x and y	
	if (y1 > y2)
		dy = y1 - y2;
	else
		dy = y2 - y1;
	if (x1 > x2)
		dx = x1 - x2;
	else
		dx = x2 - x1;
	
	// Draw first and last points
	GraphDraw(x1,y1, bColor);
	GraphDraw(x2,y2, bColor);
	
	// Change is mostly in X axis ?
	if (dx >= dy)
	{
		// Make sure x1 < x2 
		if (x1 > x2)
		{
			i = x1;
			x1 = x2;
			x2 = i;
			i = y1;
			y1 = y2;
			y2 = i;
		}
	
		for (i=1; i < dx; i++)
		{
			if (y2 > y1)
				GraphDraw(x1 + i, y1 + (dy * i) / dx, bColor);
			else
				GraphDraw(x1 + i, y1 - (dy * i) / dx, bColor);
		}
	}
	else
	{
		// Make sure y1 < y2 
		if (y1 > y2)
		{
			i = y1;
			y1 = y2;
			y2 = i;
			i = y1;
			y1 = y2;
			y2 = i;
		}
		for (i=1; i < dy; i++)
		{
			if (x2 > y1)
				GraphDraw(x1 + (dx * i) / dy, y1 + i, bColor);
			else
				GraphDraw(x1 - (dx * i) / dy, y1 + i, bColor);
		}
	}
			
}  

/*----------------------------< GraphRectangle >-----------------------------*/
void GraphRectangle(HALF x1, HALF y1, HALF x2, HALF y2, BYTE bColor, BYTE bFill)
{
	HALF i,x,y;

	// Make sure x2 and y2 are greater than x1 and y1
	if (x1 > x2)
	{
		i = x1;
		x1 = x2;
		x2 = i;
	}
	if (y1 > y2)
	{
		i = y1;
		y1 = y2;
		y2 = i;
	}

	// Now start drawing
	if (bFill)
		for (y=y1; y<=y2; y++)
			for (x=x1; x<=x2; x++)
				GraphDraw(x,y,bColor);
	else
	{
		for (x=x1; x<=x2; x++)
		{
			GraphDraw(x,y1,bColor);
			GraphDraw(x,y2,bColor);
		}
		for (y=y1; y<=y2; y++)
		{
			GraphDraw(x1, y, bColor);
			GraphDraw(x2, y, bColor);
		}
	}
}

/*----------------------------< GraphBackground >----------------------------*/
void GraphSetBackground(BYTE bColor)
{
	PLCDPALETTE[PALETTEBACK] &= 0x0000ffff;
	
	if (bColor & 1)
		PLCDPALETTE[PALETTEBACK] |= (PLCDPALETTE[bColor/2] & 0xffff0000);	
	else	
		PLCDPALETTE[PALETTEBACK] |= PLCDPALETTE[bColor/2] << 16;
}

/*----------------------------< GraphSetPalette >----------------------------*/
void GraphSetPalette(void)
{
	WORD i, z, wR=0, wG=0, wB=0;
	WORD wC1, wC2;
	
	// Set basic system palette (17 colors)
	z=0;
	for (i=0; i < 8; i++)
	{
		PLCDPALETTE[i] = Palette[z][0] + (Palette[z][1] << 5) + (Palette[z][2] << 10) +
			(Palette[z+1][0] << 16) + (Palette[z+1][1] << 21) + (Palette[z+1][2] << 26);	
		z+=2;
	}
	GraphSetBackground(BLACK);
	
	// Set common palette (216 colors)
	z=0;
	for (i=20; i < 128; i++)
	{
		wC1 = PalInc[wR] + (PalInc[wG] << 5) + (PalInc[wB] << 10); 
		if (++wR == 6)
		{
			wR=0; 
			if (++wG == 6)
			{
				wG=0;
				wB++;
			}
		}
		wC2 = PalInc[wR] + (PalInc[wG] << 5) + (PalInc[wB] << 10); 
		if (++wR == 6)
		{
			wR=0;
			if (++wG == 6)
			{
				wG=0;
				wB++;
			}
		}
		PLCDPALETTE[i] = wC1 + (wC2 << 16);
	}
}

/*-----------------------------< GraphGetPalette >----------------------------*/
WORD GraphGetPalette(WORD wColor, BYTE bBits)
{
	WORD wScale, wI;
	WORD wRed, wGrn, wBlu;
	WORD wRI, wGI, wBI;
		
	// Extract color values
	wBlu = wColor & 0x001f;
	wGrn = (wColor >> 5) & 0x01f;
	wRed = (wColor >> 10) & 0x01f;
		
	// Scale color values
	wScale = 1; //2^(bBits-5);
	wRed /= wScale;
	wGrn /= wScale;
	wBlu /= wScale;
	
	// Get Red Index
	if (wRed < 4)
		wRI = 0; 
		else if (wRed < 10)
			wRI = 1;
			else if (wRed < 17)
				wRI = 2;
				else if (wRed < 23)
					wRI = 3;
					else if (wRed < 30)
						wRI = 4;
						else 	
							wRI = 5;
	// Get Green Index
	if (wGrn < 4)
		wGI = 0;
		else if (wGrn < 10)
			wGI = 1;
			else if (wGrn < 17)
				wGI = 2;
				else if (wGrn < 23)
					wGI = 3;
					else if (wGrn < 30)
						wGI = 4;
						else 	
							wGI = 5;
	// Get Blue Index
	if (wBlu < 4)
		wBI = 0; 
		else if (wBlu < 10)
			wBI = 1;
			else if (wBlu < 17)
				wBI = 2;
				else if (wBlu < 23)
					wBI = 3;
					else if (wBlu < 30)
						wBI = 4;
						else 	
							wBI = 5;
									
	// Calculate Palette Index
	wI = 40 + wRI + wGI * 6 + wBI * 36;
	
	return wI;
}

/*--------------------------------< GraphClear >------------------------------*/
void GraphClear(void)
{
	PWORD pT; 
	WORD i, w;
	
	if (bDrawScr)
		pT = (PWORD)pVram2;
	else
		pT = (PWORD)pVram;
	w = VRAM_SIZE / 4;
	
	// Set every pixel to background color
	if (bVideoRes == VRES_VGA)
	{
		for (i=0; i < w; i++)
			*pT++ = 0x11111111;
	}
	else
	{
		for (i=0; i < w; i++)
			*pT++ = 0;				// Black in TC Mode
	}
}

/*----------------------------< GraphSetViewScreen >--------------------------*/
void GraphSetViewScreen(BYTE bScr)
{
	if (bScr)
	{
		VideoSetBuff(pVram2);
		bViewScr=1;
	}
	else
	{
		VideoSetBuff(pVram);
		bViewScr=0;
	}
}

/*----------------------------< GraphSetDrawScreen >--------------------------*/
void GraphSetDrawScreen(BYTE bScr)
{
	bDrawScr=bScr;
}

/*----------------------------< GraphDisplayBitMap >--------------------------*/
void GraphDisplayBitMap(PHALF pData, WORD wXsize, WORD wYsize, BYTE bScale)
{
	PBYTE pV, pS, pT;
	WORD x,y,c;
		
	if (bDrawScr)
		pV = pVram2;
	else
		pV = pVram;
	
	if (bScale == 1)
	{
		// Draw bitmap in center of screen
		pT = pV + (((640-wXsize) / 2) + (640 * ((480-wYsize) / 2)));
		
		for (y=0; y < wYsize; y++)
		{ 
			for (x=0; x < wXsize; x++)
				*pT++ = GraphGetPalette(*pData++, 16);
			pT += (640-wXsize);	// next line
		}
	} 
	
	if (bScale == 2)
	{
		// Draw bitmap in center of screen
		pV += (((640-(wXsize*2))/2) + (640 * ((480-(wYsize*2))/2)));
		for (y=0; y < wYsize; y++)
		{
			pS = pV;
			pT = pS + 640;
			for (x=0; x < wXsize; x++)
			{
				c = GraphGetPalette(*pData++, 16);
				*pS++ = c;
				*pS++ = c;
				*pT++ = c;
				*pT++ = c;
			}
			pV += (640 * 2);	// next screen line
		}
	}
	
}

/*----------------------------< GraphDisplayBitMapTC >--------------------------*/
void GraphDisplayBitMapTC(PHALF pData, WORD wXsize, WORD wYsize, BYTE bScale)
{
	PHALF pT, pX;
	WORD x,y;
	PHALF pV;
	
	if (bDrawScr)
		pV = (PHALF)pVram2;
	else
		pV = (PHALF)pVram;
	
	if (bScale == 1)
	{
		// Draw bitmap in center of screen
		pT = pV + (((640-wXsize) / 2) + (640 * ((480-wYsize) / 2)));
		
		for (y=0; y < wYsize; y++)
		{ 
			for (x=0; x < wXsize; x++)
				*pT++ = *pData++;
			pT += (640-wXsize);	// next line
		}
	} 
	
	if (bScale == 2)
	{
		// Draw bitmap in center of screen
		pT = pV +(((640-(wXsize*2))/2)+(640 * ((480-(wYsize*2))/2)));
		
		for (y=0; y < wYsize; y++)
		{
			// Each line twice
			pX = pData;
			for (x=0; x < wXsize; x++)
			{
				*pT++ = *pX;
				*pT++ = *pX++;
			}
			pT += (640-(wXsize*2));	// next screen line
			for (x=0; x < wXsize; x++)
			{
				*pT++ = *pData;
				*pT++ = *pData++;
			}
			pT += (640-(wXsize*2));	// next screen line
		}
	}
	
}

/*--------------------------< GraphGetResMode >-------------------------------*/
BYTE GraphGetResMode(void)
{
	return bVideoRes;
}

/*--------------------------< GraphGetOutMode >-------------------------------*/
BYTE GraphGetOutMode(void)
{
	return bVideoOut;
}

/*---------------------------< GraphGetPixel >--------------------------------*/
BYTE GraphGetPixel(WORD wX, WORD wY)
{
	WORD index;
	
	if (bVideoRes == VRES_VGA)
		index = wY * 640 + wX;
	else
		index = wY * 800 + wX;
	
	return *(pVram + index);
}


/*---------------------------< GraphDrawSprite >------------------------------*/
WORD GraphDrawSprite(WORD wX, WORD wY, PBYTE pDat)
{
	WORD wI, wH=0, wRow, wCol;
	PBYTE pV;
	
	// Find first available Handle
	while ((SpriteRec[wH].active == TRUE) && (wH < MAXSPRITES))
		wH++;
	
	if (wH == MAXSPRITES)
		return 0;
	
	SpriteRec[wH].active = TRUE;
	
	SpriteRec[wH].xpos = wX;
	SpriteRec[wH].ypos = wY;
	SpriteRec[wH].pImage = pDat;
	
	pV = pVram + (wY * 640 + wX);
	for (wRow=0; wRow<SPR_YSIZE; wRow++)
	{
		for (wI=0; wI < SPR_XSIZE; wI++)
			*pV++ = *pDat++;
		pV+=(640-SPR_XSIZE); 
	}

	return wH;		// return handle
}

/*---------------------------< GraphMoveSprite >------------------------------*/
WORD GraphMoveSprite(WORD wH, CHAR cDX, CHAR cDY)
{
	WORD wRow, wCol, wI;
	PBYTE pOld, pNew, pImage;
	
	pOld = pVram + (SpriteRec[wH].ypos * 640 + SpriteRec[wH].xpos);
	
	SpriteRec[wH].xpos += cDX;
	SpriteRec[wH].ypos += cDY;
	
	if (SpriteRec[wH].xpos > 623)
	{
		SpriteRec[wH].xpos = 623;
		return 0;
	}
	if (SpriteRec[wH].ypos > 463)
	{
		SpriteRec[wH].xpos = 463;
		return 0;
	}
			
	pNew = pVram + (SpriteRec[wH].ypos * 640 + SpriteRec[wH].xpos);
	pImage = SpriteRec[wH].pImage;
	wI=0;
	for (wRow=0; wRow<SPR_YSIZE; wRow++)
	{
		for (wCol=0; wCol < SPR_XSIZE; wCol++)
			*pNew++ = *pImage++;
		pNew+=(640-SPR_XSIZE); 
	}
	
	// Erase one line 
	if (cDY == 1)		// +1 = down 1 line
	{
		for (wI=0; wI < 16; wI++)
			*pOld++ = BG;
	}
	else if (cDY)		// -1 = up 1 line
	{
		pOld +=(15*640);
		for (wI=0; wI < 16; wI++)
			*pOld++ = BG;
	}
	else if (cDX==1)	// +1 = right 1 column
	{
		for (wI=0; wI < 16; wI++)
			*(pOld+=640) = BG;
	}
	else if (cDX)		// -1 = left 1 column
	{
		pOld += 15;
		for (wI=0; wI < 16; wI++)
			*(pOld+=640) = BG;
	}
	return 1;
}

/*-----------------------------< GraphKillSprite >---------------------------*/
void GraphKillSprite(WORD wH)
{
	PBYTE pV;
	WORD wRow, wI;
	
	pV = pVram + (SpriteRec[wH].ypos * 640 + SpriteRec[wH].xpos);
	for (wRow=0; wRow<SPR_YSIZE; wRow++)
	{
		for (wI=0; wI < SPR_XSIZE; wI++)
			*pV++ = BG;
		pV+=(640-SPR_XSIZE); 
	}
	SpriteRec[wH].active = FALSE;
}


// end of graphics.c
