;=======================================================================
; Fichier : gene_freq.asm
; Titre: GENERATEUR de Frequences ATMEGA8 -  Afficheur LCD 2x16
; Version: 1.02
; Auteur: Fred
; Date: 27/03/2006				     
;=======================================================================									
;VCO - PLL - Quartz 3.2768MHz ; sortie rect 5V compatible CMOS et TTL
;convertisseur numerique/analogique pour stabiliser la PLL et diminuer le bruit de phase
;affichage 5 chiffres 02000 a 20000
;7 GAMMES:
;2MHz - 200kHz 	(resolution 100Hz)
;200kHz - 20kHz	(resolution 10Hz)
;20kHz - 2kHz	(resolution 1Hz)
;2kHz - 200Hz	(resolution 0.1Hz)
;200Hz - 20Hz	(resolution 10mHz)
;20Hz - 2Hz	(resolution 1mHz)
;2Hz - 0.2Hz	(resolution 0.1mHz)							
;=======================================================================
;Quelques exemples de frequences de sortie:
;	1.000Hz
;	50Hz	
;	60Hz
;	1000Hz
;	15Hz a 15000Hz	(BF audible)
;	15625Hz	(TV - H)
;	455kHz	(FI)
;	1.000MHz

;=======================================================================
;LISTE DES PARTIES:

;Déclaration des Variables
;MACROS
;Déclaration des Vecteurs d'interruptions
;Routines de traitement des interruptions
;Programme de RESET
;initialisation des ports
;PROGRAMME PRINCIPAL
;ROUTINES

;mul16_8	MULTIPLICATION 8 bits x 16 bits resultat sur 24 bits
;div24_8	DIVISION 24 bits par 8 bits, resultat sur 24 bits

; PROCEDURES D'AFFICHAGE PHYSIQUE SUR LE LCD
;=======================================================================


.include "m8def.inc"	; nom du fichiers de références des registres (ATMEGA 8)


;------------------------------------------------------------------------
;Déclaration des Variables		                 	
;------------------------------------------------------------------------
;r0,1,2 = Fout du VCO (max=2^24 = 16.777216 MHz . En fait on se limitera a Fout max = 2MHz (2000 000 Hz) )
.def SAUVREG = r3	; registre de sauvegarde de SREG
.def fact_div =r4	;facteur de division (valeur appliquee au commutateur 74LS151 a la sortie du generateur)

;r15 reserve pour l'INT_EXT0

.def a=r16
.def AA=r17
.def BB=r18
.def rang = r19

;r20..25 reserve pour les variables locales
;r26,27 reserve comme registre d'adresse d'indexation (X)

.def n_touche = r28
.def point_dec = r29

;r30,31 reserve comme registre d'adresse d'indexation (Z) permet la lecture en memoire de programme

.DSEG
	dtlcd:		.BYTE 1
	V_out:		.BYTE 1
	TAB_AFF8:		.BYTE 8	;tableau de 8 octets codants 8 chiffres a afficher

	nombre1:		.BYTE 3	;nombre code sur 3 octets (binaire) 0...16 777 216
	nombre2:		.BYTE 3	;""

	dividende: 	.BYTE 3
	numerateur:	.BYTE 1
	denominateur:	.BYTE 1
 


;------------------------------------------------------------------------
;MACROS
;------------------------------------------------------------------------
	
;cette macro fait @0(variable en RAM, 24bits)  := @1 valeur(24bits) pasee en parametre
.MACRO	Mset24
	ldi	a,BYTE1(@1)
	sts	@0+0,a
	ldi	a,BYTE2(@1)
	sts	@0+1,a
	ldi	a,BYTE3(@1)
	sts	@0+2,a

.ENDMACRO	

;------------------------------------------------------------------------
;cette macro fait @0(variable en RAM, 24bits)  := r20,21,22
.MACRO	Mstore24
	sts	@0+0,r20
	sts	@0+1,r21
	sts	@0+2,r22

.ENDMACRO	

;------------------------------------------------------------------------
;cette macro fait 3 registres successifs  := @0 (variable en RAM, 24bits, pasee en parametre)
.MACRO	Mload24
	lds	r20,@0+0
	lds	r21,@0+1
	lds	r22,@0+2


.ENDMACRO	


;------------------------------------------------------------------------
;Déclaration des Vecteurs d'interruptions
;------------------------------------------------------------------------

.CSEG
.ORG 0x0000			;début zone mémoire programme
	rjmp	RESET		;RESET
	rjmp	EXT_INT0		;Interruption externe INT0
	rjmp 	EXT_INT1		;Interruption externe INT1
	rjmp 	TIM2_COMP		;Interrution comparaison réussie TIMER2
	rjmp 	TIM2_OVF		;Interrution débordement compteur TIMER2  
	rjmp 	TIM1_CAPT		;Interrution entrée capture TIMER1
	rjmp	TIM1_COMPA	;Interrution comparateurA TIMER1
	rjmp 	TIM1_COMPB	;Interrution comparateurB TIMER1
	rjmp	TIM1_OVF		;Interrution débordement compteur TIMER1 
	rjmp 	TIM0_OVF		;Interrution débordement compteur TIMER0 
	rjmp 	SPI_STC		;Interrution transmissions SPI terminée
	rjmp 	UART_RXC		;Interrution réception UART terminé RX
	rjmp 	UART_DRE		;Interrution UART vide 
	rjmp 	UART_TXC		;Interrution émission UART terminé TX
	rjmp 	ADC_ok		;Interrution Conversion A/D terminée
	rjmp 	EE_RDY		;Interrution EEPROM prête
	rjmp 	ANA_COMP		;Interrution comparaison analogique effectuée
	
