;*************************************************************************
;*************************************************************************
;**						FIRMAWARE PRO AVR PROGRAMATOR					**
;*************************************************************************
;*************************************************************************
;* Autor			: Lukas Verner		
;* Poslední úpravy	: 30.5.2009
;* Soubor			: AVRProgISP.asm
;* Urceno pro uP	: ATmega8535
;*************************************************************************


.nolist
.include "m8535def.inc"
.list





;		  *********************************************************
;		  *						Definice maker					  *
;		  *	Definujou polohu RESETU a SCK a MISO a MOSI vyvodu	  *
;		  *********************************************************

.equ	resetpin	= 2

.macro	set_reset
	cbi		portd,resetpin
.endm

.macro	clr_reset
	sbi		portd,resetpin
.endm


.macro	impulsSCK
	sbi		portb,SCK
	ldi		temp2,8		
m0:	
	dec		temp2
	brne	m0
	cbi		portb,SCK
	ldi		temp2,4		
m1:	
	dec		temp2
	brne	m1
.endm

; ***SPI konstanty***
.equ	MOSI	= 5						; Cislo bitu na portuB
.equ	MISO	= 6						; Cislo bitu na portuB
.equ	SCK		= 7						; Cislo bitu na portuB

; ***UART konstatny***
.equ	N = 23							; 19.2k pro (7,3728MHz crystal)

; ***Globalni promenne***
.def	kod_soucastky	= r1				;Kod zarizeni
.def	pgm_mod	= r2				;0 = normalni bajtovy mod
;										;<> 0 = page mod
.def	soucastka	= r16				; Kod zarizeni
.def	temp1		= r17
.def	temp2		= r18
.def	spi_data	= r19			;SPI data
.def	data_uart	= r20			;UART data
.def	addrl		= r21				;Nizsi bajt adresy
.def	addrh		= r22				;Vyssi adresy
.def	cmd3		= r23	
.def	precti_spi_data	= r24	
.def	cmd			= r25				;ISP prikaz
.def	count		= r26				;pocitadlo pro "vstup do programovaciho modu"
.def	param1		= r27
.def	cmd1		= r28
.def	cmd2		= r29


;*********************
;* Interrupt Vectors *
;*********************

.CSEG
	rjmp	RESET		


;Initialize UART.
uart:	
	ldi		temp1,N				  		; nastavi baudovou rychlost na 19,2kBd
	out		UBRRL,temp1
	ldi		temp1,1<<TXEN|1<<RXEN 		; inicializace UART pro TX a RX
	out		UCSRB,temp1
	ldi 	temp1,(1<<URSEL)|(3<<UCSZ0)	; 8 data, 1 stop bit
	out 	UCSRC,temp1
	ret
posli_znak:
	sbis	UCSRA,UDRE					; skakej dokud nebude registr TX prazdny?
	rjmp	posli_znak									;
	out		UDR,data_uart				; posli bajt na uart
	ret
prijmi_znak:
	sbis	UCSRA,RXC					; cekej dokud neni prijat znak
	rjmp	prijmi_znak
	in		data_uart,UDR				; precti z uartu bajt
	ret
zpozdeni:	
	ldi		temp2,0xff
dl:	dec		temp2
	brne	dl
	dec		temp1
	brne	zpozdeni
	ret


;		  *********************************************************
;		  *						FUNKCE precti/zapis				  *
;		  *				precte/zapise byte z/do SPI sbernice	  *
;		  *********************************************************

precti:	
	clr		spi_data
zapis:	
	ldi		temp1,8
	ldi		precti_spi_data,0
crs0:	
	rol		spi_data
	brcc	crs1
	sbi		portb,MOSI
	rjmp	crs2
crs1:	
	cbi		portb,MOSI
crs2:	
	lsl		precti_spi_data
	sbic	pinb,MISO
	ori		precti_spi_data,1
	impulsSCK
	dec		temp1
	brne	crs0
	mov		spi_data,precti_spi_data
	ret


;		  *********************************************************
;		  *						FUNKCE spiinit					  *
;		  *				vstup do programovaciho modu			  *
;		  *********************************************************

