/*
	NCP-010 VGA Framebuffer for dsPIC 
	Version 11/24/2010-A
	(C) 2010 Jason Hunt nulluser@gmail.com

	Reference: L. BAGHLI 08/01/2008

	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 (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 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

	File: main.c

	HSync connected to RF0
	VSync connected to RF1
	RGB lines connected to RF3
*/


/* Sine loopup table */
// data = sin(ang) << 14 , 256 steps
const int sin_data[] = {0,402,804,1205,1606,2006,2404,2801,3196,3590,3981,4370,4756,5139,5520,5897,6270,
			6639,7005,7366,7723,8076,8423,8765,9102,9434,9760,10080,10394,10702,11003,11297,11585,11866,
			12140,12406,12665,12916,13160,13395,13623,13842,14053,14256,14449,14635,14811,14978,15137,15286,15426,
			15557,15679,15791,15893,15986,16069,16143,16207,16261,16305,16340,16364,16379,16384,16379,16364,16340,
			16305,16261,16207,16143,16069,15986,15893,15791,15679,15557,15426,15286,15137,14978,14811,14635,14449,
			14256,14053,13842,13623,13395,13160,12916,12665,12406,12140,11866,11585,11297,11003,10702,10394,10080,
			9760,9434,9102,8765,8423,8076,7723,7366,7005,6639,6270,5897,5520,5139,4756,4370,3981,
			3590,3196,2801,2404,2006,1606,1205,804,402,0,-402,-804,-1205,-1606,-2006,-2404,-2801,
			-3196,-3590,-3981,-4370,-4756,-5139,-5520,-5897,-6270,-6639,-7005,-7366,-7723,-8076,-8423,-8765,-9102,
			-9434,-9760,-10080,-10394,-10702,-11003,-11297,-11585,-11866,-12140,-12406,-12665,-12916,-13160,-13395,-13623,-13842,
			-14053,-14256,-14449,-14635,-14811,-14978,-15137,-15286,-15426,-15557,-15679,-15791,-15893,-15986,-16069,-16143,-16207,
			-16261,-16305,-16340,-16364,-16379,-16384,-16379,-16364,-16340,-16305,-16261,-16207,-16143,-16069,-15986,-15893,-15791,
			-15679,-15557,-15426,-15286,-15137,-14978,-14811,-14635,-14449,-14256,-14053,-13842,-13623,-13395,-13160,-12916,-12665,
			-12406,-12140,-11866,-11585,-11297,-11003,-10702,-10394,-10080,-9760,-9434,-9102,-8765,-8423,-8076,-7723,-7366,
			-7005,-6639,-6270,-5897,-5520,-5139,-4756,-4370,-3981,-3590,-3196,-2801,-2404,-2006,-1606,-1205,-804,
			-402};

/* Cosine loopup table */
const int cos_data[] = {16384,16379,16364,16340,16305,16261,16207,16143,16069,15986,15893,15791,15679,15557,15426,15286,15137,
			14978,14811,14635,14449,14256,14053,13842,13623,13395,13160,12916,12665,12406,12140,11866,11585,11297,
			11003,10702,10394,10080,9760,9434,9102,8765,8423,8076,7723,7366,7005,6639,6270,5897,5520,
			5139,4756,4370,3981,3590,3196,2801,2404,2006,1606,1205,804,402,0,-402,-804,-1205,
			-1606,-2006,-2404,-2801,-3196,-3590,-3981,-4370,-4756,-5139,-5520,-5897,-6270,-6639,-7005,-7366,-7723,
			-8076,-8423,-8765,-9102,-9434,-9760,-10080,-10394,-10702,-11003,-11297,-11585,-11866,-12140,-12406,-12665,-12916,
			-13160,-13395,-13623,-13842,-14053,-14256,-14449,-14635,-14811,-14978,-15137,-15286,-15426,-15557,-15679,-15791,-15893,
			-15986,-16069,-16143,-16207,-16261,-16305,-16340,-16364,-16379,-16384,-16379,-16364,-16340,-16305,-16261,-16207,-16143,
			-16069,-15986,-15893,-15791,-15679,-15557,-15426,-15286,-15137,-14978,-14811,-14635,-14449,-14256,-14053,-13842,-13623,
			-13395,-13160,-12916,-12665,-12406,-12140,-11866,-11585,-11297,-11003,-10702,-10394,-10080,-9760,-9434,-9102,-8765,
			-8423,-8076,-7723,-7366,-7005,-6639,-6270,-5897,-5520,-5139,-4756,-4370,-3981,-3590,-3196,-2801,-2404,
			-2006,-1606,-1205,-804,-402,-0,402,804,1205,1606,2006,2404,2801,3196,3590,3981,4370,
			4756,5139,5520,5897,6270,6639,7005,7366,7723,8076,8423,8765,9102,9434,9760,10080,10394,
			10702,11003,11297,11585,11866,12140,12406,12665,12916,13160,13395,13623,13842,14053,14256,14449,14635,
			14811,14978,15137,15286,15426,15557,15679,15791,15893,15986,16069,16143,16207,16261,16305,16340,16364,
			16379};

