/**********************************************************************/
/* Project Name:  flash_S12G.c                                        */
/* Last modified: 08/20/2015                                          */
/* By:            b34874                                              */
/**********************************************************************/
/* Description: Flash routines for microcontroller S12G               */
/* This software is classified as Engineering Sample Software.        */
/**********************************************************************/
#include <hidef.h>        /* common defines and macros */
#include "derivative.h"   /* derivative-specific definitions */
#include "flash_S12G.h"   /* flash code */

unsigned long int Local2Global(unsigned long int address);
unsigned char PFLASH_Erase_Verify_Section(unsigned long int address, unsigned int number_of_phrases);
unsigned char PFLASH_Program(unsigned long int address, unsigned int *ptr);
unsigned char PFLASH_Erase_Sector(unsigned long int address);
unsigned char PFLASH_Read_Byte(unsigned long int address);
unsigned  int PFLASH_Read_Word(unsigned long int address);
void PFLASH_Init(unsigned char fdiv);

//==============================================================================
//PFLASH Send_Command / Send_Command2
//==============================================================================
//this function has to be stored in RAM memory
//the main code in C language is:
//  {
//    DisableInterrupts;      //start critical section
//    FSTAT_CCIF = 1;         //launch command
//    while(FSTAT_CCIF == 0); //wait for done
//    EnableInterrupts;       //end critical section
//  }
//Option1: we could define code in RAM as variable array with disassembled code.
static unsigned char Send_Command[]=
{
  0x14, 0x10, 0x1C, 0x01, 0x06, 0x80, 0x1F, 0x01, 0x06, 0x80, 0xFB, 0x10, 0xEF, 0x3D
};
//Option2: we could define code in RAM as C code and use #pragmas for placement it into RAM segment (see prm file).
#pragma CODE_SEG __NEAR_SEG MY_RAM
void Send_Command2(void)
{
   DisableInterrupts;      //start critical section
   FSTAT_CCIF = 1;         //launch command
   while(FSTAT_CCIF == 0); //wait for done
   EnableInterrupts;       //end critical section
}
#pragma CODE_SEG DEFAULT
//Note: behavior of both options is the same. 
//Option1 has slightly better optimized code (JSR/RTS instaed CALL/RTC instructions)
//Option2 allows simply modify code in C-language.

//==============================================================================
//Local2Global
//==============================================================================
unsigned long int Local2Global(unsigned long int address)
{
  //EEPROM
  if((address >= 0x0400) && (address <= 0x13FF))
  {
    address = (address + 0x4000) | 0x00010000;
    return address; 
  }

/*******************************************************************************/
#ifdef _S12G128  /*only for S12G128*/
  //Unpaged P-Flash 0x1400 at S12G128
  if((address >= 0x1400) && (address <= 0x1FFF))
  {
    address = address | 0x00030000;
    return address; 
  }
#endif /*_S12G128*/

#ifdef _S12G96   /*only for S12G96*/
  //Unpaged P-Flash 0x1000 at S12G96
  if((address >= 0x1000) && (address <= 0x1FFF))
  {
    address = address | 0x00030000;
    return address; 
  }
#endif /*_S12G96*/

#ifdef _S12G64   /*only for S12G64*/
  //Unpaged P-Flash 0x0C00 at S12G64
  if((address >= 0x0C00) && (address <= 0x2FFF))
  {
    address = address | 0x00030000;
    return address; 
  }
#endif /*_S12G64*/
/*******************************************************************************/
  
  //Unpaged P-Flash 0x4000 and 0xC000
  if(((address >= 0x4000) && (address <= 0x7FFF)) || ((address >= 0xC000) && (address <= 0xFFFF)))
  {
    address = address | 0x00030000;
    return address; 
  }

  //Paged P-Flash 0x8000
  if((address >= 0x00018000) && (address <= 0x000FFFFF))
  {
    address = ((address >> 2) & 0x000F0000) | (((((address & 0x000F0000) >> 16)%4) * 0x4000) | (address & 0x00007FFF));
    return address; 
  }
     
  return 0;
}