spiinit:
	nop
	ldi		temp1,(0xFF ^ (1<<MISO))	; inicializace portuB
	out		ddrb,temp1					; PB5(MISO) je vstup, zbytek vystup			; initialize port B
	cbi		portb,SCK					; nuluj SCK
	set_reset							; RESET = 1
	ldi		temp1,0xff					; zpozdeni(0xff);
	rcall	zpozdeni
	clr_reset							; RESET = 0
	ldi		temp1,0xff					; zpozdeni(0xff);
	rcall	zpozdeni
	ldi		spi_data,0xac				; zapis(0xac);	// zapis bajtu 1 na SPI
	rcall	zapis
	ldi		spi_data,0x53				; zapis(0x53);	// zapis bajtu 2 na SPI
	rcall	zapis
										; // SPI Synchronizace
	cpi		soucastka,0x20					; if ( (soucastka >= 0x20) && (soucastka <= 0x7F) )
	brlo	s2
	tst		soucastka
	brmi	s2
s0b:									; {
	ldi		count,32					;	count = 32;
s1:										;	do {
	rcall	precti						;		if (precti == 0x53)	// SPI cteni (bajt 3)
	cpi		spi_data,0x53
	breq	s3							;			break;
	ldi		spi_data,0x00				;		zapis(0x00);		// SPI zapis (bajt 4)
	rcall	zapis
	impulsSCK							;		impuls SCK
	ldi		spi_data,0xac				;		zapis(0xac);		// SPI write (bajt 1)
	rcall	zapis
	ldi		spi_data,0x53				;		zapis(0x53);		// SPI write (bajt 2)
	rcall	zapis
	dec		count						;	} while(--count);
	brne	s1
	rjmp	s3							; }
										; else
s2:										; {
	ldi		spi_data,0x00				;	zapis(0x00);		// SPI write (bajt 3)
	rcall	zapis
s3:										; }
	ldi		spi_data,0x00				;	zapis(0x00);	// SPI write (bajt 4)
	rcall	zapis
	ldi		temp1,0x10					; zpozdeni(0x10);
	rcall	zpozdeni
	ret


jmeno_programatoru:
	ldi		zl,low(ID*2)				;Ukazatel Z na string
	ldi		zh,high(ID*2)
	rcall	msg_out
	ret
;@@

RESET:
	ldi 	temp1,low(RAMEND)
	out 	SPL,temp1					; nastaveni zasobniku
	ldi 	temp1,high(RAMEND)
	out 	SPH,temp1	
	clr		temp1
	out		GIMSK,temp1					; zakaze externi preruseni
	ser		temp1		
	out		PORTD,temp1
	set_reset							; RESET=1
	out		PORTB,temp1
	sbi		ddrd,resetpin				;(RESET) je vystup
	rcall	uart						; inicializace UART


;		  *********************************************************
;		  *						HLAVNI PROGRAM					  *
;		  *				ceka a vykonava obsluzne prikazy		  *
;		  *********************************************************
cekaci_smycka:
	rcall	prijmi_znak					; while (prijmi_znak() == ESC) {};
	cpi		data_uart,0x1b
	breq	cekaci_smycka

;**** Detekce typu zarizeni ****
	cpi		data_uart,'T' 
	brne	c0
	rcall	prijmi_znak					; prijmi_znak();
	mov		soucastka,data_uart			; posli_znak(soucastka);
	rjmp	navrat_voziku

;**** Vypise Nazev software ****
c0:	
	cpi		data_uart,'S'
	brne	c1
	rcall	jmeno_programatoru			; jmeno_programatoru();
	rjmp	cekaci_smycka

;**** Vypise verzi software ****
c1:	
	cpi		data_uart,'V'
	brne	c2
	ldi		data_uart,0					;hlavni cislo
	rcall	posli_znak
	ldi		data_uart,0					;vedlejsi cislo
	rcall	posli_znak
	rjmp	cekaci_smycka


;**** Vypise verzi hardware ****
c2:	
	cpi		data_uart,'v'
	brne	c3
	ldi		data_uart,0 				; hlavni cislo
	rcall	posli_znak
	ldi		data_uart,0 				; vedlejsi cislo
	rcall	posli_znak
	rjmp	cekaci_smycka

;**** Ukaze podporovane zarizeni ****
c3:	
	cpi		data_uart,'t'
	brne	c4
	ldi		zl,low(tabulka_AVR*2)		;Ukazatel Z na tabulku zarizeni
	ldi		zh,high(tabulka_AVR*2)
show_soucastkas:
	rcall	prijmi_id_soucastky			;kod zarizni do registru kod_soucastky
	mov		data_uart,kod_soucastky
	cpi		data_uart,0xff				;Test konce tabulky
	breq	end_of_soucastkas			;konec
	rcall	posli_znak					;neni konec, posli na uart
	rjmp	show_soucastkas
