Piccolo 2.0.1

Transcription

Piccolo 2.0.1
Piccolo 2.0.1
Pierre Molinaro
8 juin 2013
Table des matières
Table des matières
2
Liste des tableaux
7
1 Pourquoi j’ai écrit Piccolo
9
2 Installation et utilisation
2.1 Installation . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.2 Options de la ligne de commande . . . . . . . . . . . . . . . .
2.2.1 Liste des options . . . . . . . . . . . . . . . . . . . . . .
2.2.2 Exemple d’utilisation de l’option --memory . . . . . .
2.2.3 Exemple d’utilisation de l’option --registers . . .
2.2.4 Exemple d’utilisation de l’option --configuration
2.3 Micro-contrôleurs supportés . . . . . . . . . . . . . . . . . . .
2.3.1 Micro-contrôleurs baseline . . . . . . . . . . . . . . . .
2.3.2 Micro-contrôleurs mid-range . . . . . . . . . . . . . . .
2.3.3 Micro-contrôleurs pic18 . . . . . . . . . . . . . . . . . .
3 Description du langage Piccolo
3.1 Éléments lexicaux . . . . . . . . . . . .
3.1.1 Commentaires . . . . . . . . . .
3.1.2 Délimiteurs . . . . . . . . . . . .
3.1.3 Séparateurs . . . . . . . . . . . .
3.1.4 Identificateurs . . . . . . . . . .
3.1.5 Mots réservés . . . . . . . . . . .
3.1.6 Constante chaîne de caractères
3.1.7 Constante caractère . . . . . . .
3.1.8 Constante entière . . . . . . . .
3.1.9 Format . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
13
13
13
13
15
15
16
16
16
17
17
.
.
.
.
.
.
.
.
.
.
19
19
19
19
19
19
19
20
20
21
21
4 Programmes pour baseline
23
4.1 Structure d’un programme pour baseline . . . . . . . . . . . . . . . . . . . 23
4.2 Routines baseline . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
4.2.1 Routine régulière . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
TABLE DES MATIÈRES
4.2.2 Routine sans retour . . . . . . . . . . . . . . . . . . . . . .
4.3 Les instructions . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
4.3.1 Les instructions que vous ne trouverez pas en Piccolo .
4.4 Les instructions simples . . . . . . . . . . . . . . . . . . . . . . .
4.4.1 Instructions nommant un registre . . . . . . . . . . . . .
4.4.2 Instructions nommant un registre, et optionnellement W
4.4.3 Opérations d’affectation de bit . . . . . . . . . . . . . . . .
4.4.4 Opérations littérales avec W . . . . . . . . . . . . . . . . .
4.4.5 Instructions identiques à celles de l’assembleur . . . . .
4.4.6 Instruction TRIS . . . . . . . . . . . . . . . . . . . . . . . .
4.4.7 Appeler une routine régulière . . . . . . . . . . . . . . . .
4.4.8 Appeler une routine sans retour . . . . . . . . . . . . . .
4.5 Section include . . . . . . . . . . . . . . . . . . . . . . . . . . . .
4.6 Gestion des pages de la mémoire programme . . . . . . . . . . .
4.6.1 Instruction jump . . . . . . . . . . . . . . . . . . . . . . . .
4.6.2 Instruction jsr . . . . . . . . . . . . . . . . . . . . . . . . .
4.6.3 Déclaration de routine et page . . . . . . . . . . . . . . . .
4.6.4 Exemple . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
4.7 Optimisation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
25
27
27
28
28
29
29
30
30
31
31
32
32
32
33
33
33
34
35
5 Programmes pour mid-range
5.1 Structure d’un programme pour mid-range . . . . . . . . . . . . . .
5.2 Routines mid-range . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5.2.1 Routine régulière . . . . . . . . . . . . . . . . . . . . . . . . .
5.2.2 Routine sans retour . . . . . . . . . . . . . . . . . . . . . . . .
5.3 Les instructions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5.3.1 Les instructions que vous ne trouverez pas en Piccolo . . .
5.4 Les instructions simples . . . . . . . . . . . . . . . . . . . . . . . . .
5.4.1 Instructions nommant un registre . . . . . . . . . . . . . . .
5.4.2 Instructions nommant un registre, et optionnellement W . .
5.4.3 Opérations d’affectation de bit . . . . . . . . . . . . . . . . . .
5.4.4 Opérations littérales avec W . . . . . . . . . . . . . . . . . . .
5.4.5 Instructions identiques à celles de l’assembleur . . . . . . .
5.4.6 Appeler une routine régulière . . . . . . . . . . . . . . . . . .
5.4.7 Appeler une routine sans retour . . . . . . . . . . . . . . . .
5.5 Section include . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5.6 Gestion des pages de la mémoire programme . . . . . . . . . . . . .
5.6.1 Instruction jump . . . . . . . . . . . . . . . . . . . . . . . . . .
5.6.2 Instruction jsr . . . . . . . . . . . . . . . . . . . . . . . . . . .
5.6.3 Déclaration de routine et page . . . . . . . . . . . . . . . . . .
5.6.4 Exemple . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5.7 Routine d’interruption mid-range . . . . . . . . . . . . . . . . . . . .
5.7.1 Allure générale de la routine d’interruption en assembleur
5.7.2 Routine d’interruption en Piccolo . . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
37
37
38
38
38
40
41
41
41
41
43
44
44
44
45
45
46
46
47
47
47
48
48
50
4
TABLE DES MATIÈRES
5.7.3 Interruption et sauvegarde du contexte en Piccolo . . . . . . . . . . 51
5.8 Optimisations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
6 Programmes pour pic18
6.1 Exemples de programmes pour pic18 . . . . . . . . . . . . . . . .
6.1.1 Premier exemple : blink led . . . . . . . . . . . . . . . . .
6.1.2 Deuxième exemple : blink led sous interruption . . . . .
6.2 Structure d’un programme pour pic18 . . . . . . . . . . . . . . .
6.3 Routines pic18 . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
6.3.1 Routine régulière . . . . . . . . . . . . . . . . . . . . . . .
6.3.2 Routine sans retour . . . . . . . . . . . . . . . . . . . . . .
6.4 Routines d’interruption . . . . . . . . . . . . . . . . . . . . . . . .
6.4.1 Qualificatif fast . . . . . . . . . . . . . . . . . . . . . . . .
6.4.2 Routine d’interruption et BSR . . . . . . . . . . . . . . . .
6.5 Les instructions . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
6.5.1 Les instructions que vous ne trouverez pas en Piccolo .
6.6 Les instructions simples . . . . . . . . . . . . . . . . . . . . . . .
6.6.1 Instructions nommant un registre . . . . . . . . . . . . .
6.6.2 Instructions nommant un registre, et optionnellement W
6.6.3 Opérations d’affectation de bit . . . . . . . . . . . . . . . .
6.6.4 Opérations littérales avec WREG . . . . . . . . . . . . . . .
6.6.5 Instructions identiques à celles de l’assembleur . . . . .
6.6.6 Instruction lfsr . . . . . . . . . . . . . . . . . . . . . . . .
6.6.7 Instruction movff . . . . . . . . . . . . . . . . . . . . . . .
6.6.8 Appeler une routine régulière . . . . . . . . . . . . . . . .
6.6.9 Appeler une routine sans retour . . . . . . . . . . . . . .
6.7 Section include . . . . . . . . . . . . . . . . . . . . . . . . . . . .
6.8 Section data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
6.9 Instructions pic18 . . . . . . . . . . . . . . . . . . . . . . . . . . .
6.9.1 Instruction ltblptr . . . . . . . . . . . . . . . . . . . . .
6.9.2 Instruction ldataptr . . . . . . . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
57
57
57
61
63
64
64
65
67
67
68
68
70
71
71
72
72
74
74
74
75
75
76
77
77
78
78
79
7 Instructions structurées
7.1 Instruction mnop . . . . . . . . . . . . . . . . . . . . . . . . . .
7.2 Instruction conditionnelle simple . . . . . . . . . . . . . . . .
7.3 Instruction conditionnelle structurée . . . . . . . . . . . . . .
7.4 Instruction de répétition infinie . . . . . . . . . . . . . . . . .
7.5 Instruction répétitive . . . . . . . . . . . . . . . . . . . . . . .
7.6 Forme générale des conditions . . . . . . . . . . . . . . . . . .
7.6.1 Conditions élémentaires pour baseline et mid-range
7.6.2 Conditions élémentaires pour pic18 . . . . . . . . . .
7.7 Instructions computed . . . . . . . . . . . . . . . . . . . . . .
7.7.1 Instruction computed retlw . . . . . . . . . . . . . .
7.7.2 Instruction computed bra . . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
81
81
82
83
84
84
85
86
86
87
87
90
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
TABLE DES MATIÈRES
5
7.7.3 Instruction computed goto . . . . . . . . . . . . . . . . . .
7.7.4 Implémentation de _computed_goto_x . . . . . . . . . . .
7.8 Instructions de gestion des bancs mémoire . . . . . . . . . . . . .
7.8.1 Instruction et banc courant . . . . . . . . . . . . . . . . . .
7.8.2 Instruction banksel . . . . . . . . . . . . . . . . . . . . . .
7.8.3 Instruction nobank . . . . . . . . . . . . . . . . . . . . . . .
7.8.4 Instruction banksave . . . . . . . . . . . . . . . . . . . . . .
7.8.5 Déclaration de routine et gestion des bancs . . . . . . . . .
7.8.6 Déclaration de routine sans qualificatif de banc . . . . . .
7.8.7 Routine déclarée avec bank:preserved . . . . . . . . . . .
7.8.8 Routine déclarée avec bank:requires b e . . . . . . . . .
7.8.9 Routine déclarée avec bank:ensures b s . . . . . . . . . .
7.8.10 Routine déclarée avec bank:requires b e ensures b s .
7.8.11 Sélection de banc et instruction conditionnelle structurée
7.8.12 Sélection de banc et instruction de répétition infinie . . .
7.8.13 Sélection de banc et instruction répétitive . . . . . . . . . .
8 Définition de variable
8.1 Comment est décrite la RAM ? . . . . . . . . .
8.1.1 Micro-contrôleur baseline . . . . . . . .
8.1.2 Micro-contrôleur mid-range . . . . . .
8.1.3 Micro-contrôleur pic18 . . . . . . . . .
8.2 Les sections ram . . . . . . . . . . . . . . . . .
8.3 Déclaration byte . . . . . . . . . . . . . . . . .
8.4 Nommage de bits dans une déclaration byte
8.5 Référence à un registre . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
91
92
92
93
93
94
94
94
94
96
97
97
98
99
99
99
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
101
. 101
. 101
. 102
. 102
. 102
. 103
. 104
. 104
9 Définition de constante
9.1 Constantes prédéfinies . . . . . . . . . . . . . . . . .
9.1.1 Exemple d’utilisation de ROM_SIZE . . . . . .
9.1.2 Exemple d’utilisation de RAM_SIZE . . . . . .
9.2 Forme générale d’une expression littérale . . . . . .
9.3 Expression littérale : valeur descriptive de registre
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
107
. 107
. 108
. 108
. 109
. 110
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
10 Registres de configuration
111
10.1 Connaître les possibilités de configuration d’un contrôleur . . . . . . . . . 111
10.2 Écrire une configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112
10.3 Combinaisons incompatibles . . . . . . . . . . . . . . . . . . . . . . . . . . . 112
Index
114
Liste des tableaux
4.1
4.2
4.3
4.4
4.5
4.6
4.7
4.8
4.9
4.10
4.11
Les sections d’un programme pour baseline . . . . . . . . . . . . .
Instructions machine des baseline . . . . . . . . . . . . . . . . . . .
Opérations baseline nommant un registre . . . . . . . . . . . . . .
Instructions baseline nommant un registre, et optionnellement W
Opérations baseline sur un bit d’un registre . . . . . . . . . . . . .
Opérations litérales avec W pour baseline . . . . . . . . . . . . . .
Instructions baseline identiques en assembleur et en Piccolo . .
Instructions tris en Piccolo . . . . . . . . . . . . . . . . . . . . . .
Mémoire programme de quelques micro-contrôleurs baseline . .
Instructions CALL et GOTO des baseline . . . . . . . . . . . . . . . .
Optimisation du code baseline . . . . . . . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
23
28
29
29
29
30
31
31
32
33
35
5.1
5.2
5.3
5.4
5.5
5.6
5.7
5.8
5.9
5.10
Les sections d’un programme pour mid-range . . . . . . . . . . . . . .
Instructions machine des mid-range . . . . . . . . . . . . . . . . . . .
Opérations mid-range nommant un registre . . . . . . . . . . . . . .
Instructions mid-range nommant un registre, et optionnellement W
Opérations mid-range sur un bit d’un registre . . . . . . . . . . . . .
Opérations litérales avec W pour mid-range . . . . . . . . . . . . . . .
Instructions mid-range identiques en assembleur et en Piccolo . . .
Mémoire programme de quelques micro-contrôleurs mid-range . . .
Instructions call et goto des mid-range . . . . . . . . . . . . . . . .
Optimisation du code mid-range . . . . . . . . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
37
42
43
43
43
44
45
46
46
55
6.1
6.2
6.3
6.4
6.5
6.6
6.7
6.8
6.9
6.10
Les sections d’un programme pour pic18 . . . . . . . . . . . . . .
Instructions assembleur pic18 [A-G] . . . . . . . . . . . . . . . .
Instructions assembleur pic18 [I-S] . . . . . . . . . . . . . . . . .
Instructions assembleur pic18 [T-X] . . . . . . . . . . . . . . . .
Instructions propres à Piccolo pour pic18 . . . . . . . . . . . . .
Opérations nommant un registre . . . . . . . . . . . . . . . . . .
Instructions pic18 nommant un registre, et optionnellement W
Opérations pic18 d’affectation de bit . . . . . . . . . . . . . . . .
Opérations statiques avec WREG pour pic18 . . . . . . . . . . . .
Instructions pic18 identiques en assembleur et en Piccolo . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
64
69
70
71
71
72
73
73
74
75
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
8
LISTE DES TABLEAUX
6.11 Codages possibles de l’instruction jump cc . . . . . . . . . . . . . . . . . . . 77
7.1 Instructions conditionnelles simples pour baseline et mid-range . . . .
7.2 Instructions conditionnelles simples pour pic18 . . . . . . . . . . . . . .
7.3 Conditions pour baseline et mid-range basées sur le test d’un bit d’un
registre . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
7.4 Conditions pour baseline et mid-range basées sur la décrémentation et
l’incrémentation d’un registre . . . . . . . . . . . . . . . . . . . . . . . . .
7.5 Conditions pour pic18 basées sur les instructions de saut conditionnel
7.6 Conditions pour pic18 basées sur les comparaisons avec WREG . . . . . .
7.7 Conditions pour pic18 basées sur le test d’un registre . . . . . . . . . . .
7.8 Conditions pour pic18 basées sur la décrémentation et l’incrémentation
d’un registre . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
7.9 Routine sans retour et qualificatifs associés aux bancs . . . . . . . . . .
7.10 Routine régulière et qualificatifs associés aux bancs . . . . . . . . . . . .
. 82
. 83
. 86
.
.
.
.
87
88
88
89
. 89
. 94
. 95
9.1 Constantes prédéfinies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108
9.2 Opérateurs d’une expression littérale, par ordre de priorité croissante . . 109
Chapitre 1
Pourquoi j’ai écrit Piccolo
J’avais écrit un programme d’environ 2400 lignes assembleur pour un PIC 18F448,
qui fonctionnait parfaitement. Ce programme n’utilisait que les registres accessibles
depuis l’access bank, et j’avais écrit toutes les instructions pour l’accès se fasse de
cette façon.
Le programme n’était pas terminé, et j’allais ajouter et code pour l’interface réseau CAN, qui utilise des registres situés dans la page 15. J’ai donc commencé par
ajouter au début du programme l’instruction MOVLB 0xF, qui affecte la valeur 15 au
registre de page BSR (il est initialisé à zéro au démarrage). Là, surprise, plus rien ne
fonctionnait. La seule explication, qui s’est avéré exacte par la suite, est que certains
accès ne s’effectuaient pas par l’access bank comme je pensais, mais via le registre
BSR. Après plusieurs heures d’examen du code source, j’ai enfin trouvé ce qui n’allait
pas, plusieurs instructions du style :
NEGF variable, F
F étant un symbole que j’avais initialisé à 1 (en fait, c’était une très mauvaise idée
de définir F !) Or l’instruction NEGF considère le second argument comme l’indicateur
précisant l’accès via le registre BSR : quand il vaut 1, l’accès se fait par BSR. J’avais
confondu avec les instructions comme DECF, où le second argument indique si la donnée décrémentée est rangée dans le registre (DECF registre, 1) ou bien dans W (DECF
registre, 0).
J’ai alors réalisé que le programme que je projetais de faire, qui devait jongler
avec les différents bancs, allait être fragile. Dès lors, je me décidai à concevoir un
langage dont le compilateur s’apercevrait des erreurs d’adressage vis à vis du banc
sélectionné via BSR.
C’est là le but premier de Piccolo : garantir l’exactitude de la valeur de BSR
vis à vis des instructions qui accèdent aux registres.
Prenons un exemple. On considère trois variables :
– var0 désignera une variable accessible via l’access bank, c’est à dire indépendamment de la valeur de BSR ;
– var1 désignera une variable de la page 1, c’est à dire accessible quand BSR vaut
1;
10
C HAPITRE 1. Pourquoi j’ai écrit Piccolo
– var2 désignera une variable de la page 2, c’est à dire accessible quand BSR vaut
2.
En assembleur, si j’écris :
clrf var0
clrf var1
clrf var2
Il n’y a pas d’erreur d’assemblage, et cependant seule la première instruction est
correcte. La deuxième instruction fera un clrf sur le registre d’adresse varBank1 %
0x100 (« % » signifiant « modulo »), et la troisième sur celui d’adresse varBank2 %
0x100.
L’écriture correcte en assembleur est :
clrf var0
movlb 1
clrf var1, BANKED
movlb 2
clrf var2, BANKED
La moindre erreur est fatale. Or, si il est facile de la détecter sur un exemple
de trois lignes, cela devient beaucoup plus laborieux quand le programme atteint
plusieurs milliers de lignes. La sécurité voudrait que l’on insère une instruction MOVLB
avant chaque accès via BSR. Mais c’est très inefficace. . . On préfère souvent initialiser
BSR pour un groupe d’instructions.
En assembleur, que ce passe t-il alors si je décide de passer var1 dans le banc 0 ?
Il faut revoir toutes les instructions nommant var1 pour changer la valeur de BSR.
Maintenant, voyons comment cette même situation est appréhendée en Piccolo.
Piccolo reprend la plupart des instructions assembleur, aussi la séquence des trois
instructions précédentes peut être écrite en Piccolo.
En Piccolo, le code est exécutable est structuré en routines, aussi on écrira :
routine
clrf
clrf
clrf
}
maRoutine {
var0
var1 # Erreur
var2 # Erreur
Quelques remarques de détail : « routine » et « clrf » sont des mots réservés de
Piccolo, et l’instruction « RETURN » est automatiquement ajoutée à la fin des instructions de la routine par la génération de code.
Que se passe-t’il si on compile un programme contenant cette routine ? En Piccolo,
la sémantique de la référence à un registre est la suivante :
– d’abord, l’analyseur sémantique regarde si le registre peut être accédé par l’access
bank ; si oui, l’instruction est correcte ;
– si non, l’analyseur sémantique regarde si le registre peut être accédé grâce au
registre BSR.
11
Or ici, aucune indication n’est fournie sur la valeur de BSR : deux erreurs apparaissent pour les 2e et 3e instructions.
On corrige ces erreurs au moyen de l’instruction « banksel » (comme MOVLB, elle
affecte une valeur à BSR, mais comme sa sémantique est différente, j’ai préféré utiliser
un autre nom). La routine corrigée est :
routine maRoutine {
c l r f var0
banksel 1
c l r f var1
banksel 2
c l r f var2
}
Mais on peut aller plus loin : l’en-tête de la routine ne dit rien au sujet de BSR, ce
qui signifie que la valeur de BSR peut être quelconque au moment de l’appel, et que la
routine peut modifier BSR à sa guise.
Si maintenant, je change l’en-tête en écrivant :
routine maRoutine bank:requires 1 {
c l r f var0
c l r f var1
banksel 2
c l r f var2
}
Cet en-tête signifie que le compilateur Piccolo vérifie que la routine ne soit appelée
que lorsque BSR vaut 1. En contre partie, l’instruction « banksel 1 » est inutile et
est enlevée (d’ailleurs sa présence provoquerait l’émission un warning signalant son
inutilité).
On retrouve là la programmation par contrat :
– obligation de l’appelant : appeler maRoutine quand BSR contient 1 ;
– bénéfice de l’appelé : pouvoir utiliser directement l’accès au banc 1.
Il y a d’autres possibilités d’écriture de l’en-tête vis à vis de BSR : elles sont décrites
dans le chapitre consacré à ce registre.
Le compilateur Piccolo simule l’évolution de l’exécution du programme vis à vis de
BSR. Ceci a apporté des contraintes dans le design de Piccolo, notamment l’abandon
de l’utilisation des instructions de saut et de saut conditionnel pour diriger le flot
d’exécution à l’intérieur d’une routine. Piccolo propose à leur place quatre instructions
structurées :
– l’instruction conditionnelle simple (exploitation directe des instructions sautant
conditionnellement l’instruction qui les suit) ;
– l’instruction conditionnelle structurée (if . . . elsif . . . else . . . end) ;
– l’instruction répétitive (do . . . while . . . while . . . end) ;
– l’instruction répétitive infinie (forever . . . end).
Une conséquence de l’emploi des ces instructions structurées est une augmentation de volume du code engendré : c’est pourquoi un optimiseur est incorporé et
12
C HAPITRE 1. Pourquoi j’ai écrit Piccolo
permet d’atteindre des résultats très proches d’un code assembleur optimisé écrit à la
main.
Chapitre 2
Installation et utilisation
2.1
Installation
La page http://piccolo.rts-software.org/download/index.php contient
les liens vers la distribution de Piccolo.
Piccolo est distribué sous la forme d’un utilitaire en ligne de commande, sauf pour
Mac OS où une application proposant un éditeur avec coloration lexicale est proposée.
2.2
2.2.1
Options de la ligne de commande
Liste des options
Piccolo accepte un certain nombre d’options, qui sont détaillées dans les pages
suivantes.
L’analyse des arguments de la ligne de commandes est simple :
– tout argument qui commence par un « - » est une option ;
– tout argument qui ne commence pas par un « - » est considéré comme un fichier
source Piccolo ;
– la seule extension acceptable pour un fichier source Piccolo est « .piccolo ».
En conséquence, un argument ne commençant pas par un « - » et n’ayant pas
l’extension « .piccolo » déclenche une erreur.
L’ordre des options et des fichiers sources est quelconque. La ligne de commande
est complètement analysée avant le traitement des fichiers sources.
Si plusieurs fichiers sources apparaissent dans la ligne de commande, ils sont
traités dans leur ordre d’apparition.
Note pour Windows. L’outil Piccolo pour Windows propose par défaut un dialogue invitant à entrer les références d’un fichier source si la ligne ne contient aucun
fichier source (c’est le cas quand on double-clique sur l’icône de l’application). Une
option « --no-dialog », spécifique à cette plate forme, permet d’inhiber l’apparition
du dialogue.
La liste des options est :
14
C HAPITRE 2. Installation et utilisation
--version Affiche le numéro de version.
-v, --verbose Affiche des messages complémentaires sur le terminal. Par défaut,
quand toutes les étapes se déroulent correctement, aucun message n’est affiché.
-n, --no-file-generation Aucune écriture sur fichier n’a lieu : ceci permet de garantir que les fichiers de sortie ne seront pas engendrés. La tentative d’écrire
un fichier est signalé par un warning.
-W, --Werror Tout warning est considéré comme une erreur. Cela peut être important dans un script, l’outil de commande renvoyant un code non nul si une ou
plusieurs erreurs ont été détectées.
-d, --detailled-error-messages Imprime de manière détaillée les messages d’erreur et d’alerte. Par défaut, ils sont de la même forme que ceux engendrés par
gcc.
-S, --asm Si la compilation s’effectue sans erreur, engendre aussi un fichier texte
assembleur, dans le même répertoire que le fichier source Piccolo, mais avec
l’extension « .asm ».
-D, --device-list Affiche sur le terminal la liste des micro-contrôleurs baseline,
mid-range et pic18 supportés.
--baseline Affiche sur le terminal la liste des micro-contrôleurs baseline supportés
(voir section 2.3.1 page 16).
--midrange Affiche sur le terminal la liste des micro-contrôleurs mid-range supportés (voir section 2.3.2 page 17).
--pic18 Affiche sur le terminal la liste des micro-contrôleurs pic18 supportés (voir
section 2.3.3 page 17).
-L, --list Produit un fichier listing contenant de nombreux détails concernant la
configuration, l’allocation de la RAM, les optimisations réalisées et la transformation des branchement relatifs en absolu. Ce fichier est écrit dans le même
répertoire que le fichier source, porte le même nom, mais avec l’extension « .list
».
-o, --optimize Effectue de manière itérative des optimisations du code produit.
-F=string, --configuration=string Affiche sur le terminal le détail des registres
de configuration du micro-contrôleur désigné par string. Un exemple se trouve
à la section 2.2.4 page 16.
-M=string, --memory=string Affiche sur le terminal la composition des bancs de
la RAM, la taille de la ROM et de l’EEPROM du micro-contrôleur désigné par
string. Un exemple se trouve à la section 2.2.2 page 15.
-R=string, --registers=string Affiche sur le terminal la liste alphabétique des
registres spéciaux du micro-contrôleur désigné par string, ainsi que le nom de
leurs bits. Un exemple se trouve à la section 2.2.3 page 15.
--no-dialog (uniquement sur Windows) L’outil Piccolo pour Windows propose par
défaut un dialogue invitant à entrer les références d’un fichier source si la ligne
2.2. Options de la ligne de commande
15
ne contient aucun fichier source (c’est le cas quand on double-clique sur l’icône
de l’application). Cette option permet d’inhiber l’apparition du dialogue.
2.2.2
Exemple d’utilisation de l’option --memory
Cette option affiche sur le terminal la composition des bancs de la RAM, la taille
de la ROM et de l’EEPROM d’un micro-contrôleur.
Par exemple, pour le 18F448, on écrit la ligne de commande :
piccolo --memory=18F448
On obtient ainsi :
18F448 device:
4 RAM banks (total 768 bytes):
bank "accessram" from 0x0 to 0x5F (96 bytes)
bank "gpr0" from 0x60 to 0xFF (160 bytes)
bank "gpr1" from 0x100 to 0x1FF (256 bytes)
bank "gpr2" from 0x200 to 0x2FF (256 bytes)
ROM size: 16384 bytes
EEPROM size: 256 bytes (at 0xF00000)
2.2.3
Exemple d’utilisation de l’option --registers
Cette option affiche sur le terminal la liste alphabétique des registres spéciaux
d’un micro-contrôleur, ainsi que le nom de leurs bits.
Par exemple, pour le 12F683, on écrit la ligne de commande :
piccolo --registers=12F683
On obtient ainsi :
12F683 has 38 special registers:
’ADCON0’ at 0x1F <ADFM, VCFG, -, -, CHS[2], GO/nDONE, ADON>
’ADRESH’ at 0x1E <ADRESH[8]>
’ADRESL’ at 0x9E <ADRESL[8]>
’ANSEL’ at 0x9F <-, ADCS[3], ANS[4]>
’CCP1CON’ at 0x15 <-, -, DC1B[2], CCP1M[4]>
’CCPR1H’ at 0x14 <CCPR1H[8]>
’CCPR1L’ at 0x13 <CCPR1L[8]>
’CMCON0’ at 0x19 <-, COUT, -, CINV, CIS, CM[3]>
’CMCON1’ at 0x1A <-, -, -, -, -, -, T1GSS, CMSYNC>
’EEADR’ at 0x9B <EEADR[8]>
’EECON1’ at 0x9C <-, -, -, -, WRERR, WREN, WR, RD>
’EECON2’ at 0x9D <EECON2[8]>
’EEDAT’ at 0x9A <EEDAT[8]>
’FSR’ at 0x4, 0x84 <FSR[8]>
’GPIO’ at 0x5 <-, -, GP5, GP4, GP3, GP2, GP1, GP0>
’INDF’ at 0x0, 0x80 <INDF[8]>
’INTCON’ at 0xB, 0x8B <GIE, PEIE, T0IE, INTE, GPIE, T0IF, INTF, GPIF>
’IOC’ at 0x96 <-, -, IOC5, IOC4, IOC3, IOC2, IOC1, IOC0>
’OPTION_REG’ at 0x81 <nGPPU, INTEDG, T0CS, T0SE, PSA, PS[3]>
’OSCCON’ at 0x8F <-, IRCF[3], OSTS, HTS, LTS, SCS>
’OSCTUNE’ at 0x90 <-, -, -, TUN[5]>
’PCL’ at 0x2, 0x82 <PCL[8]>
’PCLATH’ at 0xA, 0x8A <-, -, -, PCLATH[5]>
’PCON’ at 0x8E <-, -, ULPWUE, SBODEN, -, -, nPOR, nBOD>
’PIE1’ at 0x8C <EEIE, ADIE, CCP1IE, -, CMIE, OSFIE, TMR2IE, TMR1IE>
’PIR1’ at 0xC <EEIF, ADIF, CCP1IF, -, CMIF, OSFIF, TMR2IF, TMR1IF>
16
C HAPITRE 2. Installation et utilisation
’PR2’ at 0x92 <PR2[8]>
’STATUS’ at 0x3, 0x83 <IRP, RP[2], nTO, nPD, Z, DC, C>
’T1CON’ at 0x10 <T1GINV, TMR1GE, T1CKPS[2], T1OSCEN, nT1SYNC, TMR1CS, TMR1ON>
’T2CON’ at 0x12 <-, TOUTPS[4], TMR2ON, T2CKPS[2]>
’TMR0’ at 0x1 <TMR0[8]>
’TMR1H’ at 0xF <TMR1H[8]>
’TMR1L’ at 0xE <TMR1L[8]>
’TMR2’ at 0x11 <TMR2[8]>
’TRISIO’ at 0x85 <-, -, TRISIO5, TRISIO4, TRISIO3, TRISIO2, TRISIO1, TRISIO0>
’VRCON’ at 0x99 <VREN, -, VRR, -, VR[4]>
’WDTCON’ at 0x18 <-, -, -, WDTPS[4], SWDTEN>
’WPU’ at 0x95 <-, -, WPU5, WPU4, -, WPU2, WPU1, WPU0>
2.2.4
Exemple d’utilisation de l’option --configuration
Cette option affiche sur le terminal le détail des registres de configuration d’un
micro-contrôleur.
Par exemple, pour le 10F220, on écrit la ligne de commande :
piccolo --configuration=10F220
On obtient ainsi :
10F220 has 1 configuration registers:
REGISTER ’CONFIG’ at 0xFFF, width 5
setting ’CP’: mask 0x8 description "Code protection bit"
value 0x8 description "Disabled"
value 0x0 description "Enabled"
setting ’IOSCFS’: mask 0x1 description "Internal Oscillator Frequency Select bit"
value 0x0 description "4 MHz"
value 0x1 description "8 MHz"
setting ’MCLRE’: mask 0x10 description "GP3/MCLR Pin Function Select bit"
value 0x0 description "Disabled"
value 0x10 description "Enabled"
setting ’MCPU’: mask 0x2 description "Master Clear Pull-up Enable bit"
value 0x2 description "Disabled"
value 0x0 description "Enabled"
setting ’WDTE’: mask 0x4 description "Watchdog Timer Enable bit"
value 0x0 description "Disabled"
value 0x4 description "Enabled"
2.3
2.3.1
Micro-contrôleurs supportés
Micro-contrôleurs baseline
Pour connaître la liste des micro-contrôleurs baseline pris en charge, utiliser l’option « --baseline » :
piccolo --baseline
On obtient ainsi :
45 baseline devices supported
10F200
10F202
10F222
12C508
12CE518
12CE519
12F510
12F519
16C54C
16C55
by Piccolo version 2.0.1:
10F204
10F206
12C508A
12C509
12CR509A
12F508
16C505
16C54
16C55A
16C56
10F220
12C509A
12F509
16C54A
16C56A
2.3. Micro-contrôleurs supportés
16C57
16CR54A
16CR57C
16F526
2.3.2
16C57C
16CR54C
16CR58A
16F54
16C58A
16CR56A
16CR58B
16F57
17
16C58B
16CR57A
16F505
16F59
16CR54
16CR57B
16F506
16HV540
Micro-contrôleurs mid-range
Pour connaître la liste des micro-contrôleurs mid-range pris en charge, utiliser
l’option « --midrange » :
piccolo --midrange
On obtient ainsi :
143 mid-range devices supported by Piccolo version 2.0.1:
12C671
12C672
12CE673
12CE674
12F615
12F629
12F635
12F675
12HV609
12HV615
16C432
16C433
16C557
16C558
16C620
16C620A
16C621A
16C622
16C622A
16C62A
16C63
16C63A
16C642
16C64A
16C65B
16C66
16C662
16C67
16C710
16C711
16C712
16C715
16C717
16C72
16C72A
16C73A
16C745
16C74A
16C74B
16C76
16C77
16C770
16C771
16C773
16C781
16C782
16C923
16C924
16C926
16CE623
16CE624
16CE625
16CR620A
16CR63
16CR64
16CR65
16CR83
16CR84
16F610
16F616
16F627A
16F628
16F628A
16F630
16F636
16F639
16F648A
16F676
16F684
16F685
16F687
16F688
16F690
16F716
16F72
16F722
16F724
16F726
16F727
16F73
16F74
16F747
16F76
16F767
16F777
16F785
16F818
16F819
16F84
16F84A
16F87
16F870
16F872
16F873
16F873A
16F874
16F876
16F876A
16F877
16F877A
16F882
16F883
16F884
16F886
16F913
16F914
16F916
16F917
16HV610
16HV616
16HV785
16LF722
16LF724
16LF726
16LF727
2.3.3
12F609
12F683
16C554
16C621
16C62B
16C65A
16C71
16C716
16C73B
16C765
16C774
16C925
16CR62
16CR72
16F627
16F631
16F677
16F689
16F723
16F737
16F77
16F83
16F871
16F874A
16F88
16F887
16F946
16LF723
Micro-contrôleurs pic18
Pour connaître la liste des micro-contrôleurs pic18 pris en charge, utiliser l’option
« --pic18 » :
piccolo --pic18
On obtient ainsi :
289 pic18 devices supported by Piccolo version 2.0.1:
18C242
18C252
18C442
18C452
18C658
18C801
18C858
18F1220
18F1320
18F1330
18F13K20
18F13K22
18F14K20
18F14K22
18F14K50
18F2220
18C601
18F1230
18F13K50
18F2221
18
18F2320
18F2410
18F2439
18F2480
18F24K22
18F2520
18F2553
18F25J11
18F25K80
18F2685
18F26K20
18F4220
18F43K20
18F4423
18F4458
18F44J50
18F452
18F4550
18F45J10
18F45K50
18F4682
18F46J53
18F47J53
18F63J90
18F64J90
18F65J10
18F65K22
18F6622
18F66J11
18F66J60
18F66K22
18F6723
18F67J90
18F8310
18F8410
18F8520
18F85J11
18F85K90
18F8628
18F86J16
18F86J72
18F86K90
18F87J11
18F87J93
18F96J65
18LF13K50
18LF24J11
18LF25J11
18LF26J11
18LF26K80
18LF44J11
18LF45J50
18LF46J13
18LF47J13
C HAPITRE 2. Installation et utilisation
18F2321
18F242
18F2450
18F24J10
18F24K50
18F2523
18F258
18F25J50
18F2610
18F26J11
18F26K22
18F4221
18F43K22
18F4431
18F448
18F44K20
18F4520
18F4553
18F45J11
18F45K80
18F4685
18F46K20
18F6310
18F6410
18F6520
18F65J11
18F65K80
18F6627
18F66J15
18F66J65
18F66K80
18F67J10
18F67J93
18F8390
18F8490
18F8525
18F85J15
18F8620
18F8680
18F86J50
18F86J90
18F8720
18F87J50
18F87J94
18F96J94
18LF14K22
18LF24J50
18LF25J50
18LF26J13
18LF27J13
18LF44J50
18LF45K22
18LF46J50
18LF47J53
18F2331
18F2420
18F2455
18F24J11
18F2510
18F2525
18F2580
18F25K20
18F2620
18F26J13
18F26K80
18F4320
18F4410
18F4439
18F4480
18F44K22
18F4523
18F458
18F45J50
18F4610
18F46J11
18F46K22
18F6390
18F6490
18F6525
18F65J15
18F65K90
18F6628
18F66J16
18F66J90
18F66K90
18F67J11
18F67J94
18F8393
18F8493
18F8527
18F85J50
18F8621
18F86J10
18F86J55
18F86J93
18F8722
18F87J60
18F87K22
18F97J60
18LF14K50
18LF24K22
18LF25K22
18LF26J50
18LF27J53
18LF44K22
18LF45K50
18LF46J53
18LF65K80
18F23K20
18F2423
18F2458
18F24J50
18F2515
18F2539
18F2585
18F25K22
18F2680
18F26J50
18F27J13
18F4321
18F442
18F4450
18F44J10
18F4510
18F4525
18F4580
18F45K20
18F4620
18F46J13
18F46K80
18F6393
18F6493
18F6527
18F65J50
18F6620
18F6680
18F66J50
18F66J93
18F6720
18F67J50
18F67K22
18F83J11
18F84J11
18F8585
18F85J90
18F8622
18F86J11
18F86J60
18F86J94
18F8723
18F87J72
18F87K90
18F97J94
18LF23K22
18LF24K50
18LF25K50
18LF26J53
18LF43K22
18LF45J10
18LF45K80
18LF46K22
18LF66K80
18F23K22
18F2431
18F248
18F24K20
18F252
18F2550
18F25J10
18F25K50
18F2682
18F26J53
18F27J53
18F4331
18F4420
18F4455
18F44J11
18F4515
18F4539
18F4585
18F45K22
18F4680
18F46J50
18F47J13
18F63J11
18F64J11
18F6585
18F65J90
18F6621
18F66J10
18F66J55
18F66J94
18F6722
18F67J60
18F67K90
18F83J90
18F84J90
18F85J10
18F85K22
18F8627
18F86J15
18F86J65
18F86K22
18F87J10
18F87J90
18F96J60
18LF13K22
18LF24J10
18LF25J10
18LF25K80
18LF26K22
18LF44J10
18LF45J11
18LF46J11
18LF46K80
Chapitre 3
Description du langage Piccolo
3.1
Éléments lexicaux
3.1.1
Commentaires
Un commentaire commence par un caractère dièse « # », et s’étend jusqu’à la fin
de la ligne courante.
3.1.2
Délimiteurs
Piccolo définit les délimiteurs suivants :
3.1.3
*
*+
,
!=
<=
>=
*-
+*
;
:
==
<
>
[
]
.
&
|
{
}
(
)
/
-
+
ˆ
<<
>>
˜
@
%
=
!
Séparateurs
Tout caractère dont le code ASCII est compris entre 0x01 et 0x20 est considéré
comme un séparateur (ceci inclut donc la tabulation horizontale HT (0x09), le passage
à la ligne LF (0x0A), le retour-chariot CR (0x0D), et l’espace (0x20).
3.1.4
Identificateurs
Un identificateur commence par une lettre (minuscule ou majuscule), qui est suivie par zéro, un ou plusieurs lettres (minuscules ou majuscules), chiffres décimaux,
caractères « _ ».
La casse est significative pour les identificateurs.
3.1.5
Mots réservés
Certains identificateurs sont réservés. La casse n’est pas significative pour les
mots réservés, c’est à dire que les mots réservés peuvent être écrits indifféremment
avec des lettres minuscules ou majuscules.
20
C HAPITRE 3. Description du langage Piccolo
Mots réservés correspondant aux éléments du langage :
bank
banksave
banksel
baseline
bootloader
byte
computed
configuration
const
contextsave
data
do
end
else
elsif
ensures
fast
forever
if
implements
include
interrupt
mark
midrange
nobank
noreturn
page
pic18
preserved
ram
requires
rom
routine
uses
w
while
Dans tout ce document, ces mots réservés sont écrits en minuscule, gras et en bleu.
Mots réservés correspondant à des instructions exécutables :
addlw
addwf
addwfc
andlw
andwf
bc
bcf
bn
bnc
bnn
bov
bnov
bnz
bsf
bra
btg
bz
call
clrf
clrw
clrwdt
comf
daw
decf
incf
iorlw
iorwf
goto
jsr
jump
lfsr
ldataptr
ltblptr
mnop
movf
movff
movlw
movwf
mullw
mulwf
negf
nop
pop
option
push
rcall
reset
retlw
rlcf
rlf
rlncf
rrcf
rrf
rrncf
setf
sleep
subfwb
sublw
subwf
subwfb
swapf
tblrd
tblwt
trisxorlw xorwf
Dans tout ce document, ces mots réservés sont écrits en minuscule, gras et en
marron. Les instructions assembleur correspondantes sont écrites en majuscules.
3.1.6
Constante chaîne de caractères
Comme en C, les chaînes de caractères sont délimitées par des caractères « " ».
Les séquences d’échappement suivantes sont acceptées : « \f », « \n », « \r », « \v », «
\\ », « \" », « \' », « \0 ».
3.1.7
Constante caractère
Comme en C, les caractères sont délimités par des caractères « ' ». Les séquences
d’échappement suivantes sont acceptées : « \f », « \n », « \r », « \v », « \\ », « \' », «
\0 ».
3.1. Éléments lexicaux
3.1.8
21
Constante entière
Vous pouvez écrire les constantes entières en décimal ou en hexadécimal.
Une constante entière décimale commence par un chiffre décimal, et est suivie
par zéro, un ou plusieurs chiffres décimaux, ou caractères « _ ».
Un chiffre hexadécimal est soit un chiffre décimal, soit une lettre entre « a » et « f
», écrite indifféremment en minuscule ou en majuscule. Une constante décimale commence par la séquence « 0x », et est suivie par un ou plusieurs chiffres hexadécimal,
ou caractères « _ ».
Dans une constante entière, écrite en décimal ou en hexadécimal, le caractère « _
» peut servir de séparateur ; on peut ainsi écrire indifféremment : 100, 1_00, 1_0_0,
1___00, . . .
Attention : contrairement au C, un nombre qui commence par un zéro est un
nombre écrit en décimal ; contrairement à l’assembleur PIC, le préfixe « 0x » est indispensable pour écrire un nombre en hexadécimal.
3.1.9
Format
Le format est libre, la fin de ligne n’est pas significative (sauf pour les commentaires, qui commencent par un caractère dièse « # », et s’étendent jusqu’à la fin de la
ligne courante). Le compilateur accepte de manière indifférente que les fins de ligne
soient codés par un caractère LF (0x0A), un caractère CR (0x0D), ou par la séquence
CRLF (0x0D, 0x0A).
Chapitre 4
Programmes pour baseline
4.1
Structure d’un programme pour baseline
Un programme Piccolo pour baseline a la structure suivante :
baseline nom "nom_composant" :
liste_de_sections
end
Dans l’en-tête :
– le nom « nom » est le nom du fichier (sans son extension) qui contient ce texte
source ;
– le nom du composant « nom_composant » doit être exactement le nom de l’un des
composants supportés (pour obtenir la liste des baseline pris en charge, utiliser
l’option « --baseline », voir section 2.3.1 page 16).
Le corps du programme est constitué d’une liste non ordonnée de sections. Les
sections disponibles sont listées dans le tableau 4.1.
Type de section
Mot-clé introductif
Référence
Configuration
configuration
chapitre 10 page 111
Définition de variable
ram
chapitre 8 page 101
Définition de constante
const
chapitre 9 page 107
Définition de routine régulière
routine
section 4.2 page 24
Définition de routine sans retour
noreturn
section 4.2 page 24
Inclusion
include
section 4.5 page 32
Tableau 4.1 – Les sections d’un programme pour baseline
24
C HAPITRE 4. Programmes pour baseline
4.2
Routines baseline
Les routines définissent le code exécutable de votre programme. L’une d’entre elles
doit s’appeler main : c’est la routine qui s’exécute au démarrage. Il y a deux types de
routine, les routines régulières et les routines sans retour.
L’ordre des déclarations des routines est quelconque, il est possible d’appeler une
routine qui est déclarée après l’instruction d’appel. Simplement Piccolo engendrera
leur code dans leur ordre d’apparition.
Routine et pages de la mémoire programme. Un problème important en Piccolo
baseline est la gestion des pages de la mémoire programme (voir section 4.6 page 32).
Liste d’instructions d’une routine. Elle est structurée : Piccolo définit des instructions de sélection et de répétition : cela signifie que vous ne pouvez pas déclarer
d’étiquette, ni utiliser de goto pour effectuer des branchements à l’intérieur d’une
routine.
4.2.1
Routine régulière
C’est un sous-programme. Une routine régulière peut ne comporter aucune instruction Piccolo, l’instruction retlw est implicitement ajoutée. Une routine régulière
est déclarée par :
routine maRoutine ... {
...
}
maRoutine est le nom de la routine, celui qui sera nommé dans un instruction
d’appel de routine. Entre les accolades « { » et « } », apparaît la liste des instructions.
Appel d’une routine régulière. Utiliser l’instruction call ou l’instruction jsr (section 4.6 page 32).
Dernière instruction d’une routine régulière. Une particularité du jeu d’instructions des baseline est qu’il ne possède pas d’instruction RETURN, mais une instruction
RETLW qui combine retour de sous-programme et chargement immédiat de W. Piccolo
ne définit pas l’instruction RETLW, mais définit movlw : c’est le compilateur Piccolo
qui repère une instruction movlw comme dernière instruction d’une routine, et qui la
transforme en RETLW.
En conséquence, la liste des instructions d’une routine régulière ne peut pas être
vide. Elle doit comprendre au moins une instruction. D’une manière générale, la dernière instruction d’une routine régulière peut être :
– une instruction movlw ; le compilateur Piccolo la transformera en RETLW ;
– une instruction call ou une instruction jsr vers une routine régulière ; le compilateur Piccolo la transformera en GOTO ;
4.2. Routines baseline
25
– une instruction if structurée dont toutes les branches se terminent soit par une
instruction movlw, soit par une instruction call vers une routine régulière, soit
une autre instruction if structurée.
Voici quelques exemples de routines régulières Piccolo et leur traduction en assembleur. Noter qu’il s’agit du code engendré sans aucune optimisation.
Routine Piccolo
routine maRoutine {
movlw 45
}
Routine Piccolo
routine maRoutine {
call autreRoutineReguliere
}
Routine Piccolo
routine maRoutine {
i f (STATUS.Z)
call autreRoutineReguliere
else
movlw 45
end
}
4.2.2
Traduction assembleur
maRoutine:
RETLW 0x2D
Traduction assembleur
maRoutine:
GOTO autreRoutineReguliere
Traduction assembleur
maRoutine:
BTFSS STATUS, 2
GOTO _label_0
GOTO autreRoutineReguliere
_label_0:
RETLW 0x2D
Routine sans retour
L’exécution ne revient pas jamais à l’appelant. Ce type de routine doit donc se
terminer par des constructions particulières qui assurent le non-retour : une boucle
infinie, un branchement goto ou jump vers une autre routine sans retour.
Une routine sans retour doit être déclarée avec qualificatif noreturn :
noreturn routine maRoutine ... {
...
}
Appel d’une routine sans retour. Utiliser goto ou l’instruction jump (section 4.6
page 32).
Déclaration de la routine main. Dans un programme, il doit exister une et une
seule routine main, qui doit être déclarée comme suit :
noreturn routine main {
liste d’instructions
}
26
C HAPITRE 4. Programmes pour baseline
Le compilateur Piccolo place cette routine au début de la ROM, de façon qu’elle
soit exécutée au démarrage du micro-contrôleur.
Comment terminer une routine sans retour. La dernière instruction de la liste
des instructions d’une routine sans retour doit être :
– une instruction de répétition infinie (section 7.4 page 84) ;
– un appel vers une autre routine sans retour, au moyen d’un goto ou d’un jump ;
– une instruction conditionnelle structurée, dont toutes les branches présentent
comme dernière instruction les instructions d’appel d’un routine sans retour,
un branchement calculé (comme évoqué ci-dessus), ou encore une instruction
conditionnelle structurée, dont toutes les branches, etc.
Exemple simple : la dernière instruction est une boucle infinie :
noreturn routine maRoutine {
...
forever
...
end
}
Exemple simple : la dernière instruction est un branchement vers une routine
sans retour :
noreturn routine maRoutine {
...
goto autreRoutineSansRetour
}
La dernière instruction est un if dont aucune des branches ne se termine (r1 est
une routine sans retour) :
noreturn routine maRoutine {
i f (...)
...
goto r1
else
...
forever
...
end
end
}
La dernière instruction est un if dont la première branche se termine elle même
par un if dont les deux branches se terminent par des branchements vers des routines sans retour :
noreturn routine maRoutine {
i f (...)
...
i f (...)
4.3. Les instructions
27
...
goto r1
else
...
goto r2
end
else
...
goto r3
end
}
4.3
Les instructions
Elles sont de deux types (la distinction est importante pour l’instruction conditionnelle simple) :
– les instructions simples ;
– les instructions composées.
Les instructions simples. Elles correspondent à une partie des instructions machine. Attention, pour certaines, la syntaxe n’est pas exactement la même que celle
de l’instruction assembleur correspondante.
Le tableau 4.2 donnent la liste des instructions machine baseline et les liens vers
les sections précisant leur prise en charge en Piccolo.
Les instructions composées. Piccolo définit les instructions suivantes :
– l’instruction mnop (section 7.1 page 81) ;
– l’instruction conditionnelle simple (section 7.2 page 82) ;
– l’instruction conditionnelle structurée (section 7.3 page 83) ;
– l’instruction répétitive (section 7.5 page 84) ;
– l’instruction de répétition infinie (section 7.4 page 84) ;
– l’instruction d’appel de routine régulière jsr (section 4.6 page 32) ;
– l’instruction d’appel de routine sans retour jump (section 4.6 page 32) ;
4.3.1
Les instructions que vous ne trouverez pas en Piccolo
Elles n’existent pas en Piccolo parce qu’elles sont remplacées par des constructions structurées, ou bien engendrées automatiquement lors de la compilation.
Voici leur liste avec les liens vers les sections appropriées :
– BTFSC, BTFSS, DECFSZ et INCFSZ : ces instructions sont engendrées par l’instruction conditionnelle simple, l’instruction conditionnelle structurée et l’instruction répétitive (section 7.6.1 page 86) ;
– RETLW : utiliser une instruction MOVLW, et c’est le compilateur Piccolo qui la
remplacera par une instruction RETLW k (section 4.2.1 page 24).
28
C HAPITRE 4. Programmes pour baseline
Instruction
Description
Référence en Piccolo
ADDWF f, d
Add W and f
section 4.4.2 page 29
ANDLW k
And Literal with W
section 4.4.4 page 30
ANDWF f, d
And W with f
section 4.4.2 page 29
BCF f, b
Bit Clear f
section 4.4.3 page 29
BSF f, b
Bit Set f
section 4.4.3 page 29
BTFSC f, b
Bit Test f, Skip if Clear
section 4.3.1 page 27
BTFSS f, b
Bit Test f, Skip if Set
section 4.3.1 page 27
CALL k
Call Subroutine
section 4.4.7 page 31
CLRF f
Clear f
section 4.4.1 page 28
CLRW
Clear W
section 4.4.5 page 30
CLRWDT
Clear Watchdog Timer
section 4.4.5 page 30
COMF f, d
Complement f
section 4.4.2 page 29
DECF f, d
Decrement f
section 4.4.2 page 29
DECFSZ f, d
Decrement f, Skip if 0
section 4.3.1 page 27
GOTO k
Go to Address
section 4.4.8 page 32
INCF f, d
Decrement f
section 4.4.2 page 29
INCFSZ f, d
Increment f, Skip if 0
section 4.3.1 page 27
IORLW k
Inclusive OR Literal with W
section 4.4.4 page 30
IORWF f, d
Inclusive OR W with f
section 4.4.2 page 29
MOVF f, d
Move f
section 4.4.2 page 29
MOVLW k
Move Literal to W
section 4.4.4 page 30
MOVWF f
Move W to f
section 4.4.1 page 28
NOP
No Operation
section 4.4.5 page 30
RETLW k
Return with Literal in W
section 4.3.1 page 27
RLF f, d
Rotate Left f through Carry
section 4.4.2 page 29
RRF f, d
Rotate Right f through Carry
section 4.4.2 page 29
SLEEP
Go into Standby Mode
section 4.4.5 page 30
SUBWF f, d
Substract W from f
section 4.4.2 page 29
SWAPF f, d
Swap Nibbles in f
section 4.4.2 page 29
TRIS f
Load TRIS register
section 4.4.6 page 31
XORLW k
Exclusive OR Literal with W
section 4.4.4 page 30
XORWF f, d
Exclusive OR W with f
section 4.4.2 page 29
Tableau 4.2 – Instructions machine des baseline
4.4
4.4.1
Les instructions simples
Instructions nommant un registre
Ce sont les instructions listées dans le tableau 4.3.
4.4. Les instructions simples
Assembleur
29
Description
Écriture en Piccolo
CLRF f
Clear f
clrf f
MOVWF f
Move f
movwf f
Tableau 4.3 – Opérations baseline nommant un registre
4.4.2
Instructions nommant un registre, et optionnellement W
Ces instructions, ainsi que le traduction en Piccolo, sont listées dans le tableau
4.4. En assembleur, elles nomment deux opérandes :
– f : désigne le registre, il en est de même en Piccolo ;
– d : optionnel en Piccolo ; si absent, le registre f est destination de l’opération, si
égal à W, c’est le registre W qui est destinaire.
En Piccolo, destination :
Assembleur
Description
f
W
ADDWF f, d
Add W and f
addwf f
addwf f, W
ANDWF f, d
And W with f
andwf f
andwf f, W
COMF f, d
Complement f
comf f
comf f, W
DECF f, d
Decrement f
decf f
decf f, W
INCF f, d
Increment f
incf f
incf f, W
IORWF f, d
Inclusive OR W with f
iorwf f
iorwf f, W
MOVF f, d
Move f
movf f
movf f, W
RLF f, d
Rotate Left f through Carry
rlf f
rlf f, W
RRF f, d
Rotate Right f through Carry
rrf f
rrf f, W
SUBWF f, d
Substract W from f
subwf f
subwf f, W
SWAPF f, d
Swap Nibbles in f
swapf f
swapf f, W
XORWF f, d
Exclusive OR W with f
xorwf f
xorwf f, W
Tableau 4.4 – Instructions baseline nommant un registre, et optionnellement W
4.4.3
Opérations d’affectation de bit
Ces instructions Piccolo correspondent aux instructions machine BCF et BSF (tableau 4.5).
Assembleur
Description
Écriture en Piccolo
BCF f, b
Bit Clear f
bcf f.b
BSF f, b
Bit Set f
bsf f.b
Tableau 4.5 – Opérations baseline sur un bit d’un registre
En Piccolo, ces instructions ont toujours deux arguments :
30
C HAPITRE 4. Programmes pour baseline
– le premier argument est une référence à un registre (section 8.5 page 104) ;
– le second est le bit concerné, précédé par un point.
Pour désigner le bit concerné, vous pouvez utiliser un nombre compris entre 0 et
7. Par exemple :
bcf maVariable.3
Si le registre a été défini en déclarant des noms de bit :
ram ... {
byte maVariable <a, -, b [3], -, -, ->
}
Vous pouvez utiliser l’un de ces noms comme second argument :
bcf maVariable.a # a designe le bit 7
ou encore
bcf maVariable.b [1] # b[1] designe le bit 4
Vous pouvez de cette façon accéder aux bits des registres spéciaux. Pour connaître
la liste des registres de contrôle, utilisez l’option --registers (ou sa version courte
-R), comme décrite à la section 2.2.3 page 15 ; par exemple : piccolo -R=10F220.
4.4.4
Opérations littérales avec W
Ces opérations sont listées dans le tableau 4.6. L’instruction RETLW k n’existe
pas en Piccolo, l’optimiseur repérera une instruction MOVLW k en fin de routine et la
transformera en RETLW k.
En Piccolo, k est une expression statique. Une expression statique est une expression dont la valeur est calculée à la compilation. Sa forme générale est présentée
à la section 9.2 page 109. Le compilateur effectue tous les calculs d’une expression
statique avec des nombres entiers 32 bits signés.
Pour être valide dans les opérations statiques avec W, le résultat devra être :
– soit un nombre positif inférieur ou égal à 255 ;
– soit un nombre négatif supérieur ou égal à -128.
Par exemple : movlw -14 engendre l’instruction assembleur : MOVLW 0xf2.
Assembleur
Description
Écriture en Piccolo
ANDLW k
And Literal with W
andlw k
IORLW k
Inclusive OR Literal with W
iorlw k
MOVLW k
Move Literal to W
movlw k
XORLW k
Exclusive OR Literal with W
xorlw k
Tableau 4.6 – Opérations litérales avec W pour baseline
4.4.5
Instructions identiques à celles de l’assembleur
Ces instructions sont listées dans le tableau 4.7.
4.4. Les instructions simples
31
Assembleur
Description
Écriture en Piccolo
CLRWDT
Clear Watchdog Timer
clrwdt
CLRW
Clear W
clrw
NOP
No Operation
nop
OPTION
Load OPTION register
option
SLEEP
Go into Standby Mode
sleep
Tableau 4.7 – Instructions baseline identiques en assembleur et en Piccolo
4.4.6
Instruction TRIS
En Piccolo, l’instruction TRIS a un opérande qui peut être : GPIO, PORTA, PORTB,
PORTC, PORTD ou PORTE. Pour un composant donné, uniquement un sous-ensemble
de ces valeurs est acceptable (tableau 4.8). D’une manière générale, pour que l’un
des noms cités puisse être utilisé pour un composant donnée, il faut qu’il apparaisse
parmi ses registres spéciaux (pour connaître la liste des registres de contrôle, utilisez
l’option --registers ou sa version courte -R, comme décrite à la section 2.2.3 page
15 ; par exemple : piccolo -R=10F220).
Composant
Instructions TRIS en Piccolo
10F200, 10F202
tris GPIO
10F204, 10F206
tris GPIO
10F220
tris GPIO
12F508, 12F509
tris GPIO
12F510, 12F519
tris GPIO
16F505, 16F506
tris PORTB, tris PORTC
16F526
tris PORTB, tris PORTC
16F54
tris PORTA, tris PORTB
16F57
tris PORTA, tris PORTB, tris PORTC
16F59
tris PORTA, tris PORTB, tris PORTC, tris PORTD, tris PORTE
Tableau 4.8 – Instructions tris en Piccolo
4.4.7
Appeler une routine régulière
Une routine régulière est une routine déclarée sans le qualificatif noreturn. L’appel s’effectue au moyen de l’instruction CALL.
Syntaxiquement, il faut simplement nommer la routine appelée après le nom de
l’instruction d’appel :
call nom_routine
32
C HAPITRE 4. Programmes pour baseline
4.4.8
Appeler une routine sans retour
Appeler une routine sans retour (c’est-à-dire déclarée avec le qualificatif noreturn)
s’effectue par une instruction goto. L’appel s’écrit :
goto nom_routine
4.5
Section include
Une section include permet d’inclure un fichier contenant lui-même des sections
telles que définies dans le tableau 4.1 page 23. Son format est le suivant :
include "chemin"
chemin est le chemin vers le fichier inclus, et est :
– soit un chemin absolu (il commence par « / ») ;
– soit un chemin relatif par rapport au fichier source.
4.6
Gestion des pages de la mémoire programme
La mémoire programme d’un baseline est constituée d’une ou plusieurs pages de
512 instructions chacune. Le tableau 4.9 cite quelques micro-contrôleurs baseline et
le nombre de pages de leur mémoire programme.
Composant
Mémoire programme
Nombre de pages
10F200, 10F204
256 instructions
1
10F202, 10F206
512 instructions
1
10F220
256 instructions
1
12F508
512 instructions
1
12F509
1024 instructions
2
12F510, 12F519
1024 instructions
2
16F505, 16F506
1024 instructions
2
16F526
1024 instructions
2
16F54
512 instructions
1
16F57, 16F59
2048 instructions
4
Tableau 4.9 – Mémoire programme de quelques micro-contrôleurs baseline
Les deux instructions de branchement que le jeu d’instructions des baseline définit
sont CALL et GOTO (tableau 4.10).
En résumé, ces instructions ne permettent pas d’effectuer par elles mêmes un
branchement à tout position de la mémoire programme :
– un CALL utilisé seul exige que la routine appelée soit dans les 256 premiers
octets de la page courante ;
4.6. Gestion des pages de la mémoire programme
Instruction
Description
33
Code binaire
11
10
9
8
7
6
5
4
3
2
1
0
CALL k
Call subroutine
1
0
0
1
k
k
k
k
k
k
k
k
GOTO k
Go to Address
1
0
1
k
k
k
k
k
k
k
k
k
Tableau 4.10 – Instructions CALL et GOTO des baseline
– un GOTO utilisé seul permet d’effectuer un branchement à toute position de la
page courante.
Pour changer de page, il faut modifier les bits PA du registre STATUS avant d’appeler l’instruction CALL ou GOTO.
Voici comment ces contraintes sont prises en compte en Piccolo :
– une routine doit complètement résider à l’intérieur d’une page ;
– lors de la déclaration d’une routine, la page dans laquelle elle réside est précisée
(section 4.6.3 page 33) ;
– les instructions Piccolo call et goto effectuent des branchements à l’intérieur
de la page courante ;
– pour appeler des routines placées dans une autre page, utiliser les instructions
Piccolo jsr (section 4.6.2 page 33) et jump (section 4.6.1 page 33).
4.6.1
Instruction jump
L’instruction Piccolo jump engendre des instructions machine BCF ou BSF pour
modifier les bits PA du registre STATUS, puis l’instruction machine GOTO.
Elle occupe donc n + 1 mots de la mémoire programme, n étant le nombre de bits
PA du registre STATUS à changer.
4.6.2
Instruction jsr
L’instruction Piccolo jsr engendre des instructions machine BCF ou BSF pour modifier les bits PA du registre STATUS, puis l’instruction machine CALL, puis de nouveau
des instructions machine BCF ou BSF pour rétablir les bits PA du registre STATUS.
Elle occupe donc 2n + 1 mots de la mémoire programme, n étant le nombre de bits
PA du registre STATUS à changer pour effectuer l’appel.
4.6.3
Déclaration de routine et page
Pour spécifier dans quelle page de la mémoire programme une routine régulière
doit résider, il suffit de la déclarer comme suit :
routine nom page numero_page {
...
}
Où numero_page est un entier positif ou nul strictement inférieur au nombre de
pages.
34
C HAPITRE 4. Programmes pour baseline
Pour une routine régulière sans retour :
noreturn routine nom page numero_page {
...
}
4.6.4
Exemple
Différents exemples sont donnés sur la page http://piccolo.rts-software.
org/examples/index.php.
Le 12F510 a une mémoire programme de deux pages.
baseline pages_12F510 "12F510" :
noreturn routine main {
mnop 249
call regularRoutineInPage0
jsr regularRoutineInPage1
jump routineInPage1
}
routine regularRoutineInPage0 {
mnop 256
movlw 0
}
noreturn routine routineInPage1 page 1 {
forever
end
}
routine regularRoutineInPage1 page 1 {
movlw 0
}
end
Sans aucune optimisation, le code assembleur engendré est :
Address
0000
0000
(248
01F2
01F4
01F6
01F8
01FA
01FC
01FE
01FE
Code Mnemonic
ORG 0x0
0000
NOP
lignes semblables)
09FF
CALL regularRoutineInPage0
05A3
BSF STATUS, 5
0901
CALL regularRoutineInPage1
04A3
BCF STATUS, 5
05A3
BSF STATUS, 5
0A00
GOTO routineInPage1
; END OF ROUTINE main IN PAGE 0
; BEGIN OF ROUTINE regularRoutineInPage0
4.7. Optimisation
01FE
01FE
(255
03FE
0400
0400
0400
0400
0400
0400
0402
0402
0402
0402
0404
4.7
35
regularRoutineInPage0:
0000
NOP
lignes semblables)
0800
RETLW 0x0
; END OF ROUTINE regularRoutineInPage0 IN PAGE 0
ORG 0x200
; BEGIN OF ROUTINE routineInPage1
routineInPage1:
_label_0:
0A00
GOTO _label_0
; END OF ROUTINE routineInPage1 IN PAGE 1
; BEGIN OF ROUTINE regularRoutineInPage1
regularRoutineInPage1:
0800
RETLW 0x0
; END OF ROUTINE regularRoutineInPage1 IN PAGE 1
Optimisation
Cette section indique les différentes optimisations d’un code baseline. L’optimisation est activée par l’option « -o ».
Pour optimiser, Piccolo applique les remplacements indiqués par le tableau 4.11,
et élimine le code mort. Le code est balayé de manière répétitive, tant que des optimisations sont effectuées.
Situation
Optimisation
CALL vers RETLW k
Remplacement par MOVLW k
GOTO vers RETLW k
Remplacement par RETLW k
GOTO a vers GOTO b
Remplacement par GOTO b
GOTO vers l’instruction qui suit
Suppression
JSR vers RETLW k
Remplacement par MOVLW k
JUMP vers RETLW k
Remplacement par RETLW k
JUMP a vers JUMP b
Remplacement par JUMP b
JUMP a vers GOTO b
Remplacement par JUMP b
Tableau 4.11 – Optimisation du code baseline
Chapitre 5
Programmes pour mid-range
5.1
Structure d’un programme pour mid-range
Un programme Piccolo pour mid-range a la structure suivante :
midrange nom "nom_composant" :
liste_de_sections
end
Dans l’en-tête :
– le nom « nom » est le nom du fichier (sans son extension) qui contient ce texte
source ;
– le nom du composant « nom_composant » doit être exactement le nom de l’un
des composants supportés (pour obtenir la liste des mid-range pris en charge,
utiliser l’option « --midrange », voir section 2.3.2 page 17).
Le corps du programme est constitué d’une liste non ordonnée de sections. Les
sections disponibles sont listées dans le tableau 5.1.
Type de section
Mot-clé introductif
Référence
Configuration
configuration
chapitre 10 page 111
Définition de variable
ram
chapitre 8 page 101
Définition de constante
const
chapitre 9 page 107
Définition de routine midrange
routine
section 5.2 page 38
Définition de routine d’interruption mid-range
interrupt
section 5.7 page 48
Tableau 5.1 – Les sections d’un programme pour mid-range
38
C HAPITRE 5. Programmes pour mid-range
5.2
Routines mid-range
Les routines définissent le code exécutable de votre programme. L’une d’entre elles
doit s’appeler main : c’est la routine qui s’exécute au démarrage. Il y a deux types de
routine, les routines régulières et les routines sans retour.
L’ordre des déclarations des routines est quelconque, il est possible d’appeler une
routine qui est déclarée après l’instruction d’appel. Simplement Piccolo engendrera
leur code dans leur ordre d’apparition.
Routine et sélection de banc. Un problème important en assembleur est la gestion
de la sélection de banc par l’intermédiaire des bits RP du registre STATUS : son utilisation correcte en assembleur est complètement à la charge du programmeur. Piccolo
propose des instructions pour sécuriser son emploi : voir la section 7.8 page 92.
Liste d’instructions d’une routine. Elle est structurée : Piccolo définit des instructions de sélection et de répétition : cela signifie que vous ne pouvez pas déclarer
d’étiquette, ni utiliser des goto pour effectuer des branchements à l’intérieur d’une
routine.
Routine et pages de la mémoire programme. La mémoire programme d’un midrange est divisé en blocs de 2048 instructions, et franchir ces frontières posent des
problèmes particuliers. Piccolo impose les contraintes suivantes :
– une routine doit être complètement contenue dans une page (son code ne peut
franchir une frontière) ;
– les sauts inter-pages ne peuvent être effectués qu’avec les instructions jump ou
jsr (section 5.6 page 46).
5.2.1
Routine régulière
C’est un sous-programme. Une routine régulière peut ne comporter aucune instruction Piccolo, l’instruction return est implicitement ajoutée. Une routine régulière
est déclarée par :
routine maRoutine ... {
...
}
maRoutine est le nom de la routine, celui qui sera nommé dans un instruction
d’appel de routine. Entre les accolades « { » et « } », apparaît la liste des instructions.
Appel d’une routine régulière. Utiliser CALL ou JSR.
5.2.2
Routine sans retour
L’exécution ne revient pas jamais à l’appelant. Ce type de routine doit donc se
terminer par des constructions particulières qui assurent le non-retour : une boucle
5.2. Routines mid-range
39
infinie, un branchement (goto ou jump) vers une autre routine sans retour, un goto
calculé vers d’autres routines sans retour.
Une routine sans retour doit être déclarée avec qualificatif noreturn :
noreturn routine maRoutine ... {
...
}
Appel d’une routine sans retour. Utiliser goto ou jump.
Déclaration de la routine main. Dans un programme, il doit exister une et une
seule routine main, qui doit être déclarée comme suit :
noreturn routine main bank:requires 0 {
liste d’instructions
}
Le compilateur Piccolo insère à l’adresse zéro une instruction goto vers cette routine, de façon que la routine main soit exécutée au démarrage du micro-contrôleur. Le
qualificatif bank:requires 0 est exigé par le compilateur Piccolo car les bits RP du
registre STATUS sont initialisés à zéro au démarrage du micro-contrôleur.
Comment terminer une routine sans retour. La dernière instruction de la liste
des instructions d’une routine sans retour doit être :
– un appel vers une autre routine sans retour, au moyen d’un goto ou d’un jump ;
– un branchement calculé vers une routine parmi plusieurs, au moyen d’un computed
goto ;
– une instruction conditionnelle structurée, dont toutes les branches présentent
comme dernière instruction les instructions d’appel d’un routine sans retour,
un branchement calculé (comme évoqué ci-dessus), ou encore une instruction
conditionnelle structurée, dont toutes les branches, etc.
Exemple simple : la dernière instruction est une boucle infinie :
noreturn routine maRoutine {
...
forever
...
end
}
Exemple simple : la dernière instruction est un branchement vers une routine
sans retour :
noreturn routine maRoutine {
...
goto autreRoutineSansRetour
}
La dernière instruction est un computed goto nommant les routines r1, r2, r3
qui doivent être toutes les trois des routines sans retour :
40
C HAPITRE 5. Programmes pour mid-range
noreturn routine maRoutine {
...
computed [3] goto r1, r2, r3
}
La dernière instruction est un if dont aucune des branches ne se termine (r1 est
une routine sans retour) :
noreturn routine maRoutine {
i f (...)
...
goto r1
else
...
forever
...
end
end
}
La dernière instruction est un if dont la première branche se termine elle même
par un if dont les deux branches se terminent par des branchements vers des routines sans retour :
noreturn routine maRoutine {
i f (...)
...
i f (...)
...
goto r1
else
...
goto r2
end
else
...
goto r3
end
}
5.3
Les instructions
Elles sont de deux types (la distinction est importante pour l’instruction conditionnelle simple) :
– les instructions simples ;
– les instructions composées.
Les instructions simples. Elles correspondent à une partie des instructions ma-
5.4. Les instructions simples
41
chine. Attention, pour certaines, la syntaxe n’est pas exactement la même que celle
de l’instruction assembleur correspondante.
Le tableau 5.2 donnent la liste des instructions machine mid-range et les liens
vers les sections précisant leur prise en charge en Piccolo.
Les instructions composées. Piccolo définit les instructions suivantes :
– l’instruction mnop (section 7.1 page 81) ;
– l’instruction conditionnelle simple (section 7.2 page 82) ;
– l’instruction conditionnelle structurée (section 7.3 page 83) ;
– l’instruction répétitive (section 7.5 page 84) ;
– l’instruction de répétition infinie (section 7.4 page 84) ;
– l’instruction d’appel de routine régulière jsr (section 5.6 page 46) ;
– l’instruction d’appel de routine sans retour jump (section 5.6 page 46) ;
5.3.1
Les instructions que vous ne trouverez pas en Piccolo
Elles n’existent pas en Piccolo parce qu’elles sont remplacées par des constructions structurées, ou bien engendrées automatiquement lors de la compilation.
Voici leur liste avec les liens vers les sections appropriées :
– BTFSC, BTFSS, DECFSZ et INCFSZ : ces instructions sont engendrées par l’instruction conditionnelle simple, l’instruction conditionnelle structurée et l’instruction répétitive (section 7.6.1 page 86) ;
– RETFIE : ajoutée automatiquement par la compilateur Piccolo à la fin de la
routine d’interruption (section 5.7 page 48).
– RETURN : ajoutée automatiquement par la compilateur Piccolo à la fin d’une
routine régulière (section 5.2.1 page 38).
– RETLW : utiliser une instruction MOVLW, et c’est le compilateur Piccolo qui remplacera la séquence MOVLW k RETURN par une instruction RETLW k (section
5.2.1 page 38).
5.4
5.4.1
Les instructions simples
Instructions nommant un registre
Ce sont les instructions listées dans le tableau 5.3.
5.4.2
Instructions nommant un registre, et optionnellement W
Ces instructions, ainsi que le traduction en Piccolo, sont listées dans le tableau
5.4. En assembleur, elles nomment deux opérandes :
– f : désigne le registre, il en est de même en Piccolo ;
– d : optionnel en Piccolo ; si absent, le registre f est destination de l’opération, si
égal à W, c’est le registre W qui est destinaire.
42
C HAPITRE 5. Programmes pour mid-range
Instruction
Description
Référence en Piccolo
ADDLW k
Add Literal and W
section 5.4.4 page 44
ADDWF f, d
Add W and f
section 5.4.2 page 41
ANDLW k
And Literal with W
section 5.4.4 page 44
ANDWF f, d
And W with f
section 5.4.2 page 41
BCF f, b
Bit Clear f
section 5.4.3 page 43
BSF f, b
Bit Set f
section 5.4.3 page 43
BTFSC f, b
Bit Test f, Skip if Clear
section 5.3.1 page 41
BTFSS f, b
Bit Test f, Skip if Set
section 5.3.1 page 41
CALL k
Call Subroutine
section 5.4.6 page 44
CLRF f
Clear f
section 5.4.1 page 41
CLRW
Clear W
section 5.4.5 page 44
CLRWDT
Clear Watchdog Timer
section 5.4.5 page 44
COMF f, d
Complement f
section 5.4.2 page 41
DECF f, d
Decrement f
section 5.4.2 page 41
DECFSZ f, d
Decrement f, Skip if 0
section 5.3.1 page 41
GOTO n
Go to Address
section 5.4.7 page 45
INCF f, d
Decrement f
section 5.4.2 page 41
INCFSZ f, d
Increment f, Skip if 0
section 5.3.1 page 41
IORLW k
Inclusive OR Literal with W
section 5.4.4 page 44
IORWF f, d
Inclusive OR W with f
section 5.4.2 page 41
MOVF f, d
Move f
section 5.4.2 page 41
MOVLW k
Move Literal to W
section 5.4.4 page 44
MOVWF f
Move W to f
section 5.4.1 page 41
NOP
No Operation
section 5.4.5 page 44
RETFIE
Return from interrupt
section 5.3.1 page 41
RETLW k
Return with Literal in W
section 5.3.1 page 41
RETURN
Return from Subroutine
section 5.3.1 page 41
RLF f, d
Rotate Left f through Carry
section 5.4.2 page 41
RRF f, d
Rotate Right f through Carry
section 5.4.2 page 41
SLEEP
Go into Standby Mode
section 5.4.5 page 44
SUBLW k
Substract W from literal
section 5.4.4 page 44
SUBWF f, d
Substract W from f
section 5.4.2 page 41
SWAPF f, d
Swap Nibbles in f
section 5.4.2 page 41
XORLW k
Exclusive OR Literal with W
section 5.4.4 page 44
XORWF f, d
Exclusive OR W with f
section 5.4.2 page 41
Tableau 5.2 – Instructions machine des mid-range
5.4. Les instructions simples
Assembleur
43
Description
Écriture en Piccolo
CLRF f
Clear f
clrf f
MOVWF f
Move f
movwf f
Tableau 5.3 – Opérations mid-range nommant un registre
En Piccolo, destination :
Assembleur
Description
f
W
ADDWF f, d
Add W and f
addwf f
addwf f, W
ANDWF f, d
And W with f
andwf f
andwf f, W
COMF f, d
Complement f
comf f
comf f, W
DECF f, d
Decrement f
decf f
decf f, W
INCF f, d
Increment f
incf f
incf f, W
IORWF f, d
Inclusive OR W with f
iorwf f
iorwf f, W
MOVF f, d
Move f
movf f
movf f, W
RLF f, d
Rotate Left f through Carry
rlf f
rlf f, W
RRF f, d
Rotate Right f through Carry
rrf f
rrf f, W
SUBWF f, d
Substract W from f
subwf f
subwf f, W
SWAPF f, d
Swap Nibbles in f
swapf f
swapf f, W
XORWF f, d
Exclusive OR W with f
xorwf f
xorwf f, W
Tableau 5.4 – Instructions mid-range nommant un registre, et optionnellement W
5.4.3
Opérations d’affectation de bit
Ces instructions Piccolo correspondent aux instructions machine BCF et BSF (tableau 5.5).
Assembleur
Description
Écriture en Piccolo
BCF f, b
Bit Clear f
bcf f.b
BSF f, b
Bit Set f
bsf f.b
Tableau 5.5 – Opérations mid-range sur un bit d’un registre
En Piccolo, ces instructions ont toujours deux arguments :
– le premier argument est une référence à un registre (section 8.5 page 104) ;
– le second est le bit concerné, précédé par un point.
Pour désigner le bit concerné, vous pouvez utiliser un nombre compris entre 0 et
7. Par exemple :
bcf maVariable.3
Si le registre a été défini en déclarant des noms de bit :
ram ... {
byte maVariable <a, -, b [3], -, -, ->
44
C HAPITRE 5. Programmes pour mid-range
}
Vous pouvez utiliser l’un de ces noms comme second argument :
bcf maVariable.a # a designe le bit 7
ou encore
bcf maVariable.b [1] # b[1] designe le bit 4
Vous pouvez de cette façon accéder aux bits des registres spéciaux. Pour connaître
la liste des registres de contrôle, utilisez l’option --registers (ou sa version courte
-R), comme décrite à la section 2.2.3 page 15 ; par exemple : piccolo -R=16F690.
5.4.4
Opérations littérales avec W
Ces opérations sont listées dans le tableau 5.6. L’instruction RETLW k n’existe pas
en Piccolo, l’optimiseur repérera une instruction MOVLW k en fin de routine et la transformera en RETLW k.
En Piccolo, k est une expression statique. Une expression statique est une expression dont la valeur est calculée à la compilation. Sa forme générale est présentée
à la section 9.2 page 109. Le compilateur effectue tous les calculs d’une expression
statique avec des nombres entiers 32 bits signés.
Pour être valide dans les opérations statiques avec W, le résultat devra être :
– soit un nombre positif inférieur ou égal à 255 ;
– soit un nombre négatif supérieur ou égal à -128.
Par exemple : movlw -14 engendre l’instruction assembleur : MOVLW 0xf2.
Assembleur
Description
Écriture en Piccolo
ADDLW k
Add Literal and W
addlw k
ANDLW k
And Literal with W
andlw k
IORLW k
Inclusive OR Literal with W
iorlw k
MOVLW k
Move Literal to W
movlw k
SUBLW k
Substract W frol literal
sublw k
XORLW k
Exclusive OR Literal with W
xorlw k
Tableau 5.6 – Opérations litérales avec W pour mid-range
5.4.5
Instructions identiques à celles de l’assembleur
Ces instructions sont listées dans le tableau 5.7.
5.4.6
Appeler une routine régulière
Une routine régulière est une routine déclarée sans le qualificatif noreturn. L’appel s’effectue avec l’instruction call, si la routine se trouve dans la même page de la
5.5. Section include
45
Assembleur
Description
Écriture en Piccolo
CLRWDT
Clear Watchdog Timer
clrwdt
CLRW
Clear W
clrw
NOP
No Operation
nop
OPTION
Load OPTION register
option
SLEEP
Go into Standby Mode
sleep
Tableau 5.7 – Instructions mid-range identiques en assembleur et en Piccolo
mémoire programme que l’instruction d’appel, ou avec l’instruction jsr (section 5.6.2
page 47).
Syntaxiquement, il faut simplement nommer la routine appelée après l’instruction d’appel :
call nom_routine
Ou :
jsr nom_routine
5.4.7
Appeler une routine sans retour
Appeler une routine sans retour (c’est-à-dire déclarée avec le qualificatif noreturn)
s’effectue par une instruction goto, si la routine se trouve dans la même page de
la mémoire programme que l’instruction d’appel, ou avec l’instruction jump (section
5.6.1 page 46). L’appel s’écrit :
goto nom_routine
Ou :
jump nom_routine
5.5
Section include
Une section include permet d’inclure un fichier contenant lui-même des sections
telles que définies dans le tableau 5.1 page 37. Son format est le suivant :
include "chemin"
chemin est le chemin vers le fichier inclus, et est :
– soit un chemin absolu (il commence par « / ») ;
– soit un chemin relatif par rapport au fichier source.
46
C HAPITRE 5. Programmes pour mid-range
5.6
Gestion des pages de la mémoire programme
La mémoire programme d’un mid-range est constituée d’une ou plusieurs pages de
2048 instructions chacune. Le tableau 5.8 cite quelques micro-contrôleurs mid-range
et le nombre de pages de leur mémoire programme.
Composant
Mémoire programme
Nombre de pages
16F631
1024 instructions
1
16F677
2048 instructions
1
16F690
4096 instructions
2
16F722
2048 instructions
1
16F723, 16F724
4096 instructions
2
16F727
8192 instructions
4
Tableau 5.8 – Mémoire programme de quelques micro-contrôleurs mid-range
Les deux instructions machine de branchement que le jeu d’instructions des midrange définit sont CALL et GOTO (tableau 5.9). En résumé, ces instructions ne permettent pas d’effectuer par elles mêmes un branchement à tout position de la mémoire programme, mais uniquement un branchement à toute position de la page courante.
Instruction
Description
Code binaire
13
12
11
10 à 0
call k
Call subroutine
1
0
0
k
goto k
Go to Address
1
0
1
k
Tableau 5.9 – Instructions call et goto des mid-range
Pour changer de page en assembleur, il faut modifier les bits 3 et 4 du registre
PCLATH avant d’appeler l’instruction CALL ou GOTO.
En Piccolo, il est déconseillé d’accéder directement à PCLATH. Les instructions jsr
et jump prennent en charge les branchements vers une autre page. En résumé :
– une routine doit complètement résider à l’intérieur d’une page ;
– lors de la déclaration d’une routine, la page dans laquelle elle réside est précisée
(section 5.6.3 page 47) ;
– les instructions Piccolo call et goto effectuent des branchements à l’intérieur
de la page courante ;
– pour appeler des routines placées dans une autre page, utiliser les instructions
Piccolo jsr (section 5.6.2 page 47) et jump (section 5.6.1 page 46).
5.6.1
Instruction jump
L’instruction Piccolo jump engendre des instructions machine BCF ou BSF pour
modifier les bits 3 et 4 du registre PCLATH, puis l’instruction machine GOTO.
5.6. Gestion des pages de la mémoire programme
47
Elle occupe donc n + 1 mots de la mémoire programme, n étant le nombre de bits
du registre PCLATH à changer.
5.6.2
Instruction jsr
L’instruction Piccolo jsr engendre des instructions machine BCF ou BSF pour modifier les bits 3 et 4 du registre PCLATH, puis l’instruction machine CALL, puis de nouveau
des instructions machine BCF ou BSF pour rétablir les bits 3 et 4 du registre PCLATH.
Elle occupe donc 2n + 1 mots de la mémoire programme, n étant le nombre de bits
du registre PCLATH à changer pour effectuer l’appel.
5.6.3
Déclaration de routine et page
Pour spécifier dans quelle page de la mémoire programme une routine régulière
doit résider, il suffit de la déclarer comme suit :
routine nom page numero_page bank:... {
...
}
Où numero_page est un entier positif ou nul strictement inférieur au nombre de
pages. Par défaut, si la page n’est pas déclarée, la routine est placée dans la page zéro.
La déclaration de la page doit précéder la déclaration de banc bank.
Pour une routine régulière sans retour :
noreturn routine nom page numero_page bank:... {
...
}
5.6.4
Exemple
Différents exemples sont donnés sur la page http://piccolo.rts-software.
org/examples/index.php.
Le 16F690 a une mémoire programme de deux pages.
midrange multiple_pages "16F690" :
noreturn routine main page 1 bank:requires 0 {
jsr routinePage0
jump routineNoReturnPage0
}
noreturn routine routineNoReturnPage0 page 0 {
forever
end
}
routine routinePage0 page 0 {
jsr routinePage1
48
C HAPITRE 5. Programmes pour mid-range
}
routine routinePage1 page 1 {
}
end
5.7
Routine d’interruption mid-range
Les micro-contrôleurs mid-range acceptent qu’une seule routine d’interruption (à
l’adresse 0x04). Lors de la réponse à une interruption, seul le compteur programme
PC est empilé dans la pile matérielle. Or, il peut être nécessaire de sauver :
– le registre W ;
– le registre STATUS ;
– le registre PCLATH.
Nous allons d’abord voir comment cette sauvegarde doit être réalisée en assembleur, et ensuite nous exposerons comment elle est faite en Piccolo. Si vous voulez
uniquement savoir comment écrire une routine d’interruption en Piccolo, allez directement à la section 5.7.2 page 50.
5.7.1
Allure générale de la routine d’interruption en assembleur
La routine d’interruption a l’allure suivante :
1. sauvegarde de W ;
2. sauvegarde de STATUS ;
3. sauvegarde de PCLATH ;
4. instructions utilisateur ;
5. restitution de PCLATH ;
6. restitution de STATUS ;
7. restitution de W ;
8. instruction de retour d’interruption.
Le but de Piccolo est de simplifier au maximum l’expression des opérations de
sauvegarde et de restitution en proposant des constructions appropriées.
Sauvegarde de W
Il est nécessaire de sauver W si des instructions de la routine d’interruption le modifie. Pour effectuer cette sauvegarde, on exécute simplement l’instruction suivante,
qui copie le contenu de W dans la mémoire W_TEMP :
MOVWF W_TEMP
5.7. Routine d’interruption mid-range
49
Où déclarer W_TEMP ? C’est là où cela se complique. En effet, dans le cas général,
le micro-contrôleur présente plusieurs bancs mémoire, et il est impossible de savoir
le banc qui est sélectionné au moment où l’interruption survient.
Si le micro-contrôleur présente une zone partagée qui apparaît en miroir sur tous
les bancs, alors W_TEMP doit être déclaré dans cette zone. Souvent cette zone partagée
apparaît aux adresses 0x70 à 0x7F dans le banc 0, 0xF0 à 0xFF dans le banc 1, . . .
Malheureusement, certains micro-contrôleurs ne possèdent pas de zone partagée
entre tous les bancs : c’est par exemple le cas des 16F873 et 16F874. En fait, pour ces
deux micro-contrôleurs, les emplacements mémoire (les « General Purpose Registers
») du banc 2 sont le miroir du banc 0, et ceux du banc 3 le miroir du banc 1. La seule
possibilité en assembleur est donc de déclarer manuellement une variable dans les
deux premiers bancs de façon qu’elles apparaissent en miroir ; par exemple W_TEMP0
dans le banc 0 à l’adresse 0x20 et W_TEMP1 dans le banc 1 à l’adresse 0xA0.
Sauvegarde de STATUS
Il est à noter que l’instruction précédente MOVWF W_TEMP ne modifie pas les indicateurs du registre d’état STATUS. Pour sauver ce registre dans une mémoire STATUS_TEMP,
il faut passer par l’intermédiaire de W. Pour transférer le contenu de STATUS dans W, on
ne peut pas utiliser l’instruction MOVF car elle modififie l’indicateur Z. On utilise donc
SWAPF, et, c’est le contenu swappé du registre STATUS qui est transféré dans W. Avant
d’écrire W dans STATUS_TEMP, on exécute CLRF STATUS, ce qui a pour effet d’effacer les
bits RP : ainsi STATUS_TEMP doit être déclaré dans le banc 0. La séquence complète de
sauvegarde du registre STATUS est donc :
SWAPF STATUS, W
CLRF STATUS
MOVWF STATUS_TEMP
Sauvegarde de PCLATH
La réponse à l’interruption laisse le registre PCLATH inchangé. Si la routine d’interruption n’invoque ni l’instruction CALL ni l’instruction GOTO, PCLATH n’est pas utilisé et
il est inutile de le sauvegarder. Dans le cas contraire, et si le programme s’étend sur
plusieurs pages de la mémoire programme, le contenu des bits 3 et 4 de PCLATH – les
bits utilisés par les instructions CALL et GOTO – est inconnu. Il faut donc sauvegarder
ce registre, puis l’effacer (PCLATH_TEMP est déclaré dans le banc 0) :
MOVF PCLATH, W
MOVWF PCLATH_TEMP
CLRF PCLATH
Restitution de STATUS
La restitution de PCLATH est simple, il suffit de recopier PCLATH_TEMP dans PCLATH,
en se souvenant que PCLATH_TEMP contient la valeur swappée :
50
C HAPITRE 5. Programmes pour mid-range
SWAPF STATUS_TEMP, W
MOVWF STATUS
Restitution de W
Il y a une petite subtilité, car on ne peut pas utiliser MOVF W_TEMP, W. En effet,
cette instruction modifie l’indicateur Z. On utilise donc l’instruction SWAPF qui a la
particularité de ne modifier aucun indicateur. Pour restituer la valeur non swappée,
on effectue d’abord un swap en mémoire :
SWAPF W_TEMP, F
SWAPF W_TEMP, W
Récapitulatif
La routine d’interruption d’un mid-range a l’allure suivante en assembleur :
; 1- Sauvegarde de W
MOVWF W_TEMP
; 2- Sauvegarde de STATUS
SWAPF STATUS, W
CLRF STATUS
MOVWF STATUS_TEMP
; 3- Sauvegarde de PCLATH
MOVF PCLATH, W
MOVWF PCLATH_TEMP
CLRF PCLATH
; 4- instructions utilisateur
; 5- Restitution de PCLATH
MOVF PCLATH_TEMP, W
MOVWF PCLATH
; 6- Restitution de STATUS
SWAPF STATUS_TEMP, W
MOVWF STATUS
; 7- Restitution de W
SWAPF W_TEMP, F
SWAPF W_TEMP, W
; 8- Retour d’interruption
RETFIE
5.7.2
Routine d’interruption en Piccolo
En Piccolo, la routine d’interruption midrange est décrite dans une section interrupt :
interrupt contextsave ... {
instructions
}
5.7. Routine d’interruption mid-range
51
La section interrupt peut apparaître n’importe où dans le programme, Piccolo
rangera toujours le code engendré à l’adresse 0x04. Le qualificatif page d’une routine
ne peut pas être utilisée pour une routine d’interruption. Le qualificatif contextsave
permet de préciser les zones mémoire utilisées pour la sauvegarde du contexte (voir
à partir de la page suivante).
Dans un programme, au plus une routine d’interruption peut être définie : une
erreur est signalée si plusieurs le sont.
Toutes les instructions décrites sont autorisées, sauf les instructions qui ne terminent jamais telles que la boucle infinie et les instructions de branchement vers une
routine sans retour goto et jump.
L’instruction assembleur RETFIE n’existe pas en Piccolo : le compilateur l’insère
automatiquement à la fin de la routine d’interruption.
5.7.3
Interruption et sauvegarde du contexte en Piccolo
En conséquence de l’étude qui précède, il y a donc quatre cas à considérer, en
fonction des deux conditions suivantes :
– le micro-contrôleur présente-t-il une zone mémoire commune à tous les bancs
on non ?
– le programme s’étend-il sur plusieurs pages et le sous-programme d’interruption contient-il des branchements ou des appels de sous-programmes ? Si oui,
PCLATH devra aussi être sauvé.
Piccolo détecte automatiquement les différents cas, et propose une écriture unique :
interrupt contextsave sauveA, sauveB {
instructions
}
L’instruction assembleur RETFIE n’existe pas en Piccolo, le compilateur l’ajoute
automatiquement. Le sauvegarde du contexte engendré par Piccolo assure que le banc
0 est sélectionné pour l’exécution de instructions.
Situation (1) : le micro-contrôleur possède une zone mémoire commune à
tous les bancs, le code n’utilise que la page 0 de la mémoire programme ou
le sous-programme d’interruption n’invoque ni CALL ni GOTO. Il faut dans ce cas
sauver W et STATUS. Les deux variables sont donc déclarées de dimension 1 :
– W_TEMP pour sauver W ; cette variable doit être déclarée dans la zone commune
à tous les bancs ; celle-ci s’appelle générallement gprnobnk ;
– STATUS_TEMP pour sauver STATUS ; cette variable doit être déclarée dans le
banc 0, qui s’appelle générallement gpr0.
Le code Piccolo est le suivant :
midrange interrupt1 "16F690" :
ram gprnobnk {
byte W_TEMP
}
ram gpr0 {
52
C HAPITRE 5. Programmes pour mid-range
byte STATUS_TEMP
}
interrupt contextsave W_TEMP, STATUS_TEMP {
instructions
}
...
end
Et le code engendré pour la routine d’interruption est :
ORG 0x4
MOVWF W_TEMP
SWAPF STATUS, W
CLRF STATUS
MOVWF STATUS_TEMP
instructions
SWAPF STATUS_TEMP, W
MOVWF STATUS
SWAPF W_TEMP, F
SWAPF W_TEMP, W
RETFIE
Situation (2) : le micro-contrôleur possède une zone mémoire commune à
tous les bancs, le code occupe plusieurs pages de la mémoire programme,
et le sous-programme d’interruption invoque CALL ou GOTO. Il faut dans ce cas
donc sauver W, STATUS et PCLATH. Les deux variables sont donc déclarées :
– W_TEMP, de dimension 1, pour sauver W ; cette variable doit être déclarée dans
la zone commune à tous les bancs ; celle-ci s’appelle générallement gprnobnk ;
– STATUS_PCLATH_TEMP, de dimension 2, pour sauver STATUS et PCLATH ; cette
variable doit être déclarée dans le banc 0, qui s’appelle générallement gpr0.
Le code Piccolo est le suivant :
midrange interrupt2 "16F690" :
ram gprnobnk {
byte W_TEMP
}
ram gpr0 {
byte STATUS_PCLATH_TEMP [2]
}
interrupt contextsave W_TEMP, STATUS_PCLATH_TEMP {
instructions
}
...
end
Et le code engendré pour la routine d’interruption est :
ORG 0x4
MOVWF W_TEMP
SWAPF STATUS, W
5.7. Routine d’interruption mid-range
53
CLRF STATUS
MOVWF STATUS_PCLATH_TEMP
MOVF PCLATH, W
MOVWF STATUS_PCLATH_TEMP + 0x1
CLRF PCLATH
instructions
MOVF STATUS_PCLATH_TEMP + 0x1, W
MOVWF PCLATH
SWAPF STATUS_PCLATH_TEMP, W
MOVWF STATUS
SWAPF W_TEMP, F
SWAPF W_TEMP, W
RETFIE
Situation (3) : le micro-contrôleur ne possède pas de zone mémoire commune
à tous les bancs, le code n’utilise que la page 0 de la mémoire programme ou
le sous-programme d’interruption n’invoque ni CALL ni GOTO. Il faut dans ce cas
sauver W et STATUS.
Cette situation est celle du 16F873. Ce micro-contrôleur possède 4 bancs mémoire,
la zone RAM du banc 2 étant un miroir de la zone RAM du banc 0, et la zone RAM
du banc 3 étant un miroir de la zone RAM du banc 1. Au moment où l’interruption
survient, la sélection courante des bancs est imprévisible, aussi la sauvegarde effectif
de W est effectuée soit dans le banc 0, soit dans le banc 1. Il est donc indispensable
que les deux variables soient déclarées de façon qu’elles présentent le même
offset à partir de l’adresse de début de leur banc.
On déclare donc deux variables :
– la première W_STATUS_TEMP, de dimension 2, pour sauver W et STATUS ; cette
variable doit être déclarée doit être déclarée dans le banc 0, qui s’appelle générallement gpr0 ;
– W_TEMP_1 pour sauver W, de dimension 1 ; cette variable doit être déclarée dans
le banc 1, qui s’appelle générallement gpr1.
Rappelons que ces deux variables doivent présenter le même offset à partir de
l’adresse de début de leur banc : par exemple si W_STATUS_TEMP est à l’adresse 0x20,
alors W_TEMP_1 doit résider à l’adresse 0xA0.
Le code Piccolo est le suivant :
midrange interrupt3 "16F873" :
ram gpr0 {
byte W_STATUS_TEMP [2]
}
ram gpr1 {
byte W_TEMP_1
}
interrupt contextsave W_STATUS_TEMP, W_TEMP_1 {
instructions
}
...
54
C HAPITRE 5. Programmes pour mid-range
end
Et le code engendré pour la routine d’interruption est :
ORG 0x4
MOVWF W_STATUS_TEMP
SWAPF STATUS, W
CLRF STATUS
MOVWF W_STATUS_TEMP + 0x1
instructions
SWAPF W_STATUS_TEMP + 0x1, W
MOVWF STATUS
SWAPF W_STATUS_TEMP, F
SWAPF W_STATUS_TEMP, W
RETFIE
Situation (4) : le micro-contrôleur ne possède pas de zone mémoire commune
à tous les bancs, le code occupe plusieurs pages de la mémoire programme,
et le sous-programme d’interruption invoque CALL ou GOTO. Il faut dans ce cas
donc sauver W, STATUS et PCLATH.
Cette situation est celle du 16F873. Ce micro-contrôleur possède 4 bancs mémoire,
la zone RAM du banc 2 étant un miroir de la zone RAM du banc 0, et la zone RAM
du banc 3 étant un miroir de la zone RAM du banc 1. Au moment où l’interruption
survient, la sélection courante des bancs est imprévisible, aussi la sauvegarde effectif
de W est effectuée soit dans le banc 0, soit dans le banc 1. Il est donc indispensable
que les deux variables soient déclarées de façon qu’elles présentent le même
offset à partir de l’adresse de début de leur banc.
On déclare donc deux variables :
– la première W_STATUS_PCLATH_TEMP, de dimension 3, pour sauver W, STATUS
et PCLATH ; cette variable doit être déclarée doit être déclarée dans le banc 0,
qui s’appelle générallement gpr0 ;
– W_TEMP_1 pour sauver W, de dimension 1 ; cette variable doit être déclarée dans
le banc 1, qui s’appelle générallement gpr1.
Rappelons que ces deux variables doivent présenter le même offset à partir de
l’adresse de début de leur banc : par exemple si W_STATUS_TEMP est à l’adresse 0x20,
alors W_TEMP_1 doit résider à l’adresse 0xA0.
Le code Piccolo est le suivant :
midrange interrupt3 "16F873" :
ram gpr0 {
byte W_STATUS_PCLATH_TEMP [3]
}
ram gpr1 {
byte W_TEMP_1
}
interrupt contextsave W_STATUS_PCLATH_TEMP, W_TEMP_1 {
instructions
}
5.8. Optimisations
55
...
end
Et le code engendré pour la routine d’interruption est :
ORG 0x4
MOVWF W_STATUS_PCLATH_TEMP
SWAPF STATUS, W
CLRF STATUS
MOVWF W_STATUS_PCLATH_TEMP
MOVF PCLATH, W
MOVWF W_STATUS_PCLATH_TEMP
CLRF PCLATH
instructions
MOVF W_STATUS_PCLATH_TEMP
MOVWF PCLATH
SWAPF W_STATUS_PCLATH_TEMP
MOVWF STATUS
SWAPF W_STATUS_TEMP, F
SWAPF W_STATUS_TEMP, W
RETFIE
5.8
+ 0x1
+ 0x2
+ 0x2, W
+ 0x1, W
Optimisations
Cette section indique les différentes optimisations d’un code mid-range. L’optimisation est activée par l’option « -o ».
Pour optimiser, Piccolo applique les remplacements indiqués par le tableau 5.10,
et élimine le code mort. Le code est balayé de manière répétitive, tant que des optimisations sont effectuées.
Situation
Optimisation
CALL vers RETLW k
Remplacement par MOVLW k
GOTO vers RETLW k
Remplacement par RETLW k
GOTO a vers GOTO b
Remplacement par GOTO b
GOTO vers l’instruction qui suit
Suppression
JSR vers RETLW k
Remplacement par MOVLW k
JUMP vers RETLW k
Remplacement par RETLW k
JUMP a vers JUMP b
Remplacement par JUMP b
JUMP a vers GOTO b
Remplacement par JUMP b
Tableau 5.10 – Optimisation du code mid-range
Chapitre 6
Programmes pour pic18
6.1
Exemples de programmes pour pic18
6.1.1
Premier exemple : blink led
Ce simple programme est destiné à un 18F448 et fait clignoter à 1 Hz environ une
led connectée au port RE0. Il programme donc RE0 (broche 8) en sortie. La configuration suppose qu’il est connecté à une horloge externe à 40 MHz sur la broche OSC1
(broche 13).
Ce programme a l’allure suivante :
pic18 blink_led "18F448" :
#------------ Configuration
configuration {
...
}
#------------ RAM
ram accessram {
...
}
#------------ Routine main
noreturn routine main bank:requires 0 {
...
}
#------------ Fin
end
L’en-tête indique qu’il s’agit d’un programme pour pic18, qu’il s’appelle blink_led,
et qu’il est destiné au micro-contrôleur 18F448. Ce corps de ce programme est constitué de trois sections, décrites dans la suite de cette section : configuration, ram, et
routine main. D’autres types de sections peuvent être définies, mais sont absentes de
ce premier exemple. La section 6.2 page 63 donne une liste exhaustive des différentes
sections d’un programme pour pic18.
La section configuration décrit les valeurs qui sont affectées aux bits de configuration du micro-contrôleur. Les valeurs choisies pour cet exemple sont listées ci-
58
C HAPITRE 6. Programmes pour pic18
dessous. Pour une description complète de cette section, nous invitons le lecteur à se
reporter au chapitre 10 page 111.
#------------ Configuration
configuration {
OSC : "EC-OSC2 as RA6"
OSCS : Disabled
WDT : "Disabled-Controlled by SWDTEN bit"
WDTPS : "1:128"
STVR : Disabled
LVP : Disabled
CP_0 : Disabled
CP_1 : Disabled
CP_2 : Disabled
CP_3 : Disabled
WRT_1 : Disabled
WRT_2 : Disabled
WRT_3 : Disabled
WRT_0 : Disabled
EBTR_0 : Disabled
EBTR_1 : Disabled
EBTR_2 : Disabled
EBTR_3 : Disabled
BACKBUG : Disabled
BODEN : Disabled
BODENV : "4.5V"
CPB : Disabled
CPD : Disabled
EBTRB : Disabled
PUT : Enabled
WRTB : Disabled
WRTC : Disabled
WRTD : Disabled
}
La section ram déclare les variables du programme. Ce type de section est complètement décrit au chapitre 8 page 101. Le 18F448 possède plusieurs bancs mémoire,
dont on obtient la composition en appelant piccolo avec l’option --memory=18F448
(voir section 2.2.2 page 15) :
18F448 d e v i c e :
4 RAM banks ( t o t a l 768 bytes ) :
bank " accessram " from 0x0 t o 0x5F (96 bytes )
bank " gpr0 " from 0x60 t o 0xFF (160 bytes )
bank " gpr1 " from 0x100 t o 0x1FF (256 bytes )
bank " gpr2 " from 0x200 t o 0x2FF (256 bytes )
ROM s i z e : 16384 bytes
EEPROM s i z e : 256 bytes ( at 0xF00000 )
Ainsi, le 18F448 possède quatre bancs mémoire : accessram, gpr0, gpr1 et gpr2.
En consultant la document du 18F448, on établit que le banc accessram est accessible
6.1. Exemples de programmes pour pic18
59
quel que soit BSR, que l’accès aux variables du banc gpr0 impose que BSR soit égal à
0, que l’accès aux variables du banc gpr1 impose que BSR soit égal à 1, etc.
Pour simplifier l’écriture de ce premier programme, on place donc les trois variables dont on a besoin dans le banc accessram :
#------------ RAM
ram accessram {
byte compteurL
byte compteurH
byte compteurU
}
Cette écriture alloue les emplacements séquentiellement : compteurL sera à l’adresse
0, compteurH à l’adresse 1, et compteurU à l’adresse 2.
Il reste maintenant à décrire la routine main. Un programme valide doit toujours
contenir une routine nommée main : c’est le point d’entrée de l’exécution à la mise
sous tesion ou après un reset. L’en-tête de la routine main doit en outre comporter
deux qualificatifs :
– noreturn : ceci exprime que la routine doit se terminer par une boucle infinie,
ou un branchement à une routine elle aussi sans retour ;
– bank:requires 0 : ceci exprime que lors de l’appel, le registre BSR contient la
valeur 0 1 , et que donc on pourrait utiliser des variables dans le banc gpr0 sans
modifier BSR.
Dans le code de la routine, parmi les instructions assembleur, on trouve une instruction if structurée, dont les conditions cachent une utilisation de l’instruction
TSTFSZ, à l’intérieur d’une boucle infinie implémentée par la construction forever
... end.
#------------ Routine main
noreturn routine main bank:requires 0 {
#--- Aucune entree analogique
movlw 7
movwf ADCON1
#--- Programmer RE0 en sortie
bcf TRISE.0
#--- Initialiser les compteurs
c l r f compteurL
c l r f compteurH
c l r f compteurU
#--- Boucle infinie
forever
i f (compteurL NZ)
decf compteurL
e l s i f (compteurH NZ)
decf compteurH
setf compteurL
e l s i f (compteurU NZ)
1. BSR est initialisé à 0 à la mise sous tension ou lors d’un reset.
60
C HAPITRE 6. Programmes pour pic18
decf compteurU
setf compteurH
setf compteurL
else
#--- Reinitialiser les compteurs
movlw 0x0F
movwf compteurU
movlw 0x42
movwf compteurH
movlw 0x40
movwf compteurL
#--- Clignoter
btg PORTE.0
end
end
}
Pour terminer ce premier exemple, il est utile de jeter un coup d’œil sur le code
assembleur engendré (option « -S »), à gauche sans optimisation, à droite avec optimisations grâce à l’option « -O ». L’optimisation gagne 4 instructions (le bra main
initial, et la meilleure utilisation des trois occurrences de l’instruction TSTFSZ. Observez aussi l’optimisation des sauts (ne vous basez pas sur les noms des étiquettes,
elles changent de signification avec l’optimisation).
Code non optimisé
ORG 0
bra
main
main:
movlw 0x7
movwf ADCON1
bcf TRISE, 0
clrf compteurL
clrf compteurH
clrf compteurU
_label_0:
TSTFSZ compteurL
bra _bcc_label_0
bra _label_1
_bcc_label_0:
decf compteurL, F
bra
_label_2
_label_1:
TSTFSZ compteurH
bra _bcc_label_1
bra _label_3
_bcc_label_1:
decf compteurH, F
setf compteurL
bra
_label_4
_label_3:
TSTFSZ compteurU
bra _bcc_label_2
bra _label_5
_bcc_label_2:
decf compteurU, F
setf compteurH
setf compteurL
bra
_label_6
_label_5:
movlw 0xF
movwf compteurU
movlw 0x42
movwf compteurH
movlw 0x40
movwf compteurL
BTG PORTE, 0
_label_6:
_label_4:
_label_2:
bra
_label_0
6.1. Exemples de programmes pour pic18
Code optimisé
ORG 0
movlw 0x7
movwf ADCON1
bcf TRISE, 0
clrf compteurL
clrf compteurH
clrf compteurU
_label_0:
TSTFSZ compteurL
bra _label_1
TSTFSZ compteurH
bra _label_3
TSTFSZ compteurU
bra _label_5
movlw 0xF
movwf compteurU
movlw 0x42
6.1.2
61
movwf compteurH
movlw 0x40
movwf compteurL
BTG PORTE, 0
bra
_label_0
_label_5:
decf compteurU, F
setf compteurH
setf compteurL
bra
_label_0
_label_3:
decf compteurH, F
setf compteurL
bra
_label_0
_label_1:
decf compteurL, F
bra
_label_0
Deuxième exemple : blink led sous interruption
Ce simple programme fait clignoter à 4 Hz une led connectée au port RE0. Il programme donc RE0 (broche 8) en sortie. La configuration suppose qu’il est connecté à
une horloge externe à 40 MHz sur OSC1 (broche 13). Un sous-programme d’interruption est déclenché toutes les 0,1 ms par le timer 2. Pour illustrer les instructions de
gestion des bancs, les deux variables sont placées dans le banc 2. Supprimez ou déplacez les instructions banksel, pour mettre en évidence les vérifications faites par
le compilateur.
Le programme a l’allure suivante :
pic18 blink_led_it "18F448" :
#------------ Configuration
configuration {
...
}
#------------ RAM
ram accessram {
...
}
#------------ Routine d’interruption
interrupt high fast {
...
}
#------------ Routine main
noreturn routine main bank:requires 0 {
...
}
#------------ Fin
end
62
C HAPITRE 6. Programmes pour pic18
Le code de configuration est le même que celui du premier exemple.
ram gpr2 {
byte compteurL
byte compteurH
}
La section ram décrit l’attribution de la RAM du micro-contrôleur. Le nom gpr2 est le
nom du banc qui commence à l’adresse 0x100. Les adresses sont allouées séquentiellement : compteurL a pour adresse 0x100, et compteurH a pour adresse 0x101. Ces
deux registres ne seront donc accessibles que via le registre BSR.
interrupt high fast {
#--- Acquitter l’interruption du timer 2
bcf PIR1.TMR2IF
#--- Decompter le temps
banksel 2
i f (compteurL NZ)
decf compteurL
e l s i f (compteurH NZ)
decf compteurH
setf compteurL
else
#--- Reinitialiser les compteurs
movlw 0x13
movwf compteurH
movlw 0x87
movwf compteurL
#--- Clignoter
btg PORTE.0
end
}
Une section interrupt définit un sous-programme d’interruption. Il porte le nom «
high », ce qui signifie qu’il est attaché au point d’entrée d’adresse 0x8. Il porte aussi
le qualificatif fast, ce qui signifie que l’instruction retfie 1 sera engendrée pour
effectuer le retour d’interruption.
Dans un programme plus complexe, il n’est pas possible de prévoir à la compilation la valeur de BSR quand une interruption survient. Aussi l’instruction banksel
2 est obligatoire avant d’adresser les registres du compteur (essayez de la supprimer,
le compilateur engendrera un message d’erreur).
noreturn routine main bank:requires 0 {
#--- Aucune entree analogique
movlw 7
movwf ADCON1
#--- Programmer RE0 en sortie
bcf TRISE.0
#--- Initialiser les compteurs
banksel 2
c l r f compteurL
6.2. Structure d’un programme pour pic18
63
c l r f compteurH
#--- Initialiser le Timer 2
# Horloge de base : 10 MHz
# Le Prescaler est fixe a 4 -> 2 500 kHz
# PR2 est fixe a 250 -> 10 kHz
movlw 250 - 1 # La periode est PR2 + 1
movwf PR2
movlw @T2CON (TOUTPS:0, TMR2ON:1, T2CKPS:1)
movwf T2CON
#--- Autoriser les priorites d’interruption
bsf RCON.IPEN
#--- Autoriser l’interruption en provenance du Timer 0
bsf PIE1.TMR2IE
#--- Valider les its
bsf INTCON.GIEH
bsf INTCON.GIEL
#--- Boucle infinie
forever
end
}
Deux commentaires : ici aussi, l’instruction banksel 2 est obligatoire avant d’adresser les registres du compteur (essayez de la supprimer. . . ) ; tous les autres accès s’effectuant via l’acces bank, il n’est pas nécessaire de changer la valeur de BSR.
L’instruction
movlw
@T2CON (TOUTPS:0, TMR2ON:1, T2CKPS:1)
est particulière : elle permet de construire une valeur statique champ par champ (voir
à §§).
6.2
Structure d’un programme pour pic18
Un programme Piccolo pour pic18 a la structure suivante :
pic18 nom "nom_composant" :
liste_de_sections
end
Dans l’en-tête :
– le nom « nom » est le nom du fichier (sans son extension) qui contient ce texte
source ;
– le nom du composant « nom_composant » doit être exactement le nom de l’un
des composants supportés (pour obtenir la liste des pic18 pris en charge, utiliser
l’option « --pic18 », voir section 2.3.3 page 17).
Le corps du programme est constitué d’une liste non ordonnée de sections. Les
sections disponibles sont listées dans le tableau 6.1.
64
C HAPITRE 6. Programmes pour pic18
Type de section
Mot-clé introductif
Référence
Configuration
configuration
chapitre 10 page 111
Définition de variable
ram
chapitre 8 page 101
Définition de constante
const
chapitre 9 page 107
Définition de routine pic18
routine
section 6.3 page 64
Définition de routine d’interruption pic18
interrupt
section 6.4 page 67
Data
data
section 6.8 page 77
Include
include
section 6.7 page 77
Tableau 6.1 – Les sections d’un programme pour pic18
6.3
Routines pic18
Les routines définissent le code exécutable de votre programme. L’une d’entre elles
doit s’appeler main : c’est la routine qui s’exécute au démarrage. Il y a deux types de
routine, les routines régulières et les routines sans retour.
L’ordre des déclarations des routines est quelconque, il est possible d’appeler une
routine qui est déclarée après l’instruction d’appel. Simplement Piccolo engendrera
leur code dans leur ordre d’apparition.
Routine et BSR. Un problème important en assembleur est la gestion du registre de
sélection de banc BSR : son utilisation correcte en assembleur est complètement à la
charge du programmeur. Piccolo propose des instructions pour sécuriser son emploi :
voir la section 7.8 page 92.
Liste d’instructions d’une routine. Elle est structurée : Piccolo définit des instructions de sélection et de répétition : cela signifie que vous ne pouvez pas déclarer
d’étiquette, ni utiliser des bra ni des goto pour effectuer des branchements à l’intérieur d’une routine.
6.3.1
Routine régulière
C’est un sous-programme. Une routine régulière peut ne comporter aucune instruction Piccolo, l’instruction return est implicitement ajoutée. Une routine régulière
est déclarée par :
routine maRoutine ... {
...
}
maRoutine est le nom de la routine, celui qui sera nommé dans un instruction
d’appel de routine. Entre les accolades « { » et « } », apparaît la liste des instructions.
6.3. Routines pic18
65
Appel d’une routine régulière. Utiliser RCALL, CALL ou JSR, comme indiqué à la
section 6.6.8 page 75.
6.3.2
Routine sans retour
L’exécution ne revient pas jamais à l’appelant. Ce type de routine doit donc se
terminer par des constructions particulières qui assurent le non-retour : une boucle
infinie, un branchement (bra, Bcc, goto ou jump) vers une autre routine sans retour,
un bra calculé ou un goto calculé vers d’autres routines sans retour.
Une routine sans retour doit être déclarée avec qualificatif noreturn :
noreturn routine maRoutine ... {
...
}
Appel d’une routine sans retour. Utiliser bra, goto, Bcc ou jump.
Déclaration de la routine main. Dans un programme, il doit exister une et une
seule routine main, qui doit être déclarée comme suit :
noreturn routine main bank:requires 0 {
liste d’instructions
}
Le compilateur Piccolo insère à l’adresse zéro une instruction bra (ou goto) vers
cette routine, de façon que la routine main soit exécutée au démarrage du microcontrôleur. Le qualificatif bank:requires 0 est exigé par le compilateur Piccolo car
BSR est initialisé à zéro au démarrage du micro-contrôleur.
Comment terminer une routine sans retour. La dernière instruction de la liste
des instructions d’une routine sans retour doit être :
– une instruction de répétition infinie (section 7.4 page 84) ;
– un appel vers une autre routine sans retour, au moyen d’un bra, goto, Bcc ou
jump ;
– un branchement calculé vers une routine parmi plusieurs, au moyen d’un computed
bra ou d’un computed goto ;
– une instruction conditionnelle structurée, dont toutes les branches présentent
comme dernière instruction les instructions d’appel d’un routine sans retour,
un branchement calculé (comme évoqué ci-dessus), ou encore une instruction
conditionnelle structurée, dont toutes les branches, etc.
Exemple simple : la dernière instruction est une boucle infinie :
noreturn routine maRoutine {
...
forever
...
66
C HAPITRE 6. Programmes pour pic18
end
}
Exemple simple : la dernière instruction est un branchement vers une routine
sans retour :
noreturn routine maRoutine {
...
bra autreRoutineSansRetour
}
La dernière instruction est un computed bra nommant les routines r1, r2, r3
qui doivent être toutes les trois des routines sans retour :
noreturn routine maRoutine {
...
computed [3] bra r1, r2, r3
}
La dernière instruction est un if dont aucune des branches ne se termine (r1 est
une routine sans retour) :
noreturn routine maRoutine {
i f (...)
...
bra r1
else
...
forever
...
end
end
}
La dernière instruction est un if dont la première branche se termine elle même
par un if dont les deux branches se terminent par des branchements vers des routines sans retour :
noreturn routine maRoutine {
i f (...)
...
i f (...)
...
bra r1
else
...
bra r2
end
else
...
bra r3
end
}
6.4. Routines d’interruption
6.4
67
Routines d’interruption
Les pic18 acceptent deux niveaux d’interruption, le niveau haut (high), et le niveau faible (low) : l’occurrence d’une interruption de niveau haut provoque un branchement à l’adresse 0x08 ; l’occurrence d’une interruption de niveau faible provoque
un branchement à l’adresse 0x18.
Durant la réponse à une interruption, PC est sauvé dans la pile, et les registres
WREG, STATUS et BSR sont sauvés dans des registres cachés. Le retour d’interruption s’effectue par une instruction retfie ; si l’opérande 1 (pour FAST) est précisée
(retfie 1), alors WREG, STATUS et BSR sont restitués à partir des registres cachés.
Si les deux interruptions sont activées (enabled), Microchip précise que le retour
par retfie 1 ne peut pas être utilisée fiablement pour l’interruption de faible priorité : « If both low and high priority interrupts are enabled, the stack registers cannot
be used reliably for low priority interrupts. If a high priority interrupt occurs while
servicing a low priority interrupt, the stack register values stored by the low priority
interrupt will be overwritten ».
Nous allons voir dans la suite comment Piccolo gère cette situation.
En Piccolo, une routine d’interruption PIC 18 est décrite dans une section interrupt :
interrupt nom ... {
instructions
}
Seuls deux noms sont possibles : low pour l’interruption de faible priorité, et high
pour l’interruption de haute priorité. Bien entendu, un nom ne peut apparaître qu’une
seule fois, aussi un programme ne peut comporter qu’au plus deux routines d’interruption, qui peuvent apparaître dans un ordre quelconque.
Dans une routine d’interruption, toutes les instructions décrites sont disponibles,
sauf les instructions qui sont sans retour : boucle infinie, bra (branchement vers une
routine sans retour), etc.
L’instruction assembleur retfie n’existe pas en Piccolo : le compilateur l’insère
automatiquement à la fin des routines d’interruption.
6.4.1
Qualificatif fast
En Piccolo, vous pouvez ajouter le qualificatif fast à l’en-tête d’une routine d’interruption :
interrupt nom fast {
instructions
}
Avec ce qualificatif, l’instruction de retour engendrée par Piccolo est retfie 1
(sans ce qualificatif, c’est retfie 0).
Si les deux routines d’interruption low et high sont déclarées, et si la routine high
est déclarée fast, alors Piccolo considère comme une erreur de déclarer la routine low
avec le qualificatif fast.
68
C HAPITRE 6. Programmes pour pic18
6.4.2
Routine d’interruption et BSR
Routine déclarée avec fast. Le contenu initial de BSR, au début de la routine
d’interruption est inconnu. Il faut donc fixer sa valeur par une instruction banksel si
besoin est. Comme le retour d’interruption restituera le contenu initial de BSR, il est
inutile de le préserver par une construction banksave.
interrupt nom fast {
banksel 1
c l r f varBank1 # Variable declaree dans le banc 1
}
Routine déclarée sans fast. Le contenu initial de BSR, au début de la routine d’interruption est inconnu. Si il doit être utilisé, et comme le retour d’interruption ne restituera pas son contenu initial, il est indispensable de le préserver par une construction banksave, nommant une variable sauve_bsr déclarée dans le banc accessram.
Il faut ensuite fixer sa valeur par une instruction banksel.
interrupt nom {
banksave sauve_bsr
banksel 1
c l r f varBank1 # Variable declaree dans le banc 1
end
}
Le compilateur Piccolo détectera l’oubli de la sauvegarde par banksave, en émettant un message d’erreur.
6.5
Les instructions
Elles sont de deux types (la distinction est importante pour l’instruction conditionnelle simple) :
– les instructions simples ;
– les instructions composées.
Les instructions simples. Elles correspondent à une partie des instructions assembleur. Attention, pour certaines, la syntaxe n’est pas exactement la même que
celle de l’instruction assembleur correspondante. Dans sa version actuelle, Piccolo ne prend pas en charge le jeu d’instruction étendu. Les tableau 6.2, tableau 6.3 et tableau 6.4 donnent la liste des instructions assembleur pic18 et les liens
vers les sections précisant leur prise en charge en Piccolo. Le tableau 6.5 liste les
instructions complémentaires disponibles en Piccolo.
Les instructions composées. Piccolo définit les instructions suivantes :
– l’instruction mnop (section 7.1 page 81) ;
– l’instruction ldataptr (section 6.9.2 page 79) ;
– l’instruction ltblptr (section 6.9.1 page 78) ;
– l’instruction conditionnelle simple (section 7.2 page 82) ;
6.5. Les instructions
69
Assembleur
Description
Référence en Piccolo
ADDLW k
Add Literal and WREG
section 6.6.4 page 74
ADDWF f, d, a
Add WREG and f
section 6.6.2 page 72
ADDWFC f, d, a
Add WREG and Carry bit to f
section 6.6.2 page 72
ANDLW k
And Literal with WREG
section 6.6.4 page 74
ANDWF f, d, a
And WREG with f
section 6.6.2 page 72
BC n
Branch if Carry
section 6.6.9 page 76
BCF f, b, a
Bit Clear f
section 6.6.3 page 72
BN n
Branch if Negative
section 6.6.9 page 76
BNC n
Branch if Not Carry
section 6.6.9 page 76
BNN n
Branch if Not Negative
section 6.6.9 page 76
BNOV n
Branch if Not Overflow
section 6.6.9 page 76
BNZ n
Branch if Not Zero
section 6.6.9 page 76
BOV n
Branch if Overflow
section 6.6.9 page 76
BRA n
Branch Unconditionally
section 6.6.9 page 76
BSF f, b, a
Bit Set f
section 6.6.3 page 72
BTFSC f, b, a
Bit Test f, Skip if Clear
section 6.5.1 page 70
BTFSS f, b, a
Bit Test f, Skip if Set
section 6.5.1 page 70
BTG f, b, a
Bit Toggle f
section 6.6.3 page 72
BZ n
Branch if Zero
section 6.6.9 page 76
CALL n, s
Call Subroutine
section 6.6.8 page 75
CLRF f, a
Clear f
section 6.6.1 page 71
CLRWDT
Clear Watchdog Timer
section 6.6.5 page 74
COMF f, d, a
Complement f
section 6.6.2 page 72
CPFSEQ f, a
Compare f with WREG, Skip =
section 6.5.1 page 70
CPFSGT f, a
Compare f with WREG, Skip >
section 6.5.1 page 70
CPFSLT f, a
Compare f with WREG, Skip <
section 6.5.1 page 70
DAW
Decimal Adjust WREG
section 6.6.5 page 74
DECF f, d, a
Decrement f
section 6.6.2 page 72
DECFSZ f, d, a
Decrement f, Skip if 0
section 6.5.1 page 70
DCFSNZ f, d, a
Decrement f, Skip if Not 0
section 6.5.1 page 70
GOTO n
Go to Address
section 6.6.9 page 76
Tableau 6.2 – Instructions assembleur pic18 [A-G]
–
–
–
–
–
–
l’instruction conditionnelle structurée (section 7.3 page 83) ;
l’instruction répétitive (section 7.5 page 84) ;
l’instruction de répétition infinie (section 7.4 page 84) ;
l’instruction computed retlw (section 7.7.1 page 87) ;
l’instruction computed bra (section 7.7.2 page 90) ;
l’instruction computed goto (section 7.7.3 page 91) ;
70
C HAPITRE 6. Programmes pour pic18
Assembleur
Description
Référence en Piccolo
INCF f, d, a
Decrement f
section 6.6.2 page 72
INCFSZ f, d, a
Increment f, Skip if 0
section 6.5.1 page 70
INFSNZ f, d, a
Increment f, Skip if Not 0
section 6.5.1 page 70
IORLW k
Inclusive OR Literal with WREG
section 6.6.4 page 74
IORWF f, d, a
Inclusive OR WREG with f
section 6.6.2 page 72
LFSR f, k
Move Literal to FSR(f)
section 6.6.6 page 74
MOVF f, d, a
Move f
section 6.6.2 page 72
MOVFF fs, fd
Move fs to fd
section 6.6.7 page 75
MOVLB k
Move Literal to BSR
section 6.5.1 page 70
MOVLW k
Move Literal to WREG
section 6.6.4 page 74
MULLW k
Multiply Literal with WREG
section 6.6.4 page 74
MULWF f, a
Multiply WREG with f
section 6.6.1 page 71
NEGF f, a
Negate f
section 6.6.1 page 71
NOP
No Operation
section 6.6.5 page 74
POP
Pop Top of Return Stack
section 6.6.5 page 74
PUSH
Push Top of Return Stack
section 6.6.5 page 74
RCALL n
Relative Call
section 6.6.8 page 75
RESET
Software Device Reset
section 6.6.5 page 74
RETFIE s
Return from Interrupt Enable
section 6.5.1 page 70
RETLW k
Return with Literal in WREG
section 6.5.1 page 70
RETURN s
Return from Subroutine
section 6.5.1 page 70
RLCF f, d, a
Rotate Left f through Carry
section 6.6.2 page 72
RLNCF f, d, a
Rotate Left f (No Carry)
section 6.6.2 page 72
RRCF f, d, a
Rotate Right f through Carry
section 6.6.2 page 72
RRNCF f, d, a
Rotate Right f (No Carry)
section 6.6.2 page 72
Tableau 6.3 – Instructions assembleur pic18 [I-S]
– l’instruction banksel (section 7.8.2 page 93) ;
– l’instruction nobank (section 7.8.3 page 94) ;
– l’instruction banksave (section 7.8.4 page 94).
6.5.1
Les instructions que vous ne trouverez pas en Piccolo
Elles n’existent pas en Piccolo parce qu’elles sont remplacées par des constructions structurées, ou bien engendrées automatiquement lors de la compilation.
Voici leur liste avec les liens vers les sections appropriées :
– BTFSC, BTFSS, CPFSEQ, CPFSGT, CPFSLT, DECFSZ, DCFSNZ, INCFSZ, INFSZ, TSTFSZ :
ces instructions sont engendrées par l’instruction conditionnelle simple, l’instruction conditionnelle structurée et l’instruction répétitive ;
– MOVLB : l’affectation de BSR est pris en charge par banksel (section 7.8.2 page
6.6. Les instructions simples
71
Assembleur
Description
Référence en Piccolo
SETF f, a
Set f
section 6.6.1 page 71
SLEEP
Go into Standby Mode
section 6.6.5 page 74
SUBFWB f, d, a
Substract f from WREG with Borrow
section 6.6.2 page 72
SUBLW k
Substract WREG from Literal
section 6.6.4 page 74
SUBWF f, d, a
Substract WREG from f
section 6.6.2 page 72
SUBWFB f, d, a
Substract WREG from f with Borrow
section 6.6.2 page 72
SWAPF f, d, a
Swap Nibbles in f
section 6.6.2 page 72
TBLRD*
Table Read
section 6.6.5 page 74
TBLRD*+
Table Read with Post-Increment
section 6.6.5 page 74
TBLRD*-
Table Read with Post-Decrement
section 6.6.5 page 74
TBLRD+*
TBLWT*
Table Read with Pre-Increment
section 6.6.5 page 74
Table Write
section 6.6.5 page 74
TBLWT*+
Table Write with Post-Increment
section 6.6.5 page 74
TBLWT*-
Table Write with Post-Decrement
section 6.6.5 page 74
TBLWT+*
TSTFSZ f, a
Table Write with Pre-Increment
section 6.6.5 page 74
Test f, Skip if 0
section 6.5.1 page 70
XORLW k
Exclusive OR Literal with WREG
section 6.6.4 page 74
XORWF f, d, a
Exclusive OR WREG with f
section 6.6.2 page 72
Tableau 6.4 – Instructions assembleur pic18 [T-X]
Piccolo
Description
Référence
jump
Appel de routine sans retour
section 6.6.9 page 76
jsr
Appel de routine régulière
section 6.6.8 page 75
Tableau 6.5 – Instructions propres à Piccolo pour pic18
93) ;
– RETFIE : engendrée automatiquement lors de la compilation d’une routine d’interruption ;
– RETLW : utiliser une instruction MOVLW, et c’est l’optimiseur qui remplacera la
séquence MOVLW k, RETURN par une instruction RETLW k ;
– RETURN : engendrée automatiquement lors de la compilation d’une routine.
6.6
6.6.1
Les instructions simples
Instructions nommant un registre
Ce sont les instructions listées dans le tableau 6.6. Pour ces instructions, l’opérande a est toujours implicite. Le compilateur Piccolo regarde d’abord si le registre f
est dans l’accessram. Si oui, il engendre l’instruction assembleur correspondante avec
72
C HAPITRE 6. Programmes pour pic18
a égal à 0. Sinon, il examine si le registre BSR contient la valeur correspondant au
banc de f. Si oui, il engendre l’instruction assembleur correspondante avec a égal à
1. Sinon, le compilateur signale l’erreur.
Assembleur
Description
Écriture en Piccolo
CLRF f, a
Clear f
clrf f
MULWF f, a
Multiply WREG with f
mulwf f
NEGF f, a
Negate f
negf f
SETF f, a
Set f
setf f
Tableau 6.6 – Opérations nommant un registre
6.6.2
Instructions nommant un registre, et optionnellement W
Ces instructions, ainsi que le traduction en Piccolo, sont listées dans le tableau
6.7. En assembleur, elles nomment trois opérandes :
– f : désigne le registre, il en est de même en Piccolo ;
– d : optionnel en Piccolo ; si absent, le registre f est destination de l’opération, si
égal à W, c’est le registre WREG qui est destinaire ;
– a : toujours absent en Piccolo ; le compilateur Piccolo regarde d’abord si le registre f est dans l’accessram ; si oui, il engendre l’instruction assembleur correspondante avec a égal à 0, sinon il examine si le registre BSR contient la
valeur correspondant au banc de f ; si oui, il engendre l’instruction assembleur
correspondante avec a égal à 1, sinon le compilateur signale l’erreur.
6.6.3
Opérations d’affectation de bit
Ces instructions Piccolo correspondent aux instructions assembleur BCF, BSF et
BTG (tableau 6.8).
En Piccolo, ces instructions ont toujours deux arguments séparés par un point :
– le premier argument f est une référence à un registre (section 8.5 page 104) ;
– le second b est le bit concerné.
Le troisième argument a de l’instruction assembleur est toujours absent : le compilateur Piccolo regarde d’abord si le registre f est dans l’accessram ; si oui, il engendre l’instruction assembleur correspondante avec a égal à 0, sinon il examine si
le registre BSR contient la valeur correspondant au banc de f ; si oui, il engendre
l’instruction assembleur correspondante avec a égal à 1, sinon le compilateur signale
l’erreur.
Pour désigner le bit concerné, vous pouvez utiliser un nombre compris entre 0 et
7. Par exemple :
bcf maVariable.3
Si le registre a été défini en déclarant des noms de bit :
6.6. Les instructions simples
73
En Piccolo, destination :
Assembleur
Description
f
WREG
ADDWF f, d, a
Add WREG and f
addwf f
addwf f, W
ADDWFC f, d, a
Add WREG and Carry bit to f
addwfc f
addwfc f, W
ANDWF f, d, a
And WREG with f
andwf f
andwf f, W
COMF f, d, a
Complement f
comf f
comf f, W
DECF f, d, a
Decrement f
decf f
decf f, W
INCF f, d, a
Increment f
incf f
incf f, W
IORWF f, d, a
Inclusive OR WREG with f
iorwf f
iorwf f, W
MOVF f, d, a
Move f
movf f
movf f, W
RLCF f, d, a
Rotate Left f through Carry
rlcf f
rlcf f, W
RLNCF f, d, a
Rotate Left f (No Carry)
rlncf f
rlncf f, W
RRCF f, d, a
Rotate Right f through Carry
rrcf f
rrcf f, W
RRNCF f, d, a
Rotate Right f (No Carry)
rrncf f
rrncf f, W
SUBFWB f, d, a
Substract f from WREG with Borrow
subfwb f
subfwb f, W
SUBWF f, d, a
Substract WREG from f
subwf f
subwf f, W
SUBWFB f, d, a
Substract WREG from f with Borrow
subwfb f
subwfb f, W
SWAPF f, d, a
Swap Nibbles in f
swapf f
swapf f, W
XORWF f, d, a
Exclusive OR WREG with f
xorwf f
xorwf f, W
Tableau 6.7 – Instructions pic18 nommant un registre, et optionnellement W
Assembleur
Description
En Piccolo
BCF f, b, a
Bit Clear f
bcf f.b
BSF f, b, a
Bit Set f
bsf f.b
BTG f, b, a
Bit Toggle f
btg f.b
Tableau 6.8 – Opérations pic18 d’affectation de bit
ram ... {
byte maVariable <a, -, b [3], -, -, ->
}
Vous pouvez utiliser l’un de ces noms comme second argument :
bcf maVariable.a # a designe le bit 7
ou encore
bcf maVariable.b [1] # b[1] designe le bit 4
Vous pouvez de cette façon accéder aux bits des registres spéciaux. Pour connaître
la liste des registres de contrôle, utilisez l’option --registers (ou sa version courte
-R), comme décrite à la section 2.2.3 page 15 ; par exemple : piccolo -R=18F442.
74
C HAPITRE 6. Programmes pour pic18
6.6.4
Opérations littérales avec WREG
Ces opérations sont listées dans le tableau 6.9. L’instruction RETLW k n’existe pas
en Piccolo, l’optimiseur repérera une séquence MOVLW k RETURN et la transformera
en RETLW k.
En Piccolo, k est une expression statique. Une expression statique est une expression dont la valeur est calculée à la compilation. Sa forme générale est présentée
à la section 9.2 page 109. Le compilateur effectue tous les calculs d’une expression
statique avec des nombres entiers 32 bits signés.
Pour être valide dans les opérations statiques avec WREG, le résultat devra être :
– soit un nombre positif inférieur ou égal à 255 ;
– soit un nombre négatif supérieur ou égal à -128.
Par exemple : movlw -14 engendre l’instruction assembleur : MOVLW 0xf2.
Assembleur
Description
Écriture en Piccolo
ADDLW k
Add Literal and WREG
addlw k
ANDLW k
And Literal with WREG
andlw k
IORLW k
Inclusive OR Literal with WREG
iorlw k
MOVLW k
Move Literal to WREG
movlw k
MULLW k
Multiply Literal with WREG
mullw k
SUBLW k
Substract WREG from Literal
sublw k
XORLW k
Exclusive OR Literal with WREG
xorlw k
Tableau 6.9 – Opérations statiques avec WREG pour pic18
6.6.5
Instructions identiques à celles de l’assembleur
Ces instructions sont listées dans le tableau 6.10.
6.6.6
Instruction lfsr
En assembleur comme en Piccolo, l’instruction lfsr f, k comporte deux arguments :
– f : le numéro du registre destination : une expression statique dont l’évaluation
donne le résultat 0, 1 ou 2 ;
– k : la valeur à charger dans le registre, une expression statique dont l’évaluation
donne un résultat compris entre 0 et 4095.
La forme générale des expressions statiques est donnée à la section 9.2 page 109.
Une expression statique est évaluée à la compilation. Le compilateur effectue tous les
calculs avec des nombres entiers 32 bits signés.
6.6. Les instructions simples
75
Assembleur
Description
Écriture en Piccolo
CLRWDT
Clear Watchdog Timer
clrwdt
DAW
Decimal Adjust WREG
daw
NOP
No Operation
nop
POP
Pop Top of Return Stack
pop
PUSH
Push Top of Return Stack
push
RESET
Software Device Reset
reset
SLEEP
Go into Standby Mode
sleep
TBLRD*
Table Read
TBLRD*+
Table Read with Post-Increment
tblrd *
tblrd *+
TBLRD*-
Table Read with Post-Decrement
TBLRD+*
Table Read with Pre-Increment
TBLWT*
Table Write
TBLWT*+
TBLWT*-
Table Write with Post-Decrement
TBLWT+*
Table Write with Pre-Increment
Table Write with Post-Increment
tblrd *tblrd +*
tblwt *
tblwt *+
tblwt *tblwt +*
Tableau 6.10 – Instructions pic18 identiques en assembleur et en Piccolo
6.6.7
Instruction movff
En assembleur comme en Piccolo, l’instruction movff fs, fd comporte deux arguments :
– fs : le registre source (section 8.5 page 104) ;
– fd : le registre destination (section 8.5 page 104).
6.6.8
Appeler une routine régulière
Une routine régulière est une routine déclarée sans le qualificatif noreturn. L’appel s’effectue au moyen d’une des trois instructions rcall, CALL ou JSR.
Ces instructions se distinguent par leurs propriétés suivantes :
– rcall correspond à l’instruction assembleur RCALL : elle occupe deux octets,
mais sa compilation entraîne une erreur si le déplacement est trop important
pour être codé ;
– call correspond à l’instruction assembleur CALL : elle peut coder tout appel,
mais occupe quatre octets ;
– jsr est un ajout propre à Piccolo : cette instruction est par défaut codée par
un RCALL, mais, si le déplacement est trop important, elle est silencieusement
codée par un CALL.
Syntaxiquement, il faut simplement nommer la routine appelée après le nom de
l’instruction d’appel :
rcall nom_routine
ou
76
C HAPITRE 6. Programmes pour pic18
call nom_routine
ou
jsr nom_routine
6.6.9
Appeler une routine sans retour
Appeler une routine sans retour (c’est-à-dire déclarée avec le qualificatif noreturn)
peut s’effectuer :
– soit inconditionnellement par une instruction bra, goto ou jump ;
– soit conditionnellement par une instruction BCC (nom collectif pour les huit
instructions de saut conditionnels : bc, bnc, bn, bnn, bov, bnov, bz ou bnz) ;
– soit conditionnellement par une instruction jump cc (où cc est un nom collectif
pour les huit conditions : c, nc, n, nn, ov, nov, z ou nz).
bra correspond à l’instruction assembleur BRA : elle occupe deux octets, mais sa compilation entraîne une erreur si le déplacement est trop important pour être codé.
L’appel s’écrit :
bra nom_routine
goto correspond à l’instruction assembleur GOTO : elle peut coder tout branchement
inconditionnel, mais occupe quatre octets. L’appel s’écrit :
goto nom_routine
jump est un ajout propre à Piccolo. Dans sa forme incondionnelle, il engendre par
défaut une instruction BRA, ou, si le déplacement est trop important, une instruction
GOTO. L’appel s’écrit :
jump nom_routine
BCC correspond à l’une des huit instructions assembleur citées ci dessus : elle occupe deux octets, mais sa compilation entraîne une erreur si le déplacement est trop
important pour être codé. L’appel s’écrit par exemple :
bz nom_routine
La forme conditionnelle de l’instruction jump nomme une des huit conditions cc (voir
ci-dessus). L’appel s’écrit par exemple :
jump z nom_routine
Le code engendré par une instruction jump cc peut occuper deux, quatre ou six
octets en fonction de la distance entre l’instruction d’appel et la routine (tableau 6.11).
Piccolo choisit toujours le codage le plus court.
6.7. Section include
77
Code engendré
Instruction Piccolo
2 octets
4 octets
6 octets
jump c routine
BC routine
BNC $ + 4 bra
routine
BNC $ + 6
goto routine
jump nc routine
BNC routine
BC $ + 4 bra
routine
BC $ + 6
goto routine
jump z routine
BZ routine
BNZ $ + 4 bra
routine
BNZ $ + 6
goto routine
jump nz routine
BNZ routine
BZ $ + 4 bra
routine
BZ $ + 6
goto routine
jump n routine
BN routine
BNN $ + 4 bra
routine
BNN $ + 6
goto routine
jump nn routine
BNN routine
BN $ + 4 bra
routine
BN $ + 6
goto routine
jump ov routine
BOV routine
BNOV $ + 4
bra routine
BNOV $ + 6
goto routine
jump nov routine
BNOV routine
BOV $ + 4
bra routine
BOV $ + 6
goto routine
Tableau 6.11 – Codages possibles de l’instruction jump cc
6.7
Section include
Une section include permet d’inclure un fichier contenant lui-même des sections
telles que définies dans le tableau 6.1 page 64. Son format est le suivant :
include "chemin"
chemin est le chemin vers le fichier inclus, et est :
– soit un chemin absolu (il commence par « / ») ;
– soit un chemin relatif par rapport au fichier source.
6.8
Section data
La section data permet de définir des tableaux de données qui seront placés en
Flash. Son format est le suivant :
data nom {element0, element1, ... }
nom est le nom donné au tableau. element0, element1, . . . , sont des expressions
statiques pouvant s’écrire sur 16 bits. L’octet de poids faible de chaque valeur apparaît
à une adresse paire, celui de poids fort en adresse impaire.
Pour accéder au tableau :
– nom est utilisé en argument de l’instruction ldataptr (section 6.9.2 page 79)
pour charger les registres TBLPTRU, TBLPTRH et TBLPTRL pour qu’ils désignent
le début du tableau ;
78
C HAPITRE 6. Programmes pour pic18
– la constante nom_BYTE_COUNT est définie à la valeur du nombre d’octets du
tableau : elle vaut donc le double du nombre d’éléments du tableau.
Exemple. Si on déclare :
data monTableau {0x1234, 0x5678}
Ceci engendre la séquence assembleur :
data_monTableau:
WORD 0x1234
WORD 0x5678
Et la constante monTableau_BYTE_COUNT est définit et vaut 4.
6.9
Instructions pic18
Ces instructions n’existent en Piccolo que pour les pic18. Elles sont classées parmi
les instructions dites structurées car elles ne peuvent pas apparaître comme instruction associée à une instruction conditionnelle simple (section 7.2 page 82).
6.9.1
Instruction ltblptr
Cette instruction permet de charger les registres TBLPTRU, TBLPTRH et TBLPTRL
à partir d’une expression statique qui représente une adresse en Flash.
Son format est :
ltblptr adresse-en-flash
Le code engendré pour l’instruction est le suivant :
MOVLW
MOVWF
MOVLW
MOVWF
MOVLW
MOVWF
(adresse-en-flash >> 16) & 0xFF
TBLPTRU
(adresse-en-flash >> 8) & 0xFF
TBLPTRH
adresse-en-flash & 0xFF
TBLPTRL
Le registre WREG est donc modifié par cette opération.
Optimisation. Si une valeur immédiate est 0, l’opération CLRF est utilisée ; si une
valeur immédiate est 0xFF, l’opération SETF est utilisée. Par exemple,
ltblptr 0x3FFF
Engendre le code :
CLRF TBLPTRU
MOVLW 0x3F
MOVWF TBLPTRH
SETF TBLPTRL
6.9. Instructions pic18
79
Exemple d’utilisation. Associée à la constante ROM_SIZE, elle permet d’écrire une
routine générique, valable pour tous les types de pic18, qui renvoie dans WREG, la
somme des octets de la flash (section 9.1.1 page 108).
6.9.2
Instruction ldataptr
Cette instruction permet de charger les registres TBLPTRU, TBLPTRH et TBLPTRL
qui représente l’adresse d’un élément de tableau en flash, défini par une section data
(section 6.8 page 77).
Elle peut prendre deux formes.
Première forme.
ldataptr nom_section_data
Cette instruction charge les registres TBLPTRU, TBLPTRH et TBLPTRL pour qu’ils
désignent l’octet de poids faible premier élément du tableau défini par la section data
nom_section_data.
Le code engendré est le suivant :
MOVLW
MOVWF
MOVLW
MOVWF
MOVLW
MOVWF
(nom_section_data >> 16) & 0xFF
TBLPTRU
(nom_section_data >> 8) & 0xFF
TBLPTRH
nom_section_data & 0xFF
TBLPTRL
Cette séquence n’est jamais optimisée, et WREG est toujours modifié.
Seconde forme.
ldataptr nom_section_data [indice_element]
Dans cette seconde forme, l’instruction charge les registres TBLPTRU, TBLPTRH et
TBLPTRL pour qu’ils désignent l’octet de poids faible de l’élément indice_element
du tableau défini par la section data nom_section_data. Si indice_element est
nul, elle est équivalent à la première forme.
Le code engendré est le suivant :
MOVLW
MOVWF
MOVLW
MOVWF
MOVLW
MOVWF
((nom_section_data + 2 * indice_element) >> 16) & 0xFF
TBLPTRU
((nom_section_data + 2 * indice_element) >> 8) & 0xFF
TBLPTRH
(nom_section_data + 2 * indice_element) & 0xFF
TBLPTRL
Cette séquence n’est jamais optimisée, et WREG est toujours modifié.
Chapitre 7
Instructions structurées
En Piccolo, il existe deux types d’instructions :
– les instructions simples ;
– les instructions composées.
Les instructions simples sont propres à type de micro-contrôleur (baseline, midrange et pic18) : elles sont donc présentées dans des sections disctinctes :
– pic18 : section 6.6 page 71.
Les instructions structurées sont en grande partie communes aux baseline, midrange et pic18 : aussi elles sont présentées dans ce chapitre commun.
Piccolo définit les instructions structurées suivantes :
–
–
–
–
–
–
–
–
–
–
–
7.1
l’instruction mnop (section 7.1 page 81) ;
l’instruction conditionnelle simple (section 7.2 page 82) ;
l’instruction conditionnelle structurée (section 7.3 page 83) ;
l’instruction répétitive (section 7.5 page 84) ;
l’instruction de répétition infinie (section 7.4 page 84) ;
l’instruction computed retlw (section 7.7.1 page 87) ;
l’instruction computed bra (section 7.7.2 page 90) ;
l’instruction computed goto (section 7.7.3 page 91) ;
l’instruction banksel (section 7.8.2 page 93) ;
l’instruction nobank (section 7.8.3 page 94) ;
l’instruction banksave (section 7.8.4 page 94).
Instruction mnop
Cette instruction n’existe pas en assembleur. En Piccolo, mnop k engendre une
séquence de k instructions NOP.
k est une expression statique. La forme générale des expressions statiques est
donnée à la section 9.2 page 109. Une expression statique est évaluée à la compilation.
Le compilateur effectue tous les calculs avec des nombres entiers 32 bits signés.
82
C HAPITRE 7. Instructions structurées
7.2
Instruction conditionnelle simple
Ces instructions permettent d’exploiter directement les instructions assembleur
qui ignorent conditionnellement l’instruction qui les suit, c’est à dire :
– pour les baseline et les mid-range : DECFSZ, INCFSZ, BTFSC et BTFSS ;
– pour les pic18 : CPFSEQ, CPFSGT, CPFSLT, DECFSZ, DCFSNZ, INCFSZ, INFSNZ,
TSTFSZ, BTFSC et BTFSS.
La syntaxe de l’instruction conditionnelle simple est la suivante :
i f condition_simple : instruction_simple
Noter que l’instruction exécutée conditionnellement ne peut être qu’une instruction simple.
Noter aussi la différence suivante : l’instruction assembleur indique la condition
de saut de l’instruction suivante, tandis que le conditionnelle simple nomme sa condition d’exécution, c’est à dire son complémentaire.
Pour les pic18, seules trois comparaisons d’un registre avec W existent pour l’instruction conditionnelle simple : elles correspondent aux instructions assembleur CPFSEQ,
CPFSGT et CPFSLT. L’instruction conditionnelle structurée implémente les six comparaisons.
Pour le test individuel d’un bit, la notation registre.bit est utilisée : celle-ci est
présentée §.
Le tableau 7.1 donne la liste de toutes les instructions conditionnelles simples
pour les baseline et mid-range, et le tableau 7.2 pour celles des pic18.
Instruction
Code engendré
if reg.bit : instruction
BTFSC reg, bit
instruction
if ! reg.bit : instruction
BTFSS reg, bit
instruction
if decf reg nz : instruction
DECFSZ reg
instruction
if decf reg, W nz : instruction
DECFSZ reg, W
instruction
if incf reg nz : instruction
INCFSZ reg
instruction
if incf reg, W nz : instruction
INCFSZ reg, W
instruction
Tableau 7.1 – Instructions conditionnelles simples pour baseline et mid-range
7.3. Instruction conditionnelle structurée
83
Instruction
Code engendré
if reg != W : instruction
CPFSEQ reg
instruction
if reg >= W : instruction
CPFSLT reg
instruction
if reg <= W : instruction
CPFSGT reg
instruction
if reg nz : instruction
TSTFSZ reg
instruction
if reg.bit : instruction
BTFSC reg, bit
instruction
if ! reg.bit : instruction
BTFSS reg, bit
instruction
if decf reg z : instruction
DCFSNZ reg
instruction
if decf reg, W z : instruction
DCFSNZ reg, W
instruction
if decf reg nz : instruction
DECFSZ reg
instruction
if decf reg, W nz : instruction
DECFSZ reg, W
instruction
if incf reg z : instruction
INFSNZ reg
instruction
if incf reg, W z : instruction
INFSNZ reg, W
instruction
if incf reg nz : instruction
INCFSZ reg
instruction
if incf reg, W nz : instruction
INCFSZ reg, W
instruction
Tableau 7.2 – Instructions conditionnelles simples pour pic18
7.3
Instruction conditionnelle structurée
L’instruction conditionnelle est une construction classique des langages de programmation et accepte zéro, un ou plusieurs blocs elsif, et zéro ou un bloc else ;
voici différents exemples, cca, ccb et ccc étant des expressions conditionnelles (voir
section 7.6 page 85).
Pas de branche elsif ni else :
i f (cca)
instructions 1
end
Pas de branche elsif et une branche else :
i f (cca)
84
C HAPITRE 7. Instructions structurées
instructions 1
else
instructions 3
end
Une branche elsif et une branche else :
i f (cca)
instructions 1
e l s i f (ccb)
instructions 2
else
instructions 3
end
Plusieurs branches elsif, pas de branche else :
i f (cca)
instructions 1
e l s i f (ccb)
instructions 2
e l s i f (ccc)
instructions 3
end
7.4
Instruction de répétition infinie
Cette instruction exprime la répétition infinie des instructions qu’elle contient.
forever
liste d’instructions simples ou structurees
end
Implémentation : la répétition infinie est simplement réalisée par une instruction
de jump (pour les pic18) ou goto (pour les baseline et mid-range), placée à la fin de la
séquence d’instructions, qui renvoie l’exécution au début de la séquence.
7.5
Instruction répétitive
L’instruction répétitive a une structure moins répandue car elle accepte un ou plusieurs blocs while ; voici plusieurs exemples, cca, ccb et ccc étant des expressions
conditionnelles (voir section 7.6 page 85).
Voici un premier exemple, qui a une exécution équivalente à l’instruction while
(cca) do { instructions } du langage C :
do
while (cca)
instructions
end
7.6. Forme générale des conditions
85
La construction suivante a une exécution équivalente à l’instruction do { instructions
} while (cca) ; du langage C :
do
instructions
while (cca)
end
Enfin, voici un exemple avec deux branches while :
do
instructions 1
while (cca)
instructions 2
while (ccb)
instructions 3
end
Ce type d’instruction n’est pas classique, c’est pourquoi son organigramme est
donné.
7.6
Forme générale des conditions
Les instructions conditionnelles structurées (section 7.3 page 83) et les instructions répétitives (section 7.5 page 84) acceptent les mêmes formes de conditions, qui
sont des expressions :
– dont les conditions élémentaires sont données à la section 7.6.2 page 86 (pic18) ;
– dont les opérateurs sont la négation « ! », le et logique « & », et le ou logique « |
»;
– utilisant les parenthèses « ( » et « ) » pour forcer le groupement.
La négation « ! ». C’est un opérateur unaire préfixé : si cc est une condition, !cc
exprime la condition complémentaire.
Le et logique « & ». C’est un opérateur infixe : si cc1 et cc2 sont deux conditions,
cc1 & cc2 exprime le et logique de ces deux conditions. Son fonctionnement est du
type court-circuit : cc1 est toujours évaluée la première et, si elle est évaluée fausse,
cc2 n’est pas évaluée. Ce point est important si l’évaluation de la seconde condition a
un effet de bord (c’est le cas des conditions nommant decf ou incf).
Le ou logique « | ». De même, c’est un opérateur infixe : cc1 | cc2 exprime le ou
logique de ces deux conditions. Son fonctionnement est aussi du type court-circuit :
cc1 est toujours évaluée la première et, si elle est évaluée vraie, cc2 n’est pas évaluée.
Priorité des opérateurs. La négation « ! » est l’opérateur le plus prioritaire, le ou
logique « | » le moins prioritaire. Par exemple, !cc1&cc2 est équivalent à (!cc1)&cc2
et cc1|cc2&cc3 est équivalent à cc1|(cc2&cc3).
86
C HAPITRE 7. Instructions structurées
Associativité des opérateurs. Le et logique « & » et le ou logique « | » sont associatifs
à gauche. Par exemple, cc1&cc2&cc3 est équivalent à (cc1&cc2)&cc3.
7.6.1
Conditions élémentaires pour baseline et mid-range
Les conditions élémentaires exploitent les quatre instructions conditionnelles des
baseline et des mid-range : BTFSC, BTFSS, DECFSZ et INCFSZ. Les conditions exprimables en Piccolo peuvent être classées en deux groupes :
– les conditions basées sur le test d’un bit d’un registre (tableau 7.3) ;
– les conditions basées sur la décrémentation ou l’incrémentation d’un registre
(tableau 7.4).
Les conditions basées sur le test d’un bit d’un registre sont listées dans le tableau
7.3.
Condition
Signification
Code engendré
reg.bit
reg.bit 6= 0 ?
BTFSS reg,bit
GOTO label
! reg.bit
reg.bit = 0 ?
BTFSC reg,bit
GOTO label
Tableau 7.3 – Conditions pour baseline et mid-range basées sur le test d’un bit d’un
registre
Le tableau 7.4 liste les conditions basées sur les instructions DECFSZ et INCFSZ.
La non symétrie du jeu d’instructions provoquent des codes engendrés de tailles différentes selon les conditions exprimées.
7.6.2
Conditions élémentaires pour pic18
Les conditions élémentaires exploitent les différentes instructions conditionnelles
des pic18. L’abondance de ces instructions engendre une liste très fournie :
– les conditions basées sur les instructions de saut conditionnelles (tableau 7.5) ;
– les conditions basées sur la comparaison d’un registre avec WREG (tableau 7.6) ;
– les conditions basées sur le test d’un registre ou de l’un de ses bits (tableau 7.7) ;
– les conditions basées sur la décrémentation ou l’incrémentation d’un registre
(tableau 7.8).
Les instructions de saut conditionnel permettent d’exploiter directement la valeur des bits du registre STATUS. Les conditions correspondantes sont listées dans le
tableau 7.5. Leur compilation engendre un code de 2, 4 ou 6 octets, selon l’amplitude
du branchement. Piccolo engendre toujours le code le plus petit possible.
La comparaison du contenu d’un registre avec la valeur de WREG est effectuée par
les instructions assembleur CPFSEQ, CPFSLT et CPFSGT. En Piccolo, les 6 comparaisons
sont possibles, et la non symétrie des instructions assembleur provoque la génération
7.7. Instructions computed
87
Condition
Signification
Code engendré
decf reg z
reg := reg - 1
DECFSZ reg
reg = 0 ?
GOTO $ + 2
GOTO label
decf reg, W z
WREG := reg - 1
DECFSZ reg, W
WREG = 0 ?
GOTO $ + 2
GOTO label
decf reg nz
reg := reg - 1
DECFSZ reg
reg 6= 0 ?
GOTO label
decf reg, W nz
WREG := reg - 1
DECFSZ reg, W
WREG 6= 0 ?
GOTO label
incf reg z
reg := reg + 1
INCFSZ reg
reg = 0 ?
GOTO $ + 2
GOTO label
incf reg, W z
WREG := reg + 1
INCFSZ reg, W
WREG = 0 ?
GOTO $ + 2
GOTO label
incf reg nz
incf reg, W nz
reg := reg + 1
INCFSZ reg
reg 6= 0 ?
BRA label
WREG := reg + 1
INCFSZ reg, W
WREG 6= 0 ?
GOTO label
Tableau 7.4 – Conditions pour baseline et mid-range basées sur la décrémentation et
l’incrémentation d’un registre
d’un code pouvant atteindre 8 octets. Piccolo engendre toujours le code le plus petit
possible. Le tableau 7.6 liste ces conditions.
Les conditions basées sur le test d’un registre sont listées dans le tableau 7.7. Remarquer que les conditions listées ne sont pas toutes élémentaires. Ainsi, la condition !registre nz est équivalente à registre z, et !registre z équivalente à
registre nz. Remarquer aussi que l’absence d’instruction complémentaire à TSTFSZ
entraîne un code plus long pour la condition registre z.
Enfin, le tableau 7.8 liste les conditions basées sur les instructions DECFSZ, DECFSZ,
INCFSZ et INFSNZ. Là encore, elles ne sont pas toutes élementaires : par exemple, decf
registre nz est équivalente à ! decf registre z.
7.7
7.7.1
Instructions computed
Instruction computed retlw
Cette instruction n’est valide que pour les pic18.
88
C HAPITRE 7. Instructions structurées
Condition
Signification
Code engendré
2 octets
4 octets
6 octets
c
Le bit C de STATUS estil non nul ?
BC label
BNC $ + 4
BRA label
BNC $ + 6
GOTO label
nc
Le bit C de STATUS estil nul ?
BNC label
BC $ + 4
BRA label
BC $ + 6 GOTO
label
z
Le bit Z de STATUS estil non nul ?
BZ label
BNZ $ + 4
BRA label
BNZ $ + 6
GOTO label
nz
Le bit Z de STATUS estil nul ?
BNZ label
BZ $ + 4
BRA label
BZ $ + 6 GOTO
label
n
Le bit N de STATUS estil non nul ?
BN label
BNN $ + 4
BRA label
BNN $ + 6
GOTO label
nn
Le bit N de STATUS estil nul ?
BNN label
BN $ + 4
BRA label
BN $ + 6 GOTO
label
ov
Le bit OV de STATUS
est-il non nul ?
BOV label
BNOV $ + 4
BRA label
BNOV $ + 6
GOTO label
nov
Le bit OV de STATUS
est-il nul ?
BNOV label
BOV $ + 4
BRA label
BOV $ + 6
GOTO label
Tableau 7.5 – Conditions pour pic18 basées sur les instructions de saut conditionnel
Condition
reg != W
reg >= W
Code engendré
4 octets
6 octets
CPFSEQ reg
CPFSEQ reg
BRA label
GOTO label
CPFSLT reg
CPFSLT reg
BRA label
GOTO label
reg <= W
CPFSGT reg
CPFSGT reg
reg <= W
BRA label
GOTO label
reg == W
reg > W
reg > W
8 octets
CPFSEQ reg
CPFSEQ reg
BRA $ + 4
BRA $ + 6
BRA label
GOTO label
CPFSGT reg
CPFSGT reg
BRA $ + 4
BRA $ + 6
BRA label
GOTO label
CPFSLT reg
CPFSLT reg
BRA $ + 4
BRA $ + 6
BRA label
GOTO label
Tableau 7.6 – Conditions pour pic18 basées sur les comparaisons avec WREG
Description. L’instruction computed retlw est la suivante :
computed [n] retlw cst0, cst1, cst2, ...
7.7. Instructions computed
Condition
Signification
reg nz
reg 6= 0 ?
reg z
reg = 0 ?
reg.bit
reg.bit 6= 0 ?
! reg.bit
reg.bit = 0 ?
89
Code engendré
Court
Long
TSTFSZ reg
TSTFSZ reg
BRA label
GOTO label
TSTFSZ reg
TSTFSZ reg
BRA $ + 4
BRA $ + 6
BRA label
GOTO label
BTFSS reg,bit
BTFSS reg,bit
BRA label
GOTO label
BTFSC reg,bit
BTFSC reg,bit
BRA label
GOTO label
Tableau 7.7 – Conditions pour pic18 basées sur le test d’un registre
Condition
decf reg z
Signification
Code engendré
Court
Long
reg := reg - 1
DCFSNZ reg
DCFSNZ reg
reg = 0 ?
BRA label
GOTO label
decf reg, W z
WREG := reg - 1
DCFSNZ reg, W
DCFSNZ reg, W
WREG = 0 ?
BRA label
GOTO label
decf reg nz
reg := reg - 1
DECFSZ reg
DECFSZ reg
reg 6= 0 ?
BRA label
GOTO label
WREG := reg - 1
DECFSZ reg, W
DECFSZ reg, W
WREG 6= 0 ?
BRA label
GOTO label
reg := reg + 1
INFSNZ reg
INFSNZ reg
reg = 0 ?
BRA label
GOTO label
incf reg, W z
WREG := reg + 1
INFSNZ reg, W
INFSNZ reg, W
WREG = 0 ?
BRA label
GOTO label
incf reg nz
reg := reg + 1
INCFSZ reg
INCFSZ reg
reg 6= 0 ?
BRA label
GOTO label
WREG := reg + 1
INCFSZ reg, W
INCFSZ reg, W
WREG 6= 0 ?
BRA label
GOTO label
decf reg, W nz
incf reg z
incf reg, W nz
Tableau 7.8 – Conditions pour pic18 basées sur la décrémentation et l’incrémentation
d’un registre
n est une expression statique et doit être égale au nombre des constantes citées
après retlw. Ce codage redondant permet de s’assurer que la liste des constantes
présente une longueur correcte. Chaque cst i doit avoir une valeur comprise entre
-128 et 255.
L’implémentation limite à 128 la valeur de n.
90
C HAPITRE 7. Instructions structurées
Fonctionnement. Cette instruction remplace la valeur de WREG par la constante qui
lui correspond : cst0 pour 0, cst1 pour 1, . . . , cstn−1 pour n − 1.
Limitation. À l’exécution, il est nécessaire que la valeur non signée de WREG soit
strictement inférieur au nombre de constantes. Le code engendré par Piccolo
n’effectue aucune vérification. Il est donc probable que ne pas respecter cette
règle entraîne un plantage du programme.
Exemple. Voici comment un chiffre hexadécimal peut être transformé en le caractère
ASCII le représentant :
routine CONVERSION_HEX_ASCII {
andlw
0x0F
computed [16] retlw
’0’, ’1’, ’2’, ’3’, ’4’, ’5’, ’6’, ’7’,
’8’, ’9’, ’A’, ’B’, ’C’, ’D’, ’E’, ’F’
}
Le code assembleur engendré est le suivant :
CONVERSION_HEX_ASCII:
ANDLW
0x0F
RCALL _computed_goto_2
RETLW ’0’
RETLW ’1’
RETLW ’2’
RETLW ’3’
RETLW ’4’
RETLW ’5’
RETLW ’6’
RETLW ’7’
RETLW ’8’
RETLW ’9’
RETLW ’A’
RETLW ’B’
RETLW ’C’
RETLW ’D’
RETLW ’E’
RETLW ’F’
Si la distance d’appel est trop longue pour une instruction RCALL, une instruction
CALL est utilisée.
Ce code fait appel à la routine _computed_goto_2 qui est ajoutée automatiquement si nécessaire, à la fin du code (section 7.7.4 page 92).
7.7.2
Instruction computed bra
Cette instruction n’est valide que pour les pic18.
Description. Celle-ci est :
7.7. Instructions computed
91
computed [n] bra routine0, routine1, routine2, ...
n est une expression statique doit être égal au nombre de routines citées après
bra. Ce codage redondant permet de s’assurer que la liste des routines présente une
longueur correcte. Les routines citées doivent toutes être sans retour (c’est-à-dire déclarées avec le qualificatif noreturn), et pouvoir être atteintes par une instruction
bra (si ce n’est pas possible, utiliser le computed goto décrite à la section 7.7.3 page
91).
L’implémentation limite à 128 la valeur de n.
Fonctionnement. Cette instruction agit comme une instruction de branchement en
fonction de la valeur de WREG : branchement vers routine0 pour 0, vers routine1
pour 1, ..., vers routinen−1 pour n − 1.
Limitation. À l’exécution, il est nécessaire que la valeur non signée de WREG soit strictement inférieur au nombre de routines. Le code engendré par Piccolo n’effectue
aucune vérification. Il est donc probable que ne pas respecter cette règle entraîne
un plantage du programme.
Ce code fait appel à la routine _computed_goto_2 qui est ajoutée automatiquement si nécessaire, à la fin du code (section 7.7.4 page 92).
7.7.3
Instruction computed goto
Cette instruction n’est valide que pour les pic18.
Description. Celle-ci est :
computed [n] goto routine0, routine1, routine2, ...
n est une expression statique doit être égal au nombre de routines citées après
goto. Ce codage redondant permet de s’assurer que la liste des routines présente
une longueur correcte. Les routines citées doivent toutes être sans retour (c’est-à-dire
déclarées avec le qualificatif noreturn).
L’implémentation limite à 64 la valeur de n.
Fonctionnement. Cette instruction agit comme une instruction de branchement en
fonction de la valeur de WREG : branchement vers routine0 pour 0, vers routine1
pour 1, ..., vers routinen−1 pour n − 1.
Limitation. À l’exécution, il est nécessaire que la valeur non signée de WREG soit strictement inférieur au nombre de routines. Le code engendré par Piccolo n’effectue
aucune vérification. Il est donc probable que ne pas respecter cette règle entraîne
un plantage du programme.
Ce code fait appel à la routine _computed_goto_4 qui est ajoutée automatiquement si nécessaire, à la fin du code (section 7.7.4 page 92).
92
C HAPITRE 7. Instructions structurées
7.7.4
Implémentation de _computed_goto_x
Les deux routines _computed_goto_2 et _computed_goto_4 sont automatiquement ajoutées à la fin du code, si nécessaire. Voici l’implémentation de _computed_goto_2 :
_computed_goto_2:
ADDWF WREG, W
ADDWFC TOSL, F
MOVLW 0
ADDWFC TOSH, F
ADDWFC TOSU, F
RETURN
Et celle de _computed_goto_4 :
_computed_goto_4:
ADDWF WREG, W
ADDWF WREG, W
ADDWFC TOSL, F
MOVLW 0
ADDWFC TOSH, F
ADDWFC TOSU, F
RETURN
À noter que Piccolo effectue un partage de code lorsque ces routines sont toutes
les deux nécessaires :
_computed_goto_4:
ADDWF WREG, W
_computed_goto_2:
ADDWF WREG, W
ADDWFC TOSL, F
MOVLW 0
ADDWFC TOSH, F
ADDWFC TOSU, F
RETURN
7.8
Instructions de gestion des bancs mémoire
Les baseline ne possèdent pas de mécanisme pour fixer l’adressage d’un banc :
l’accès direct est toujours dans le banc 0 (les adresses comprises entre 0x0 et 0x1F).
Les autres bancs doivent être accédés via l’adressage indirect.
Les instructions de gestion de bancs mémoire décrites dans cette section
ne sont valides que pour les mid-range et les pic18.
Pour un mid-range, le banc sélectionné est désigné par la valeur des bits RP du
registre STATUS. Certains micro-contrôleurs ont jusqu’à 8 bancs, il y a donc 3 bits RP.
En assembleur, le changement de banc est réalisé par une séquence d’instructions
BCF et BSF sur ces bits (trois instructions dans le pire des cas).
7.8. Instructions de gestion des bancs mémoire
93
Pour un pic18, le banc sélectionné est désigné par la valeur du registre BSR. En
assembleur, le changement de banc est réalisé par une instruction MOVLB.
Le but des instructions de gestion des bancs mémoire de Piccolo est multiple :
– proposer des instructions génériques pour les mid-range et les pic18 ;
– effectuer à la compilation la vérification de la bonne gestion des bancs.
Le second point est primordial lors que l’on ré-utilise un code écrit auparavant
pour un autre micro-contrôleur : il peut y a entre les différents micro-contrôleurs de
la même famille des différences de localisation de bancs pour des registres spéciaux
de même nom ; Piccolo repérera toutes les erreurs d’adressage de banc.
Le second point a aussi des implications importantes dans les attributs associés
aux routines : ceci est discuté dans les sections suivantes.
7.8.1
Instruction et banc courant
Piccolo définit précisément pour chaque instruction son comportement vis à vis de
la sélection courante de banc. Cela permet au compilateur de signaler toute erreur.
En entrée. Une instruction peut :
– accepter n’importe quelle sélection de banc ;
– exiger qu’un certain banc soit sélectionné.
Par exemple, considérons l’instruction Piccolo bsf STATUS.C. Pour un mid-range,
le registre STATUS est présent dans tous les bancs, cette instruction est donc indifférente vis à vis de la sélection de banc. Pour un pic18, le registre STATUS est situé dans
l’accessbank, cette instruction est aussi indifférente vis à vis de la sélection de banc.
Considérons maintenant l’instruction Piccolo clrf var. Pour un mid-range, si
la variable var est déclarée dans gprnobank qui apparaît en miroir dans tous les
bancs, cette instruction est donc indifférente vis à vis de la sélection de banc. Sinon,
l’instruction exige que le banc correspondant soit sélectionné. Pour un pic18, si la
variable var est situés dans l’accessbank, cette instruction est aussi indifférente vis
à vis de la sélection de banc. Sinon, l’instruction exige que le banc correspondant soit
sélectionné.
En sortie. L’exécution d’une instruction peut :
– préserver la sélection courante de banc ;
– sélectionner un banc particulier ;
– rendre inconnue la sélection courante de banc.
7.8.2
Instruction banksel
banksel b
Cette instruction sélectionne le banc b. Pour les mid-range, le compilateur engendre une à trois instructions BCF ou BSF des bits RP du registre STATUS. Pour les
pic18, le compilateur engendre une instruction MOVLB.
94
C HAPITRE 7. Instructions structurées
Le compilateur Piccolo garde trace de cette instruction, et permet d’assurer que
l’accès aux registres du banc b est possible pour les instructions suivantes.
7.8.3
Instruction nobank
nobank
Cette instruction signifie qu’à partir de ce point, on considère que la sélection courante de banc est inconnue. Elle n’engendre aucun code, c’est juste une information
destinée au compilateur.
7.8.4
Instruction banksave
banksave var
liste_instructions
end
Pour les mid-range, cette instruction sauve le registre STATUS dans la variable
var avant l’exécution de liste_instructions, et restitue le registre STATUS quand
le end est atteint. La variable var doit être accessible à partir de n’importe quel banc,
c’est-à-dire déclaré dans la zone commune grpnobnk. L’accumulateur W est utilisé à
chaque fois, aussi bien pour la sauvegarde que pour la restitution.
Pour les pic18, cette instruction sauve le registre BSR dans la variable var avant
l’exécution de liste_instructions, et restitue le registre BSR quand le end est atteint. Comme les deux transferts sont réalisés au moyen de l’instruction MOVFF, la
variable var peut être déclarée dans n’importe quel banc.
7.8.5
Déclaration de routine et gestion des bancs
Le comportement des routines vis à vis de la sélection courante de banc doit être
déclaré dans son en-tête. Il y a deux qualificatifs possibles pour une routine sans
retour (tableau 7.9), et cinq pour une routine régulière (tableau 7.10).
Qualificatif
Sélection de banc lors de l’appel
aucun
?
bank:requires be
Banc be
Tableau 7.9 – Routine sans retour et qualificatifs associés aux bancs
7.8.6
Déclaration de routine sans qualificatif de banc
Le contrat est le suivant :
– pour l’appelant, aucune contrainte sur la sélection de banc au moment de l’appel, aucune garantie sur la sélection de banc après l’exécution de la routine ;
– pour la routine appelée, cela signifie que la sélection de banc est inconnue au
début de la routine : seul un registre accessible à partir de n’importe quel banc
pourra être accédé directement ; pour les autres, il faudra utiliser une instruction banksel pour fixer la sélection d’un banc ; la valeur de sélection de banc à
la fin de l’exécution de la routine est quelconque.
7.8. Instructions de gestion des bancs mémoire
Qualificatif
95
Sélection de banc
Lors de l’appel
Lors du retour
aucun
?
?
bank:preserved
?
Sélection préservée
bank:requires be
Banc be
?
bank:ensures bs
?
Banc bs
bank:requires be ensures bs
Banc be
Banc bs
Tableau 7.10 – Routine régulière et qualificatifs associés aux bancs
Exemple 1. varAnyBank est accessible quel soit la sélection de banc, varBank1 est
déclaré dans le banc 1, varBank2 dans le banc 2 :
routine maRoutine {
c l r f varAnyBank
banksel 1
c l r f varBank1
banksel 2
c l r f varBank2
}
Cette routine est compilée sans erreur.
Exemple 2. varAnyBank est accessible quel soit la sélection de banc, varBank1 est
déclaré dans le banc 1, varBank2 dans le banc 2 :
routine maRoutine {
c l r f varAnyBank
c l r f varBank1
c l r f varBank2
}
La compilation de cette routine provoque deux erreurs (utiliser l’option -d pour
obtenir des messages détaillés) :
clrf varBank1
-------------ˆ
semantic error: Accessing the ’varBank1’ needs the bank selection
set to 1, but current bank selection cannot be known
clrf varBank2
-------------ˆ
semantic error: Accessing the ’varBank2’ needs the bank selection
set to 2, but current bank selection cannot be known
96
C HAPITRE 7. Instructions structurées
Exemple 3. varAnyBank est accessible quel soit la sélection de banc, varBank1 est
déclaré dans le banc 1, varBank2 dans le banc 2 :
routine maRoutine {
c l r f varAnyBank
banksel 1
c l r f varBank1
c l r f varBank2
}
La compilation de cette routine provoque une erreur (utiliser l’option -d pour
obtenir des messages détaillés) :
clrf varBank2
-------------ˆ
semantic error: Accessing the ’varBank2’ needs the bank selection
set to 2, but but current bank selection is set to 1
7.8.7
Routine déclarée avec bank:preserved
Le contrat est le suivant :
– pour l’appelant, aucune contrainte sur la sélection de banc au moment de l’appel, garantie que l’exécution de la routine ne modifiera pas la sélection de banc ;
– pour la routine appelée, cela signifie que la sélection de banc est inconnue au
début de la routine : seul un registre accessible à partir de n’importe quel banc
pourra être accédé directement ; pour les autres, il faudra utiliser une instruction banksel pour fixer la sélection d’un banc ; la valeur de sélection de banc à
la fin de l’exécution doit être préservée.
Si utiliser une instruction banksel dans la routine est indispensable, la seule
façon de préserver la sélection de banc est d’utiliser l’instruction banksave.
Exemple 1. varAnyBank est accessible quel soit la sélection de banc, varBank1 est
déclaré dans le banc 1, varBank2 dans le banc 2 :
routine maRoutine bank:preserved {
c l r f varAnyBank
banksave save_bank
banksel 1
c l r f varBank1
banksel 2
c l r f varBank2
end
}
Cette routine est compilée sans erreur.
Exemple 2. varAnyBank est accessible quel soit la sélection de banc, varBank1 est
déclaré dans le banc 1, varBank2 dans le banc 2 :
7.8. Instructions de gestion des bancs mémoire
97
routine maRoutine bank:preserved {
c l r f varAnyBank
banksel 1
c l r f varBank1
banksel 2
c l r f varBank2
}
La compilation de cette routine provoque deux erreurs (utiliser l’option -d pour
obtenir des messages détaillés) :
banksel 1
---------ˆ
semantic error: cannot use "banksel" here: bank selection should
be preserved (use "banksave" instruction)
banksel 2
---------ˆ
semantic error: cannot use "banksel" here: bank selection should
be preserved (use "banksave" instruction)
7.8.8
Routine déclarée avec bank:requires b e
Le contrat est le suivant :
– pour l’appelant, le banc b e doit être sélectionné au moment de l’appel, aucune
garantie sur la sélection de banc après l’exécution de la routine ;
– pour la routine appelée, garantie que le banc b e banc est sélectionné au début
de la routine : ainsi les registres accessibles à partir de n’importe quel banc et
du banc be peuvent être accédés directement ; pour les autres, il faudra utiliser
une instruction banksel pour fixer la sélection ; la valeur de sélection de banc
à la fin de l’exécution de la routine est quelconque.
Par exemple (varAnyBank est accessible quel soit la sélection de banc, varBank1
est déclaré dans le banc 1, varBank2 dans le banc 2) :
routine maRoutine bank:requires 1 {
c l r f varAnyBank
c l r f varBank1
banksel 2
c l r f varBank2
}
7.8.9
Routine déclarée avec bank:ensures b s
Le contrat est le suivant :
– pour l’appelant, aucune contrainte sur la sélection de banc au moment de l’appel, garantie que le banc b s est sélectionné après l’exécution de la routine ;
98
C HAPITRE 7. Instructions structurées
– cela signifie que la sélection de banc est inconnue au début de la routine : seul
un registre accessible à partir de n’importe quel banc pourra être accédé directement ; pour les autres, il faudra utiliser une instruction banksel pour fixer
la sélection d’un banc ; le banc b s doit être sélectionné à la fin de l’exécution.
Par exemple (varAnyBank est accessible quel soit la sélection de banc, varBank1
est déclaré dans le banc 1, varBank2 dans le banc 2) :
routine maRoutine bank:ensures 1 {
c l r f varAnyBank
banksel 1
c l r f varBank1
banksel 2
c l r f varBank2
banksel 1
}
La dernière instruction banksel 1 est indipensable pour garantir que le banc 1
est sélectionné à la fin de l’exécution de la routine.
Par contre, dans le cas suivant, on n’a pas besoin de fixer la sélection de banc à la
fin de la routine :
routine maRoutine bank:ensures 2 {
c l r f varAnyBank
banksel 1
c l r f varBank1
banksel 2
c l r f varBank2
}
7.8.10
Routine déclarée avec bank:requires b e ensures b s
Le contrat est le suivant :
– pour l’appelant, le banc b e doit être sélectionné au moment de l’appel, garantie
que le banc b s est sélectionné après l’exécution de la routine ;
– cela signifie que le banc b e est sélectionné au début de la routine : seul un
registre accessible à partir de n’importe quel banc pourra être accédé directement ; pour les autres, il faudra utiliser une instruction banksel pour fixer la
sélection d’un banc ; le banc b s doit être sélectionné à la fin de l’exécution.
Par exemple (varAnyBank est accessible quel soit la sélection de banc, varBank1
est déclaré dans le banc 1, varBank2 dans le banc 2) :
routine maRoutine bank:requires 1 ensures 2 {
c l r f varAnyBank
c l r f varBank1
banksel 2
c l r f varBank2
}
7.8. Instructions de gestion des bancs mémoire
7.8.11
99
Sélection de banc et instruction conditionnelle structurée
L’instruction conditionnelle structurée est présentée à la section 7.3 page 83 et a
la forme générale suivante :
i f (cca)
instructions 1
e l s i f (ccb)
instructions 2
else
instructions 3
end
Règles :
– la sélection initiale de banc sera utilisée pour l’évaluation des conditions cca et
ccb, et sera la valeur au début de l’exécution de chaque branche ;
– toutes les branches doivent laisser la sélection de banc dans le même état : soit
avec la même valeur connue, soit avec une valeur considérée comme invalide.
L’exemple à cette page §§ illustre comment les instructions « banksel » et « nobank
» peuvent être utilisées pour vérifier cette règle.
7.8.12
Sélection de banc et instruction de répétition infinie
L’instruction de répétition infinie est présentée à la section 7.4 page 84 et a la
forme générale suivante :
forever
instructions_repetees
end
Règle :
– la liste d’instructions instructions_repetees doit préserver la sélection de
banc.
7.8.13
Sélection de banc et instruction répétitive
L’instruction de répétition infinie est présentée à la section 7.5 page 84 et a la
forme générale suivante :
do
instructions 1
while (cca)
instructions 2
while (ccb)
instructions 3
end
Règles :
100
C HAPITRE 7. Instructions structurées
– chaque liste d’instructions (instructions 1, instructions 2, instructions
3) doit préserver la sélection de banc ;
– l’évaluation des conditions cca et ccb s’effectue avec cette sélection de banc.
Chapitre 8
Définition de variable
8.1
Comment est décrite la RAM ?
Préalablement, vous devez savoir comment la mémoire RAM de votre micro-contrôleur
est décrite par Piccolo. Pour cela, utiliser l’option « -memory » (ou sa version courte «
-M= »), en nommant ensuite votre micro-contrôleur (voir section 2.2.2 page 15).
Pour chaque micro-contrôleur, veuillez vérifier l’exactitude de ces renseignements en comparant les résultats fournis par Piccolo avec le data
sheet de Microchip. La description des micro-contrôleurs est obtenue automatiquement, et je n’ai pas vérifié individuellement chaque description.
8.1.1
Micro-contrôleur baseline
Par exemple, pour le 16F57, entrez la ligne de commande piccolo -M=16F57.
Piccolo affiche alors les renseignements suivants :
16F57 device:
5 RAM banks (total 72 bytes):
bank "gpr0" from 0x10 to 0x1F (16 bytes)
bank "gpr1" from 0x30 to 0x3F (16 bytes)
bank "gpr2" from 0x50 to 0x5F (16 bytes)
bank "gpr3" from 0x70 to 0x7F (16 bytes)
bank "gprnobnk" from 0x8 to 0xF (8 bytes), mirror at 0x28, 0x48, 0x68
ROM size: 2048 instructions
No EEPROM
Pour les micro-contrôleurs baseline, l’accès direct est possible pour les registres
dont l’adresse est comprise entre 0x0 et 0x1F : pour le 16F57, ceci comprend les registres spéciaux (non décrits ici, entre 0x0 et 0x07), le banc gprnobank, et le banc
gpr0. Les trois deux autres bancs, gpr1, gpr2 et gpr3, ne sont accessibles que par
adressage indirect via FSR.
L’instruction banksel n’est donc pas valide pour les baseline.
102
8.1.2
C HAPITRE 8. Définition de variable
Micro-contrôleur mid-range
Par exemple, pour le 16F690, entrez la ligne de commande piccolo -M=16F690.
Piccolo affiche alors les renseignements suivants :
16F690 device:
4 RAM banks (total 256 bytes):
bank "gpr0" from 0x20 to 0x6F (80 bytes)
bank "gpr1" from 0xA0 to 0xEF (80 bytes)
bank "gpr2" from 0x120 to 0x16F (80 bytes)
bank "gprnobnk" from 0x70 to 0x7F (16 bytes), mirror at 0xF0, 0x170, 0x1F0
ROM size: 4096 instructions (2 pages)
EEPROM size: 256 bytes (at 0x2100)
Pour les micro-contrôleurs mid-range, l’accès aux différents bancs est régi par les
bits RP du registre STATUS : c’est le cas des trois premiers bancs gpr0 à gpr2. Le
dernier banc, gprnobnk, est toujours accessible quelque soient les bits RP du registre
STATUS car il apparaît en miroir dans tous les bancs.
En Piccolo, on ne manipule jamais directement les RP du registre STATUS : on
utilise l’instruction banksel. L’avantage est que celle-ci permet une vérification statique, à la compilation, de la sélection correcte des bancs.
8.1.3
Micro-contrôleur pic18
Par exemple, pour le 18F448, entrez la ligne de commande piccolo -M=18F448.
Piccolo affiche alors les renseignements suivants :
18F448 device:
4 RAM banks (total 768 bytes):
bank "accessram" from 0x0 to 0x5F (96 bytes)
bank "gpr0" from 0x60 to 0xFF (160 bytes)
bank "gpr1" from 0x100 to 0x1FF (256 bytes)
bank "gpr2" from 0x200 to 0x2FF (256 bytes)
ROM size: 16384 bytes
EEPROM size: 256 bytes (at 0xF00000)
Le premier banc, accessram, est toujours accessible directement sans utiliser la
valeur du registre BSR. Suivant le micro-contrôleur, ce banc occupe les 96 ou les 128
premiers octets de la RAM.
Ensuite, l’accès aux bancs gprX imposent la valeur X au registre BSR. Voir à ce
sujet la section 7.8 page 92 qui explique comment le registre BSR est géré en Piccolo.
En Piccolo, on ne manipule jamais directement le registre BSR : on utilise l’instruction banksel. L’avantage est que celle-ci permet une vérification statique, à la
compilation, de la sélection correcte des bancs.
8.2
Les sections ram
En Piccolo, les sections ram ... { ... } décrivent l’attribution de la RAM. Leur
syntaxe est la même pour les baseline, les mid-range et les pic18. Dans un programme
vous pouvez utiliser autant de sections ram que vous voulez.
Voici un exemple :
8.3. Déclaration byte
103
ram accessram {
byte premiere ;
byte deuxieme ;
byte troisieme [5] ;
byte quatrieme ;
}
Le mot clé ram est suivi du nom du banc utilisé pour l’attribution. Ce nom doit
être l’un des noms de bancs affichés par l’option --memory (voir section précédente).
Ensuite, les variables déclarées sont servies dans leur ordre d’apparition.
Si la section ci-dessus est la première qui nomme accessram (pour un pic18, le
banc accessram commençe à l’adresse 0), la variable premiere reçoit l’adresse 0,
deuxieme l’adresse 1, troisieme l’adresse 2, et quatrieme l’adresse 7 (troisieme
occupe 5 octets).
Vous pouvez déclarer autant de variables que vous voulez, à condition de ne pas
dépasser la capacité du banc : le compilateur Piccolo émet alors un message d’erreur.
Les sections ram ont un effet cumulatif : si vous déclarez plusieurs sections pour
le même banc, Piccolo attribue les adresses dans l’ordre d’apparition des déclarations.
Par exemple, les déclarations suivantes :
ram accessram {
byte premiere
byte deuxieme
}
...
ram accessram {
byte troisieme [5]
byte quatrieme
}
ont le même effet que la déclaration précédente.
Dans cette situation aussi, le compilateur Piccolo émet un message d’erreur quand
la capacité d’un banc est dépassée.
8.3
Déclaration byte
Comme vous l’avez sûrement deviné à partir des exemples de la section précédente, la déclaration
byte var
déclare une variable var occupant un octet. Vous pouvez aussi déclarer une variable occupant n octets consécutifs en indiquant la dimension entre crochets :
byte var [n]
n doit être une constante littérale. Évidemment, n doit être strictement positif. Si
n vaut 1, la déclaration est sémantiquement équivalente à la première forme.
104
C HAPITRE 8. Définition de variable
8.4
Nommage de bits dans une déclaration byte
Vous pouvez nommer les bits individuels lors de la déclaration d’une variable,
comme par exemple :
byte var <-, A, b, -, d, e [2], ->
La déclaration est effectuée entre les délimiteurs « < » et « > », et séparés par une
virgule. Le premier bit déclaré est le bit de poids fort (7), le dernier le bit de poids
faible (0).
Si vous ne voulez pas nommer un bit particulier, mettez le délimiteur « - » : c’est
le cas des bits 7, 4 et 0 dans l’exemple ci-dessus.
À un nom est associé le numéro du bit correspondant : ainsi A est associé à 6, b à
5, et d à 3. e est complété par une dimension, ce qui signifie que e[1] prend la valeur
2, et e[0] la valeur 1.
La déclaration doit toujours comprendre la définition des 8 bits, autrement dit à
la dernière définition, juste avant le « > », est associé le numéro 0.
Les noms de bits sont locaux, c’est à dire que vous pouvez utiliser les mêmes noms
dans des déclarations byte différentes.
Enfin, vous pouvez combiner la présence d’une dimension explicite avec le nommage de bits :
byte var [2] <-, A, b, -, d, e [2], ->
Dans ce cas, tous les octets du tableaux ont leurs bits nommés de la même façon.
Comment utiliser ces noms de bits ? Dans les conditions de l’instruction conditionnelle simple, les conditions élémentaires des instructions conditionnelles structurées
et répétitive, dans les instructions bcf, bsf et btg (voir la section 4.4.3 page 29 pour
les baseline, la section 5.4.3 page 43 pour les mid-range et la section 6.6.3 page 72
pour les pic18).
8.5
Référence à un registre
De nombreuses instructions nomment un registre, qui peut être :
– un registre spécial (dont vous pouvez avoir la liste grâce à l’option « --registers
») ;
– une variable déclarée dans une section ram.
En Piccolo, il suffit de nommer simplement le registre avec l’instruction, par exemple :
c l r f registre
Cette écriture ressemble à l’assembleur, mais il y a une différence fondamentale :
le bit indiquant si l’adressage devra être fait via l’access bank ou via le registre BSR
n’apparaît jamais dans l’instruction Piccolo. Lors de la génération de code, Piccolo
fixera automatiquement la valeur de ce bit.
Par exemple, en supposant que varAccesBank est accessible via l’access bank,
que varBank1 est dans le banc 1 et varBank2 dans le banc 2, on écrira en Piccolo :
8.5. Référence à un registre
105
c l r f varAccesBank
banksel 1
c l r f varBank1
banksel 2
c l r f varBank2
Contrairement à l’assembleur, l’oubli d’une instruction « banksel » est détectée
par le compilateur Piccolo.
Pour plus de précision sur le contrôle de BSR, voir les pages consacrées au contrôle
de BSR en Piccolo.
Si une variable est déclarée avec une dimension, par exemple :
ram ... {
byte maVariable [4]
}
Le premier élément du tableau est maVariable [0], et le dernier maVariable
[3].
On peut ainsi écrire :
c l r f maVariable # Equivalent a clrf maVariable [0]
c l r f maVariable [3]
c l r f maVariable [4] # Erreur detectee par Piccolo
Chapitre 9
Définition de constante
Des constantes peuvent être définies pour les trois types de programme (baseline,
mid-range, pic18).
Une constante est définie par la construction :
const nom_constante = expression_immediate
La forme générale d’une expression immédiate est décrite à la section 9.2 page
109. Voici des exemples de définition de constantes :
const multiplicateur = 249
const v1 = 128 - (2500 / multiplicateur)
const v2 = 128 + (2500 / multiplicateur)
L’évaluation des expressions s’effectue en 32 bits signé, et doit pouvoir être calculée à la compilation ; elle peut faire référence à d’autres constantes, du moment
qu’elles sont définies auparavant dans le programme. Par exemple, l’écriture suivante
déclenche une erreur à la compilation :
const valeur = mult * 11 # Erreur : mult non defini
const mult = 23
Vous pouvez utiliser une constante dans toute expression immédiate. Par exemple :
i f (! PORTB.7)
i f (! PORTB.6)
movlw butSensTrigo
else # Sens horaire
movlw butSensHoraire
end
end
9.1
Constantes prédéfinies
Piccolo prédéfinit les constantes listées dans le tableau 9.1, en fonction du type du
micro-contrôleur. Le fichier listing (obtenu par l’option « -L », voir section 2.2.1 page
13) contient la liste des constantes prédéfinies et leur valeur.
108
C HAPITRE 9. Définition de constante
Nom
Signification
ROM_SIZE
Taille de la ROM
BOOTLOADER_SIZE
Taille du bootloader
RAM_SIZE
Taille de la RAM
Baseline
Mid-range
Pic18
X
X
X
X
Tableau 9.1 – Constantes prédéfinies
Attention ! La valeur de ROM_SIZE est le nombre d’octets pour un pic18 (une
instruction occupe 2 ou 4 octets), et le nombre d’instructions de 14 bits pour un midrange.
La constante BOOTLOADER_SIZE n’est prédéfinie que pour l’implémentation d’un
bootloader ou d’un programme applicatif d’un pic18. Elle n’est pas définie pour un
programme monolithique. Elle a pour valeur la taille en octets réservée pour l’implémentation du bootloader.
La constante RAM_SIZE permet d’écrire un code générique pour pic18 réalisant
l’initialisation à zéro de la RAM.
9.1.1
Exemple d’utilisation de ROM_SIZE
La constante ROM_SIZE est définie pour un pic18. Associée avec l’instruction ltblptr,
elle permet d’écrire une routine générique, valable pour tous les types de pic18, qui
renvoie dans WREG, la somme des octets de la flash.
routine CALCULER_SOMME_CONTROLE_FLASH {
#--- Entrer l’adresse du dernier mot de la flash
ltblptr ROM_SIZE - 1
#--- Accumulateur de la somme de controle
movlw 0
#--- Boucle
do
tblrd *addwf TABLAT, W
while (TBLPTRL NZ | TBLPTRH NZ | TBLPTRU NZ)
end
}
9.1.2
Exemple d’utilisation de RAM_SIZE
La constante RAM_SIZE est définie pour un pic18. Elle permet l’écriture d’une
séquence générique mettant à zéro toute la RAM d’un micro-contrôleur. On peut faire
figurer cette séquence au début de la routine main.
l f s r 0, RAM_SIZE - 1
do
c l r f POSTDEC0
while (FSR0L NZ | FSR0H NZ)
9.2. Forme générale d’une expression littérale
109
end
c l r f INDF0
9.2
Forme générale d’une expression littérale
Opérandes. Piccolo définit les opérandes suivants :
– une constante littérale entière, écrite en décimal ou en hexadécimal ;
– une constante littérale caractère (qui a pour valeur le code ASCII correspondant) ;
– une constante définie par la construction const (voir à cette page) ;
– un registre accompagné éventuellement d’un indice ;
– une valeur descriptive de registre (section 9.3 page 110).
Parenthèses. Vous pouvez utiliser des parenthèses pour exprimer le groupement.
Opérateurs. Les opérateurs définis pour une expression littérale sont donnés par le
tableau 9.2 (par ordre de priorité croissante).
Assembleur
Priorité
Description
|
0
Ou inclusif bit à bit
ˆ
0
Ou exclusif bit à bit
&
1
Et bit à bit
==
2
Test d’égalité
!=
2
Test d’inégalité
>=
2
Test supérieur ou égal
>
2
Test strictement supérieur
<=
2
Test inférieur ou égal
<
2
Test strictement inférieur
+
3
Addition
<<
3
Décalage à gauche
>>
3
Décalage arithmétique à gauche
*
/
4
Multiplication
4
Division
%
4
Modulo
-
5
Opérateur unaire : négation
~
5
Opérateur unaire : complémentation logique
Tableau 9.2 – Opérateurs d’une expression littérale, par ordre de priorité croissante
110
C HAPITRE 9. Définition de constante
9.3
Expression littérale : valeur descriptive de registre
Pour illustrer cette construction, nous prenons un exemple. Nous voulons initialiser le registre T2CON du timer 2 (qui existe sur plusieurs PIC18, dont le 18F448).
La documentation du composant nous fournit la description ci-contre. Nous voulons
affecter les valeurs suivantes :
– TOUTPS à 5 (1 :6 postscale) ;
– TMR2ON à 1 ;
– T2CKPS à 1 (Prescaler is 1).
Une première solution est de calculer à la main la valeur à affecter à T2CON. Cela
donne :
movlw
movwf
0x2D
T2CON
La seconde solution est d’utiliser la construction que nous allons décrire. Pour
cela, il faut d’abord s’assurer que Piccolo connaît la description des champs de bits
de T2CON. On lance donc la commande piccolo -R=18F448 dans le terminal, et on
recherche la définition du registre T2CON dans le listing obtenu ; on obtient :
'T2CON'at 0xFCA <-, TOUTPS[4], TMR2ON, T2CKPS[2]>
Les noms correspondent bien. En utilisant ces noms, nous pouvons écrire :
movlw
movwf
@ T2CON (TOUTPS:5, TMR2ON:1, T2CKPS:1)
T2CON
L’expression litérale présente la syntaxe suivante : elle est introduite par le délimiteur « @ », suivi d’un nom de registre, puis, entre parenthèses, une séquence de
couples formés d’un nom de champ et d’une valeur immédiate :
@ nom_registre (nom_champ:expression_literale, ...)
Tout registre spécial, toute variable déclarée (dans une section ram) ayant des
champs définis peut être utilisée.
Le nom de champ (nom_champ) est l’un des champs définis par ce registre ou par
cette variable. On ne peut utiliser des noms de champ d’un autre registre ou d’une
autre variable.
L’expression_literale associée obéit à la syntaxe générale des expressions litérales. La valeur obtenue doit être positive ou nulle, et inférieure ou égal à la valeur
maximum que peut contenir le champ (c’est à dire, pour un champ de n bits, 2n − 1).
Un nom de champ ne peut apparaître qu’une fois. L’ordre d’apparition des champs
dans cette construction n’a pas d’importance. Tout champ non mentionné a implicitement tous les bits correspondants à zéro.
Chapitre 10
Registres de configuration
Dans un fichier source Piccolo, une section configuration vous permet de définir
le contenu des registres de configuration. Les valeurs ainsi définies sont contenues
dans le fichier hex et le fichier asm engendré.
Les règles d’utilisation sont les suivantes :
– en l’absence de section configuration, aucun code de configuration n’est défini ;
– si une ou plusieurs sections configuration existent, le code de configuration
est engendré.
Plusieurs sections configuration peuvent exister, leur effet est le même que si
tous leurs contenus étaient rassemblés dans une seule. L’ordre des sections configuration,
et leurs positions parmi les autres sections sont quelconques.
La description définie par les sections configuration doit être exhaustive, c’est
à dire que tous les champs de tous les registres de configuration doivent être définis
explicitement.
Le fichier listing (obtenu par l’option -L ou --list) contient le détail de la configuration et les éventuels messages d’erreur de configuration.
10.1
Connaître les possibilités de configuration d’un contrôleur
Pour connaître les possibilités de configurations d’un micro-contrôleur particulier
(section 2.2.4 page 16), utiliser l’option -F ou --configuration, par exemple, pour
le 10F220 :
piccolo -F=10F220
Cette commande affiche :
10F220 has 1 configuration registers:
REGISTER ’CONFIG’ at 0xFFF, width 5
setting ’CP’: mask 0x8 description "Code protection bit"
value 0x8 description "Disabled"
value 0x0 description "Enabled"
setting ’IOSCFS’: mask 0x1 description "Internal Oscillator Frequency Select bit"
112
C HAPITRE 10. Registres de configuration
value
value
setting
value
value
setting
value
value
setting
value
value
0x0 description "4 MHz"
0x1 description "8 MHz"
’MCLRE’: mask 0x10 description "GP3/MCLR Pin Function Select bit"
0x0 description "Disabled"
0x10 description "Enabled"
’MCPU’: mask 0x2 description "Master Clear Pull-up Enable bit"
0x2 description "Disabled"
0x0 description "Enabled"
’WDTE’: mask 0x4 description "Watchdog Timer Enable bit"
0x0 description "Disabled"
0x4 description "Enabled"
Le texte obtenu indique qu’il y a cinq champs à définir : CP, IOSCFS, MCLRE, MCPU
et WDTE. Le champ IOSCFS peut prendre deux valeurs différentes : "4 MHz" et "8
MHz". Les autres informations telles que le nom des registres, la chaîne d’information,
la valeur des masques, . . . ne sont pas utiles ici.
Il est indispensable d’effectuer cette opération pour chaque micro-contrôleur, les
noms des champs et des valeurs associées peuvent être différents entre le data sheet
du micro-contrôleur et Piccolo.
10.2
Écrire une configuration
Le principe est simple, il suffit d’écrire dans une ou plusieurs sections configuration
une liste d’affectations du type :
setting : description
où setting est le nom d’un champ d’un registre de configuration, et description
la chaîne descriptive de la valeur souhaitée (voir section précédente).
Par exemple, pour le 10F220 :
configuration {
IOSCFS : "4 MHz"
CP : "Enabled"
...
}
Si la valeur descriptive est un identificateur (comme pour CP), alors on peut simplifier l’écriture en :
CP : Enabled
La règle est simple : il faut que tous les champs soient explicitement affectés.
L’ordre des affectations est sans importance. Piccolo vérifie et considère comme une
erreur tout manque.
Un champ ne peut être affecté qu’une seule fois. Plusieurs affectations du même
champ déclenchent une erreur.
10.3
Combinaisons incompatibles
Pour certains micro-contrôleurs, certaines affectations particulières sont incompatibles. Ces interdictions sont contenues dans les fichiers de description des micro-
10.3. Combinaisons incompatibles
113
contrôleurs, et par conséquent sont vérifiées par Piccolo.
C’est par exemple le cas du 18F4431 : le champ BOREN ne doit pas prendre la valeur Disabled quand le champ BORV prend la valeur Reserved. Cette incompabilité
peut être mise en évidence en affichant la description de la configuration du 18F4431
(piccolo -F=18F4431), dont voici l’extrait qui nous intéresse :
...
REGISTER ’CONFIG2L’ at 0x300002 , width 4
i l l e g a l value 0xC mask 0xC
d e s c r i p t i o n "Brown out v o l t a g e cannot be s e t t o an undefined valued i f Brown Out Detect
s e t t i n g ’BOREN’ : mask 0x2 d e s c r i p t i o n "Brown−out Reset Enable b i t s "
value 0x0 d e s c r i p t i o n " Disabled "
value 0x2 d e s c r i p t i o n " Enabled "
s e t t i n g ’BORV’ : mask 0xC d e s c r i p t i o n "Brown Out Reset Voltage b i t s "
value 0xC d e s c r i p t i o n " Reserved "
value 0x8 d e s c r i p t i o n "VBOR s e t t o 2 . 7V"
value 0x4 d e s c r i p t i o n "VBOR s e t t o 4 . 2V"
value 0x0 d e s c r i p t i o n "VBOR s e t t o 4 . 5V"
...
La ligne qui commence par illegal décrit cette incompabilité. Si on tente de
définir cette combinaison interdite :
pic18 illegal_configuration "18F4431" :
configuration {
BOREN : Disabled
BORV : Reserved
...
}
La compilation Piccolo de ce programme avec Piccolo provoque un message d’erreur qui cite la chaîne de description de l’incompabilité :
error: illegal setting for ’CONFIG2L’ register: Brown out voltage
cannot be set to an undefined valued if Brown Out Detect is enabled
Le fichier listing (obtenu par l’option -L ou --list) contient le détail de la configuration et ce message d’erreur.
Index
-1à716F873, 49
16F874, 49
-BBaseline
Instructions machine, 27
Liste, 16
-MMid-range
Liste, 17
MidRange
Instructions machine, 40
Mot réservé
configuration, 23
const, 23
include, 23
noreturn, 23
ram, 23
routine, 23
Mots réservés, 19
-OOptions de la ligne de commande, 13
-PPic18
Liste, 17
-RRoutine régulière
baseline, 24
Routine sans retour
baseline, 25