/*
** PAL Video using T3 and Output Compare interrupts
**
** Graphic page 
**
** LDJ rev 2.1 - 07/14/07
*/

#include <p24fj128ga010.h>
#include "Graphic.h"

// I/O definitions
#define SYNC    _LATG0  // output 
#define SDO     _RF8    // SPI1 SDO

// timing definitions for NTSC video vertical state machine
#define V_PAL   312    // 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_PAL -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 H_PAL  1024    // 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 _T3Interrupt( void)
{
    // Start a Synch pulse
    SYNC = 0;

    // decrement the vertical counter
    VCount--;
    
    // vertical state machine
    switch ( VState) {
        case SV_PREEQ:
           // horizontal sync pulse
            OC3R = HSYNC_T;
            OC3CON = 0x0009;    // single event 
            break;
            
        case SV_SYNC:
            // vertical sync pulse
            OC3R = H_PAL - HSYNC_T;
            OC3CON = 0x0009;    // single event 
            break;
            
        case SV_POSTEQ:
            // horizontal sync pulse
            OC3R = HSYNC_T;
            OC3CON = 0x0009;    // single event 
            // on the last posteq prepare for the new frame
            if ( VCount == 0)
            {   
                VPtr = VMap;
            }
            break;
            
        default:            
        case SV_LINE:
            // horizontal sync pulse
            OC3R = HSYNC_T;
            OC3CON = 0x0009;    // single event 

            // 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

    // advance the state machine
    if ( VCount == 0)
    {
        VCount = VC[ VState];
        VState = VS[ VState];
     }

    // clear the interrupt flag
    _T3IF = 0;
} // T3Interrupt



void _ISRFAST _OC3Interrupt( void)
{
    SYNC = 1;   // bring the output up to the black level
    _OC3IF = 0; // clear the interrupt flag

} // OC3Interrupt

void _ISRFAST _OC4Interrupt( void) 
{    
    // 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;

} // OC4Interrupt


void initVideo( void)
{
    // set the priority levels
	_T3IP = 4; 		// this is the default value anyway
	_OC3IP = 4;
    _OC4IP = 4;

	TMR3 = 0;		// clear the timer
	PR3 = H_PAL;	// set the period register to PAL line 
	
	// 2.1 configure Timer3 modules
	T3CON = 0x8000;	// enabled, prescaler 1:1, internal clock 	
	
	// 2.2 init Timer3/OC3/OC4 Interrupts, clear the flag
	_OC3IF = 0;	_OC3IE = 1;
    _OC4IF = 0; _OC4IE = 1;
    _T3IF = 0;  _T3IE = 1;
    	
	// 2.3 init the processor priority level
	_IPL = 0;	// this is the default value anyway

    // init the SPI1 
    if ( PIX_T == 2)
        SPI1CON1 = 0x043B;      // Master, 16 bit, disable SCK, disable SS, prescaler 1:3
    else
        SPI1CON1 = 0x0437;      // 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
    	
	// 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 

