; Équivalences

.equ PINA = 0x00
.equ DDRA = 0x01
.equ PORTA = 0x02
.equ PINC = 0x06
.equ DDRC = 0x07
.equ PORTC = 0x08
.equ PING = 0x12
.equ DDRG = 0x13
.equ PORTG = 0x14

.equ WDTCSR = 0x60

.equ TTCR0A = 0x24
.equ TTCR0B = 0x25
.equ TMSK0 = 0x6e

.equ EIMSK = 0x3D
.equ EICRA = 0x69
.equ EICRB = 0x6A

.equ ADMUX = 0x7C
.equ ADCSRB = 0x7B
.equ ADCSRA = 0x7A
.equ ADCH = 0x79

.equ SPH = 0x3E
.equ SPL = 0x3D

; Nommage des registres utilisés

.def etat = r12
.def compteur = r6 ; Utilisé pour modifier des valeurs dans le menu

.def reference = r13 ; Température de référence actuelle

.def d3 = r2 ; Digit 3 (tout à gauche)
.def d2 = r3 ; Digit 2
.def d1 = r4 ; Digit 1
.def d0 = r5 ; Digit 0 (tout à droite)

.def t2 = r8 ; Jour (3 premiers bits) et Heure (5 derniers bits)
.def t1 = r9 ; Minutes
.def t0 = r10 ; Secondes / 2
.def tt = r11 ; Watchdog artificiel

.def temp = r7 ; Utilisé pour différentes choses mais très brièvement

; Nommage des adresses RAM utilisées

.equ assocs = 0x0200 ; Association des modes aux heures (tableau de taille 168 octets)
.equ tempJour = 0x02a8 ; Température du mode jour
.equ tempNuit = 0x02a9 ; Température du mode nuit

; Vecteurs d'interruptions

.org 0x000 ; Vecteur RESET
    jmp debut

.org 0x0002 ; INT0
    jmp valider

.org 0x0004 ; INT1
    jmp decrementer
    
.org 0x0006 ; INT2
    jmp incrementer

.org 0x0008 ; INT3
    jmp retour

.org 0x0018 ; Watchdog
    jmp watchdog

.org 0x002E ; Timer
    jmp timer

.org 0x003A ; ADC
    jmp adc


.org 0x0080

; Tableaux de la mémoire du programme

afficheur:
    .DB 0x3F, 0x30, 0x6D, 0x79, 0x72, 0x5B, 0x5F, 0x31, 0x7F, 0x7B
    ;      0,    1,    2,    3,    4,    5,    6,    7,    8,    9

; Programme

debut:
; Configuration des composants
    ; Ports parallèles
    DDRA@IO <- 0xFF
    DDRC@IO <- 0xFF
    DDRG@IO <- 0xFF

    ; Barres de chargement
    PORTA@IO <- 0x40
    PORTC@IO <- 0x00

    ; Watchdog
    ;WDTCSR <- 0b00010000
    ;WDTCSR <- 0b01000111

    ; Timer
    TTCR0A@IO <- 0b00000000
    TTCR0B@IO <- 0b00000100 ; Timer toutes les 4ms
    TMSK0  <-    0b00000001

    ; Interruptions externes
    EIMSK <- 0b00001111
    EICRA <- 0b10101010
    EICRB <- 0b00000000

    ; ADC
    ADMUX  <- 0b01100000
    ADCSRB <- 0b00000010
    ADCSRA <- 0b11011110

; Initialisation des registre
    etat <- 0
    ; compteur initialisé quand on en aura besoin
    ; reference initialisé avec agirHeure
    ; d3, d2, d1, d0 initialisés avec afficherHorloge

    ; Mardi 14h00'55
    t2 <- 0b00101110
    t1 <- 0
    t0 <- 55
    tt <- 0

    ; temp initialisé quand on en aura besoin