end_of_soucastkas:
	clr		data_uart
	rcall	posli_znak					;konec listu zarizeni
	rjmp	cekaci_smycka


;**** Vypise typ programatoru ****
c4:	
	cpi		data_uart,'p'     		
	brne	c51
	ldi		data_uart,'S'     			; 'S' jako seriovy programator
	rcall	posli_znak
	rjmp	cekaci_smycka

;**** podpora pro automatickou inkrementaci adresy?
c51:	
	cpi		data_uart,'a'	
	brne	c5
	ldi		data_uart,'Y'				; posli_znak('Y'); 	//ANO, podporovano
	rcall	posli_znak
	rjmp	cekaci_smycka
c5:    
	cpi		data_uart,'x'				; Prikazy x a y jsou ignorovany 
	brne    c6							; maj vyznam jen pro originalni atmel programatory,
	rcall	prijmi_znak        			; obsahujici LED.
	rjmp    navrat_voziku				; Musi zde byt, kvuli odstraneni chyby v AVRStudiu, tzv. "Flush" error.
c6:    
	cpi		data_uart,'y'     
	brne    c7
	rcall   prijmi_znak		
	rjmp    navrat_voziku

;**** Vstup do programovaciho modu ****
; Test, zda byl vybrat kod zarizeni pred vyslanim jakyhokoliv prikazu
c7:

	push	temp2					;uloz temp2 
	ldi		zl,low(tabulka_AVR*2)	;Ukazatel Z na tabulku zarizeni
	ldi		zh,high(tabulka_AVR*2)
c7_lp:
	rcall	prijmi_id_soucastky		;Do registru kod_soucastky ulozi kod zarizeni
	mov		temp2,kod_soucastky
	cpi		temp2,0xff				;Kontrola konce tabulky
	breq	soucastka_err				;soucastka nenalezena
	cp		temp2,soucastka			;Nalezen kod?
	breq	x_c7_lp					;Ano
	rjmp	c7_lp					;Dalsi soucastka code
x_c7_lp: 
	pop		temp2					;Obnov temp2	
	rjmp	c72
soucastka_err:
	pop		temp2					;Obnov temp2
	rjmp	chyba					;Goto chyba();

;Vstup do programovaciho modu
c72:	
	cpi		data_uart,'P'		
	brne	c8
	rcall	spiinit					; spiinit();
	rjmp	navrat_voziku

;**** Zapis pameti programu, Vyssi bajt ****
c8:	
	cpi		data_uart,'C'		
	brne	c9
	rcall	prijmi_znak
c81:	
	ldi		spi_data,0x48			; zapis(0x48);		// Zapis bajtu 1 na SPI
	rcall	zapis
	mov		spi_data,addrh			; zapis(addrh);		// Zapis bajtu 2 na SPI
	rcall	zapis
	mov		spi_data,addrl			; zapis(addrl);		// Zapis bajtu 3 na SPI
	rcall	zapis

	mov		spi_data,data_uart		; zapis(data_uart);	// Zapis bajtu 4 na SPI
	rcall	zapis

	ldi		temp1,0x01				; Automaticka inkrementace adresy
	clr		temp2
	add		addrl,temp1
	adc		addrh,temp2
	rjmp	zapisFLASHzpozdeni

;**** Zapis pameti programu, Nizsi bajt ****
c9:	
	cpi		data_uart,'c'
	brne	c12
	rcall	prijmi_znak
	ldi		spi_data,0x40			;	zapis(0x40);	 // Zapis bajtu 1 na SPI
	rcall	zapis
	mov		spi_data,addrh			;	spi_data = addrh;
c91:								; }
	rcall	zapis					; zapis(spi_data);	// Zapis bajtu 2 na SPI
	mov		spi_data,addrl			; zapis(addrl);		// Zapis bajtu 3 na SPI
	rcall	zapis
	mov		spi_data,data_uart			; zapis(data_uart);	// Zapis bajtu 4 na SPI
	rcall	zapis
zapisFLASHzpozdeni:

	tst		pgm_mod
	brne	c92						; Pokud je podporovan page mod, zadne zpozdeni(hodnota 0)

	ldi		temp1,0x20				; zpozdeni(0x20);		// 24585 cycles zpozdeni
	rcall	zpozdeni					; // Page mode nepotrebuje zpozdeni!
c92:
	rjmp	navrat_voziku					; goto reply();

