/*==================================< prog.c=================================*/
// 
//    Title:      PROG MODULE
//    Saved date: 02 Jun 2003 11:31:47
//    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 prog module contains functions for handling the storage, status and
// execution of program (in the future 'programs') resident in Flash memory.
//
// These functions are used by the bootloader and kickstart programs, but are
// available for other application use also.
//
// Flash and SDRAM must be initialized before calling any functions in this
//	unit.  
//
// History: 
//	
// 1.00	11-10-02	JAG	First release
// 1.01	04-16-03 JAG	Fixed bug with 24 bit addresses
//
/*===========================================================================*/

#include <stddefs.h>
#include <regs.h>
#include <prog.h>
#include <string.h>
#include <flash.h>
#include <uart1.h>   

/*----------------------------------< Defines >-----------------------------*/

#define MAX_S19_LINE    100      // Maximum number of characters in a S19 line
#define BUFFER_SIZE		0x100000	// 256K Words = 1M Bytes


/*-------------------------< Local Function Prototypes >---------------------*/

BYTE ProgLoadS19Line(BYTE bSpace, PBYTE pDat);

/*-----------------------------< Global Variables >--------------------------*/

// SDRAM Buffer 
BYTE bBuff[BUFFER_SIZE]  __attribute__ ((section (".sdram"))) = {0};			
CHAR sLine[100];
CHAR sString[30];


/*==============================< Global Functions >=========================*/

/*---------------------------------< ProgInit >------------------------------*/
void ProgInit(void) 
{
	WORD i;

	// FF out entire buffer
	for (i=0; i < BUFFER_SIZE; i++)
		bBuff[i] = 0xff;

}

/*-------------------------------< ProgMainValid >---------------------------*/
BYTE ProgMainValid(void)
{
	// Short cct logic for now
	return TRUE;
}

/*----------------------------< ProgSetMainValid >---------------------------*/
void ProgSetMainValid(void)
{
	FlashWriteHalf(PROG_STATUSADD, PROG_MAINVALID);
}

/*--------------------------< ProgRequestDownload >--------------------------*/
BYTE ProgRequestDownload(void)
{
	// Initialise Port A bit 0 as input
	PORTA_DDR &= 0xfffffffe;
	SSPMUX &= 0xfffffffb;
	// Check for presence of "download request" jumper/switch
	if (PORTA_DATA & 0x0001)
		return FALSE;
	else
		return TRUE;
}

/*----------------------------< ProgLoadIntoRAM >---------------------------*/
BYTE ProgLoadIntoRAM(BYTE bSpace)
{
	BYTE bStatus = PROG_OK;
	
	while (!UartKey());
	
	while (bStatus == PROG_OK)
	{
		// Read in a line
		UartGet(sLine, MAX_S19_LINE);
		//UartSend(sLine);
		bStatus = ProgLoadS19Line(bSpace, sLine);
	}
	
	switch (bStatus) 
	{
		case PROG_COMPLETE:
			break;
		case PROG_NOT_ERASED: 
			UartSend("Required Flash sectors not erased\r\n");
			break;
		case PROG_BAD_CSUM:
			UartSend("S19 checksum error\r\n");
			UartSend(sLine);
			break;
		case PROG_S19_ERROR:
			UartSend("Error decoding S19 data\r\n");
			UartSend(sLine);
			break;
		case PROG_ADDRESS_BAD:
			UartSend("Address was outside limits\r\n");
			UartSend(sLine);
			break;
		case PROG_ADDR_ALLIGN:
			UartSend("Can't write to non-alligned addresses\r\n");
			UartSend(sLine);
			break;
		default:
			UartSend("Unknown Problem with download\r\n");
	} 
	UartSend("\r\n");
	if (bStatus == PROG_COMPLETE)
		return TRUE;
	else
		return FALSE;
}  

/*------------------------------< ProgIntoFlash >-----------------------------*/
BYTE ProgIntoFlash(BYTE bSpace)
{
	HALF hDat;
	WORD wFlashAddr, wEndAddr, wAddr;
	
	FlashUnlockAll();
	ProgEraseFlash(bSpace);	
	wAddr=0;
	
	if (bSpace == PROG_BOOTSPACE)
	{
		wFlashAddr = PROG_BOOTSTARTADD + FLASH_BASEADD;
		wEndAddr = PROG_BOOTENDADD + FLASH_BASEADD;
		
	}
	else
	{
		wFlashAddr = PROG_MAINSTARTADD + FLASH_BASEADD;
		wEndAddr = 0x80000 + FLASH_BASEADD;
	}
		
	// Transfer buffer from SDRAM into Flash 
	while (wFlashAddr <= wEndAddr)
	{
		hDat = (HALF)bBuff[wAddr] + (256 * (HALF)(bBuff[wAddr+1]));
		if (!(hDat == 0xffff))
		{
			FlashWriteHalf(wFlashAddr, hDat);
		}
		wFlashAddr+=2;
		wAddr+=2;
	}
		
	FlashLockAll();

	return TRUE;
}

/*-----------------------------< ProgEraseFlash >-----------------------------*/
void ProgEraseFlash(BYTE bSpace)
{
	if (bSpace == PROG_BOOTSPACE)
		FlashBlockErase(0, 1);
	else
		FlashBlockErase(8, 22);
}