; Initialisation de la RAM
    ; Valeurs de démonstration : 7° - 27° (on jouerait plus sur du 17° - 21° en réalité)
    tempNuit <- 20
    tempJour <- 220

    ; Initialisation des associations
    compteur <- 0 ; Jour
initJour:
    temp <- 0 ; Heure
initHeure:
    assocs[compteur * 24 + temp] <- 0 ; Par défaut le mode jour
    si temp <= 7 alors assocs[compteur * 24 + temp] <- 1 ; Entre minuit et 7h le mode nuit
    inc temp
    if temp < 24 saut initHeure
    inc compteur
    if compteur < 7 saut initJour

; Logique de démarrage
    call afficherHorloge
    call agirHeure

    ; On active les interruptions une fois que tout est prêt
    sei

boucle:
    sleep
    jmp boucle

; Fonctions

; Affichages
afficherHorloge:
    ; Affiche l'heure actuelle
    temp <- t2 & 0b00011111
    d3 <- 0x00
    si temp > 10 alors d3 <- afficheur@ROM[temp/10]
    d2 <- afficheur@ROM[temp%10] | 0b10000000
    d1 <- afficheur@ROM[t1/10]
    d0 <- afficheur@ROM[t1%10]
    ret

afficherTemperature:
    ; Considère le registre compteur comme une température et l'affiche
    d0 <- 0x63 ; Sigle degré
    d1 <- afficheur@ROM[compteur%10] ; Décimale
    temp <- compteur / 10 + 5 ; On stocke temporairement la partie entière pour simplifier
    d2 <- afficheur@ROM[temp%10] | 0b10000000 ; Chiffre des unités, auquel on ajoute le point
    d3 <- 0x00 ; On affiche le chiffre des dizaines si la température est > 10
    si temp > 10 alors d3 <- afficheur@ROM[temp/10]
    ret

afficherHeur:
    ; Considère le registre compteur comme une heure et l'affiche
    d3 <- afficheur@ROM[compteur/10]
    d2 <- afficheur@ROM[compteur%10] | 0b10000000
    d1 <- 0x00
    d0 <- 0x00
    ret

afficherMinu:
    ; Considère le registre compteur comme une minute et l'affiche
    d3 <- 0x00
    d2 <- 0b10000000
    d1 <- afficheur@ROM[compteur/10]
    d0 <- afficheur@ROM[compteur%10]
    ret

afficherJour:
    ; Considère le registre compteur comme un jour et l'affiche
    d1 <- 0x00;
    d0 <- 0x00;
    si compteur = 0 saut afficherLu
    si compteur = 1 saut afficherMa
    si compteur = 2 saut afficherMe
    si compteur = 3 saut afficherJe
    si compteur = 4 saut afficherVe
    si compteur = 5 saut afficherSa
    si compteur = 6 saut afficherDi
    ret ; Ne devrait pas arriver, mais permet d'éviter les dégâts

afficherLu:
    d3 <- 0x0E
    d2 <- 0x1c    
    ret

afficherMa:
    d3 <- 0x37
    d2 <- 0x77    
    ret

afficherMe:
    d3 <- 0x37
    d2 <- 0xcf    
    ret

afficherJe:
    d3 <- 0x3c
    d2 <- 0xcf    
    ret

afficherVe:
    d3 <- 0x1c
    d2 <- 0xcf    
    ret

afficherSa:
    d3 <- 0x5b
    d2 <- 0x77    
    ret

afficherDi:
    d3 <- 0xfc
    d2 <- 0x04    
    ret

; Logique

agir1s:
    ; Met à jour les registres de temps, active agir10s ou agirHeure si nécessaire
    inc t0
    si t0%10 != 0 saut ret ; Continue si on est sur un multiple de 10 secondes
    call agir10s
    si t0 < 60 saut ret ; Continue si on est sur un multiple de 60 secondes
    t0 <- 0
    inc t1
    si t1 < 60 saut ret ; Continue si on est sur un multiple de 60 minutes
    t1 <- 0
    inc t2
    si t2 & 0b00011111 >= 7 saut agir1sHeur ; Continue si il est minuit
    t2 <- t2 + 0b00100000 ; Jour suivant
    t2 <- t2 & 0b11100000 ; Heure à minuit
    si t2 >= 0b11100000 alors t2 <- 0b00000000 ; Si on est dimanche soir, on revient à lundi matin
