/*****************************************************************************
* File:    bootloader_lib.c
*
* Purpose: Provide utilities for MC56F801x serial bootloader application.
* Dept:    Freescale TSPG MCD DSC Team (Tempe, AZ)
* Email:   support@freescale.com
* Date:    2005.08.15
******************************************************************************
* Notes:
*
*****************************************************************************/

/* Including used modules for compiling procedure */
#include "Cpu.h"
#include "Events.h"
/* Including shared modules, which are used for whole project */
#include "Types.h"

/* Include custom modules */
#include "bootloader_lib.h"

#define SEE_INDICATOR

#define IsInFlash(addr)		(((addr >= PP_FLASH_START) && (addr <= PP_FLASH_END)) || ((addr >= PD_FLASH_START) && (addr <= PD_FLASH_END)) \
							|| ((addr >= (DP_FLASH_START | DATA_MEM_ADDRESS_MASK)) && (addr <= (DP_FLASH_END | DATA_MEM_ADDRESS_MASK))) \
							|| ((addr >= (DD_FLASH_START | DATA_MEM_ADDRESS_MASK)) && (addr <= (DD_FLASH_END | DATA_MEM_ADDRESS_MASK))))


// Global Variables
UInt16 bytesum;
UInt16 status;
char *data_in = 0;
char *data_out;
char hex_string[5];
char rx_data[RX_DATA_SIZE];
UInt16 srec_data[MAX_SREC_WORDS];
UInt16 fisr_cntr;


// Function Prototypes
char get_char(char **ptr);
char int_to_char(UInt16 i);
char *int_to_string(UInt16 i);
void fisr_sci_rx_full(void);
void mem_init(register char *mem, register char init, register UInt16 sz);
void sci_tx(char *msg);
void sci_tx_char(char msg);
void srec_decode(void);
UInt16 char_to_int(char c);
UInt16 get_byte(char **ptr);
UInt16 hfm_command(UInt16 cmd, UInt16 *addr, UInt16 *data, UInt16 cnt);


#ifdef	DYNAMIC_BOOT
#pragma section pram_code begin
#endif


/*****************************************************************************
* UInt16 char_to_int(char c)
*
* Convert ASCII hexadecimal character into integer.
*
* In:  c - Character to convert
*
* Out: Returns integer representation of ASCII hexadecimal character.
*****************************************************************************/
UInt16 char_to_int(char c)
{
   UInt16 i = (UInt16) c;

   if ((c >= '1') && (c <= '9'))
      i -= '0';
   else if ((c >= 'A') && (c <= 'F'))
      i -= ('A' - 0xA);
   else
      i = 0;

   return (i);
}




#ifndef	DYNAMIC_BOOT
#pragma section pram_code begin
#endif
/////////////////////////////////////////////////////////////////////////////
// A S S E M B L Y   R O U T I N E S
/////////////////////////////////////////////////////////////////////////////

/*****************************************************************************
* void fisr_sci_rx(void)
*
* Fast Interrupt Service Routine for SCI Receive Data Full Interrupts
*
* In:  n/a
*
* Out: n/a
*****************************************************************************/
asm void fisr_sci_rx_full(void)
{
        move.w  x:(r1),y1           ;// SCISR must be read to clear RDRF flag
        move.w  x:(QSCI_DATA),y0    ;// Read SCIDR to get RX data
        bftstl  #0x0f00,y1          ;// Check SCISR for RX errors
        bcs     NO_RX_ERRORS        ;// Skip next 2 lines if no RX errors
        move.w  y1,x:(r1)           ;// Clear SCI RX error flags (OR,NF,PF,FE)
        bfset   #ERR_SCI_RX,status  ;// Track errors in "status"
        bra     EXIT_FISR           ;// Exit if RX errors detected
NO_RX_ERRORS:
        move.bp y0,x:(r0)+          ;// Write SCI RX data to circ buffer
        move.w  r0,data_in          ;// Put circ buffer index in "data_in"
EXIT_FISR:
        frtid                       ;// Delayed return from FISR
        nop                         ;// Stall
        nop                         ;// Stall
}

#ifndef	DYNAMIC_BOOT
#pragma section pram_code end
#endif

/*****************************************************************************
* char int_to_char(UInt16 i)
* 
* Get ASCII character representation of a 4-bit integer value.
*
* In:  i - Integer to convert (4-bit)
* 
* Out: Returns ASCII value used to display 4-bit integer.
*
* Note: This function only uses i[3:0] in translation!
*****************************************************************************/
char int_to_char(UInt16 i)
{
   char j;

   i &= 0x000F;
   j = (char) ((i < 10) ? (i + '0'): (i - 10 + 'A'));

   return (j);
}


