module bootfor16_E2

include "RS485_E"  'set uart baudrate and RS232/422 /485 or 485 half-duplex

'Prototypes in this module:

sub function  Usart_Write_Loop(dim send, receive as byte) as byte
sub procedure Start_Bootload
sub procedure Start_Program
'*******************************************************************************
'global variable used in org and orgall for main
const P_Size as word = (22*32)            'program size rounded up to multiple of 32
const boot_main =  __FLASH_SIZE - (P_size) '__FLASH_SIZE comes from pic defs file
'*******************************************************************************
implements
' below are local variables visible only from this unit

const page_mem = 0x800 - (P_size)       'location on 2K page of boot_main
dim  block as word[32]                  'used to store blocks of the new program
     rcv_1, rcv_2,topm,uadd as word     'auxiliary variables
     intsave as byte               ' Intcon save
'(******************************************************************************/
'/*                                                                            */
'/*  function: Start_program                                                   */
'*   Purpose: After bootloading user routines, these NOPs get replaced with     */
'/*  code to point to the USER_MAIN routine.
'/*  CallParams: none                                                          */
'/*  ReturnValues: none                                                        */
'/*                                                                            */
'/*  Requires:  This procedure is usually placed at the very end of ROM space  */
'/*             Its address must be known to bootloader, therefore it is       */
'/*               seprately orged                                              */
'/*                                                                            */
'/****************************       CHANGE LOG       **************************/
'/* Version | ACTION                                           |  DATE  | SIG  */
'/* --------|--------------------------------------------------|--------|----- */
'/*         |                                                  |        |      */
'/*    0.01 | Created function                                 |        | AR   */
'/*    0.02 | added nops to match new approach                 | 110408 | ZR   */
'/*         |                                                  |        |      */
'/******************************************************************************)

sub procedure Start_program() org(__FLASH_SIZE - 8)   'find top 8 words of Prog ROM
  nop  nop  nop  nop
  nop  nop  nop           'some of these NOPs get overwritten by
                          'loaded program
                          'last location gets a Return (0x0008)end sub
end sub
'(******************************************************************************/
'/*                                                                            */
'/* Was  function: Flash_Write_Address:                                        */
'/* Now replaced  by << Write_prog(dim madd as word)                           */
'/*  Purpose:  Writes 32 words to program memory starting from the             */
'/*            address <madd x 32>                                             */
'/* eg. To write to location 0x0000 - Write_prog(0x0000)                       */
'/*     To write to location 0x0020 - Write_prog(0x0010)                       */
'/*     To write to location 0x3FE0 - Write_prog(0x01FE)                       */
'/*                                                                            */
'/*                                                                            */
'/*  Requires:  Usart_Init to be called                                        */
'/*                                                                            */
'/****************************       CHANGE LOG       **************************/
'/* Version | ACTION                                           |  DATE  | SIG  */
'/* --------|--------------------------------------------------|--------|----- */
'/*         |                                                  |        |      */
'/*    0.01 | Created function                                 |        | AR   */
'/*    0.02 | Changed for E versions                           |DEC 2012| AD   */                                              |        |      */
'/******************************************************************************
'*******************************************************************************
Sub procedure Erase_Prog()    'only called by program routine, so local to module
    EEADRL = lo(uadd)
    EEADRH = hi(uadd)
    setbit   (EECON1,EEPGD)
    ClearBit (EECON1,CFGS)
    SetBit   (EECON1,FREE)   'tells uC to clear 32 words from address above
    SetBit   (EECON1,WREN)
    EECON2 = 0x55
    EECON2 = 0XAA
    SetBit (EECON1,WR)        'start erase of 32 words
    nop
    nop
    while EECON1.FREE         'FREE is cleared by HW at end of erase
    wend
end sub
'*******************************************************************************
Sub procedure Write_Prog (dim madd as word)
dim i as byte 'local i, no relation to any other i
'madd is memory address as block of 32 words, 0 thru 0x1ff  equates to 0x0000 thru 0x3FFF (00 to 1ff * 32)
'data has to be available in array of 32 words, last parameter can only be true or false, ( or 1 or 0)
'INTCON is preserved and restored. Interrupts HAVE to be stopped during programming.
'Have to erase block of 32 words first.

     intsave = INTCON             'save for restore later
     clearbit (INTCON,GIE)        'stop any ints for now, restore at end of programming
     uadd = madd <<5              'shift page to get true address
     Erase_Prog()                 'do erase

     for i = 0 to 31                'do it 32 times
          EEADRL =  lo(uadd + i)     'get lower address byte (0-7)
          EEADRH =  hi(uadd)         'get higher address byte (8-14)
          EEDATL =  lo(Block[i])     'get lower data byte
          EEDATH =  hi(Block[i])     'get higher data byte (8-15)
          setbit   (EECON1,EEPGD)    'EEPROM area
          ClearBit (EECON1,CFGS)     'Not CONFIG area
          SetBit   (EECON1,LWLO)     'Set to write to latches
          SetBit   (EECON1,WREN)     'Enable writes
          'now load 7 words to latches then do real write on 8th word, writes 8th word to
          'latch and then all 8 words to memory on next EECON1.WR high.
          select case i
              case 7,15,23,31  clearBit(EECON1, LWLO)  'clear to write to memory
          end select
          'same sequence has to be used for all writes for latches or memory
          EECON2 = 0x55          'EEPROM write sequence
          EECON2 = 0XAA
          SetBit (EECON1,WR)     'start write to latch if LWLO = 1 or memory if LWL = 0
          nop                    'stops at first nop, uC suspends everything until write is done
          nop
          while EECON1.WR        'belt & braces check of WR bit, cleared by HW at end of prog
          wend
          'end of write latch or memory section
      next i                     'do for all 32 words in store

    ClearBit (EECON1,WREN)        'no more EEPROM writes now
    INTCON = intsave              'restore intcon