agir1sHeur:
    call agirHeure
    ret

agir10s:
    ; Déclenche une conversion ADC
    ADCSRA <- 0b11011101
    ; Switche l'état de veille si veille il y a
    si etat =   0 saut a10sVeilleTemp
    si etat =   1 saut a10sVeilleHeur
    ret
a10sVeilleHeur:
    etat <- 0
    call afficherHorloge
    ret
a10sVeilleTemp:
    etat <- 1
    compteur <- ADCH
    call afficherTemperature
    ret

agirHeure:
    ; Recharge la température de référence
    temp <- (t2 / 0b00100000) * 24 + (t2 & 0b00011111)
    ;       ↑ numéro du jour de la semaine
    ;                                ↑ heure actuelle
    if assocs[temp] = 0x00 alors reference <- tempJour
    if assocs[temp] = 0x01 alors reference <- tempNuit
    ret

; Recadrage
boundHeur:
    si compteur =  255 alors compteur <- 23
    si compteur >= 24  alors compteur <- 0
    ret

boundMinu:
    si compteur =  255 alors compteur <- 59
    si compteur >= 60  alors compteur <- 0
    ret

boundJour:
    si compteur = 255 alors compteur <- 6
    si compteur >= 7  alors compteur <- 0
    ret

boundTemperature:
    si compteur < 5   alors compteur <- 250
    si compteur > 250 alors compteur <- 5
    ret

; Puisque l'assembleur étendu ne supporte pas les retours conditionnels
ret:
    ret
reti:
    reti

; États
; × : Arrivée à l'état par un niveau supérieur
; R : Arrivée à l'état par un niveau inférieur
; U : Mise à jour de l'affichage
; C : Changement de l'état

etatVeilleHeur:
    etat <- 0
    call afficherHorloge
    reti

etatVeilleTemp:
    etat <- 1
    compteur <- ADCH
    call afficherTemperature
    reti

etatMenuAssoc:
    etat <- 2
    d3 <- 0x77
    d2 <- 0x5b
    d1 <- 0x5b
    d0 <- 0x5c
    reti

etatMenuJours:
    ; Par défaut on prend le jour actuel
    compteur <- t2 / 0b00100000
etatMenuJoursU:
    etat <- 3 + compteur
    call afficherJour
    reti

etatMenuJoursR:
    compteur <- etat - 7 - 3
    jmp etatMenuJoursU

etatMenuJoursC:
    call boundJour
    jmp etatMenuJoursU

etatParaJours:
    etat <- etat + 7
    compteur <- t2 & 0b00011111
etatParaJoursU:
    d3 <- afficheur@ROM[compteur/10]
    d2 <- afficheur@ROM[compteur%10]
    d1 <- 0x00
    temp <- (etat - 10) * 24 + compteur
    ; Si la RAM n'est pas initialisée à cet endroit, on préfère afficher un tiret
    d0 <- 0x40
    si assocs[temp] = 0b00000000 alors d0 <- 0x3c
    si assocs[temp] = 0b00000001 alors d0 <- 0x54
    reti

etatParaJoursC:
    call boundHeur
    jmp etatParaJoursU

etatParaJoursS: ; Switch
    temp <- (etat - 10) * 24 + compteur
    assocs[temp] <- (assocs[temp] + 1) & 0b00000001
    call agirHeure ; Au cas où on change l'heure en cours
    jmp etatParaJoursU