//==============================================================================
//PFLASH_Erase_Verify_Section
//==============================================================================
unsigned char PFLASH_Erase_Verify_Section(unsigned long int address, unsigned int number_of_phrases)
{
  //check if address is aligned (global address [2:0] = 000)
  if((address & 0x00000007) != 0)
    return MISALIGNED_ADDRESS; 
  
  while(FSTAT_CCIF == 0);   //wait if command in progress
  FSTAT = 0x30;             //clear ACCERR and PVIOL
  
  FCCOBIX = 0x00;
  FCCOB = 0x0300 | ((address & 0x00FF0000)>>16);
  
  FCCOBIX = 0x01;
  FCCOB = (address & 0x0000FFFF);
  
  FCCOBIX = 0x02;
  FCCOB = number_of_phrases;
  
  asm JSR Send_Command;  //Option1
  //Send_Command2();       //Option2
  
  if((FSTAT & (FSTAT_ACCERR_MASK | FSTAT_FPVIOL_MASK)) != 0)
    return ACCESS_ERROR;
  
  //check if phrases are erased and return result
  if(FSTAT_MGSTAT != 0)
    return NON_ERASED;
  else
    return ERASED;
}


//==============================================================================
//PFLASH_Program
//==============================================================================
unsigned char PFLASH_Program(unsigned long int address, unsigned int *ptr)
{
  unsigned char i;
  
  //check if address is aligned (global address [2:0] != 000)
  if((address & 0x00000007) != 0)
    return MISALIGNED_ADDRESS; 
  
  //check if the phrase is erased    
  if((PFLASH_Erase_Verify_Section(address, 1)) == NON_ERASED)
    return NON_ERASED;  
    
  while(FSTAT_CCIF == 0);   //wait if command in progress
  FSTAT = 0x30;             //clear ACCERR and PVIOL    
  
  FCCOBIX = 0x00;
  FCCOB = 0x0600 | ((address & 0x00030000)>>16);
  
  FCCOBIX = 0x01;
  FCCOB = (address & 0x0000FFFF);
  
  for(i=2; i<6; i++)  //fill data (4 words) to FCCOB register
  {
    FCCOBIX = i;
    FCCOB = *ptr;
    ptr++;  
  }
      
  asm JSR Send_Command;  //Option1
  //Send_Command2();       //Option2    
  
  if((FSTAT & (FSTAT_ACCERR_MASK | FSTAT_FPVIOL_MASK)) != 0)
    return ACCESS_ERROR;
  else    
    return OK;
}


//==============================================================================
//PFLASH_Erase_Sector
//==============================================================================
unsigned char PFLASH_Erase_Sector(unsigned long int address)
{
  //size of sector is 512B
  
  //check if address is aligned (global address [2:0] != 000)  
  if((address & 0x00000007) != 0)
    return MISALIGNED_ADDRESS; 
    
  while(FSTAT_CCIF == 0);   //wait if command in progress
  FSTAT = 0x30;             //clear ACCERR and PVIOL
  
  FCCOBIX = 0x00;
  FCCOB = 0x0A00 | ((address & 0x00030000)>>16);
  
  FCCOBIX = 0x01;
  FCCOB = (address & 0x0000FFF8);
   
  asm JSR Send_Command;  //Option1
  //Send_Command2();       //Option2
  
  if((FSTAT & (FSTAT_ACCERR_MASK | FSTAT_FPVIOL_MASK)) != 0)
    return ACCESS_ERROR;
  else    
    return OK;
}

//==============================================================================
//PFLASH_Read_Byte
//==============================================================================
unsigned char PFLASH_Read_Byte(unsigned long int address)
{
  unsigned char data8;
  data8 = *(unsigned char *far)address;
  return data8;
}

//==============================================================================
//PFLASH_Read_Word
//==============================================================================
unsigned int PFLASH_Read_Word(unsigned long int address)
{
  unsigned int data16;
  data16 = *(unsigned int *far)address;
  return data16;
}

//==============================================================================
//PFLASH_Init
//==============================================================================
void PFLASH_Init(unsigned char fdiv)
{
  while(FSTAT_CCIF == 0);   //wait if command in progress  
  FCLKDIV = fdiv;           //set flash clock to approximately 1MHz    
}
