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