#include <p30fxxxx.h>

_FOSC(CSW_FSCM_OFF & XT_PLL8);	
_FWDT(WDT_OFF);
_FBORPOR(PBOR_OFF & BORV_27 & PWRT_16 & MCLR_EN);
_FGS(CODE_PROT_OFF);


#define ABS(a) (( a ) < 0 ? - (a) : a)			// Abs value


#define FOSC			80000000		// Osc 10 MHZ xtal  HS PLLx8
#define FCY	  		(FOSC / 4)		// Instruction clock  

#define HOST_BAUD		57600			// Baud for host pc

#define hsync 			_LATF0			// Hsync signal
#define vsync 			_LATF1			// Vsync signal

#define TIMER1_INTERVAL 	635			// Horz Freq 31.50 khz

#define DISP_ROWS		80			// Number of rows and columns in the display buffer
#define DISP_COLS		128

#define VGA_ROWS		480			// Visible VGA scanlines
#define VGA_LINES		525			// Actual VGA scanlines
#define COL_BYTES		(DISP_COLS / 8)		// Bytes per column

#define MODE_DRAW		0			// Draw mode
#define MODE_ERASE		1			// Erase Mode

#define MAX_TRIANGLE		12			// Max number of triangles
#define MAX_POINTS		8			// Max number of points
#define MAX_OBJECT		2			// Max number of objects


// Vertex type
typedef struct 
{
	int x, y, z;					// Local coords
	int rx, ry, rz;					// Rotated
	unsigned char osx, osy;				// Old Screen coords
	unsigned char sx, sy;				// Screen coords
} point_type;


// Triangle type
typedef struct
{
	unsigned char p0, p1, p2;			// Index into point array
							// This is smaller than three pointers
} triangle_type;


// Object Type
typedef struct
{
	int tx, ty, tz;					// Position

	point_type points[MAX_POINTS];
	unsigned char num_points;

	triangle_type triangles[MAX_TRIANGLE];
	unsigned char num_triangles;


	unsigned char ang_x;				// Rotation Angles
	unsigned char ang_y;
	unsigned char ang_z;

	unsigned char dx;

} object_type;


unsigned int p1 = 40;					// Tuning Params
unsigned int p2 = 28;					// 

unsigned int cur_row = 0;				// Current vga row in the frame buffer
unsigned int disp_row = 0;				// Current display rom in the frame buffer
unsigned char rc = 0; 					// Row counter for duplicate rows

unsigned char send = 0;					// Send param signal
unsigned char refresh = 0;				// Refresh display signal

unsigned char draw_mode = 0;				// Current drawing_mode

unsigned char row_data[DISP_ROWS][COL_BYTES];		// The framebuffer


object_type obj[MAX_OBJECT];				// The 3D object data
unsigned char num_objects;


/************* 
*** Serial ***
*************/

/* Setup Serial Port */
void serial_init( void )
{
	U2BRG = (((FCY / HOST_BAUD) / 16) - 1);		// Create and set baud counter

	U2MODEbits.UARTEN = 1;
	U2STAbits.UTXEN = 1;

	IFS1bits.U2RXIF = 0;				// Clear interrupt flag
	IEC1bits.U2RXIE = 0;
	IEC1bits.U2TXIE = 0;
}
/* End of serial_init */

/* Send a char to the debug port */
void debug_ch( char c )
{
	while (U2STAbits.UTXBF);
	U2TXREG  = c;
}

/* End of debug char */

/* Send a string to the debug port */
void debug( char *s )
{
	while(*s) 
		debug_ch(*s++);
}
/* End of debug */