;------------------------------------------------------------------------
;Routines de traitement des interruptions
;------------------------------------------------------------------------
;F-in limite avec Qx=16MHz = 450kHz

			
EXT_INT0:		reti
;------------------------------------------------------------------------
EXT_INT1:		reti
;------------------------------------------------------------------------
TIM2_COMP:	reti
;------------------------------------------------------------------------
TIM2_OVF:		reti
;------------------------------------------------------------------------
TIM1_CAPT:	reti
;------------------------------------------------------------------------
TIM1_COMPA:	reti
;------------------------------------------------------------------------
TIM1_COMPB:	reti
;------------------------------------------------------------------------
TIM1_OVF:		reti	
;------------------------------------------------------------------------
TIM0_OVF:		reti
;------------------------------------------------------------------------
SPI_STC:		reti
;------------------------------------------------------------------------
UART_RXC:		reti
;------------------------------------------------------------------------
UART_DRE:   	reti
;------------------------------------------------------------------------
EE_RDY:		reti
;------------------------------------------------------------------------
ANA_COMP:		reti
;------------------------------------------------------------------------
ADC_ok:		reti
;------------------------------------------------------------------------
UART_TXC:		reti
;------------------------------------------------------------------------
UART_RXC1:	reti
;------------------------------------------------------------------------


;------------------------------------------------------------------------
;Programme de RESET
;------------------------------------------------------------------------
texte:	.DW	'G'+256*'E','N'+256*'E',' '+256*'H','F'+256*' '

RESET:	ldi	a,low(RAMEND)
	out	SPL,a		; Initialisation de la pile à   
	ldi	a,high(RAMEND)	; l'adresse haute de la SRAM
	out	SPH,a	
;------------------------------------------------------------------------
;initialisation des ports
	ldi 	a,0b11111111	;toutes en sorties
	out 	ddrb,a

	ldi 	a,0b00000000	;toutes en entree
	out 	ddrd,a
	ldi	a,0b11011111	;R de tirage validee pour switchs
	out 	portd,a


	ldi 	a,0b11111111	;toutes en sorties	
	out 	ddrc,a
	clr 	a
	out 	portc,a		;sorties = 0
;------------------------------------------------------------------------
;wachtdog	
;	wdr			
;	ldi 	a,0b00001111	; wachtdog enable; delai = 2s (voir p:82)
;	out 	wdtcr,a

	wdr
	ldi	a,0b00011111
	out 	wdtcr,a
	ldi	a,0b00010111
	out 	wdtcr,a		; disable le wachtdog. (voir p:83)


;------------------------------------------------------------------------
;TIMSK
;Timer Interrupt Mask; voir pdf p:70
;bit7 (OCIE2)
;bit6 (TOIE2)
;bit5 (TICIE1) Timer/Counter1, Input Capture Interrupt Enable
;bit4 (OCIE1A) Timer/Counter1, Output Compare A Match Interrupt Enable
;bit3 (OCIE1B) Timer/Counter1, Output Compare B Match Interrupt Enable
;bit2 (TOIE1)  Timer/Counter1, Overflow Interrupt Enable
;bit1 inutilise
;bit0 (TOIE0) Timer/Counter0 Overflow Interrupt Enable

		;   76543210
	ldi	a,0b00000000	
	out	TIMSK,a
;------------------------------------------------------------------------
;TCCR1A
;definit le mode de fonctionnement  du Timer1
;voir Tavernier p:93

;bit7 (COM1A1)
;bit6 (COM1A0)
;bit5 (COM1B0)
;bit4 (COM1B1)
;bit3,2 NC
;bit1 (PWM11)
;bit0 (PWM10)

		;   76543210
	ldi	a,0b01000000
	out	TCCR1A,a
;------------------------------------------------------------------------
;TCCR1B
;definit le mode de fonctionnement  du Timer1
;voir Tavernier p:94-95 ;et p:81-82 du datasheet.pdf

;bit7 (ICNC1): enable/disable reducteur de bruit
;bit6 (ICES1)=1: transfert du registre de comptage TCNT1 dans le registre de capture ICR1 sur front montant du pin ICP
;bit5 : inutilise
;bit4 : inutilise
;bit3 (WGM12 ou CTC1): raz registre de comptage (TCNT1) apres comparaison
;bits2,1,0: taux de predivision applique a l'horloge systeme. voir Tavernier p:95

		;   76543210
	ldi	a,0b00001110	;horloge externe pour le Timer1, RAZ du compteur (TCNT1) lors de comparaison OK
	out	TCCR1B,a