; Appelé depuis l'état  2 après un appui sur le  bouton Incrémenter
;                      19                               Décrémenter
;                      18                               Valider
;                      18                               Retour
etatMenuTJour:
    etat <- 17
    d3 <- 0x3c ; "JOUR"
    d2 <- 0x5c
    d1 <- 0x1c
    d0 <- 0x44
    reti

; Appelé depuis l'état 17 après un appui sur Valider
etatParaTJour:
    etat <- 18
    compteur <- tempJour ; Lecture depuis la RAM
etatParaTJourU:
    call afficherTemperature
    reti

; Appelé depuis l'état 18 après un appui sur Incrementer ou Decrementer
etatParaTJourC:
    call boundTemperature ; Remet la température dans les bornes
    tempJour <- compteur ; Écriture vers la RAM
    call agirHeure ; Au cas où on change le mode de l'heure actuelle, on recharge la température de référence
    jmp etatParaTJourU

etatMenuTNuit:
    etat <- 19
    d3 <- 0x54 ; "NUIT"
    d2 <- 0x1c
    d1 <- 0x04
    d0 <- 0x4e
    reti

etatParaTNuit:
    etat <- 20
    compteur <- tempNuit
etatParaTNuitU:
    call afficherTemperature
    reti

etatParaTNuitC:
    call boundTemperature
    tempNuit <- compteur
    call agirHeure ; Au cas où on change le mode en cours
    jmp etatParaTNuitU

etatMenuHorloge:
    etat <- 21
    d3 <- 0x76 ; "HORL"
    d2 <- 0x5c
    d1 <- 0x44
    d0 <- 0x0E
    reti

etatMenuHJour:
    etat <- 22
    d3 <- 0x3c ; "JOUR"
    d2 <- 0x5c
    d1 <- 0x1c
    d0 <- 0x44
    reti

etatMenuHHeur:
    etat <- 23
    d3 <- 0x76 ; "HEUR"
    d2 <- 0xcf
    d1 <- 0x1c
    d0 <- 0x44
    reti

etatMenuHMinu:
    etat <- 24
    d3 <- 0x37 ; "MINU"
    d2 <- 0x04
    d1 <- 0x54
    d0 <- 0x1c
    reti

etatParaHJour:
    etat <- 25
    compteur <- t2 / 0b00100000
    call afficherJour
    reti

etatParaHJourC:
    call boundJour
    t2 <- (compteur * 0b00100000) + (t2 & 0b00011111)
    call agirHeure
    call afficherJour
    reti

etatParaHHeur:
    etat <- 26
    compteur <- t2 & 0b00011111
    call afficherHeur
    reti

etatParaHHeurC:
    call boundHeur
    t2 <- (t2 & 0b11100000) + compteur
    call agirHeure
    call afficherHeur
    reti

etatParaHMinu:
    etat <- 27
    compteur <- t1
    call afficherMinu
    reti

etatParaHMinuC:
    call boundMinu
    t1 <- compteur
    call afficherMinu
    reti

; Interruption boutons

incrementer:
    inc compteur
    si etat =   0 saut etatVeilleTemp
    si etat =   1 saut etatVeilleHeur
    si etat =   2 saut etatMenuTJour
    si etat <=  9 saut etatMenuJoursC
    si etat <= 16 saut etatParaJoursC
    si etat =  17 saut etatMenuTNuit
    si etat =  18 saut etatParaTJourC
    si etat =  19 saut etatMenuHorloge
    si etat =  20 saut etatParaTNuitC
    si etat =  21 saut etatMenuAssoc
    si etat =  22 saut etatMenuHHeur
    si etat =  23 saut etatMenuHMinu
    si etat =  24 saut etatMenuHJour
    si etat =  25 saut etatParaHJourC
    si etat =  26 saut etatParaHHeurC
    si etat =  27 saut etatParaHMinuC
    reti ; Ne devrait pas arriver, mais permet d'éviter les dégâts

