;******************************************************************************
;   Bootloader for Microchip PIC16F873A/876A/877A                             *
;                     (c) 2003-2004, Peter Huemer                             *
;                          Freely distributable                               *
;******************************************************************************
;                                                                             *
; Name of file:      16F88.asm                                                *
; Date:              7th Jul 2004                                             *
; Version:           1.2                                                      *
;                                                                             *
; Author:            Alain POLLET                                             *
; Company:           NEO-TECH                                                 *
; Email:             webmaster@neo-tech.fr                                    *
; Url:               www.neo-tech.fr                                          *
;******************************************************************************
;                                                                             *
; Notes on Version 1.0 (02.08.2005):                                          *
; - Exportation du code pour la version de pic 16F88                          *
;      - On va travailer avec des segments de 32 Words                        *
;      - un nouvelle fonction fait son apparition : ERASE FLASH               *
;                                                                             *
;******************************************************************************
;   Bootloader for Microchip PIC16F87/88                                      *
;                     (c) 2002-2005, Alain POLLET                             *
;                          Freely distributable                               *
;******************************************************************************
;                                                                             *
; Name of file:      bootldr.asm                                              *
; Date:              29th Dec 2002                                            *
; Version:           2.6                                                      *
;                                                                             *
; Author:            Petr Kolomaznik                                          *
; Company:           EHL elektronika, Czech Republic                          *
; Email:             kolomaznik@ehl.cz                                        *
; Url:               www.ehl.cz/pic                                           *
; Modified by:       Shane Tolmie (v2.30)                                     *
; Company:           DesignREM                                                *
; Email:             shane@microchipc.com                                     *
; Url:               www.microchipc.com                                       *
;                                                                             *
;******************************************************************************
; How to use this bootloader:                                                 *
; - open project bootldr.pjt in MPLAB                                         *
; - check and modify of parameters in the user setting section with <<< mark  *
; - make project with MPLAB and MPASM                                         *
; - use any programmer for programming of bootldr.hex to microcontroller      *
; - set configuration bits                                                    *
; - use PIC downloader program for user program download                      *
; - check if user program doesn't use the top 214 program memory locations    *
;                                                                             *
; Notes:                                                                      *
; - tab size for editor is 2                                                  *
; - bootloader is compatible with HI-TECH's and Shane Tolmie's bootl./downl.  *
;******************************************************************************
; 2.70 - 24.09.2004 (Shane Tolmie [shane@microchipc.com and Peter Huemer      *
;    p.huemer@eduhi.at). Fixed quad-alignment problem with 16F87xA devices.   *
;    The code used to fail unless the .hex file had a length that wasn't a    *
;    multiple  of 4.                                                          *
; 2.60 - 29.12.2002 (Shane Tolmie [shane@microchipc.com])                     *
;  - made it so that frequencies <=4Mhz have XT, and freq >4Mhz have HS       *
;    configuration in the fuse bits, to enable it to work at all freq with    *
;    resonators.                                                              *
; 2.50 - 28.8.2002 (Shane Tolmie [shane@microchipc.com])                      *
;  - switched off BODEN which resets part if low voltage power supply used    *
;    (many grateful thanks to Richard (joshanks@earthlink.net)                *
; 2.50 - 28.8.2002 (Shane Tolmie [shane@microchipc.com])                      *
;  - switched off BODEN which resets part if low voltage power supply used    *
;    (many grateful thanks to Richard (joshanks@earthlink.net)                *
; 2.20 - 8.8.2001 (Shane Tolmie [shane@microchipc.com])                       *
;  - added watchdog user change, defaults to off (leaving it on can create    *
;    difficult to track down problems)                                        *
; 2.10 - 3.8.2001 (jvo@vinylium.ch)                                           *
;  - substantially reduced size of program                                    *
;  - restores USART to reset condition before starting user program           *
;  - made user program start variable                                         *
;  - added trap to avoid unintended running into bootloader                   *
;  - watchdog timeout is directly handled over to user program                *
; 2.00 - 30.03.2001 (Shane Tolmie [shane@microchipc.com])                     *
;  - for the 16F876, the 4 instructions in the original .hex file at 0x0000   *
;    to 0x0003 are copied to 0x1F00 to 0x1F03, and executed once the          *
;    bootloader is finished, to jump back to user code. The reset vector in   *
;    the chip at 0x0000 to 0x0003 points to the bootloader.  Inserted a       *
;    pagesel 0x0000 (2 instructions) at 0x1F00 to make a short jump into a    *
;    long jump so it would work with .hex files that didnt have a long jump   *
;    as the first 4 instructions in the .hex file.                            *
;    This addition dramatically increases compatibility with .hex files.      *
; 1.06 - 30.03.2001 (Shane Tolmie)                                            *
;  - added config bits                                                        *
;  - program now jumps immediately to user program after download             *
; 1.02 - 15.11.2000                                                           *
;  - added check of user constants                                            *
;  - added support for the new 16F870/1/2                                     *
;  - added errorlevel directive for message 302 and 306                       *
;******************************************************************************


	errorlevel -302, -306		; no message 302 and 306
	list       b=2			; tabulator size = 2

;================== User setting section ======================================

	list p=16f88			; <<< set type of microcontroller  (16f873a or 16f876a)
					;     set same microcontroller in the project
	#define ICD_DEBUG 0		; <<< if using MPLAB ICD Debugger, moves bootloader down 256 bytes to make room for it [0|1]													  
	#define FOSC D'4000000' 	; <<< set quartz frequence [Hz], max. 20 MHz
	#define BAUD D'19200'		; <<< set baud rate [bit/sec]
	;#define BAUD D'38400'		; <<< set baud rate [bit/sec]
	#define	BAUD_ERROR	D'4'	;	<<< set baud rate error [%]
	#define PIN			; <<< set method of bootloader start PIN/TIME
					;     PIN	: start on low level of trigger pin
                            		;     TIME: start on receive IDENT byte in TIMEOUT
    	#define	TRIGGER		PORTB,3 ; <<< only for PIN - set PORT_X,PIN_NR
	#define	TIMEOUT		D'200'	; <<< only for TIME - set time [0.1s], max. 25 sec
	#define WATCHDOGTIMER 0		; <<< Watchdog timer default OFF/ON [0|1]

;=================== Configuration ============================================

	__IDLOCS H'2100'		; version ID of bootloader

  IF WATCHDOGTIMER == 0
    #define MY_WDT _WDT_OFF
  ELSE
    #define MY_WDT _WDT_ON
  ENDIF

  ;note: for high voltage parts, you can set BODEN_ON, but for low voltage parts it resets the circuit continuously!
  IF FOSC<D'4000000'
    #define _MYCRYSTAL _XT_OSC ;see datasheet
  ELSE
    #define _MYCRYSTAL _HS_OSC  
  ENDIF
  
  __CONFIG    _CONFIG1, _CP_OFF & _CCP1_RB0 & _DEBUG_OFF & _WRT_PROTECT_OFF & _CPD_OFF & _LVP_OFF & _BODEN_OFF & _MCLR_OFF & _PWRTE_ON & _WDT_OFF & _MYCRYSTAL
  __CONFIG    _CONFIG2, _IESO_OFF & _FCMEN_OFF

;=============== End of user setting section ==================================


;================== Check User Constants ======================================

  IFNDEF FOSC
    ERROR "FOSC must be defined"
  ENDIF

  IFNDEF BAUD
    ERROR "BAUD must be defined"
  ENDIF

	IF FOSC > D'20000000'
		ERROR "max. quartz frequency = 20 MHz"
	ENDIF

	IFNDEF PIN
		IFNDEF TIME
			ERROR "wrong start method of bootloader, must be PIN or TIME"
		ENDIF
	ENDIF

	IF TIMEOUT > D'254'
		ERROR "max. timeout = 25.4 sec"
	ENDIF

	IF ICD_DEBUG != 0
		IF ICD_DEBUG != 1
			ERROR "ICD debug must be 1 (enabled) or 0 (not used)"
		ENDIF
	ENDIF

;========================== Constants =========================================

	IF ((FOSC/(D'16' * BAUD))-1) < D'256'
		#define DIVIDER (FOSC/(D'16' * BAUD))-1
		#define HIGH_SPEED 1
	ELSE
		#define DIVIDER (FOSC/(D'64' * BAUD))-1
		#define HIGH_SPEED 0
	ENDIF

BAUD_REAL	EQU	FOSC/((D'64'-(HIGH_SPEED*D'48'))*(DIVIDER+1))

	IF BAUD_REAL > BAUD
		IF (((BAUD_REAL - BAUD)*D'100')/BAUD) > BAUD_ERROR
			ERROR	"wrong baud rate"
		ENDIF
	ELSE
		IF (((BAUD - BAUD_REAL)*D'100')/BAUD) > BAUD_ERROR
			ERROR	"wrong baud rate"
		ENDIF
	ENDIF

	IF FOSC > D'10240000'
		#define	T1PS 8
		#define	T1SU 0x31
	ELSE
		IF FOSC > D'5120000'
			#define T1PS 4
			#define T1SU 0x21
		ELSE
			IF FOSC > D'2560000'
				#define T1PS 2
				#define T1SU 0x11
			ELSE
				#define T1PS 1
				#define T1SU 0x01
			ENDIF
		ENDIF
	ENDIF

TIMER	EQU	(D'65538'-(FOSC/(D'10'*4*T1PS))); reload value for TIMER1 (0.1s int)

    IFDEF __16F88
        #include <p16f88.inc>
        #define ProgHI 0x0FFF
    ELSE
        ERROR   "incorrect type of microcontroller"
    ENDIF

#define LoaderSize	0x1E0  		                ; size of bootloader (not yet optimized)


#define LoaderTop 	ProgHI-(ICD_DEBUG*0x100)        ; top address of bootloader
#define LoaderStart	(LoaderTop)-LoaderSize+1-.32    ; start address of bootloader
#define LoaderMain	(LoaderTop)-LoaderSize+1        ; start address of bootloader

#define	NumRetries	1				; number of writing retries


#define WRITE       0xE3				; communication protocol
#define WR_OK       0xE4
#define WR_BAD      0xE5

#define DATA_OK     0xE7
#define DATA_BAD    0xE8

#define IDENT       0xEA
#define IDACK       0xEB

#define DONE        0xED

;=============== Variables ====================================================

buff			EQU	0x20		; 16 adresses - 8  WORDS
block			EQU	0x30		; 64 adresses - 32 WORDS

; RAM address 0x70 reserved for MPLAB-ICD
amount			EQU	0x71
chk1			EQU	0x72
chk2			EQU	0x73
retry			EQU	0x74
address			EQU     0x75
tmpaddr			EQU     0x77
temp			EQU	0x79
time			EQU	0x7A
count			EQU     0x7B
;>>> ADAPT  
help                    EQU     0x7C
lcount			EQU	0x7d
;>>>

;------------------------------------------------------------------------------
    ORG     0x0000                                      ; reset vector of microcontroller
	nop						; for compatibility with ICD
	pagesel Main
    	goto    Main
	nop

;==============================================================================
;::::::::::::::::::::::::: LOADERSTART ::::::::::::::::::::::::::::::::::::::::
;==============================================================================
	ORG		LoaderStart
TrapError
	pagesel TrapError				; Point d'arret si on entre dans la zone 
	goto	TrapError				; BootLoader par erreur
	nop
	nop
UserStart
	; this instruction never gets overwritten
	clrf	PCLATH					; set PCLATH to program page 0
	; the following 4 instructions get overwritten by user program
	pagesel UserStart				; set PCLATH to program page of UserStart
	goto	UserStart				; loop for first start without a user program
	nop                             
   
;==============================================================================
;::::::::::::::::::::::::: MAIN :::::::::::::::::::::::::::::::::::::::::::::::
;==============================================================================
	ORG		LoaderMain	
Main
	btfss STATUS,NOT_TO				; run user program after WatchDog-TimeOut
	goto UserStart
start

	;bsf	STATUS,RP0		; On se place dans le Bank 1
	;MOVLW	B'00000000'   		; PORTA // all analog pins digital
	;MOVWF	ANSEL			; PORTA // all analog pins digital
	;MOVLW	B'01100000'		; 4 MHz
	;MOVWF	OSCCON			;
        ;CLRF	VRCON           	; Les références de voltage sont inhibées
	;bcf	STATUS,RP0		; On se place dans le Bank 0				
	;MOVLW	h'07'			; 
	;MOVWF	CMCON 			; SET GP<2:0> en entrée/sortie;

	movlw	0x90					; SPEN = 1, CREN = 1
	movwf	RCSTA
	bsf	STATUS,RP0				; bank1
	IF HIGH_SPEED == 1				; USART SYNC=0; SPEN=1; CREN=1; SREN=0;
		bsf	TXSTA,BRGH                      ;       TX9=0; RX9=0; TXEN=1;
	ELSE
		bcf	TXSTA,BRGH
	ENDIF
	BSF	TXSTA,TXEN

	IFDEF __16F876A
	BCF	TRISC,6
	ELSE
	BCF	TRISB,5
	ENDIF

	MOVLW	DIVIDER					; baud rate generator
	MOVWF	SPBRG
	IFDEF PIN
		bsf	TRIGGER				; for PIN:  setting of TRIS for selected pin
		bcf	STATUS,RP0
		movlw	'>'				; send IDACK
		call	putbyte				; send of byte
		movlw	'O'				; send IDACK
		call	putbyte				; send of byte
		movlw	'K'				; send IDACK
		call	putbyte				; send of byte
		btfss	TRIGGER
		goto    receive				; go to bootloader
		goto	user_restore		        ; run user program
	ELSE
		bcf	STATUS,RP0
		movlw	'>'				; send IDACK
		call	putbyte				; send of byte
		movlw	'O'				; send IDACK
		call	putbyte				; send of byte
		movlw	'K'				; send IDACK
		call	putbyte				; send of byte
		movlw	TIMEOUT+1			; for TIME: set timeout
		movwf	time
		movlw	T1SU
		movwf	T1CON				; TIMER1 on, internal clock, prescale T1PS
		bsf     PIR1,TMR1IF
		call	getbyte				; wait for IDENT
		xorlw	IDENT
		btfss	STATUS,Z
		goto	user_restore
		clrf	time				; no more wait for IDENT
		goto	inst_ident			; bootloader identified, send of IDACK
	ENDIF

;==============================================================================
;::::::::::::::::::::::::: RECEIVE subroutine :::::::::::::::::::::::::::::::::
;==============================================================================
receive							; programming
	call	getbyte					; get byte from USART
	movwf	temp
	xorlw	WRITE
	btfsc	STATUS,Z
	goto	inst_write				; write instruction
	movf	temp,w
	xorlw	IDENT
	btfsc	STATUS,Z
	goto	inst_ident				; identification instruction
	movf	temp,w
	xorlw	DONE
	btfss	STATUS,Z				; done instruction ?
	goto	receive

;------------------------------------------------------------------------------
inst_done						; very end of programming
;------------------------------------------------------------------------------
	movlw	WR_OK
  	call	putbyte					; send of byte
	movlw	TIMEOUT+1
	movwf	time
  	call	getbyte                                 ; has built in timeout - waits until done
;------------------------------------------------------------------------------
user_restore
	clrf    T1CON					; shuts off TIMER1
	clrf    RCSTA
	bsf     STATUS,RP0
	clrf    TXSTA					; restores USART to reset condition
	bcf     STATUS,RP0
	clrf    PIR1
	goto	UserStart				; run user program

;------------------------------------------------------------------------------
inst_ident
	movlw	IDACK					; send IDACK
	goto	send_byte
;------------------------------------------------------------------------------
inst_write
	call	getbyte
	movwf	address+1				; high byte of address
	call	getbyte
	movwf	address					; low byte of address
	call	getbyte
	movwf	amount					; number of bytes -> amount -> count
	movwf 	count
	call	getbyte					; checksum -> chk2
	movwf	chk2
	clrf	chk1					; chk1 = 0

	movlw 	buff
	movwf 	FSR
							; FSR pointer = &buff
receive_data
	call	getbyte					; receive next byte -> buff[FSR]
	movwf	INDF
	addwf	chk1,f					; chk1 := chk1 + buff[FSR]
	incf  	FSR,f					; FSR++
	decfsz 	count,f
	goto receive_data				; repeat until (--count==0)

	; On Controle le CHECKSUM ?
	;==========================
checksum
	movf	chk1,w
	xorwf	chk2,w					; if (chk1 != chk2)
	movlw 	DATA_BAD				;
	btfss	STATUS,Z				;
	goto	send_byte				; ==> Erreur de checksum
checksum_ok						;
	movlw	DATA_OK					; checksum OK
	call	putbyte					; ==> On valide la trame reçue
write_byte						;
	call	write_flash				; write to eeprom
	iorlw	0					; On teste si la programmation est OK ?
	movlw 	WR_OK					; writing OK
	btfsc	STATUS,Z				;
	movlw	WR_BAD					; writing WRONG

	; Fin du traitement de la trame
	; On peut retourner attendre la prochaine
	;========================================
send_byte
	call	putbyte					; send of byte
	goto	receive					; go to receive from UART
;==============================================================================

;==============================================================================
;::::::::::::::::::::::::: PUTBYTE subroutine :::::::::::::::::::::::::::::::::
;==============================================================================
putbyte
	clrwdt
	btfss	PIR1,TXIF				; while(!TXIF)
	goto	putbyte
	movwf	TXREG					; TXREG = byte
	return

;==============================================================================
;::::::::::::::::::::::::: GETBYTE subroutine :::::::::::::::::::::::::::::::::
;==============================================================================
getbyte
	clrwdt
	IFNDEF PIN					; for TIME
		movf	time,w
		btfsc	STATUS,Z			; check for time==0
		goto	getbyte3
		btfss	PIR1,TMR1IF			; check for TIMER1 overflow
		goto	getbyte3			; no overflow
		bcf	T1CON,TMR1ON			; timeout 0.1 sec
		decfsz	time,f				; time--
		goto	getbyte2
		retlw 	0				; if time==0 then return
getbyte2
		bcf	PIR1,TMR1IF
		movlw	high TIMER
		movwf	TMR1H				; preset TIMER1 for 0.1s timeout
		bsf	T1CON,TMR1ON
	ENDIF
getbyte3
	btfss	PIR1,RCIF				; while(!RCIF)
	goto	getbyte
	movf	RCREG,w					; RCREG
	return

;==============================================================================
;::::::::::::::::::::::::: ERASE_EEPROM subroutine ::::::::::::::::::::::::::::
;==============================================================================		
erase_flash
	MOVF  address+1,w	;
	BSF   STATUS,RP1	;
	MOVWF EEADRH		;    EEADRH = address.high8; //Set the address
	BCF   STATUS,RP1	;
	MOVF  address,w		;
	BSF   STATUS,RP1	;
	MOVWF EEADR		;    EEADR = address.low8; //Set the address
	BSF   STATUS,RP0
	BSF   EECON1,EEPGD	;
	BSF   EECON1,WREN	;    WREN = 1; //Enable EE Writes
	BSF   EECON1,FREE	;    FREE = 1; //Allow program memory changes
	MOVLW 0x55		;    EECON2 = 0x55
	MOVWF EECON2		;
	MOVLW 0xAA		;    EECON2 = 0xAA;
	MOVWF EECON2		;
	BSF   EECON1,WR		;    WR = 1;
	NOP  			;    //Processor stalls and resumes after second NOP
	NOP  			;
	BCF   EECON1,WREN	;    WREN = 0;
	return
;==============================================================================
;::::::::::::::::::::: generique WRITE_BLOCK subroutine :::::::::::::::::::::::
;==============================================================================		
write_block

	BANKSEL EECON1 		;prepare for WRITE procedure
	BSF 	EECON1,EEPGD 	;point to program memory
	BSF 	EECON1,WREN 	;allow write cycles

write_block_loop
	BANKSEL EEDATA
	MOVF 	INDF,W 		;indirectly load EEDATA
	MOVWF 	EEDATA
	INCF 	FSR,F 		;increment data pointer
	MOVF 	INDF,W 		;indirectly load EEDATH
	MOVWF 	EEDATH
	INCF 	FSR,F 		;increment data pointer

	BANKSEL EECON1
	MOVLW 	0x55 		;required sequence
	MOVWF 	EECON2
	MOVLW 	0xAA
	MOVWF 	EECON2
	BSF 	EECON1,WR 	;set WR bit to begin write
	NOP 			;instructions here are ignored as processor
	NOP

	BANKSEL EEADR
	INCF 	EEADR,f 	;load next word address
	BANKSEL lcount
	DECFSZ 	lcount,f 	;have 4 words been written?
	GOTO 	write_block_loop;NO, continue with writing

	BANKSEL EECON1
	BCF EECON1,WREN 	;YES, 4 words complete, disable writes

	return
;==============================================================================
;::::::::::::::::::::::::: WRITE_EEPROM subroutine ::::::::::::::::::::::::::::
;==============================================================================
write_flash

	movlw	0x21					; if (0x2100 <= tmpaddr <= 0x21FF) ..........
	subwf	address+1,w
	btfsc	STATUS,Z
	goto	Buffer2Block_end			; si adresses eeprom ==> adapt12_3end

	banksel	EECON1
	bsf	EECON1,EEPGD				; EEPGD = 1 -> program memory
	banksel	address
	movf	address,w
	andlw	0xE0
	bsf	STATUS,RP1
	movwf	EEADR					; EEADR = low addr
	bcf	STATUS,RP1
	movf	address+1,w
	bsf	STATUS,RP1
	movwf	EEADRH					; EEADRH = high addr
	movlw 	buff
	movwf 	FSR
	movlw	.32
	movwf	lcount

	;--- Ont lit le BLOCK mémoire et on la stock dans block[...]
	;------------------------------------------------------
BlockRead_loop
	bcf	STATUS,RP1
	movf	lcount,w
	btfsc	STATUS,Z
	goto	BlockRead_end				; no more read operation from Flash Memory
	banksel	EECON1
	bsf	EECON1,RD								
	nop
	nop
	bcf	STATUS,RP0
	movf   	EEDATH,w
	movwf	INDF
	incf  	FSR,f					; FSR++
	movf   	EEDATA,w
	movwf	INDF
	incf  	FSR,f					; FSR++
	incf	EEADR,f
	bcf	STATUS,RP1
	decf	lcount,f
	goto	BlockRead_loop
BlockRead_end

	;--- On transfert le buffer reçu dans le Block
	;--- BLOCK[...] <== BUFFER[...]
	;----------------------------------------
Buffer2Block_Swap
	movfw	count
	movwf	lcount					; lcount <-- count (nombre de caractères)

	movlw	buff
	movwf	amount					; amount <-- adresse buffer

	movfw	address
	andlw	0x1F
	movwf	help
	bcf	STATUS,C
	rrf	help,f
	addlw	block					; 
	movwf	help					; help <-- adresse block[adresse]

					 
Buffer2Block_loop
	movfw	amount
	movwf	FSR
	
	movfw	INDF
	movwf	tmpaddr
	incf	FSR,f					; FSR++
	movfw	INDF
	movwf	tmpaddr+1
	incf	amount,f				; amount++
	incf	amount,f				; amount++

	movfw	help					;
	movwf	FSR
	
	movfw	tmpaddr
	movwf	INDF
	incf	FSR,f					; FSR++
	movfw	tmpaddr+1
	movwf	INDF
	incf	help,f					; help++
	incf	help,f					; help++

	decf	lcount,f
	decfsz	lcount,f
	goto	Buffer2Block_loop
Buffer2Block_end

	;--- On efface le BLOCK mémoire à l'adresse qui va etre écrit
	;------------------------------------------------------------
	bcf	STATUS,RP1
	movfw	address + 1
	bsf	STATUS,RP1
	movwf	EEADRH
	bcf	STATUS,RP1
	movfw	address
	addlw	0xE0
	bsf	STATUS,RP1
	movwf	EEADR
	bcf	STATUS,RP1
	call	erase_flash

	movf	address,w
	movwf	tmpaddr					; tmpaddr <== address
	movf	address+1,w
	movwf	tmpaddr+1
	clrf	count					; count <== 0
	movlw	.64
	movwf	amount					; amount <== 64

	;================
	;== WRITE LOOP ==
	;================
write_loop
	movlw	NumRetries+1				; retry = NumRetries+1
	movwf	retry
	
	; On teste si la programmation est finie ?
	;=========================================
w_e_l_1
	movf	amount,w
	subwf	count,w					; while (count<amount)
	btfsc	STATUS,C
	retlw	1	
	movf	count,w
	addlw	buff					; set buffer pointer
	movwf	FSR

	; On teste si on est dans la zone DATA ?
	;=======================================
w_e_l_2
	movlw	0x21					; if (0x2100 <= tmpaddr <= 0x21FF) ..........
	subwf	tmpaddr+1,w
	bsf	STATUS,RP1
	bsf	STATUS,RP0				; (bank3)
	btfsc	STATUS,Z
	goto	data_eeprom				; goto data_eeprom

	; ZONE FLASH-PROGRAMME
	;======================
program_eeprom
	bsf	EECON1,EEPGD				; EEPGD = 1 -> program memory
	clrf	STATUS
	movlw	high (LoaderStart)			; if (tmpaddr >= LoaderStart) ...............
	subwf	tmpaddr+1,w
	movlw	low (LoaderStart)			; mask Booloader, [ICD-Debugger],
	btfsc	STATUS,Z				;      __IDLOCS & __CONFIG
	subwf	tmpaddr,w
	btfsc	STATUS,C
	goto 	write_next_adr				; next address
	goto  	w_e_l_3

	; ZONE FLASH-DATA
	;================
data_eeprom
	bcf	EECON1,EEPGD				; EEPGD = 0 -> data memory
	clrf	STATUS

	; ALL WRITE ...
	; On va tester si on est dans les 4 premières adresses...
	;=========================================================
w_e_l_3
	movf	tmpaddr,w
	bsf	STATUS,RP1
	movwf	EEADR					; EEADR = low tmpaddr
	bcf	STATUS,RP1
	movf	tmpaddr+1,w				; if (tmpaddr < 0x0004) .....................
	btfss 	STATUS,Z
	goto 	w_e_l_4
	movlw	4
	subwf	tmpaddr,w
	btfsc	STATUS,C
	goto	w_e_l_4
	bsf 	STATUS,RP1                  		; (bank3)
	bsf 	STATUS,RP0
	btfss 	EECON1,EEPGD              		; skip if (EEPGD)
	goto	w_e_l_31
	bcf	STATUS,RP0                  		; (bank2)
	IFDEF __16F88
		movlw low UserStart+2			; EEADRL + low UserStart+1
	ELSE
		ERROR   "incorrect type of microcontroller"
	ENDIF
	addwf 	EEADR,f                   		; (relocated first 4 user instructions)

	; Relocalisation des 4 premières adresses ...
	;============================================
w_e_l_31
	clrf	STATUS                    		; (bank0)

	;-- ERRASE FLASH : virtual reset vector[0x0E60]
	bsf	STATUS,RP1
	movlw	high (LoaderStart)
	movwf	EEADRH
	movlw	low (LoaderStart)
	movwf	EEADR
	bcf	STATUS,RP1
	call	erase_flash
	;-- Programmer BUFFER[0,1,2,3] ==> Org UserStart
	bsf	STATUS,RP1
	movlw	high (UserStart)
	movwf	EEADRH
	movlw	low (UserStart)
	movwf	EEADR
	bcf	STATUS,RP1
	movlw	.4
	movwf	lcount
	movlw	buff
	movwf	FSR
	call	write_block
	;-- Remplir BUFFER[0,1,2,3] <== Constantes
	movlw	buff
	movwf	FSR
	movlw	0x15
	movwf	INDF
	incf	FSR,f
	movlw	0x8A
	movwf	INDF
	incf	FSR,f
	movlw	0x2E
	movwf	INDF
	incf	FSR,f
	movlw	low (TrapError)
	movwf	INDF
	incf	FSR,f
	movlw	0x00
	movwf	INDF
	incf	FSR,f
	movlw	0x00
	movwf	INDF
	incf	FSR,f
	movlw	0x00
	movwf	INDF
	incf	FSR,f
	movlw	0x00
	movwf	INDF
	;-- Programmer BUFFER[0,1,2,3] ==> Org UserStart
	bsf	STATUS,RP1
	movlw	high (TrapError)
	movwf	EEADRH
	movlw	low (TrapError)
	movwf	EEADR
	bcf	STATUS,RP1
	movlw	.4
	movwf	lcount
	movlw	buff
	movwf	FSR
	call	write_block
	;-- Remplir BLOCK[0,1,2,3] <== Constantes
	movlw	buff
	movwf	FSR
	movlw	0x15
	movwf	INDF
	incf	FSR,f
	movlw	0x8A
	movwf	INDF
	incf	FSR,f
	movlw	0x2E
	movwf	INDF
	incf	FSR,f
	movlw	low (LoaderMain)
	movwf	INDF
	incf	FSR,f
	movlw	0x00
	movwf	INDF
	incf	FSR,f
	movlw	0x00
	movwf	INDF
	incf	FSR,f
	movlw	0x00
	movwf	INDF
	incf	FSR,f
	movlw	0x00
	movwf	INDF

	;movlw	high UserStart				; EEADRH = high UserStart
	movf	tmpaddr+1,w				; EEADRH = high tmpaddr
	goto	w_e_l_5
	
	; Adresses > ORG 0x0004 ...
	;===========================
w_e_l_4
	movf	tmpaddr+1,w				; EEADRH = high tmpaddr

	; ALL WRITE ...
	;===============
w_e_l_5
	bsf	STATUS,RP1
	movwf	EEADRH					; set EEADRH
	movf	INDF,w
	;MOVLW	0x12
	movwf	EEDATH					; EEDATH = buff[count]
	incf  	FSR,f
	movf  	INDF,w
	;MOVLW	0x34
	movwf	EEDATA					; EEDATA = buff[count+1]
	bsf	STATUS,RP0
	bsf	EECON1,WREN				; WREN=1
	movlw	0x55					; EECON2 = 0x55
	movwf	EECON2
	movlw	0xAA					; EECON2 = 0xAA
	movwf	EECON2
	bsf	EECON1,WR				;	WR=1
	nop						; instructions are ignored
	nop						; microcontroller waits for a complete write
	clrf	STATUS

;>>> ADAPT  
	banksel	EECON1
	btfsc   EECON1,EEPGD               		; skip if (EEPGD=0 / write to EEPROM)
	goto	ver_flash
;>>>

	banksel	PIR2
wait_write
	clrwdt
	btfss	PIR2,EEIF				; necessary for a write to data eeprom
	goto	wait_write
	bcf	PIR2,EEIF
	bsf 	STATUS,RP0				; (bank3)
	bsf 	STATUS,RP1
	bcf	EECON1,WREN				; WREN=0


	bsf	EECON1,RD				; RD=1
	nop
	nop
	bcf 	STATUS,RP0				; (bank2)
	decf  	FSR,f
	movf  	INDF,w					; if ((EEDATH != buff[count]) || (EEDATA != buff[count+1]))
	xorwf 	EEDATH,w
	btfss 	STATUS,Z
	goto	write_cycle_erreur			; repeat write
	incf  	FSR,f
	movf  	INDF,w
	xorwf 	EEDATA,w
	btfsc	STATUS,Z
	goto	write_next_adr				; verification OK, next address

	;===============================================
	;== FIN DE CYCLE DE PROGRAMMATION AVEC ERREUR ==
	;===============================================
write_cycle_erreur
	clrf	STATUS					; (bank0)
	decfsz	retry,f
	goto	w_e_l_1					; if (--retry != 0) repeat write
	retlw	0					; else return 0 (BAD)

	;======================================
	;== On se place à l'adresse suivante ==
	;======================================
write_next_adr
    	banksel count
	movlw	.2					; count := count + 2
	addwf	count,f
	incf	tmpaddr,f				; tmpaddr := tmpaddr + 1
	btfsc	STATUS,Z
	incf	tmpaddr+1,f
	goto	write_loop

	;======================================
	;== On vérifie la zone programmer... ==
	;======================================
ver_flash
	banksel	PIR2
	bcf	PIR2,EEIF
	banksel	EECON1
	bcf	EECON1,WREN				; WREN=0

    	banksel EEADR
    	movf    EEADR,w
    	andlw   0x03
    	sublw   0x03
    	btfss   STATUS,Z
    	goto    write_next_adr        			; if (EEADR & 0x03) = 0x03

    	movlw   .3
    	subwf   EEADR,f         			; EEADR <- EEADR - 3

    	banksel help
    	movlw   .4
    	movwf   help            			; help <- 4

    	movlw   .7
    	subwf   FSR,f	           			; FSR <- FSR - 7

ver_fl_1
    	nop
	banksel	EECON1
	bsf	EECON1,RD				; RD=1
	nop
	nop
	nop
	nop
	nop
	nop

	movf    INDF,w					; if ((EEDATH != buff[count]) || (EEDATA != buff[count+1]))
	banksel	EEDATH
	xorwf   EEDATH,w
	btfss   STATUS,Z
	goto	ver_fl_2				; repeat write
	incf    FSR,f
	movf    INDF,w
	xorwf   EEDATA,w
	btfsc	STATUS,Z
	goto	ver_fl_3

	; Erreur pendant la vérifiaction
	;================================
ver_fl_2                    				
    	banksel tmpaddr
    	movlw   0x0fc
    	andwf   tmpaddr,f       			; clear least significant two bits in [tmpaddr]      

    	movlw   6
    	subwf   count,f           			; count <- count - 6

    	goto    write_cycle_erreur

	; vérifiaction OK ==> Adresse suivante
	;======================================
ver_fl_3
    	banksel EEADR
	incf    EEADR,f
	incf    FSR,f

    	banksel help
    	decfsz  help,f
    	goto    ver_fl_1
   	goto    write_next_adr
  
;>>>


;==============================================================================
;==============================================================================

	END