/*****************************************************************************
* char *int_to_string (UInt16 i)
*
* Convert integer into 4-character ASCII Hex format.
*
* Input:  i - Integer to convert.
*
* Output: Returns string containing converted value.
*****************************************************************************/
char *int_to_string (UInt16 i)
{
   char *s = hex_string;
   int j;

   for (j=12; j>=0; j-=4)
      *s++ = int_to_char(0xF & (i >> j));
   *s++ = '\0';
   
   return (hex_string);
}


/*****************************************************************************
* void sci_tx(char *msg)
*
* Transmit string characters until NULL character is reached.
*
* In:  *msg - Transmit Buffer
*
* Out: n/a
*****************************************************************************/
void sci_tx(char *msg)
{
   do {
 
	 while(!((*(UInt16*)QSCI_STAT) & QSCI0_STAT_TDRE)){		 
	 }
	 *((UInt16*)QSCI_DATA) = *msg++;

   } while (*msg);
}


/*****************************************************************************
* void sci_tx_char(char msg)
*
* Transmit single character over SCI port.
*
* In:  msg - Transmit Character
*
* Out: n/a
*****************************************************************************/
asm void sci_tx_char(register char msg)
{
WAIT_TDRE:
        brclr   #QSCI0_STAT_TDRE,x:>QSCI_STAT,>WAIT_TDRE
        move.w  msg,x:>QSCI_DATA
        rts
}


#pragma optimization_level  4
/*****************************************************************************
* void srec_decode(void)
*
* Parse S-record data contained in buffer.
*
* In:  n/a
*
* Out: n/a
*****************************************************************************/
void srec_decode(void)
{
   char type;
   int i,j;
   UInt16 bytes, data, checksum;
   UInt32 addr;
   UInt16 wordsCount;
   
   // Process S-record "type", "byte count", and "address" fields
   get_char(&data_out);
   type = get_char(&data_out);
   bytesum = 0;
   bytes = get_byte(&data_out);
   wordsCount = (bytes-5)>>1;
   for (addr=0,i=4; i>0; i--) {
      addr <<= 8;
      addr += (UInt32)get_byte(&data_out);
   }

   // Process S-record "data" field (content depends on "type" field)
   if (type == '0') {

      for (i=((int)bytes)-4-1; i>0; i--) {
         sci_tx_char(get_byte(&data_out));
      }
                
     status |= RX_HEADER;
#ifndef	DYNAMIC_BOOT
#if	!(defined(MC56F8454x) || defined(MC56F8444x))
     // WARN: flash will be secured after erase block
     // need re-program security at 0x206 
     // mass erase p-flash/primary flash
     if(NO_PFLASH_PAGES_ERASE>0)
     {
   	  hfm_command(FTFL_ERASE_BLOCK,(UInt16*)PFLASH_ADDR_MASS_ERASE,0,0);
     }
#else     
      // sector by sector erase for p-flash
      for (i=0; i<NO_PFLASH_PAGES_ERASE;i++)
      {
      	hfm_command(FTFL_ERASE_SECTOR, (UInt16*)(i*FLASHP_PAGE_SIZE+PFLASH_START_ADDR), 0, 0);
      	if(status & 0x0FFF)
      	{
      		break;
      	}
      } 
#endif      
      // sector by sector erase for d-flash
      for (i=0; i<NO_DFLASH_PAGES_ERASE;i++)
      {
      	hfm_command(FTFL_ERASE_SECTOR, (UInt16*)(i*FLASHD_PAGE_SIZE+DFLASH_START_ADDR), 0, 0);
      	if(status & 0x0FFF)
      	{
      		break;
      	}      	
      }      
#else
      // WARN: flash will be secured after erase block
      // need re-program security at 0x206 
      // mass erase p-flash/primary flash
      if(NO_PFLASH_PAGES_ERASE>0)
      {
    	  hfm_command(FTFL_ERASE_BLOCK,(UInt16*)PFLASH_ADDR_MASS_ERASE,0,0);
      }
      
      // mass erase d-flash/secondary flash
      if(NO_DFLASH_PAGES_ERASE>0)
      {
    	  hfm_command(FTFL_ERASE_BLOCK,(UInt16*)DFLASH_ADDR_MASS_ERASE,0,0);	 
      }
#endif
   }
   else if (type == '3') 
   {
	  // NOTE: the following statement may miss the last byte!
	  // ASSUME: CW will always pad the bytes in s-record to words
      for (i=((int)bytes)-4-1,j=0; i>0; i-=2) 
      {
         data = get_byte(&data_out);
         data += (i>1) ? (get_byte(&data_out) << 8): 0xFF00u;

         // store the data into srec_data
         srec_data[j++] = data;

        if (status & DOWNLOAD_ERROR) break;
      }
      if(IsInFlash(addr))
      {
		  // Now program srec data in words
		  fprogram(addr,&srec_data[0],wordsCount);
      }
	  //addr += wordsCount;

   }
   else if (type == '7') 
   { 
   
  	status |= RX_END_OF_FILE; 
 	fprogram(BOOT_START_ADDRESS_PLACE, &addr, 2);	// save new application start address read from S7 record into 
  								// BOOT_START_ADDRESS_PLACE
  }
   else  { status |= ERR_SREC_TYPE; }

   // Process S-record "checksum" field (compare with calculated value).
   if (!(status & DOWNLOAD_ERROR)) {
      checksum = (~bytesum) & 0xFF;
      if (checksum != get_byte(&data_out)) { 
         status |= ERR_SREC_CKSUM; 
      }

      // Discard EOL characters
      get_char(&data_out);
   }

   // Use "*" to indicate progress
#ifdef SEE_INDICATOR   
   sci_tx_char('*');
#endif   
}




