
; Minimalistic 256-byte XMODEM-based bootloader
; for ATMega8 running at 8MHz
;
; Based on code found on this thread:
; http://www.groupsrv.com/computers/about98124.html
; specifically, at: http://www.ejberg.dk/temp/boot16.zip
;
; Allows writing to the flash only. There is no support for reading
; from the flash, nor there is support for reading/writing to the
; EEPROM and/or fuses/lock bits. But it does check the CRC within
; XMODEM packets.

.include "m8def.inc"

.equ	UARTBaudRateReg	=51	        ; = 9600 baud at 8 MHz
.equ	TC1Reload	=65536-15625	; = 1 second at Ck/1024 prescaler

.equ	Xmodem_SOH	=0x01
.equ	Xmodem_EOT	=0x04
.equ	Xmodem_ACK	=0x06
.equ	Xmodem_NAK	=0x15
.equ	Xmodem_CAN	=0x18
.equ	Xmodem_C	=0x43

.equ    crc_polynomial = 0x1021
.equ    crc_init_value = 0x0000

; r0-r1 reserved for mul results
.def	w1	=r16	; General purpose working register
.def	w2	=r17	; General purpose working register
.def	w3	=r18	; General purpose working register
.def	w4	=r19	; General purpose working register
.def	Retries	=r20	; Protocol retry counter
.def	ExpSeq	=r21	; Expected next sequence number
.def	RetryChar=r22
.def    w5  = r23
; r26-31 reserved to X, Y and Z registers
; X  Receivebufferpointer


.dseg
XmodemBuffer:	;Equals next byte
XmodemBlk:	.byte 1
XmodemBlkCom:	.byte 1
XmodemData:	.byte 128
XmodemCRC:	.byte 2


.cseg

.org	0x0F80

;**** Bootloader initialization
RESET:
;	ldi	w1,low(RAMEND)
;	out	SPL,w1			;Init stackpointer
	ldi	w1,high(RAMEND)-1
	out	SPH,w1
	
;	clr	w1			;Init Timer/Counter 1
;	out	TCCR1A,w1
	ldi	w1,(5<<CS10)
	out	TCCR1B,w1
	
;	ldi	w1,high(UARTBaudRateReg);Init USART
;	out	UBRRH,w1
	ldi	w1,low(UARTBaudRateReg)
	out	UBRRL,w1
;	clr	w1
;	out	UCSRA,w1
	ldi	w1,(1<<RXEN)|(1<<TXEN)
	out	UCSRB,w1
;	ldi	w1,(1<<URSEL)|(3<<UCSZ0)
;	out	UCSRC,w1

start:
	ldi     w1,Xmodem_C
	out     UDR,w1		;TX Xmodem ready to receive (CRC)
	ldi     Retries,10
	ldi     RetryChar,Xmodem_C
	ldi     ExpSeq,1
	
StateIdle:
    rcall   StartTimer
StateIdle2:
	sbic	UCSRA,RXC
	rjmp	SI_Receive
	rcall	Timeout
	brtc	StateIdle2
	dec	    Retries
	brne	SI_MoreRetries
	rjmp	Boot
SI_MoreRetries:
	out     UDR,RetryChar	;TX Xmodem NAK
	rjmp	StateIdle
SI_Receive:
	in      w1,UDR
	cpi     w1,Xmodem_SOH
	breq    SI_SOH
	cpi     w1,Xmodem_EOT
	brne	StateIdle2
	ldi     w1,Xmodem_ACK
Boot0:
	out     UDR,w1		;TX Xmodem ACK
Boot:
    rcall reenable_rww
    rjmp FLASHEND+1
SI_SOH:
	cpi     RetryChar,Xmodem_C
	brne	SI_NotStart
	ldi     RetryChar,Xmodem_NAK
	ldi     Retries,10
SI_NotStart:
	ldi     XL,low(XmodemBuffer)
	ldi     XH,high(XmodemBuffer)
	rcall   StartTimer
StateRX:
	sbic	UCSRA,RXC
	rjmp	SRX_Receive
	rcall	Timeout
	brtc	StateRX
SRX_NakIdle:
	ldi	w1,Xmodem_NAK
	out	UDR,w1		;TX Xmodem NAK
	rjmp	StateIdle
SRX_Receive:
	rcall	StartTimer
	in      w1,UDR
	st      X+,w1
	cpi     XL,low(XmodemCRC+2)
	brne    StateRX
	ldi     w1,130
	ldi     ZL,low(XmodemData)
	ldi     ZH,high(XmodemData)
    ldi     XL,low(crc_init_value)
    ldi     XH,high(crc_init_value)
    ldi     YL,low(crc_polynomial)
    ldi     YH,high(crc_polynomial)
crc16_byteloop:
    ld      w2,Z+
    eor     XH,w2
    ldi     w2,8
crc16_bitloop:
    lsl     XL
    rol     XH
    brcc    crc16_nextbit
    eor     XL,YL
    eor     XH,YH
crc16_nextbit:
    dec     w2
    brne    crc16_bitloop
    dec     w1
    brne    crc16_byteloop
	or      XL,XH
	brne    SRX_NakIdle	; CRC error
	lds     w1,XmodemBlk
	cp      w1,ExpSeq
	breq    FlashPage
	inc     w1
	cpse    w1,ExpSeq
	rjmp    SRX_Abort
SendAck:
	ldi     w1,Xmodem_ACK	; Received previous frame
	out     UDR,w1		; TX Xmodem ACK
	rjmp    StateIdle
SRX_Abort:
	ldi	w1,Xmodem_CAN
	rjmp	Boot0

FlashPage:
	clr     YL
	;lds     ZH,XmodemBlk
    mov     YH,ExpSeq
	dec     YH
	lsr     YH
	ror     YL
    ldi     w4,2
    ldi     XL,low(XmodemData)
    ldi     XH,high(XmodemData)
FP_Block:
    movw    ZL,YL
	ldi     w1,(1<<PGERS)|(1<<SPMEN)
    rcall   do_spm
    ldi     w2,PAGESIZE
FP_WriteLoop:
	ld      r0,X+
	ld	    r1,X+
    ldi     w1,(1<<SPMEN)
    rcall   do_spm
	adiw	ZL,2
	dec     w2
	brne	FP_WriteLoop

	movw	ZL,YL
	ldi	    w1,(1<<PGWRT)|(1<<SPMEN)
    rcall   do_spm
    adiw    YL,PAGESIZE
    adiw    YL,PAGESIZE
    dec     w4
    brne    FP_Block

	inc	    ExpSeq
    rjmp    SendAck

StartTimer:
	ldi	w5,high(TC1Reload)
	out	TCNT1H,w5
	ldi	w5,low(TC1Reload)
	out	TCNT1L,w5
	ldi	w5,(1<<TOV1)
	out	TIFR,w5		; Clear overflow flag
	ret

reenable_rww:
    ldi     w1,(1<<RWWSRE)|(1<<SPMEN)
do_spm:
    in      w3,SPMCR
    sbrc    w3,SPMEN
    rjmp    do_spm
    out     SPMCR,w1
    spm
Timeout:
	in	w5,TIFR
    bst w5,TOV1
	ret