/* Deal with console data */
void host_data( unsigned char d )
{
	U2STAbits.FERR = 0;
	U2STAbits.OERR = 0;

	// Check for change in tuning params 
/*	if (d=='q') p1++;
	if (d=='a' && p1 > 0) p1--;

	if (d=='w') p2++;
	if (d=='s' && p2 > 0) p2--;*/

	if (d=='a') obj[0].tx--;
	if (d=='d') obj[0].tx++;

	if (d=='w') obj[0].ty--;
	if (d=='s') obj[0].ty++;

	if (d=='q' && obj[0].tz > 1) obj[0].tz--;
	if (d=='e') obj[0].tz++;

	if (d==' ') refresh = 1;

	send = 1; // Send param data
}
/* End of host_data */

/******************** 
*** End of Serial ***
********************/


/****************** 
*** VGA Related ***
******************/
void vga_clear( void );


/* Setup the vga output */
void vga_init( void )
{
	// Setup timer for 1ms and enble interrupt
	T1CONbits.TCKPS = 0;

	PR1 = TIMER1_INTERVAL;
	T1CONbits.TON = 1;				// Enable timer
	IEC0bits.T1IE = 1;				// Enable interrupt

	T2CONbits.TON = 1;				// Enable timer

	SPI1STATbits.SPIEN = 1;				// Enable SPI port
	
	SPI1CONbits.SMP = 1;
	SPI1CONbits.SPRE = 0b111;
	SPI1CONbits.PPRE = 0b10;
	SPI1CONbits.MSTEN = 1;

	vga_clear();
}
/* End of vga_init */


/* Update a scan row */
inline void vga_row( void )
{
	hsync = 0;					// Start Hsync

	if (cur_row >= VGA_LINES-1)			// End of row
	{
		cur_row = 0;
		disp_row = 0;
		rc = 0;
	}

	if (cur_row == 11 || cur_row == 12)		// Vsync on these scanlines
		vsync = 0;		
	else	
		vsync = 1;

	hsync = 1;					// End Hsync

	cur_row++;

	if (cur_row <  45) return;			// Do not write RGB data for non display area

	unsigned int i = COL_BYTES;					// Column byte counter

	unsigned char *p = ((unsigned char *)&row_data[disp_row]);		

//	asm("nop \n nop \n nop \n nop \n nop \n");		// Delay for row start

	// Deal with row repeating
	if (rc >= VGA_ROWS / DISP_ROWS)
	{
		rc = 0;
		disp_row ++;
	} 
	else
	{
		asm("nop \n nop \n nop \n nop \n ");	
	}

	// Write the row to spi
	while(i--)
	{
		// Wait for previous SPI to finish 
		asm("nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n ");
		asm("nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop  \n ");

		SPI1BUF = *p++;				// Start SPI transfer
	}

	rc++;
}
/* End of vga_row */

/* Horz timter */
void __attribute__ ((__interrupt__, auto_psv)) _T1Interrupt ( void )
{
	IFS0bits.T1IF = 0;

	vga_row();
}
/* End of timer 1 interrupt */

/************************* 
*** End of VGA Related ***
*************************/


/**************************
*** High Level Graphics ***
**************************/

/* Return a random byte */
unsigned char rnd( void )
{
	static unsigned int s = 7347;
	
	s =  37513 * (unsigned long)s + 12291 + (TMR1 * 16);

	return (s >> 4);
}
/* End of rnd() */

/* Refresh the frame buffer */
void vga_rand( void )
{
	// Write the snow pattern 
	unsigned int i, j;

	for (i = 1; i < DISP_ROWS-1; i++)
	{
		for (j = 0; j < COL_BYTES; j++)
			row_data[i][j] = rnd();

		row_data[i][0] |= 0x80;
		row_data[i][COL_BYTES-1] |= 0x01;
	}

	for (i = 0; i < COL_BYTES; i++)
	{
		row_data[0][i] = 0xff;
		row_data[DISP_ROWS-1][i] = 0xff;
	}
}
/* End of vga_rand */

/* Clear the buffer */
void vga_clear( void )
{
	// Write the snow pattern 

	unsigned int i, j;

	for (i = 0; i < DISP_ROWS; i++)
	{
		for (j = 0; j < COL_BYTES; j++)
			row_data[i][j] = 0;

		//row_data[i][0] |= 0x80;
		//row_data[i][COL_BYTES-1] |= 0x01;
	}

	/*for (i = 0; i < COL_BYTES; i++)
	{
		row_data[0][i] = 0xff;
		row_data[DISP_ROWS-1][i] = 0xff;
	}*/
}
/* End of vga_clear */

