#include <system.h>
#include "ekboostload.h"
/**********************************************************
* EKBoostLoader 
*
*  BoostC PIC16F87X Bootloader utility.
*
*  Written by ryan@electronikits.com 
*
*  Revision 0.0.0.2
*  History:
*     Nov 30 2004 - Created
*     Dec 2  2004 - Bootloader now exits with nothing on stack.
*                Fixed Addressing Bug
*                Software Flow Control ( XON/XOFF )
*                Added Support For other partnumbers
*                Added Comments.
*
*   NOTES:
*
**********************************************************/

#define COMM_TIMEOUT 50000//2 seconds

#pragma CLOCK_FREQ CLOCK_SPEED

//Linker Must be passed the following parameters
// to start bootloader at 0x1E04
// -rb 7685

//Boot Jump Vector (To Bootloader..) @ 0x1E00
//This is the first executed code block and it jumps straight to the bootloader.
#if defined( _16F877 ) || defined( _16F876 )
#pragma DATA 0x0000, 0x301E, 0x008A, 0x3005, 0x0082
#elif defined( _16F874 ) || defined( _16F873 )
#pragma DATA 0x0000, 0x300E, 0x008A, 0x3005, 0x0082
#elif defined( _16F871 ) || defined( _16F870 )
#pragma DATA 0x0000, 0x3006, 0x008A, 0x3005, 0x0082
#endif
// movlw 0x1E  or 0x0E, or 0x06
// movwf pclath
// movlw 0x05
//  movwf   pcl
//////////////////////////////////////////////////////////////////////////////////

//This is where the first 4 instructions of the application code are relocated to.
//#pragma DATA BOOTLOAD_JUMP, 0x018A, 0x0000, 0x018A, 0x0082
#if defined( _16F877 ) || defined( _16F876 )
#pragma DATA 0x1E00, 0x018A, 0x0000, 0x0000, 0x018A, 0x0082
#elif defined( _16F874 ) || defined( _16F873 )
#pragma DATA 0x0E00, 0x018A, 0x0000, 0x0000, 0x018A, 0x0082
#elif defined( _16F871 ) || defined( _16F870 )
#pragma DATA 0x0600, 0x018A, 0x0000, 0x0000, 0x018A, 0x0082
#endif
//  clrf    pclath
// nop            //THESE LAST 4 INSTRUCTIONS ARE OVERWRITTEN BY APPLICATION. 
//  nop     
// clrf    pclath 
// clrf    pcl
//////////////////////////////////////////////////////////////////////////////////