/*****************************************************************************
* UInt16 get_byte(char **ptr)
*
* Get two characters from circular buffer and returns the ASCII hex value.
* Post increments character pointer using modulo arithmetic.  Accumulates 
* ASCII hex values in "bytesum" global.
* 
* In:   ptr - Pointer to location in circular buffer
*
* Out:  Returns ASCII hexadecimal value of two characters in buffer.
*****************************************************************************/
asm UInt16 get_byte(register char **ptr)
{
        adda    #2,sp                   ;//
        move.w  m01,x:(sp-1)            ;// Save M01 to stack
        moveu.w #(RX_DATA_SIZE-1),M01   ;// Set buffer size
        nop
        moveu.w x:(ptr),r0              ;// Get circular buffer pointer
        move.bp x:(r0)+,y0              ;// Get 1st character (high nibble)
        jsr     char_to_int             ;// Convert into integer
        asll.w  #<4,y0                  ;// Shift to account for high nibble
        move.w  y0,y1                   ;// Accumulate result into Y1
        move.bp x:(r0)+,y0              ;// Get 2nd character (low nibble)
        jsr     char_to_int             ;// Convert into integer
        add     y1,y0                   ;// Accumulate result into Y0 (return value)
        add.w   y0,bytesum              ;// Accumulate result into global
        move.w  r0,x:(ptr)              ;// Update circular buffer pointer
        move.w  #bytesum+2,x0
        moveu.w x:(sp-1),m01            ;// Restore M01
        suba    #2,sp                   ;// Restore stack pointer
        rts
}


/*****************************************************************************
* char get_char(char **ptr)
*
* Get a character from circular buffer.  Post increments character pointer 
* using modulo arithmetic.
*
* In:   ptr - Pointer to location in circular buffer
*
* Out:  Returns character in circular buffer.
*****************************************************************************/
asm char get_char(register char **ptr)
{
        adda    #2,sp                   ;//
        move.w  m01,x:(sp-1)            ;// Save M01 to stack
        moveu.w #(RX_DATA_SIZE-1),M01   ;// Set buffer size
        nop
        moveu.w x:(ptr),r0              ;// Get circular buffer pointer
        move.bp x:(r0)+,y0              ;// Get 1st character
        move.w  r0,x:(ptr)              ;// Save updated pointer
        moveu.w x:(sp-1),m01            ;// Restore M01
        suba    #2,sp                   ;// Restore stack pointer
        rts
}

/*****************************************************************************
* void mem_init(register char *mem, register char init, register UInt16 sz)
*
* Initialize a string with desired character.
*
* In:  mem - String to initialize
* 
* Out: n/a
*****************************************************************************/
asm void mem_init(register char *mem, register char init, register UInt16 sz)
{
        rep     sz
        move.bp init,x:(mem)+
        rts
}


#ifdef	DYNAMIC_BOOT
#pragma section pram_code end
#endif


	