decrementer:
    dec compteur
    si etat =   0 saut etatVeilleTemp
    si etat =   1 saut etatVeilleHeur
    si etat =   2 saut etatMenuHorloge
    si etat <=  9 saut etatMenuJoursC
    si etat <= 16 saut etatParaJoursC
    si etat =  17 saut etatMenuAssoc
    si etat =  18 saut etatParaTJourC
    si etat =  19 saut etatMenuTJour
    si etat =  20 saut etatParaTNuitC
    si etat =  21 saut etatMenuTNuit
    si etat =  22 saut etatMenuHMinu
    si etat =  23 saut etatMenuHJour
    si etat =  24 saut etatMenuHHeur
    si etat =  25 saut etatParaHJourC
    si etat =  26 saut etatParaHHeurC
    si etat =  27 saut etatParaHMinuC
    reti ; Ne devrait pas arriver, mais permet d'éviter les dégâts

valider:
    si etat <=  1 saut etatMenuAssoc
    si etat =   2 saut etatMenuJours
    si etat <=  9 saut etatParaJours
    si etat <= 16 saut etatParaJoursS
    si etat =  17 saut etatParaTJour
    si etat =  18 saut etatMenuTJour
    si etat =  19 saut etatParaTNuit
    si etat =  20 saut etatMenuTNuit
    si etat =  21 saut etatMenuHJour
    si etat =  22 saut etatParaHJour
    si etat =  23 saut etatParaHHeur
    si etat =  24 saut etatParaHMinu
    si etat =  25 saut etatMenuHJour
    si etat =  26 saut etatMenuHHeur
    si etat =  27 saut etatMenuHMinu
    reti ; Ne devrait pas arriver, mais permet d'éviter les dégâts

retour:
    si etat <=  1 saut reti
    si etat =   2 saut etatVeilleHeur
    si etat <=  9 saut etatMenuAssoc
    si etat <= 16 saut etatMenuJoursR
    si etat =  17 saut etatVeilleHeur
    si etat =  18 saut etatMenuTJour
    si etat =  19 saut etatVeilleHeur
    si etat =  20 saut etatMenuTNuit
    si etat =  21 saut etatVeilleHeur
    si etat <= 24 saut etatMenuHorloge
    si etat =  25 saut etatMenuHJour
    si etat =  26 saut etatMenuHHeur
    si etat =  27 saut etatMenuHMinu
    reti ; Ne devrait pas arriver, mais permet d'éviter les dégâts

; Interruption Watchdog
watchdog:
    call agir1s
    reti

; Interruption ADC
adc:
    ADCSRA <- 0b11011101 ; DEBUG Lire la température continuellement
    si ADCH > reference + 5 saut eteindreChaudiere
    si ADCH < reference - 5 saut allumerChaudiere
    reti
allumerChaudiere:
    PORTG@IO <- 0xFF
    reti
eteindreChaudiere:
    PORTG@IO <- 0x00
    reti

; Interruption timer (~4 ms)
timer:
    ; Notre composant requiert d'envoyer 0 sur PC7-4
    ; afin d'activer le digit qui y est connecté

    ; Affiche le digit suivant sur l'afficheur 7seg
    temp <- PORTC@IO ; On lis l'état précédent du port
    lsr temp ; On change de digit
    ; Il y a 4 digits et 8 bits, on doit donc boucler plus tôt
    si temp < 0b00001111 alors temp <- 0b11110111
    PORTC@IO <- temp
    ; On envoie le registre correpspondant
    si temp = 0b11110111 alors PORTA@IO <- d3
    si temp = 0b01111011 alors PORTA@IO <- d2
    si temp = 0b00111101 alors PORTA@IO <- d1
    si temp = 0b00011110 alors PORTA@IO <- d0

    ; Watchdog artificiel
    inc tt
    if tt < 244 saut reti
    ;if tt < 10 saut reti ; DEBUG Avancer le temps
    tt <- 0
    call agir1s

    reti

