;**** A P P L I C A T I O N   N O T E   A V R 236 ************************
;*
;* Title:		CRC check of program memory
;* Version:		1.0
;* Last updated:	98.06.15
;* Target:		AT90Sxxxx (All AVR Devices with LPM instr, not AT90S1200)
;*
;* Support E-mail:	avr@atmel.com
;*
;* NOTE: Always check out Atmels web site, www.atmel.com for the latest and updated 
;* version of the software.
;*
;* DESCRIPTION
;* This application note describes how to perform CRC computation 
;* of code memory contents using a simple algoritm. 
;* To generate CRC checksum load the register "status" with 00 and call the routine 
;* "crc_gen". The resulting checksum is placed in the registers
;* byte2(low byte) and byte3(high byte).
;*
;* To check the CRC checksum load the register "status" with FF and call the routine 
;* "crc_gen". The resulting checksum is placed in the registers
;* byte2(low byte) and byte3(high byte). If the checksum is 00 the program code is
;* correct, if the checksum is non-zero an error has been introduced in the program code
;**************************************************************************


.include "..\8515def.inc"
.device AT90S8515

;***** Constants

.equ 	PROGSIZE 	= 0x1FFF	; Size of program memory(bytes)
.equ	CR		= 0x8005	; CRC divisor value

;**************************************************************************
;*
;*      PROGRAM START - EXECUTION STARTS HERE
;*
;**************************************************************************

;        .cseg

        .org $0000
        rjmp RESET      ;Reset handle


;***************************************************************************
;*
;* "crc_gen" - Generation and checking of CRC checksum
;*
;* This subroutine generates the checksum for the program code.
;* 32 bits are loaded into 4 register, the upper 16 bits are XORed
;* with the divisor value each time a 1 is shifted into the carry flag 
;* from the MSB.
;*
;* If the status byte is 0x00,the routine will generate new checksum:
;* After the computing the code 16 zeros are
;* appended to the code and the checksum is calculated. 
;* 
;* If the status byte is different from 0X00, the routine will check if the current checksum is valid.
;* After the computing the code the original checksum are
;* appended to the code and calculated. The result is zero if no errors occurs
;* The result is placed in registers byte2 and byte3
;*  
;* Number of words	:44 + return
;* Number of cycles	:program memory size(word) * 175(depending on memory contens)
;* Low registers used	:6 (byte0,byte1,byte2,byte3)
;* High registers used  :7 (sizel,sizeh,crdivl,crdivh,count,status,zl,zh)	
;*
;***************************************************************************

;***** Subroutine Register Variables

.def	byte0	= r0		; Lower byte of lower word
.def	byte1	= r1		; Upper byte of lower word
.def	byte2	= r2		; Lower byte of upper word
.def	byte3	= r3		; Upper byte of upper word
.def	crc	= r4		; CRC checksum low byte
.def	crch	= r5		; CRC checksum high byte
.def	sizel 	= r17		; Program code size register
.def	sizeh 	= r18
.def	crdivl	= r19		; CRC divisor register
.def	crdivh	= r20
.def 	count	= r21		; Bit counter 
.def	status	= r22		; Status byte: generate(0) or check(1)

crc_gen:ldi 	sizel,low(PROGSIZE) 	;Load end of program memory address
	ldi 	sizeh,high(PROGSIZE)	
	clr 	zl			;Clear Z pointer
	clr 	zh				
	ldi 	crdivh,high(CR)		;Load divisor value
	ldi	crdivl,low(CR)
	lpm				;Load first memory location
	mov	byte3,byte0		;Move to highest byte
	adiw 	zl,0x01			;Increment Z pointer
	lpm				;Load second memory location
	mov	byte2,byte0		

new_word:cp	zl,sizel		;Loop starts here
	cpc	zh,sizeh		;Check for end of code
	brge	end			;Jump if end of code
	adiw 	zl,0x01			
	lpm				;Load high byte
	mov	byte1,byte0		;Move to upper byte
	adiw	zl,0x01			;Increment Z pointer
	lpm				;Load program memory location
	rcall 	rot_word		;Call the rotate routine
	rjmp 	new_word

end:	;ret	;uncomment this line if checksum is stored in last flash memory address.
	ldi    	count,0x11		
	cpi    	status,0x00		
	brne   	check
        clr    	byte0			;Append 16 bits(0x0000) to
        clr    	byte1			;the end of the code for CRC generation
	rjmp   	gen	
check:	mov    	byte0,crc		;Append the original checksum to
        mov    	byte1,crch    		;the end of the code for CRC checking