;------------------------------------------------------------------------
;MCUCR bits[1,0]
;ISC11 ISC10 Description
;0 0 The low level of INT1 generates an interrupt request.
;0 1 Any logical change on INT1 generates an interrupt request.
;1 0 The falling edge   of INT1 generates an interrupt request.
;1 1 The rising  edge   of INT1 generates an interrupt request.

	ldi	a,0b00000000
	out	MCUCR,a		;voir p:64 du datasheet.pdf et Tavernier p:30

;------------------------------------------------------------------------
;GICR (GENERAL INTERRUPT CONTROL REGISTER)
;page 47 et 65 du datasheet
;bit 7 = INT1 enable
;bit 6 = INT0 enable

	ldi	a,0b00000000
	out	GICR,a

;------------------------------------------------------------------------
initvar:	clr	a
	sts	TAB_AFF8+0,a
	sts	TAB_AFF8+1,a
	sts	TAB_AFF8+2,a
	sts	TAB_AFF8+3,a
	sts	TAB_AFF8+4,a
	sts	TAB_AFF8+5,a
	sts	TAB_AFF8+6,a
	sts	TAB_AFF8+7,a

	ldi	a,5		;c'est a dire 10 puissance 5
	mov	fact_div,a

	ldi	r20,BYTE1(16670)
	ldi	r21,BYTE2(16670)
	ldi	r22,BYTE3(16670)

	mov	r0,r20	;r20,21,22 := Fout
	mov	r1,r21
	mov	r2,r22

	ldi	rang,4		;rang (position decimale) du chiffre a modifier


;------------------------------------------------------------------------
;zone de tests de procedures