/* Set a screen pixel */
inline void put_pixel( int x, int y )
{
//	if(x < 0||x>=DISP_COLS) return;
//	if(y < 0||y>=DISP_ROWS) return;

	row_data[y][x >> 3] |= 0x80 >> (x & 0x07);
}
/* End of put_pixel */

/* Set a screen pixel */
inline void clear_pixel( int x, int y )
{
//	if(x < 0||x>=DISP_COLS) return;
//	if(y < 0||y>=DISP_ROWS) return;

	row_data[y][x >> 3] &= ~(0x80 >> (x & 0x07));
}
/* End of clear_pixel */

/* Draw a line */
// Straight from wiki reference
void draw_line( int x0, int y0, int  x1, int  y1 )
{
	int steep;

	if (ABS(y1 - y0) > ABS(x1 - x0))
		steep = 1;
	else
		steep = 0;

	int tmp;

	if (steep)
	{
		tmp = x0; x0 = y0; y0 = tmp;
		tmp = x1; x1 = y1; y1 = tmp;
	}

	if (x0 > x1)
	{
		tmp = x0; x0 = x1; x1 = tmp;
		tmp = y0; y0 = y1; y1 = tmp;
	}

	int deltax = x1 - x0;
	int deltay = ABS(y1 - y0);
	int error = deltax / 2;
	int ystep;
	int y = y0;

	if (y0 < y1) 
		ystep = 1; 
	else 
		ystep = -1;

	int x;

	for (x = x0; x < x1; x++)
	{
         	if (steep)
		{
			if (draw_mode == MODE_DRAW) put_pixel(y, x); else
			if (draw_mode == MODE_ERASE) clear_pixel(y, x); 
		}
		else 
		{
			if (draw_mode == MODE_DRAW) put_pixel(x, y); else
			if (draw_mode == MODE_ERASE) clear_pixel(x, y); 
		}

         	error = error - deltay;

         	if (error < 0)
		{
	            	y = y + ystep;
             		error = error + deltax;
		}
	}
}
/* End of draw_line */

/* Draw a triangle */
void draw_triangle ( triangle_type t, point_type *p )
{
	if (draw_mode == MODE_DRAW)
	{

		// Backface culling
		if (((long)p[t.p0].sy * (p[t.p2].sx - p[t.p1].sx) + 
		     (long)p[t.p1].sy * (p[t.p0].sx - p[t.p2].sx) + 
		     (long)p[t.p2].sy * (p[t.p1].sx - p[t.p0].sx)) > 0) return;

		draw_line(p[t.p0].sx, p[t.p0].sy,   p[t.p1].sx,p[t.p1].sy);
		draw_line(p[t.p1].sx, p[t.p1].sy,   p[t.p2].sx,p[t.p2].sy);

	} else
	if (draw_mode == MODE_ERASE)
	{
		draw_line(p[t.p0].osx, p[t.p0].osy,   p[t.p1].osx,p[t.p1].osy);
		draw_line(p[t.p1].osx, p[t.p1].osy,   p[t.p2].osx,p[t.p2].osy);
	}
}
/* End of draw_triangle */

/* Draw all triangles */
void draw_triangles ( object_type *o )
{
	draw_mode = MODE_DRAW;

	unsigned int i;

	for (i = 0; i < o->num_triangles; i++)
		draw_triangle(o->triangles[i], o->points);
}
/* End of draw_triangles */

/* Draw all triangles */
void erase_triangles ( object_type *o )
{
	draw_mode = MODE_ERASE;

	unsigned int i;

	for (i = 0; i < o->num_triangles; i++)
		draw_triangle(o->triangles[i], o->points);
}
/* End of erase_triangles */

/* Rotation about origin */
inline void rotate( unsigned char a, int x1, int y1, int  *nx, int *ny )
{
	*nx = ((unsigned long)x1 * cos_data[a] - (unsigned long)y1 * sin_data[a])>> 14;        
	*ny = ((unsigned long)x1 * sin_data[a] + (unsigned long)y1 * cos_data[a])>> 14; 
}
/* End of rotate */

/* Process all points */
void process_points ( object_type *o )
{
	unsigned int i;

	point_type *p = o->points;

	for (i = 0; i < o->num_points; i++)
	{
		// Rotate
		rotate(o->ang_y, p->x,  p->z,  &p->rx, &p->rz);
		rotate(o->ang_x, p->rz, p->y,  &p->rz, &p->ry);
		rotate(o->ang_z, p->rx, p->ry, &p->rx, &p->ry);

		// Translate
		p->rx += o->tx;
		p->ry += o->ty;
		p->rz += o->tz;

		// Save old screen points
		p->osx = o->points[i].sx; 	
		p->osy = o->points[i].sy; 

		// Project
		p->sx = DISP_COLS/2 + ((long)p->rx << 8) / p->rz; 
		p->sy = DISP_ROWS/2 + ((long)p->ry << 8) / p->rz;

		*p++;
	}
}
/* End of process_points */

