/*
** NTSC Video 
** using: 
**      T3      Interrupt for main timing
**      OC3     Horizontal Synchronization pulse
**      OC4     SPI buffer reload timing
**      SPI1    pixel serialization (8x16-bit FIFO)
**
** Graphic page 
**
** LDJ rev 2.0 - 06/06/06
**     rev 2.1 - 01/07/08 disabled sck
**     rev 3.0 - 04/28/08 OC3 in PWM mode hw optimization
*/

#include <p24fj128ga010.h>
#include <graphic.h>

#define _NOPSV __attribute__((no_auto_psv))
// I/O definitions
//#define SYNC    _LATG0  // output replaced directly by OC3 
//#define SDO     _RF8    // SPI1 SDO 

// timing definitions for NTSC video vertical state machine
#define V_NTSC   262    // total number of lines composing a frame
#define VSYNC_N  3      // V sync lines

// count the number of remaining black lines top+bottom
#define VBLANK_N    (V_NTSC -VRES - VSYNC_N)  

#define PREEQ_N   VBLANK_N /2          // pre equalization + bottom blank lines
#define POSTEQ_N  VBLANK_N - PREEQ_N   // post equalization + top blank lines

// definition of the vertical sync state machine
#define SV_PREEQ    0
#define SV_SYNC     1
#define SV_POSTEQ   2
#define SV_LINE     3

// timing definitions for NTSC video horizontal state machine
#define LINE_T  1016    // total number of Tcy in a line (63.5us)
#define HSYNC_T  90     // Tcy in a horizontal synch pulse 
#define BPORCH_T 90     // Tcy in a back porch 
#define PIX_T    3      // Tcy in each pixel, valid values are only 2 or 3

#define _FAR __attribute__(( far))

int _FAR VMap[VRES * (HRES/16)];

volatile int *VPtr;
volatile int HCount, VCount, VState, HState;

// next state table
int VS[4] = { SV_SYNC, SV_POSTEQ, SV_LINE, SV_PREEQ};
// next counter table
int VC[4] = { VSYNC_N,  POSTEQ_N,    VRES,  PREEQ_N};


void _ISRFAST _NOPSV _T3Interrupt( void)
{
    _RA0=1;
    // advance the state machine
    if ( --VCount == 0)
    {
        VCount = VC[ VState];
        VState = VS[ VState];
     }
    
    // vertical state machine
    switch ( VState) {
        case SV_PREEQ:  // 0
            // prepare for the new frame
            VPtr = VMap;
            break;
            
        case SV_SYNC:   // 1
            // vertical sync pulse
            OC3R = LINE_T - HSYNC_T - BPORCH_T;
            break;
            
        case SV_POSTEQ: // 2
            // horizontal sync pulse
            OC3R = HSYNC_T;
            break;
            
        default:            
        case SV_LINE:   // 3
            // activate OC4 for the SPI loading
            OC4R = HSYNC_T + BPORCH_T;
            OC4CON = 0x0009;    // single event 
            HCount = HRES/128;  // loads 8x16 bits at a time 
            break;                                               
    } //switch

    // clear the interrupt flag
    _T3IF = 0;
    _RA0=0;
} // T3Interrupt


void _ISRFAST _NOPSV _OC4Interrupt( void) 
{    
    _RA0=1;
    // load SPI FIFO with 8 x 16-bit words = 128 pixels
    SPI1BUF = *VPtr++; 
    SPI1BUF = *VPtr++; 
    SPI1BUF = *VPtr++; 
    SPI1BUF = *VPtr++; 
    SPI1BUF = *VPtr++; 
    SPI1BUF = *VPtr++; 
    SPI1BUF = *VPtr++; 
    SPI1BUF = *VPtr++; 
                            
    if ( --HCount > 0)
   	{   // activate again in time for the next SPI load
            OC4R += ( PIX_T * 7 * 16); 
            OC4CON = 0x0009;    // single event                 
	}
	        
	// clear the interrupt flag
	_OC4IF = 0;
_RA0=0;
} // OC4Interrupt


void initVideo( void)
{
    TRISA=0xfffe;  
    // set the priority levels
	_T3IP = 4; 		// this is the default value anyway
//	_OC3IP = 4;
    _OC4IP = 4;

	TMR3 = 0;	    // clear the timer
	PR3 = LINE_T;   // set the period register to NTSC line 
	
	// 2.1 configure Timer3 modules
	T3CON = 0x8000;	// enabled, prescaler 1:1, internal clock 
	
	// 2.2 configure OC3 in continuous pulse mode
	OC3R = HSYNC_T;
	OC3RS = 0;
	OC3CON = 0x000d;	
	
	// 2.3 init Timer3/OC3/OC4 Interrupts, clear the flag
//	_OC3IF = 0;	_OC3IE = 1;
    _OC4IF = 0; _OC4IE = 1;
    _T3IF = 0;  _T3IE = 1;
    	
	// 2.4 init the processor priority level
	_IPL = 0;	// this is the default value anyway

    // 2.5 init the SPI1 
    if ( PIX_T == 2)
        SPI1CON1 = 0x143B;      // Master, 16 bit, disable SCK, disable SS, prescaler 1:3
    else
        SPI1CON1 = 0x1437;      // Master, 16 bit, disable SCK, disable SS, prescaler 1:2
    SPI1CON2 = 0x0001;      // Enhanced mode, 8x FIFO
    SPI1STAT = 0x8000;      // enable SPI port
    
    // init PORTF for the Sync
//    _TRISG0 = 0;            // output the SYNC pin
    	
	// 2.6 init the vertical sync state machine
	VState = SV_PREEQ;
    VCount = PREEQ_N;
    
} // initVideo


void clearScreen( void)
{
    int i;
    int *v;
    
    v = (int *)&VMap[0];
    
    // clear the screen     
    for ( i=0; i < (VRES*( HRES/16)); i++)
        *v++ = 0;
} //clearScreen


void haltVideo( void)
{
    T3CONbits.TON = 0;   // turn off the vertical state machine
} //haltVideo

void synchV( void) 
{
    while ( VCount != 1);
} // synchV


void plot( unsigned x, unsigned y) 
{
     if ((x<HRES) && (y<VRES) )
        VMap[ ((VRES-1-y)<<4) + (x>>4)] |= (0x8000 >> (x & 0xf));
} // plot

#define abs( a)     (((a)> 0) ? (a) : -(a))

void line( int x0, int y0, int x1, int y1)
{
     int steep, t ;
     int deltax, deltay, error;
     int x, y;
     int ystep;

     steep = ( abs(y1 - y0) > abs(x1 - x0));
     
     if ( steep )
     { // swap x and y
         t = x0; x0 = y0; y0 = t;
         t = x1; x1 = y1; y1 = t;
     }
     if (x0 > x1) 
     {  // swap ends
         t = x0; x0 = x1; x1 = t;
         t = y0; y0 = y1; y1 = t;
     }
     
     deltax = x1 - x0;
     deltay = abs(y1 - y0);
     error = 0;
     y = y0;
     
     if (y0 < y1) ystep = 1; else ystep = -1;
     for (x = x0; x < x1; x++)
     {
         if ( steep) plot(y,x); else plot(x,y);
         error += deltay;
         if ( (error<<1) >= deltax)
         {
             y += ystep;
             error -= deltax;
         } // if 
     } // for
} // line 

