/* Monochrome NTSC composite video output for TI's Launchpad.
 * 
 * Schmatic:
 *                    470
 * P1.2 SYNC --------/\/\/\--------|
 *                                 |----- RCA jack Signal
 *                    220          |
 * P1.6 VOUT --------/\/\/\--------|
 * 
 * Launchpad GND ------------------------ RCA jack GND
 * 
 * 
 * By: NatureTM
 * more info on naturetm.com
 */ 

#include <msp430g2452.h>


#define TACCR0				TA0CCR0
#define TACCR1				TA0CCR1
#define TACCTL0				TA0CCTL0
#define TACCTL1				TA0CCTL1
#define TIMERA0_VECTOR		TIMER0_A0_VECTOR
#define TIMERA1_VECTOR		TIMER0_A1_VECTOR

// Image file
//#include "miss_nature_close.h"
#include "miss_nature_wide.h"

#define SYNC				BIT2
#define VOUT				BIT6


//#define NS_TICK				62.5	// NS = nanoseconds
#define TICKS_HSYNC			72
#define TICKS_SHORT_SYNC	38
#define TICKS_HBLANK		163
#define TICKS_SCANLINE		1017
#define TICKS_HALF_SCANLINE	508
#define TICKS_RIGHT_EDGE	973

#define ROW_HEIGHT			1		// in scanlines
#define WORDS_SCANLINE		12		

unsigned int* currentRowPtr;
unsigned int imageOffset;
unsigned int scanline = 0;
char imageLine;
char subRowCounter;
unsigned int sleep;



int main(){	
	DCOCTL = CALDCO_16MHZ;
	BCSCTL1 = CALBC1_16MHZ;
	
	WDTCTL = WDTPW + WDTHOLD;					// Stop WDT
	_BIS_SR(GIE);								//enable interrupts	
	
	USICKCTL = USIDIV1 + USISSEL1 + USISSEL0;// + USICKPL;	// USI Clock = SMCLK / 4
	USICNT = USI16B;							// 16 bit shift out
	USICTL0 |= USIMST;							// Use internal clock source
	USICTL0 &= ~USISWRST;						// Clear USI reset bit (enable USI)
	USICTL0 |= USILSB;							// USISR output LSB first
	USICTL0 |= USIGE;
	
	TACTL |= MC_0;								// Stop timer A
	TACTL |= TASSEL1;							// TASrc = SMClk = MClk
	TACTL &= ~TASSEL0;							
	TACTL |= TACLR;								// Clear the timer
	CCR0 = TICKS_SCANLINE;						// Scanline timing - CCR0 triggers hSync
	CCR1 = TICKS_HSYNC;							// End of hSync
	TACTL |= MC_1;								// Up mode, Start 
	TACCTL0 |= CCIE;							// Enable Timer A CCR0 interrupt
	TACCTL1 |= OUTMOD_3;						// TimerA reset sync pin on CCR0
	TACCTL1 |= CCIE;							// Enable Timer A CCR1 interrupt
	P1SEL = SYNC;								// Allow timerA to set sync pin low on beginning of scanline
	
	P1DIR = SYNC + VOUT + BIT5;		
}


// vsync broad sync pulse section
void vSyncTripleBroad(){
	int count;
	for(count = 0; count < 3; count++){
		while(TAR < TICKS_HALF_SCANLINE - TICKS_HSYNC){}
		P1OUT = SYNC;
		while(TAR < TICKS_HALF_SCANLINE){}
		P1OUT = 0;
		while(TAR < TICKS_SCANLINE - TICKS_HSYNC){}
		P1OUT = SYNC;
		while(TAR > TICKS_SCANLINE - TICKS_HSYNC){}
		P1OUT = 0;
	}
}

// vsync short sync pulse section
void vSyncTripleShort(){
	int count;
	for (count = 0; count < 3; count++){
		while(TAR < TICKS_SHORT_SYNC){}
		P1OUT = SYNC;
		while(TAR < TICKS_HALF_SCANLINE){}
		P1OUT = 0;
		while(TAR < TICKS_HALF_SCANLINE + TICKS_SHORT_SYNC){}
		P1OUT = SYNC;
		while(TAR > TICKS_HALF_SCANLINE + TICKS_SHORT_SYNC){}
		P1OUT = 0;
	}
}

// This ISR is run at the beginning of every scanline and keeps track of which
// scanline we are on.  At the beginning of a frame, it handles vSync output in
// software.  Next, it hands over control of the sync pin to TimerA, allowing
// hsync timing to be done in hardware.
#pragma vector=TIMERA0_VECTOR
__interrupt void Timer_A0 (void){
	
	// software vSync
	if(scanline == 0){
		P1OUT = 0;
		P1SEL = 0;					// let software control P1.5 instead of TACCR0
		vSyncTripleShort();
		vSyncTripleBroad();
		vSyncTripleShort();
		scanline = 8;				// vSync takes several scanlines
		P1SEL = SYNC;				// let TimerA handle hSync
	}
	
	// visible area starts/vBlank over
	else if(scanline == 21){
		scanline++;
		TACCTL1 |= CCIE;							// Start drawing picture
		subRowCounter = 0;
		imageOffset = 0;
	}
	
	else if(scanline < 263){
		scanline++;
	}
	
	// bottom of screen, get ready for vSync
	else{
		TACCTL1 &= ~CCIE;							// Stop drawing picture
		scanline = 0;
	}
}

// After vSync, this ISR begins USI output of the image.  
#pragma vector=TIMERA1_VECTOR
__interrupt void Timer_A1 (void){
	int wordCounter = 0;
	TAIV = 0;				// Clear TimerA's interrupt vector register;
	
	USICTL0 |= USIOE;		// USI output enabled
	USICTL0 |= USIPE6;		// Port 1.6 USI data out
	
	while (TAR < TICKS_HBLANK){}
	do{
		USICNT |= 16;
		USISR = currentRowPtr[wordCounter];
		wordCounter++;

		// software delay allowing full USI shift out
		sleep = 0;
		while(sleep < 2)
			sleep++;
		_nop();
		_nop();
		_nop();
		_nop();
		_nop();
		_nop();
		_nop();
	}while(wordCounter < WORDS_SCANLINE);
	
	if(subRowCounter == ROW_HEIGHT){
		subRowCounter = 0;
		imageOffset += WORDS_SCANLINE;
	}
	subRowCounter++;
	currentRowPtr = (unsigned int*)image + imageOffset;
	
	while(TAR < TICKS_RIGHT_EDGE){}	// Wait for edge of screen
	USICTL0 &= ~(USIPE6 + USIOE);	// Release control of video pin to software
}