/* Update object data */
void update_object( object_type *o )
{
	o->ang_x += o->dx;
	o->ang_y++;
	o->ang_z++;
}
/* End of update object */

/* Render the buffer */
void draw_screen( void )
{
	unsigned int i;

	for (i = 0; i < num_objects; i++)
	{
		update_object(&obj[i]);

		process_points(&obj[i]);	// Rotate about center of object

		erase_triangles (&obj[i]);	// Erase Old triangles

		draw_triangles (&obj[i]);	// Draw triangles
	}
}
/* End of draw_screen */

/* Add a point to the point array */
void add_point( object_type *o, int px, int py, int pz )
{
	o->points[o->num_points].x = px;
	o->points[o->num_points].y = py;
	o->points[o->num_points].z = pz;

	o->num_points++;
}
/* End of add_point */

/* Add a triangle to the triangle array */
void add_triangle( object_type *o, int p0, int p1, int p2 )
{
	o->triangles[o->num_triangles].p0 = p0;
	o->triangles[o->num_triangles].p1 = p1;
	o->triangles[o->num_triangles].p2 = p2;

	o->num_triangles++;
}
/* End of add_triangle */

/* Add a cube */
void add_cube(int x, int y, int z)
{
	if (num_objects >= MAX_OBJECT) return;

	object_type *o = &obj[num_objects];

	o->tx = x;			// Position
	o->ty = y;
	o->tz = z;

	o->ang_x = 0;			// Rotation Angles
	o->ang_y = 0;
	o->ang_z = 0;

	o->dx = 1;

	o->num_points = 0;

	// Setup the points
	add_point(o, -10,  10, -10);
	add_point(o,  10,  10, -10);
	add_point(o, -10, -10, -10);
	add_point(o,  10, -10, -10);

	add_point(o, -10,  10,  10);
	add_point(o,  10,  10,  10);
	add_point(o, -10, -10,  10);
	add_point(o,  10, -10,  10);

	o->num_triangles = 0;

	add_triangle(o, 2, 0, 1);
	add_triangle(o, 1, 3, 2);

	add_triangle(o, 2, 6, 4);
	add_triangle(o, 4, 0, 2);

	add_triangle(o, 4, 6, 7);
	add_triangle(o, 7, 5, 4);

	add_triangle(o, 1, 5, 7);
	add_triangle(o, 7, 3, 1);

	add_triangle(o, 4, 5, 1);
	add_triangle(o, 1, 0, 4);

	add_triangle(o, 7, 6, 2);
	add_triangle(o, 2, 3, 7);

	num_objects++;
}
/* End of add_cube */


/* Setup the objects */
void add_objects( void )
{
	num_objects = 0;

	add_cube(-15, 0, 150);
	add_cube(20, 0, 150);

	if (num_objects > 0) obj[1].dx = -1;
}
/* End of vga_objects */



/********************************* 
*** End of high level graphics ***
*********************************/




/* Setup the device */
void device_init ( void )
{
	// Set port directions
	ADPCFG = 0xFFFF;	

	TRISA = 0;
	TRISB = 0;
	TRISC = 0;
	TRISD = 0;

	TRISF = 0b00011000;
}
/* End of device_init */

/* main */
int main ( void )
{
	device_init();
	serial_init();
	vga_init();
	vga_clear();

	add_objects();


	unsigned int c = 0;

	while(1)
	{
		TMR2 = 0;

	//	while(TMR2 < 0x1000);

	//	if(c++ > 50)
		{
			c = 0;

			draw_screen();
		}		


		//ang_z++;

		// Check for new byte 
		if (U2STAbits.URXDA) 
			host_data(U2RXREG & 0xff);

	//	while (!(U2STAbits.URXDA));
	//		unsigned char tmp = U2RXREG & 0xff;

		// Redraw screen		
	//	if (refresh)
		{
		//	refresh = 0;
		//	draw_screen();
		}

		// Send param info
		if (send)
		{
			send = 0;	
		
		//	char s[32];
		//	sprintf(s, "x %d y %d %c%c", x, y, 0x0d, 0x0a);
		//	debug(s);
		}
	}

	return(0);
}
/* End of main */