;**** Cteni pameti programu ****
c12:	
	cpi	data_uart,'R'
	brne	c10		
	ldi	spi_data,0x28					; zapis(0x28);		// Zapis bajtu 1 na SPI
	rcall	zapis
	mov	spi_data,addrh				; spi_data = addrh;
	rcall	zapis					; zapis(spi_data);	// Zapis bajtu 2 na SPI
	mov	spi_data,addrl				; zapis(addrl);		// Zapis bajtu 3 na SPI
	rcall	zapis
	rcall	precti					; posli_znak(precti());	// Posli data na uart (bajt 4)
	mov	data_uart,spi_data
	rcall	posli_znak
	ldi	spi_data,0x20					; zapis(0x20);	// SPI zapis (bajt 1)
	rcall	zapis
	mov	spi_data,addrh				; zapis(addrh);	// SPI zapis (byte 2)
	rcall	zapis
	mov	spi_data,addrl				; zapis(addrl);	// SPI zapis (bajt 3)
	rcall	zapis
	rcall	precti					; posli_znak(precti());	// Posli data na uart (bajt 4)
	mov	data_uart,spi_data
	rcall	posli_znak
	rjmp	readaddrinc

;**** Nacti adresu ****
c10:	
	cpi	data_uart,'A'
	brne	c11
	rcall	prijmi_znak				; addrh = prijmi_znak();
	mov	addrh,data_uart
	rcall	prijmi_znak				; addrl = prijmi_znak();
	mov	addrl,data_uart
	rjmp	navrat_voziku			; goto reply();

;**** Zapis pameti dat ****
c11:	
	cpi		data_uart,'D'
	brne	c13
	rcall	prijmi_znak
	ldi		spi_data,0xc0
	rcall	zapis
	mov		spi_data,addrh
	rcall	zapis
	mov		spi_data,addrl
	rcall	zapis
	mov		spi_data,data_uart
	rcall	zapis
	ldi		temp1,0x20
	rcall	zpozdeni
	ldi		temp1,0x01				; Automaticka inkrementace adresy
	clr		temp2
	add		addrl,temp1
	adc		addrh,temp2
	rjmp	navrat_voziku

;**** Cteni pameti dat ****
c13:	
	cpi		data_uart,'d'
	brne	c14
	ldi		spi_data,0xa0			;	zapis(0xA0);	// SPI zapis (bajt 1)
	rcall	zapis
	mov		spi_data,addrh			;	spi_data = addrh;
	rcall	zapis					; zapis(spi_data);	// SPI zapis (bajt 2)
	mov		spi_data,addrl			; zapis(addrl);		// SPI zapis (bajt 3)
	rcall	zapis
	rcall	precti					; posli_znak(precti());	// Send data (bajt 4)
	mov	data_uart,spi_data
	rcall	posli_znak
readaddrinc:
	ldi		temp1,0x01				; Automaticka inkrementace adresy!!!!
	clr		temp2
	add		addrl,temp1
	adc		addrh,temp2
	rjmp	cekaci_smycka					; goto cekaci_smycka();

;**** Opustit programovaci mod ****
c14:	
	cpi		data_uart,'L'		
	brne	c15
	sbi		ddrd,resetpin			; PD2 (RESET) je vystup
	set_reset						; RESET = 1
	rjmp    navrat_voziku

;**** Smazani cipu ****
c15:	
	cpi		data_uart,'e'     
	brne	c16
	ldi		spi_data,0xac
	rcall	zapis
	ldi		spi_data,0x80
	rcall	zapis
	ldi		spi_data,0x04
	rcall	zapis
	ldi		spi_data,0x00
	rcall	zapis
	ldi		temp1,0x30
	rcall	zpozdeni
	rjmp	navrat_voziku

;**** Zapis Lock Bitu ****
c16:	
	cpi		data_uart,'l'	
	brne	c17
	rcall	prijmi_znak
	ldi		spi_data,0xac
	rcall	zapis
	mov		spi_data,data_uart
	andi	spi_data,0x06
	ori		spi_data,0xe0
	rcall	zapis
	ldi		spi_data,0x00
	rcall	zapis
c162:	
	ldi		spi_data,0x00
	rcall	zapis
	ldi		temp1,0x30
	rcall	zpozdeni
	rjmp	navrat_voziku
;**** Cteni signatury ****

c17:	
	cpi		data_uart,'s'
	brne	c18
	ldi		param1,0x02
	rcall	c17call
	ldi		param1,0x01
	rcall	c17call
	ldi		param1,0x00
	rcall	c17call
	rjmp	cekaci_smycka