end sub
'*******************************************************************************
sub function Usart_Write_Loop(dim send, receive as byte) as byte
  dim stop_flag, r, tmp as byte
   r     = 0
  stop_flag  = 0
  while (stop_flag <> 1)
      Delay_ms(5)
      uw(send)
      Delay_ms(5)
      tmp  = 0
      if UART1_Data_Ready then
        tmp  = UART1_Read
      end if
      Inc(r)
      if ((r = 255) or (tmp = receive)) then  ' if r = 255 -> 255 chars arrived, none of them matched
        stop_flag  = 1
      end if
   wend
  result  = r
end sub

'/******************************************************************************/
'/*                                                                            */
'/*  function: Write_Begin                                                     */
'/*  Purpose:  Recovers the bootlocation 0x0000 and 0x0001 to point
'/*            to the bootloader's main.
'/* It will also move the reset vector of the program that is uploaded to a new
'/* location.
'/* NOTE:
'/* Loading program goto_main must reside in bottom 2K of code.
'/*                                                                            */
'/*  CallParams: when j = 0,  first block of 32 words received by bootloader                                                              */
'/*    of program being bootloaded.                                                                              */
'/*  ReturnValues: None                                                        */
'/*                                                                            */
'/*                                                                            */
'/****************************       CHANGE LOG       **************************/
'/* Version | ACTION                                           |  DATE  | SIG  */
'/* --------|--------------------------------------------------|--------|----- */
'/*         |                                                  |        |      */
'/*    0.01 | Created function                                 |        | AR   */
'/*    0.02 | Adapted for new concept                          |        | IR   */
'/*   0.03  |modified for 32 word erase                        |        | AD   */
'/******************************************************************************)
sub procedure Write_Begin        'sorts out position of bootloaded program
dim psave as word[32]            'save 32 words for later
dim i as byte                    'local counter
  topm = (__FLASH_SIZE - 32)     'locates the top 32 bytes of Program ROM
  rcv_1 = Flash_Read(0x0000)     'original page setting
  rcv_2 = Flash_read(0x0001)     'original goto main for bootloader
  for i = 0 to 31
     psave[i] = block[i]         'save 32 words, replace with NOPS
     block[i] = 0x0000
  next i
  'psave now has complete 1st 32 words of program being bootloaded
  'only need psave[0 thru 5] for next write but have to erase 32 words and write
  'block to latches. Have to set page to bottom 2K and goto to user's main
  '
  block[24] = 0x3180          ' Movlp 0x000 Pclath to get to page 00
  for i = 0 to 4
     block[i + 25] = psave[i] 'will include GOTO and any other instructions from
  next i                      'original program below 0x0005
     block[29] = 0x0000
     block[30] = 0x0000
     block[31] = 0x0008        'now have data to write in Block [0-31]
  
  Write_prog (topm >>5)        'overwrites the top 32 words of ROM
  Block[0] = rcv_1  'restore original page + goto boot_main
  block[1] = rcv_2  'from orginal bootloader program data
  'now can start writing desired program to ROM
  for i = 2 to 31
    block[i]  = psave[i]         'restore program data except 0 & 1
  next i
end sub
'(******************************************************************************/
'/*                                                                            */
'/*  function: Start_Bootload                                                  */
'/*  Purpose:  Handles communication between PIC and PC
'/*                                                                            */
'/*  CallParams:                                                               */
'                                                                              */
'/*  ReturnValues: None                                                        */
'/*                                                                            */
'/*  Requires:                                                                 */
'/*                                                                            */
'/****************************       CHANGE LOG       **************************/
'/* Version | ACTION                                           |  DATE  | SIG  */
'/* --------|--------------------------------------------------|--------|----- */
'/*         |                                                  |        |      */
'/*    0.01 | Created function                                 |        | AR   */
'/*         |                                                  |        |      */
'/******************************************************************************)
sub procedure Start_Bootload      'Gets program to bootload from PC via UART
              'builds data store in chunks of 32 Words then writes to program ROM
dim i as byte        'j counts blocks of 32 words, gives base address. Writes to
    j as word        'j * 32. 0 based addressing. i is position in block
  i  = 0             'creates an array of j x 32 words in ROM when written
  j  = 0
  while true
      if (i = 32) then               ' If 32 words received then erase flash
           if (j = 0) then          ' The first block of 32 instructions contains "GOTO MAIN"
             Write_Begin()          'and gets written to very top of ROM
           end if
           Write_prog(j)            'j gets multiplied by 32 and is a "page" pointer to
                                    'blocks of 32 bytes
           i  = 0
           Inc(j)                  'note j is never reset here but just increments
                                   'whilever data is read.
      end if
      uw("y")                        ' Ask for data low byte
      do
        nop
      loop until (Uart1_Data_Ready() = 1)
      rcv_2  = Uart1_Read()              ' Read rcv_2
      uw("x")                            ' Ask for data high byte
      do
        nop
      loop until (Uart1_Data_Ready() = 1)
      rcv_1  = Uart1_Read()              ' Read rcv_1
      block[i]  = rcv_1 << 8             ' Save rcv_1 and rcv_2 in block[i]
      block[i]  = block[i] or rcv_2
      Inc(i)                             ' Next program word
   wend
end  sub

end.