;	cli	;arret des interruptions (pendant l'affichage qui utilise une tempo)
;	ldi	a,0
;	sts	V_out,a
;
;test1:	lds	a,V_out
;	inc	a
;	sts	V_out,a
;	rcall	dt_out2
;	rcall	tp1ms
;
;	rjmp	test1


;------------------------------------------------------------------------
;PROGRAMME PRINCIPAL
;------------------------------------------------------------------------

	cli	;arret des interruptions (pendant l'affichage qui utilise une tempo)
	ldi	a,0b00010000
	mov	r15,a	;valeur constante dans r15 durant tout le programme (pour economiser une instruction dans INT_EXT0)

zero:	rcall 	dspclr	;efface LCD
	rcall 	home
	rcall 	fset
	rcall 	setmod0
	rcall 	dsp10c1

	ldi	a,0
	rcall	ddras

	rcall	tp100ms

	mov	r20,r0	;r20,21,22 := Fout
	mov	r21,r1
	mov	r22,r2

	rcall	storeF

.equ nbr_chiffres = 5

	ldi	a,0
	rcall	ddras

	rcall	affnb8
	ldi	a,nbr_chiffres
	ldi	point_dec,0
	set
	rcall	affiphyN

	mov	a,rang
	subi	a,8-nbr_chiffres
	rcall	ddras

	rcall	tp100ms

	sei	;Sets the Global Interrupt flag (I) in SREG (status register)


	rjmp	entree

;------------------------------------------------------------------------
;BOUCLE PRINCIPALE
bcl0:	rcall	scrute

entree:	cli
	ldi	a,0
	rcall	ddras

	mov	r20,r0	;r20,21,22 := Fout
	mov	r21,r1
	mov	r22,r2

	rcall	affnb8	;affiche la frequence du VCO
	ldi	a,nbr_chiffres
	ldi	point_dec,0
	set
	rcall	affiphyN

	rcall	affi_fact_div
	rcall	out74151
	rcall	affiFout

	mov	a,rang
	subi	a,8-nbr_chiffres
	rcall	ddras

	sei

	rcall	attente	;attend relachement des touches
	rcall	tp100ms	;ne pas supprimer

	rjmp	bcl0
	
;------------------------------------------------------------------------
;ROUTINES
;------------------------------------------------------------------------
;attend qu'une touche soit appuyee et la traite

scrute:	in	n_touche,pind
	com	n_touche
	andi	n_touche,0b11011011	;masque
	breq	scrute		;si aucune touche appuyee

;----------------------------------
;TOUCHE DEPLACE CURSEUR A GAUCHE
	cpi	n_touche,1	
	brne	scr2

	dec	rang
	cpi	rang,4
	brpl	scr1a
	ldi	rang,3	


scr1a:	rcall	tp100ms
	rjmp	fin_scr	

;----------------------------------
;TOUCHE DEPLACE CURSEUR A DROITE
scr2:	cpi	n_touche,2	
	brne	scr3

	inc	rang
	cpi	rang,8
	brlo	scr2a
	ldi	rang,7

scr2a:	rcall	tp100ms
	rjmp	fin_scr

;----------------------------------
;TOUCHE INCREMENTE Fout (uniquement le digit sous le curseur)
scr3:	cpi	n_touche,8	
	brne	scr4

	rcall	charge1p	;(valeurs positives pour incrementer Fout)

	mov	r20,r0	;r20,21,22 := Fout
	mov	r21,r1
	mov	r22,r2

	rcall	add24a	;r21,22,23:=r21,22,23 + nombre1 Modifie : Fout:=Fout + nombre1(multiple de 10)

.equ Fmax = 20000
	ldi	r23,BYTE1(-Fmax)	; -1 * butee max (20000 max)
	ldi	r24,BYTE2(-Fmax)
	ldi	r25,BYTE3(-Fmax)

	add	r23,r20
	adc	r24,r21
	adc	r25,r22

	brcc	scr31

	ldi	r20,BYTE1(Fmax)
	ldi	r21,BYTE2(Fmax)
	ldi	r22,BYTE3(Fmax)


scr31:	rjmp	storeF

;----------------------------------
;TOUCHE DECREMENTE Fout (uniquement le digit sous le curseur)
scr4:	cpi	n_touche,16	
	brne	scr5
	
	rcall	charge1m	;(valeurs negatives pour decrementer Fout)

	mov	r20,r0	;r20,21,22 := Fout
	mov	r21,r1
	mov	r22,r2

	rcall	add24a	;r21,22,23:=r21,22,23 + nombre1

;TEST si pas <0
;ne pas supprimer ce test qui ne fait pas double emploi avec le suivant !
	brcs	scr41	;si pas debordement (par valeur negative)
	rcall	charge1p	;si debordement  (par valeur negative)
	rcall	add24a	;RAZ derniere decrementation

;BUTEE MIN (2000)
.equ	Fmini = 2000

scr41:	ldi	r23,BYTE1(-Fmini)	;butee min 
	ldi	r24,BYTE2(-Fmini)
	ldi	r25,BYTE3(-Fmini)

	add	r23,r20
	adc	r24,r21
	adc	r25,r22

	brcs	scr42

	ldi	r20,BYTE1(Fmini)
	ldi	r21,BYTE2(Fmini)
	ldi	r22,BYTE3(Fmini)

scr42:	rjmp	storeF

;----------------------------------
;TOUCHE decremente facteur de division
scr5:	cpi	n_touche,64	
	brne	scr6

	dec	fact_div
	brpl	scr5b
	inc	fact_div

scr5b:	rcall	tp100ms
	rjmp	fin_scr

;----------------------------------
;TOUCHE incremente facteur de division
scr6:	cpi	n_touche,128	
	brne	fin_scr

	inc	fact_div
	mov	a,fact_div
	cpi	a,6	;c'est a dire 1/10^6 = 1/1000 000 (voir schema)
	brlo	scr6b
	ldi	a,6
	mov	fact_div,a

scr6b:	rcall	tp100ms
	rjmp	fin_scr

;----------------------------------
;on memorise la [frequence a generer en sortie du VCO]x10 dans r0,1,2
;r0,1,2 = [2000...20000]
; 2000 pour 200kHz en sortie du VCO
;20000 pour 2MHz   en sortie du VCO
storeF:	mov	r0,r20
	mov	r1,r21
	mov	r2,r22

	Mset24 	nombre1, -1	;pour que le rapport de division soit exact (le registre compte depuis zero)
	rcall	add24a
	
;page 77 du datasheet:
;To do a 16-bit write, the High byte must be written before the Low byte. For a 16-bit
;read, the Low byte must be read before the High byte.
	cli
	out 	OCR1AH,r21
	out 	OCR1AL,r20
	sei

	rcall	sortie_V

fin_scr:	ret

;------------------------------------------------------------------------
;sortie d'une tension vers le 4015 du convertisseur N/A (externe) pour piloter le VCO
;la valeur a sortir sur vers le 4015 est egale a: (voir feuille tableur)
;V_out = 42 + 0.0025 * [r0,1,2]
;c.a.d = 42 + [r0,1,2]/400
;=42 + [r0,1,2]/80/5

sortie_V:	mov	r20,r0
	mov	r21,r1
	mov	r22,r2

	ldi	r23,80
	rcall	div24_8
	ldi	r23,5
	rcall	div24_8	;le resultat tient dans r20

	ldi	a,42
	add	r20,a	;le resultat (=51..87) tient toujours dans r20
	sts	V_out,r20	;on le memorise en RAM dans V_out

	rcall	dt_out2

	ret
;------------------------------------------------------------------------
;attend relachement des touches
attente:	in	n_touche,pind
	com	n_touche
	andi	n_touche,0b00001111
	cpi	n_touche,0
	brne	attente
	ret
;------------------------------------------------------------------------
;charge une valeur decimale multiple de 10 dans nombre1
;remarque : un tableau en FLASH (lu par l'instruction LPM) ne m'a pas paru judicieux, vu la taille des mots (3 octets)
;je verrais plutot une version calculee de cette routine (par multiplication de 10 dans une boucle)
charge1p:	cpi	rang,7
	brne	cg6p
	Mset24 	nombre1, 1	;appel ; nombre1(sur 3octets):=1
cg6p:	cpi	rang,6
	brne	cg5p
	Mset24 	nombre1, 10	;appel ; nombre1(sur 3octets):=10
cg5p:	cpi	rang,5
	brne	cg4p
	Mset24 	nombre1, 100	;appel ; nombre1(sur 3octets):=100
cg4p:	cpi	rang,4
	brne	cg3p
	Mset24 	nombre1, 1000	;appel ; nombre1(sur 3octets):=1000
cg3p:	cpi	rang,3
	brne	cg2p
	Mset24 	nombre1, 10000	;appel ; nombre1(sur 3octets):=10000
cg2p:	cpi	rang,2
	brne	cg1p
	Mset24 	nombre1, 100000	;appel ; nombre1(sur 3octets):=100000
cg1p:	cpi	rang,1
	brne	cg0p
	Mset24 	nombre1, 1000000	;appel ; nombre1(sur 3octets):=1000000
cg0p:	ret
;------------------------------------------------------------------------
;charge une valeur decimale multiple de 10 dans nombre1
charge1m:	cpi	rang,7
	brne	cg6m
	Mset24 	nombre1, -1	;appel ; nombre1(sur 3octets):=-1
cg6m:	cpi	rang,6
	brne	cg5m
	Mset24 	nombre1, -10	;appel ; nombre1(sur 3octets):=-10
cg5m:	cpi	rang,5
	brne	cg4m
	Mset24 	nombre1, -100	;appel ; nombre1(sur 3octets):=-100
cg4m:	cpi	rang,4
	brne	cg3m
	Mset24 	nombre1, -1000	;appel ; nombre1(sur 3octets):=-1000
cg3m:	cpi	rang,3
	brne	cg2m
	Mset24 	nombre1, -10000	;appel ; nombre1(sur 3octets):=-10000
cg2m:	cpi	rang,2
	brne	cg1m
	Mset24 	nombre1, -100000	;appel ; nombre1(sur 3octets):=-100000
cg1m:	cpi	rang,1
	brne	cg0m
	Mset24 	nombre1, -1000000	;appel ; nombre1(sur 3octets):=-1000000
cg0m:	ret


;----------------------------------------------------------------------
;affiche le facteur de division (utilise par les CD4518 en sortie)
affi_fact_div:
	ldi	a,6		;emplacement d'affichage sur la 1ere ligne
	rcall	ddras

	ldi	AA,8
fdv1:	ldi	a,' '
	rcall	ecrire
	dec	AA
	brne	fdv1

	mov	a,fact_div
	rcall	affi_a
	ldi	a,1
	ldi	point_dec,0
	set
	rcall	affiphyN	

	ldi	a,6		;emplacement d'affichage sur la 1ere ligne
	rcall	ddras

	ldi	a,'1'
	rcall	ecrire

	mov	AA,fact_div
	cpi	AA,0
	breq	fdv3

fdv2:	ldi	a,'0'
	rcall	ecrire
	dec	AA
	brne	fdv2


fdv3:	ret
;----------------------------------------------------------------------
;Affiche la frequence de sortie sur la deuxieme ligne
affiFout:	ldi	a,64		
	rcall	ddras

	mov	r20,r0
	mov	r21,r1
	mov	r22,r2
	rcall	affnb8	;en memoire

	mov	a,fact_div
	cpi	a,3
	brlo	aff2
	rcall	aff_Hz
	rjmp	fin_af

aff2:	rcall	aff_kHz
	rjmp	fin_af

fin_af:	ret

;----------------------------------------------------------------------
aff_kHz:	ldi	a,5
	ldi	point_dec,2
	add	point_dec,fact_div
	clt
	rcall	affiphyN

	ldi	a,'k'
	rcall	ecrire
	ldi	a,'H'
	rcall	ecrire
	ldi	a,'z'
	rcall	ecrire
	ret

;----------------------------------------------------------------------
aff_Hz:	ldi	point_dec,-1
	add	point_dec,fact_div
	set		;pour afficher 0 au debut
	mov	a,fact_div
	cpi	a,6
	breq	aff_Hz2
	clt		;pour ne pas afficher 0 au debut

aff_Hz2:	ldi	a,5
	rcall	affiphyN

	ldi	a,'H'
	rcall	ecrire
	ldi	a,'z'
	rcall	ecrire
	ldi	a,' '
	rcall	ecrire
	ret

;----------------------------------------------------------------------
;sortie physique du facteur de division vers le 74LS151
out74151:	mov	a,fact_div

;------------
;les 8 lignes suivantes permuttent les valeurs a sortir conformement au cablage "tordu" du LS151 sur mon proto
;ne pas les conserver sur le montage definitif
;ca aura au moins eu l'avantage de m'apprendre a utiliser les tableaux en memoire de programme...!
	ldi 	ZH, high(Table_1<<1);Initialize Z pointer (le shift gauche because le LSB est reserve. voir doc)
	ldi 	ZL, low(Table_1<<1)
	andi	ZL,0b11111110	;RAZ LSB pour acceder aux octets de poids faible
	lsl	a		;shift gauche aussi
	add	ZL,a		;decallage dans le tableau (voir plus bas)
	clr	a
	adc	ZH,a
	lpm 	a, Z		;Load constant from program
;------------

	cbi	portb,0
	sbrc	a,0
	sbi	portb,0

	cbi	portc,4
	sbrc	a,1
	sbi	portc,4

	cbi	portc,5
	sbrc	a,2
	sbi	portc,5
	
	ret
;----------------------------------------------------------------------
Table_1:	.DW	0,2,3,5,6,4,1

;========================================================================
; PROCEDURES D'AFFICHAGE PHYSIQUE SUR LE LCD
;========================================================================


;FUNCTION SET

fset:	cbi	portc,2	;E=0
	cbi	portc,3	;R/S=0

	ldi	a,56	;56=(32 + 16(=8bits) + 8(=2lignes))  ou 48=(32 + 16(=8bits) + 0(=1ligne))
	sts	dtlcd,a		

	rcall 	dt_out1	;transmission série vers 4015 -> DATA // LCD
	rcall 	impuls
	ret

;----------------------------------------------------------------------
;DISPLAY CLEAR
dspclr:	cbi	portc,2	;E=0
	cbi	portc,3	;R/S=0

	ldi	a,1
	sts	dtlcd,a
	rcall 	dt_out1
	rcall 	impuls
	ret
;----------------------------------------------------------------------
;RETURN HOME
home:	clr	a
	out 	portc,a	;clrf portc
	ldi	a,2	;movlw	.2
	sts	dtlcd,a
	rcall 	dt_out1
	rcall 	impuls
	ret

;----------------------------------------------------------------------
;ENTRY MODE SET 0
;le curseur se déplace
setmod0:	cbi	portc,2	;E=0
	cbi	portc,3	;R/S=0
	ldi	a,6	;( 4 + 2(sens) + (accompagnement))	
	sts	dtlcd,a
	rcall 	dt_out1
	rcall 	impuls
	ret


;----------------------------------------------------------------------
;DISPLAY ON curseur OFF
dsp10c0:	cbi	portc,2	;E=0
	cbi	portc,3	;R/S=0
	ldi	a,12	;( 8+4 )	
	sts	dtlcd,a
	rcall 	dt_out1
	rcall 	impuls
	ret
;----------------------------------------------------------------------
;DISPLAY ON curseur ON
dsp10c1:	cbi	portc,2	;E=0
	cbi	portc,3	;R/S=0
	ldi	a,14	;( 8+4+2 )	
	sts	dtlcd,a
	rcall 	dt_out1
	rcall 	impuls
	ret

;----------------------------------------------------------------------
;DDRAM ADRESS SET (A)
;a doit contenir l'adresse (position d'affichage. voir doc)
.def b=r20
ddras:	push	r20
	cbi	portc,2	;E=0
	cbi	portc,3	;R/S=0
	ldi	b,128	;addlw	.128
	add	a,b
	sts	dtlcd,a
	rcall 	dt_out1
	rcall 	impuls
	pop	r20
	ret

;----------------------------------------------------------------------
;ECRIRE
;a doit contenir la valeur ASCII du caractère à afficher
ecrire:	wdr
	cbi	portc,2	;E=0
	sbi	portc,3	;R/S=1
	sts	dtlcd,a
	rcall 	dt_out1
	sbi	portc,2		;impuls E à 1 avec RS=1
	rcall 	tp1ms
	sbi	portc,2		;fin impulsion
	rcall 	tp1ms
	ret

;----------------------------------------------------------------------
;impulsion E à 1 avec RS=0
impuls:	wdr
	cbi	portc,3		;R/S=0
	sbi	portc,2		;impulsion E à 1
	rcall 	tp1ms
	cbi	portc,2		;fin impulsion
	rcall 	tp1ms
	ret

;----------------------------------------------------------------------
;SORTIE DATA (8 bits) en série sur PC0 vers CD4015(1) PC1=Clock 4015(1)
;dtlcd doit contenir la donnee

.def	n=r20
dt_out1:	push	r20
	ldi	n,8
	lds	a,dtlcd
dtbcl1:	rol	a	;bit de poids fort -> dans c 	;rlf	dtlcd,f
	cbi	portc,0	;bcf	portc,0	;bit = 0 (à priori)
	brcc	dt_out11	;btfsc	STATUS,0	;test carry
	sbi	portc,0	;bsf	portc,0	;non,  bit = 0
dt_out11:	sbi	portc,1	;clock 4015(1)
	rcall	tp1ms
	cbi	portc,1	;fin clock
	rcall	tp1ms
	dec	n	;decfsz	n,f
	cpi	n,0
	brne	dtbcl1
	pop	r20
	ret

;----------------------------------------------------------------------
;SORTIE DATA (8 bits) en série sur PC0 vers CD4015(2) PB2=Clock 4015(2)
;V_out	doit contenir la donnee a sortir

.def	n=r20
dt_out2:	ldi	n,8
	lds	a,V_out
dtbcl2:	rol	a	;bit de poids fort -> dans c 	;rlf	dtlcd,f
	cbi	portc,0	;bcf	portc,0	;bit = 0 (à priori)
	brcc	dt_out21	;btfsc	STATUS,0	;test carry
	sbi	portc,0	;bsf	portc,0	;non,  bit = 0
dt_out21:	sbi	portb,2	;clock 4015(2)
	nop	;attention, duree tres breve
	cbi	portb,2	;fin clock
	nop	;attention, duree tres breve
	dec	n	;decfsz	n,f
	cpi	n,0
	brne	dtbcl2
	ret


;-----------------------------------------------------------------------
;decompose un octet [0..255] -> notation BCD dans variables aff1..4
;unites -> aff1
;dizaines -> aff2
;centaines -> aff3
;milliers (en fait toujours zero) -> aff4
;a doit contenir l'octet à afficher
;ex: a=184

affi_a:	push	AA
	push	BB
	mov	AA,a
	rcall 	cvBDU	;BB=18 et AA=4 ; fractionne les unites et les dizaines
	sts	TAB_AFF8,AA	;aff1=4
	mov	AA,BB	;AA=18
	rcall 	cvBDU	;BB=1 et AA=8 ; si les dizaines sont > 9, fractionne en dizaines et centaines
	sts	TAB_AFF8+1,AA	;aff2=8
	sts	TAB_AFF8+2,BB	;aff3=1
	ldi	a,0
	sts	TAB_AFF8+3,a	;affiche toujours zero
	sts	TAB_AFF8+4,a
	sts	TAB_AFF8+5,a
	sts	TAB_AFF8+6,a
	sts	TAB_AFF8+7,a
	pop	BB
	pop	AA
	ret

;------------------------------------------------------------------------
;CONVERSION BINAIRE --> BCD
;appellee depuis affi_a qui push pop AA et BB
;nombre à convertir dans AA
;resultat dans BB (dizaines) et dans AA (unités)
;ex: AA=237 -> BB=23 et AA=7
;ex2 a=84  -> BB=8  et AA=4
;
cvBDU:	clr	BB
conv2:	cpi	AA,10	;AA:=AA-10
	brcs	conv3	;9 passages au max si nb <= 99
	subi	AA,10
	inc	BB
	rjmp	conv2
conv3:	ret
	
;------------------------------------------------------------------------
;decode un nombre de 5 chiffres (2 octets) en vu de l afficher en notation decimale
;NB = 0...65535
;CONVERSION BINAIRE 2 octets -> Affi5,4,3,2,1
;r20,21 doivent contenir le nombre a convertir. r20=poids faible


affnb:	push	AA
	clr	r22	;byte du dividende non utilisee
	ldi	r23,10	;diviseur

	ldi	r26,LOW(TAB_AFF8)	;registre x = adresse (TAB_AFF8)
	ldi	r27,HIGH(TAB_AFF8)	;registre x

	ldi	AA,5	;compteur de boucle

affnb1:	rcall	Div24_8	;(ne touche pas r26)
	st	x+,r24	;reste de la division par 10

	dec	AA
	brne	affnb1

	pop	AA
	ret

;------------------------------------------------------------------------
;decode un nombre de 8 chiffres (3 octets) en vu de l afficher en notation decimale
;NB = 0...16777215
;CONVERSION BINAIRE 3 octets -> Affi8,7,6,5,4,3,2,1
;r20,21,22 doivent contenir le nombre a convertir. r20=poids faible
;ex: 123456 =1 *256*256 + 226 *256 +64 *256
;r20=64
;r21=226
;r22=1

affnb8:	push	AA
	ldi	r23,10		;diviseur

	ldi	r26,LOW(TAB_AFF8)	;registre x = adresse de (TAB_AFF8)
	ldi	r27,HIGH(TAB_AFF8)	;registre x

	ldi	AA,8		;compteur de boucle

affnb81:	rcall	Div24_8		;(ne touche pas r26)
	st	x+,r24		;reste de la division par 10 - cette instruction incremente x a chaque passage

	dec	AA
	brne	affnb81

	pop	AA
	ret

;------------------------------------------------------------------------
;affichage physique de N chiffres (N=1..8)
;a doit contenir N
;point_dec doit contenir la position du point decimal
;le flag T gere l'affichage des zeros non significatifs:
;T=0 -> pas d'affichage des 0
;T=1 -> afficher tout 

affiphyN:	push	AA
	push	BB
	mov	BB,a
	ldi	r26,LOW(TAB_AFF8+8)	;registre x
	ldi	r27,HIGH(TAB_AFF8+8);registre x

	ldi	AA,8		;compteur de boucle
	
affphyN1:	ld	a,-x		;cette instruction decremente x a chaque passage

	ldi	b,48		;code ASCII de zéro
	add	a,b
	cp	BB,AA		;compare AA-BB
	brlo	affphyN3		;defense d'afficher

	brts	affphyN2		;saute la gestion des zeros non significatifs si T=1

;ne pas afficher un zero non significatif
	brne	affphyN2		;est-ce le premier chifffre a afficher?
	cpi	a,'0'		;oui. est-ce un zero ?
	brne	affphyN2		;non

	ldi	a,' '		;afficher un espace a la place du zero non significatif
	rcall	ecrire
	rjmp	affphyN3


affphyN2:	rcall 	ecrire

	cp	AA,point_dec
	brne	affphyN3

	ldi	a,'.'
	rcall	ecrire

affphyN3:	dec	AA
	brne	affphyN1

	pop	BB
	pop	AA
	ret


;----------------------------------------------------------------------
;ADDITION de registres sur 3 octets
;r20,21,22:= r20,21,22 + r0,r1,r2

add24r:	add	r20,r0
	adc	r21,r1
	adc	r22,r2
	ret	


;----------------------------------------------------------------------
;ADDITION sur 3 octets (1)
;r20,21,22:= r20,21,22 + nombre 1

add24a:	lds	a,nombre1+0
	add	r20,a
	lds	a,nombre1+1
	adc	r21,a
	lds	a,nombre1+2
	adc	r22,a
	ret	

;----------------------------------------------------------------------
;ADDITION sur 3 octets (2)
;nombre1:= nombre 1 +nombre2

add24b:	push	AA
	push	BB
	lds	AA,nombre1+0
	lds	BB,nombre2+0
	add	AA,BB
	sts	nombre1+0,AA

	lds	AA,nombre1+1
	lds	BB,nombre2+1
	adc	AA,BB
	sts	nombre1+1,AA
	
	lds	AA,nombre1+2
	lds	BB,nombre2+2
	adc	AA,BB
	sts	nombre1+1,AA
	pop	BB
	pop	AA
	ret	

;----------------------------------------------------------------------
;MULTIPLICATION 8 bits x 16 bits resultat sur 24 bits
;multiplie r20(poids faible),r21 , r22(poids fort) par 'a'
;produit dans r20,r21,r22

.def produit0=r20	;produit poids faible
.def produit1=r21	;produit
.def produit2=r22	;produit poids fort
.def n1=r23

mul16_8:	ldi	n1,17		;compteur de boucle
		
mul16_81:	ror	produit2
	ror	produit1
	ror	produit0

	brcc	mul16_82		;lecture d'un bit de r20 ; test de ce bit
	add	produit2,a	;si c' est un '1' on ajoute 'a' à A2

mul16_82:	dec	n1
	brne	mul16_81

	ret

;----------------------------------------------------------------------
;DIVISION 24 bits par 8 bits, resultat sur 24 bits
;note; le quotient est stocke dans le dividende; permet de decaller les deux a la fois

;r20,21,22 doivent contenir le dividende (r20=LSB)
;r23 doit contenir le diviseur
;resultat (Quotient) dans r20 (LSB) et r21 et r22
;reste dans r24
;le bit T=1 indique un debordement

.def Dividend0=r20	;pour la routine Div24_8
.def Dividend1=r21	;pour la routine Div24_8
.def Dividend2=r22	;pour la routine Div24_8
.def Diviseur =r23	;pour la routine Div24_8.
.def Aux0=r24	;pour la routine Div24_8
.def Aux1=r25
.def n1=r16	;attention: ecrase r16 (=a)

div24_8:	ldi	n1,24	
	clr	Aux0		;clrf	Aux+0

div24_80:	lsl	Dividend0
	rol	Dividend1
	rol	Dividend2

div24_81:	rol	Aux0	;decalle le dividende dans 'Aux' (passage d'un bit par "c")
	rol	Aux1

	cp	aux0,Diviseur
	brlo	auxPetit
	
auxGrand:	sub	Aux0,Diviseur
	sec
	rjmp	saut1

auxPetit:	clc
	
saut1:	rol	Dividend0	;decalle a gauche en incorporant 'c' comme bit 0
	rol	Dividend1	;decalle a gauche avec passage d'un bit par "c"
	rol	Dividend2 ;decalle a gauche et envoie un bit par 'c' dans 'Aux' (apres le saut)

	dec	n1	;important: ne touche pas a 'c'
	brne	Div24_81
	
	ret

;----------------------------------------------------------------------
;TEMPO 1ms exactement avec un Qx=16.000MHz
;attention: sous reserve de ralentissement par les interruptions...

.def	i1=r20
.def	i2=r21

tp1ms:	push	r20
	push	r21

	ldi	i1,16
bcl2:	ldi	i2,248	
bcl3:	dec	i2
	;cpi	i2,0
	brne	bcl3

	nop		;pour ajuster exactement la duree
	nop
	nop
	dec	i1
	;cpi	i1,0
	brne	bcl2

	nop
	pop	r20
	pop	r21
	ret

;----------------------------------------------------------------------
;tempo 100ms

.def n=r16

tp100ms:	push	r16
	ldi	n,100

bcl4:	rcall	tp1ms
	dec	n
	;cpi	n,0
	brne	bcl4

	pop	R16
	ret

;----------------------------------------------------------------------