void main( )
{
unsigned char hiAddr;
unsigned char loAddr;
unsigned char hiData;
unsigned char loData;
unsigned char bLoading;
unsigned char idx;
unsigned char length;
unsigned char rcType;
unsigned char a;
unsigned char b;

//Initialize Serial Port
serial_init( );

//Send Prompt For Start.
serial_putchar( '>' );

//Receive Hex Records..
bLoading = 1;
while( bLoading )
{
a = serial_getbyte();
if( g_CommTimeOut != 0 )
{
//Exit Loop and continue to Application code jump.
bLoading = 0;
}
else if (a == ':')
{
// Example Hex Record
//  :1000100008003C3023020318112830302302A300CB
//    :10 0010 00 08 00 3C 30 23 02 03 18 11 28 30 30 23 02 A3 00 CB 
//  ln addr cd d1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 cs
//Get Record Length
a = serial_getbyte();
b = serial_getbyte();
length = ascii2byte( a, b );
//Get Address
a = serial_getbyte();
b = serial_getbyte();
hiAddr = ascii2byte( a, b );
a = serial_getbyte();
b = serial_getbyte();
loAddr = ascii2byte( a, b );
//Get Record Type
a = serial_getbyte();
b = serial_getbyte();
rcType = ascii2byte( a, b );
if( rcType == 0x01 )//If Last Record..
{
bLoading = 0;
}
else if( rcType == 0x00 )//If Data Record, write to flash.
{
//Dont write over loader.
if (hiAddr < ( ( BOOTLOAD_START >> 8 ) & 0x00FF ) )
{
//Shift Address Once To Right..
//Because we are addressing every 2 bytes, not every byte.  
loAddr = (loAddr >> 1) + ((hiAddr & 1)*128);
hiAddr = hiAddr >> 1;

//Retrieve All Data and Write To Flash
idx = 0;
while (idx < length)
{
a = serial_getbyte();
b = serial_getbyte();
loData = ascii2byte( a, b );
a = serial_getbyte();
b = serial_getbyte();

serial_putchar( XOFF );//Pause Transmission
hiData = ascii2byte( a, b );

if( hiAddr == 0 && loAddr < 4 )
//Relocate Boot Sector Data To Upper Flash
//write_flash(0x1F, loAddr+0xFA, hiData, loData);
write_flash( BL_JUMP_HIBYTE, loAddr+BL_JUMP_LOBYTE, hiData, loData);
else
write_flash(hiAddr, loAddr, hiData, loData);

//Jump to next 2 bytes.
idx = idx + 2;
//Increment Address.
loAddr++;
if (loAddr == 0) hiAddr++;

serial_putchar( XON );//Resume Transmission
}
}
}
}
}

//Too Late.. Jumping To App. Or Restarting Bootloader.
serial_putchar( '!' );

//Jump To Application Code
asm
{
//Set pclath
#if defined( _16F877 ) || defined( _16F876 )
movlw0x1E
#elif defined( _16F874 ) || defined( _16F873 )
movlw0x0E
#elif defined( _16F871 ) || defined( _16F870 )
movlw0x06
#endif   
movwf_pclath
//set pcl
movlw0x00
movwf_pcl
}

//How did we get here??  Loop forever..
while(1)
continue;
}

void write_flash( unsigned char hiAddr, unsigned char loAddr, unsigned char hiData, unsigned char loData )
{
//Address
eeadr  = loAddr;
eeadrh = hiAddr;
//Data
eedata = loData;
eedath = hiData;

set_bit( eecon1, EEPGD );
set_bit( eecon1, WREN );
clear_bit( intcon, GIE );

eecon2 = 0x55;
eecon2 = 0xAA;
set_bit( eecon1, WR );
nop();
nop();
nop();
clear_bit( eecon1, WR );
}

//Converts Two Ascii characters to its hex equivalent
unsigned char ascii2byte(unsigned char hiNibble, unsigned char loNibble)
{
if ( hiNibble < 0x3C )
hiNibble = hiNibble - 0x30;
else
hiNibble = hiNibble - 0x37;

if ( loNibble < 0x3C )
loNibble = loNibble - 0x30;
else
loNibble = loNibble - 0x37;

return ( ( hiNibble << 4 ) + loNibble );
}

void serial_init( )
{
//init Hardware RS232
g_CommTimeOut = 0;
txsta = 0x24;//Enable Tx, BRGH=1
rcsta = 0x90;//Enable SPort, Enable Rx
spbrg = BR_CONST;//9600 Baud
}

//Writes Character to Serial Port
void serial_putchar( unsigned char cByte )
{
while( ( txsta & 0x02 ) == 0 )
continue;
txreg = cByte;
}

//Receives Character from Serial Port
//Check g_CommTimeOut global to check for timeout.
unsigned char serial_getbyte( )
{
unsigned char cRxByte;
unsigned int nTimeOut;

nTimeOut = 0;
while( ( pir1 & 0x20 ) == 0 )
{
delay_us( 40 );
nTimeOut++;
if( nTimeOut > COMM_TIMEOUT )
{
//Comm TimeOut Occurred
g_CommTimeOut = 1;
return( 0 );
}
}

cRxByte = rcreg;
return( cRxByte );
}