gen:	rcall  	rot_word		;Call the rotate routine
	mov    	crc,byte2		
	mov    	crch,byte3
	ret				;Return to main prog
	
rot_word:ldi	count,0x11
rot_loop:dec 	count			;Decrement bit counter
	breq	stop			;Break if bit counter = 0
	lsl	byte0			;Shift zero into lowest bit
	rol	byte1			;Shift in carry from previous byte
	rol	byte2			;Preceede shift
	rol	byte3			
	brcc	rot_loop		;Loop if MSB = 0			
	eor 	byte2,crdivl
	eor 	byte3,crdivh		;XOR high word if MSB = 1
	rjmp	rot_loop
stop:	ret

;***************************************************************************
;* 
;* EERead_seq
;*
;* This routine reads the 
;* EEPROM into the global register variable "temp".
;*
;* Number of words	:4+ return
;* Number of cycles	:8 + return 
;* High Registers used	:4 (temp,eeadr,eeadrh,eedata)
;*
;***************************************************************************

.def	temp    = r16 
.def	eeadr	= r23
.def	eeadrh	= r24
.def	eedata	= r25

;***** Code

eeread:
	out	EEARH,eeadrh	;output address high byte
	out	EEARL,eeadr	;output address low byte
	sbi	EECR,EERE	;set EEPROM Read strobe
	in	eedata,EEDR	;get data
	ret

;***************************************************************************
;* 
;* EEWrite
;*
;* This subroutine waits until the EEPROM is ready to be programmed, then
;* programs the EEPROM with register variable "EEdwr" at address "EEawr"
;*
;* Number of words	:7 + return
;* Number of cycles	:13 + return (if EEPROM is ready)
;* Low Registers used	:None
;* High Registers used:	;4 (temp,eeadr,eeadr,eedata)
;*
;***************************************************************************

.def	temp    = r16 
.def	eeadr	= r23
.def	eeadrh	= r24
.def	eedata	= r25

eewrite:sbic	EECR,EEWE	;If EEWE not clear
	rjmp	EEWrite		;    Wait more
	out	EEARH,eeadrh	;Output address high byte
	out	EEARL,eeadr	;Output address low byte
	out	EEDR,eedata	;Output data
	sbi	EECR,EEMWE
	sbi	EECR,EEWE	;Set EEPROM Write strobe
	ret


;************************************************************************
;*
;*  Start Of Main Program
;*
.cseg
.def	crc     = r4		;Low byte of checksum to be returned
.def	crch	= r5		;High byte of checksum to be returned
.def	temp    = r16  
.def	status	= r22		;Status byte: generate(0) or check(1)
.def	eeadr	= r23
.def	eeadrh	= r24
.def	eedata	= r25

RESET:	ldi	r16,high(RAMEND) ;Initialize stack pointer	
	out	SPH,r16	         ;High byte only required if 
	ldi	r16,low(RAMEND)	 ;RAM is bigger than 256 Bytes
	out	SPL,r16

	ldi 	temp,0xff	
	out 	DDRB,temp	;Set PORTB as output
	out 	PORTB,temp	;Write 0xFF to PORTB
	clr 	status		;Clear status register, prepare for CRC generation
	rcall 	crc_gen
	
	ldi 	eeadr,0x01	;Set address low byte for EEPROM write
	ldi 	eeadrh,0x00	;Set address high byte for EEPROM write
	mov 	eedata,crc	;Set CRC low byte in EEPROM data
	rcall	eewrite		;Write EEPROM

	ldi 	eeadr,0x02	;Set address low byte for EEPROM write
	ldi 	eeadrh,0x00	;Set address high byte for EEPROM write
	mov 	eedata,crch	;Set CRC high byte in EEPROM data
	rcall	eewrite		;Write EEPROM

	out 	PORTB,crc	;Output CRC low value to PORTB
	
mainloop:sbic	EECR,EEWE	;If EEWE not clear
	rjmp	mainloop	;    Wait more

;********** Insert program code here *************



	ldi 	eeadr,0x01	;Set address low byte for EEPROM read
	ldi 	eeadrh,0x00	;Set address high byte for EEPROM read
	rcall	eeread		;Read EEPROM
	mov 	crc,eedata	;Read CRC low byte from EEPROM

	ldi 	eeadr,0x02	;Set address low byte for EEPROM read
	ldi 	eeadrh,0x00	;Set address high byte for EEPROM read
	rcall	eeread		;Read EEPROM
	mov 	crch,eedata	;Read CRC low byte from EEPROM

	ser	status		;Set status register, prepare for CRC checking
	rcall 	crc_gen

loop:	out	PORTB,crc	;Output CRC low value to PORTB
	rjmp 	loop

.exit