c17call:
	ldi		spi_data,0x30
	rcall	zapis
	ldi		spi_data,0x00
	rcall	zapis
	mov		spi_data,param1
	rcall	zapis
	rcall	precti
	mov		data_uart,spi_data
	rcall	posli_znak
	ret

;**** Zapis pameti programu po strankach, tzv. Memory page mode ****
c18:	
	cpi		data_uart,'m'			
	brne	c99
	ldi		spi_data,0x4c			;	zapis(0x4c);	// SPI zapis (byte 1)
	rcall	zapis
	mov		spi_data,addrh			;	zapis(addrh);	// SPI zapis (byte 2)
	rcall	zapis
	mov		spi_data,addrl			;	zapis(addrl);	// SPI zapis (byte 3)
	rcall	zapis
	ldi		spi_data,0x00			;	zapis(0x00);	// SPI zapis (byte 4)
	rcall	zapis
	ldi		temp1,0xff				; 	zpozdeni(0xFF);	// 0x20 = 24585 cycles zpozdeni
	rcall	zpozdeni
	rjmp	navrat_voziku


;**** Prikaz Error ****
c99:
chyba:
	ldi		data_uart,'?'				; posli_znak('?');	\\ send '?'
	rcall	posli_znak
	rjmp	cekaci_smycka

;**** Prikaz pro odpoved ****
navrat_voziku:
	ldi		data_uart,0x0d				; posli_znak(0x0D);	\\ send CR
	rcall	posli_znak
	rjmp	cekaci_smycka
zpozdeni1us:								;    count               R16
	nop                                                       
	cpi     temp1,0x03
	breq    finished
	nop
	nop
loop:   
	dec     temp1 
	cpi     temp1,0x03
	brne    loop               
finished:
	ret     



;Retezec znaku pro ukazatel Z
MSG_OUT:
    lpm					
    mov		data_uart,r0	
    cpi		data_uart,0x00	
    breq	END_MSG		
	rcall	posli_znak		
	adiw	ZL,1
	rjmp	MSG_OUT
END_MSG:
	ret


prijmi_id_soucastky:
	lpm					
	mov		kod_soucastky,r0	
	adiw	ZL,1		
	lpm					
	mov		pgm_mod,r0	
	adiw	ZL,1		
	ret

;Identifikator programatoru
ID:
.db	"AVR ISP",0



tabulka_AVR:

.db	0x10,0		;AT90S1200 rev. A	
.db	0x11,0		;AT90S1200 rev. B
.db	0x12,0		;AT90S1200 rev. C
.db	0x13,0		;AT90S1200		
.db	0x20,0		;AT90S2313		
.db	0x28,0		;AT90S4414		
.db	0x30,0		;AT90S4433		
.db	0x34,0		;AT90S2333		
.db	0x38,0		;AT90S8515		
.db	0x3A,'P'	;ATmega8515
.db	0x3B,'P'	;ATmega8515 BOOT	
.db	0x41,'P'	;ATmega103
.db	0x42,'P'	;ATmega603
.db	0x43,'P'	;ATmega128
.db	0x44,'P'	;ATmega128 BOOT
.db	0x45,'P'	;ATmega64	
.db	0x46,'P'	;ATmega64 BOOT	
.db	0x48,0		;AT90S2323			
.db	0x4C,0		;AT90S2343	
.db	0x55,0		;ATtiny12
.db	0x56,0		;ATtiny15		
.db	0x58,0		;ATtiny19			
.db	0x5C,0		;ATtiny28		
.db	0x5E,'P'	;ATtiny26	
.db	0x60,'P'	;ATmega161
.db	0x61,'P'	;ATmega161 BOOT	
.db	0x64,'P'	;ATmega163
.db	0x65,'P'	;ATmega83
.db	0x66,'P'	;ATmega163 BOOT
.db	0x67,'P'	;ATmega83 BOOT
.db	0x68,0		;AT90S8535
.db	0x69,'P'	;ATmega8535		
.db	0x6C,0		;AT90S4434		
.db	0x70,0		;AT90C8534
.db	0x71,0		;AT90C8544
.db	0x72,'P'	;ATmega32
.db	0x73,'P'	;ATmega32 BOOT
.db	0x74,'P'	;ATmega16
.db	0x75,'P'	;ATmega16 BOOT
.db	0x76,'P'	;ATmega8
.db	0x77,'P'	;ATmega8 BOOT
.db	0x78,'P'	;ATmega169	
.db	0x79,'P'	;ATmega169 BOOT	
end_of_tabulka_AVR:
	.dw	0xffff