/*-----------------------------< ProgVerifyFlash >----------------------------*/
BYTE ProgVerifyFlash(BYTE bSpace)
{
	HALF hDat;
	WORD wFlashAddr, wEndAddr, wAddr;
	
	wAddr=0;

	if (bSpace == PROG_BOOTSPACE)
	{
		wFlashAddr = PROG_BOOTSTARTADD + FLASH_BASEADD;
		wEndAddr = PROG_BOOTENDADD + FLASH_BASEADD;
	}
	else
	{
		wFlashAddr = PROG_MAINSTARTADD + FLASH_BASEADD;
		//wEndAddr = PROG_MAINENDADD + FLASH_BASEADD;
		wEndAddr = 0x80000 + FLASH_BASEADD;
	}

	// Compare SDRAM buffer to Flash
	while (wFlashAddr < wEndAddr)
	{
		hDat = (HALF)bBuff[wAddr] + (256 * (HALF)(bBuff[wAddr+1]));
		if (hDat != *((PHALF)wFlashAddr))
		{
			UartSend("\r\nError at Address :");
			SprintHex(sString, (WORD)(wFlashAddr - FLASH_BASEADD), 6);
   		UartSend(sString);
   		UartSend(" Got ");
   		SprintHex(sString, (WORD)*((PHALF)wFlashAddr), 6);
   		UartSend(sString);
   		UartSend(" Expected ");
   		SprintHex(sString, (WORD)hDat, 6);
   		UartSend(sString);
   		UartSend("\r\n");
			return FALSE;
		}
		wAddr+=2;
		wFlashAddr+=2;
	}
	return TRUE;
}

/*-----------------------------< ProgCopyImage >------------------------------*/
void ProgCopyImage(void)
{
	// Copy image from Flash into SDRAM
	PWORD	pFlash, pRam;

	pFlash = (PWORD)(PROG_MAINSTARTADD + FLASH_BASEADD);
	pRam = (PWORD)PROG_RAMSTART;

	// Transfer ProgMain data from Flash to SDRAM
	while (pFlash < (PWORD)(PROG_MAINENDADD+FLASH_BASEADD))
	{
		*pRam = *pFlash;
		pRam++;
		pFlash++;
	}
}

/*-----------------------------< ProgExecuteMain >----------------------------*/
void ProgExecuteMain(void)
{
	// Remap SDRAM to 0000:0000h and jump to start of code
	RCPCREMAP = 1;		

	__asm__ ("MOV   pc, #0 ");			// 32 bit jump from anywhere

	while (1);	 // should not return
}


/*=============================< LOCAL FUNCTIONS >============================*/

/*-----------------------------< ProgLoadS19Line >----------------------------*/
BYTE ProgLoadS19Line(BYTE bSpace, PBYTE pDat)
{
	BYTE bI, bType, bAddrSize, bCSum, bBytes;
   PBYTE pCSDat;
   WORD wAddr;

	bI=0;
 	while ((*pDat != 'S') && (bI++ < MAX_S19_LINE))   // find 'S'
 		pDat++;
   
   if (bI >= MAX_S19_LINE)
      return PROG_S19_ERROR;     // no 'S' found
   
   pDat++;
   bType = *pDat++;              // get record type
   
   pCSDat = pDat;
   bBytes = AsciiToInt(2,pDat);  // get record length (bytes - including checksum)
   pDat+=2;
   
   switch (bType)
   {
      case '0':            // Header (ignore)
            return PROG_OK;
            break;
            
      case '1':            // 2 Byte Address
      		wAddr = AsciiToInt(4,pDat);
            bAddrSize = 4;
            break;
      case '2':            // 3 Byte Address
      		wAddr = AsciiToInt(6,pDat);
      		bAddrSize = 6;
      		break;
      case '3':            // 4 Byte Address
            wAddr = AsciiToInt(8,pDat);
            bAddrSize = 8;
            break;

      case '7':           // End Of File (get entry point)
      case '8':
      case '9':
          return PROG_COMPLETE;
   }
   
  // if (wAddr & 0x03)   // non-alligned addresses are invalid
  //    return PROG_ADDR_ALLIGN;
      
   pDat += bAddrSize;
   
   // Check Checksum
   bCSum=0;
   for (bI=0; bI < bBytes; bI++)
   {
      bCSum += (BYTE)AsciiToInt(2, pCSDat);
      pCSDat+=2;
      //SprintHex(sString, (WORD)bCSum, 2);
   	//UartSend(sString);
   }
   bI = ((BYTE)AsciiToInt(2, pCSDat)) ^ 0xff;
   
   if (bCSum != bI)
   {
   	UartSend("Got =");
   	SprintHex(sString, (WORD)bCSum, 2);
   	UartSend(sString);
   	UartSend("Exp =");
   	SprintHex(sString, (WORD)bI, 2);
   	UartSend(sString);
   	UartGetKey();
      return PROG_BAD_CSUM;
   }

   // Test display address
   SprintHex(sString, (WORD)wAddr, 6);
   UartSend(sString);
   UartSend("\r");
    
   // Store data field to SDRAM
   bBytes -= (bAddrSize / 2);    // address length
   bBytes --;                    // csum length
   for (bI=0; bI < bBytes; bI++)
   { 
      // Check Address bounds
      if (bSpace == PROG_BOOTSPACE)
      {
      	if ((wAddr < PROG_BOOTSTARTADD) || (wAddr > PROG_BOOTENDADD))
         	return PROG_ADDRESS_BAD;
      }
      else
      {
      	// must be going into main program space
      	if (wAddr > PROG_MAINSIZE)
         	return PROG_ADDRESS_BAD;
      }
         
      // Now convert and store (May need to correct endianess)
      bBuff[wAddr] = (BYTE)AsciiToInt(2, pDat);
      pDat+=2; 
      wAddr++;               
   }
   return PROG_OK;
}

// end of prog.c
