}}}
[[PageOutline]]
= Préambule =
La difficulté de la programmation en assembleur PIC se résume en deux points:
* ''seulement 35 instructions'',[[BR]]
'''mais pas de structures de contrôle, pas de fonctions...'''
* ''et quelques centaines d'octets de mémoire'',[[BR]]
'''mais pas de typage, pas de pointeurs, pas de fichiers...'''
La pauvreté du langage entraine des difficultés pour exprimer des choses aussi simples qu'un test IF-THEN-ELSE. Le but de
cette page est de vous donner une aide sur la bonne façon d'écrire de l'assembleur PIC efficacement. Il n'y a aucune utilisation
des périphériques internes ou externes du PIC.
Le code que vous trouverez ici est issu de nombreuses sources à la fois sur internet et dans les
livres. Les auteurs (ou sources) sont cités quand ils sont connus.
= liste des instructions pic 16 = #instruction
__'''Instructions sur les registres octet'''__::
||'''`Mnémotique `'''||'''`arguments`'''||'''`Commentaire `'''||'''`drapeaux`'''||'''`cycles `'''||
|| addwf || ''reg'', ''dst'' || add w to ''reg'', result in ''dst'' || z, dc, c || 1 ||
|| andwf || ''reg'', ''dst'' || and w and ''reg'', result in ''dst'' || z || 1 ||
|| clrf || ''reg'' || write zero to ''reg'' || z || 1 ||
|| clrw || || write zero to w i || Z || 1 ||
|| comf || ''reg'', ''dst'' || complement ''reg'', result in ''dst'' || Z || 1 ||
|| decf || ''reg'', ''dst'' || decrement ''reg'', result in ''dst'' Z || Z || 1 ||
|| incf || ''reg'', ''dst'' || Increment ''reg'', result in ''dst'' Z || Z || 1 ||
|| iorwf || ''reg'', ''dst'' || Or W and ''reg'', result in ''dst'' || Z || 1 ||
|| movf || ''reg'', ''dst'' || Move ''reg'' to ''dst'' || Z || 1 ||
|| movwf || ''reg'' || Move W to ''reg'' || none || 1 ||
|| nop || || No operation || none || 1 ||
|| rlf || ''reg'', ''dst'' || Rotate ''reg'' left, result in ''dst'' || C || 1 ||
|| rrf || ''reg'', ''dst'' || Rotate ''reg'' right, result in ''dst'' || C || 1 ||
|| subwf || ''reg'', ''dst'' || Subtract W from ''reg'', result in ''dst'' || Z, DC, C || 1 ||
|| swapf || ''reg'', ''dst'' || Swap nibbles of ''reg'', result in ''dst'' || none || 1 ||
|| xorwf || ''reg'', ''dst'' || Xor W and ''reg'', result in ''dst'' || Z || 1 ||
|| decfsz || ''reg'', ''dst'' || Decrement ''reg'', result in ''dst'', skip IF zero || none || 1 (2 if skip) ||
|| incfsz || ''reg'', ''dst'' || Increment ''reg'', result in ''dst'', skip IF zero || none || 1 (2 if skip) ||
__'''Instructions sur les bits'''__::
||'''`Mnémotique `'''||'''`arguments`'''||'''`Commentaire `'''||'''`drapeaux`'''||'''`cycles `'''||
|| bcf || ''reg'', ''bit'' || Clear ''bit'' of ''reg'' || none || 1 ||
|| bsf || ''reg'', ''bit'' || Set ''bit'' of ''reg'' || none || 1 ||
|| btfsc || ''reg'', ''bit'' || Skip next instruction IF ''bit'' of ''reg'' is clear|| none || 1 (2 if skip) ||
|| btfss || ''reg'', ''bit'' || Skip next instruction IF ''bit'' of ''reg'' is set || none || 1 (2 if skip) ||
__'''Instructions sur les immediats'''__::
||'''`Mnémotique `'''||'''`arguments`'''||'''`Commentaire `'''||'''`drapeaux`'''||'''`cycles `'''||
|| addlw || ''imm'' || Add immediate to W || Z, DC, C || 1 ||
|| andlw || ''imm'' || And immediate to W || Z || 1 ||
|| iorlw || ''imm'' || Inclusive OR littéral to W || Z || 1 ||
|| movlw || ''imm'' || Move littéral to W || none || 1 ||
|| retlw || ''imm'' || Load W with immediate and return || none || 2 ||
|| sublw || ''imm'' || Subtract W from littéral || Z, DC, C || 1 ||
|| xorlw || ''imm'' || Xor W and littéral || Z || 1 ||
__'''Instructions de controle'''__::
||'''`Mnémotique `'''||'''`arguments`'''||'''`Commentaire `'''||'''`drapeaux`'''||'''`cycles `'''||
|| clrwdt || || Reset watchdog timer || TO, PD || 1 ||
|| call || ''addr'' || Call subroutine || none || 2 ||
|| goto || ''addr'' || Go to ''addr'' || none || 2 ||
|| retfie || || Return from interrupt || GIE || 2 ||
|| return || || Return from subroutine || none || 2 ||
|| sleep || || Enter sleep mode || TO, PD || 1 ||
= Structures de contrôle = #struc_ctrl
== ` ` Branchements conditionnels == #test
Comme vous le savez en assembleur, pour faire une rupture de séquence on ne sait faire qu'une chose: tester une condition et
si cette condition est vraie ou fausse alors sauter à une adresse, c'est peu. En outre, en assembleur PIC, il y a une
difficulté supplémentaire: si la condition est vraie on se contente de sauter une instruction.
En PIC, il existe 4 instructions de sauts conditionnels: ''decfsz'', ''incfsz'', ''btfss'', ''btfsc.'' Les deux
première instructions sont surtout utilisés pour les boucles. Nous allons ici, nous intéresser au comparaisons sur les nombres
8 bits signés (en complément à 2) et non signés, il y a 6 possibles:
* égalité (== ou BEQ)
* inégalité (!= ou BNE)
* inférioité stricte (< ou BLT ou BLTS)
* supériorité stricte (> ou BGT ou BGTS)
* inférioité ou égalité (<= ou BLE ou BLES)
* supériorité ou égalité (>= ou BGE ou BGES)
Les registres doivent être dans le même banc. Le tableau qui suit donne en commentaire (après les ;) une notation de type
macro instruction. Les comparaisons sont dépendent du type des données qui peuvent être non signées (valeur de 0 à 255) ou
signées (valeurs de -128 à +127). Il est parfois nécessaire d'utiliser un registres temporaire TMP
Égalité registre/registre et registre/immédiat::
{{{
; si (REG1 == REG2) goto LAB
; BEQ REG1, REG2, goto, LAB
movf REG1, W
xorwf REG2, W
btfsc STATUS, Z
goto LAB
}}}
{{{
; si (REG1 != REG2) goto LAB
; BNE REG1, REG2, goto, LAB
movf REG1, W
xorwf REG2, W
btfss STATUS, Z
goto LAB
}}}
{{{
; si (REG == IMM) goto LAB
; BEQ REG, IMM, goto, LAB
movlw IMM
xorwf REG, W
btfsc STATUS, Z
goto LAB
}}}
{{{
; si (REG1 != IMM) goto LAB
; BNE REG1, IMM, goto, LAB
movlw IMM
xorwf REG, W
btfss STATUS, Z
goto LAB
}}}
Comparaisons registre/registre sur des nombres non signés::
{{{
; si (REG1 < REG2) goto LAB
; BLT REG1, REG2, goto, LAB
movf REG2, W
subwf REG1, W
btfss STATUS, C
goto LAB
}}}
{{{
; si (REG1 > REG2) goto LAB
; BGT REG1, REG2, goto, LAB
movf REG1, W
subwf REG2, W
btfss STATUS, C
goto LAB
}}}
{{{
; si (REG1 <= REG2) goto LAB
; BLE REG1, REG2, goto, LAB
movf REG1, W
subwf REG2, W
btfsc STATUS, C
goto LAB
}}}
{{{
; si (REG1 >= REG2) goto LAB
; BGE REG1, REG2, goto, LAB
movf REG2, W
subwf REG1, W
btfsc STATUS, C
goto LAB
}}}
Comparaisons registre/immediat sur des nombres non signés::
{{{
; si (REG > IMM) goto LAB
; BGT REG, IMM, goto, LAB
movf REG, W
sublw IMM
btfss STATUS, C
goto LAB
}}}
{{{
; si (REG < IMM) goto LAB
; BLT REG, IMM, goto, LAB
movlw IMM
subwf REG, W
btfss STATUS, C
goto LAB
}}}
{{{
; si (REG <= IMM) goto LAB
; BLE REG, IMM, goto, LAB
movf REG, W
sublw IMM
btfsc STATUS, C
goto LAB
}}}
{{{
; si (REG >= IMM) goto LAB
; BGE REG, IMM, goto, LAB
movlw IMM
subwf REG, W
btfsc STATUS, C
goto LAB
}}}
Comparaisons immediat/registre sur des nombres non signés::
{{{
; si (IMM < REG) goto LAB
; BLT IMM, REG, goto, LAB
movf REG,W
sublw IMM
btfss STATUS, C
goto LAB
}}}
{{{
; si (IMM > REG) goto LAB
; BGT IMM, REG, goto, LAB
movlw IMM
subwf REG,W
btfss STATUS, C
goto LAB
}}}
{{{
; si (IMM <= REG) goto LAB
; BLE IMM, REG, goto, LAB
movlw IMM
subwf REG,W
btfsc STATUS, C
goto LAB
}}}
{{{
; si (IMM >= REG) goto LAB
; BGE IMM, REG, goto, LAB
movf REG,W
sublw IMM
btfsc STATUS, C
goto LAB
}}}
comparaisons registre/registre sur des nombres signés::
{{{
; si (REG1 < REG2) goto LAB
; BLTS REG1, REG2, goto, LAB
movf REG1, W
addlw 0x80
movwf TMP
movf REG2, W
addlw 0x80
subwf TMP, W
btfss STATUS, C
goto LAB
}}}
{{{
; si (REG1 > REG2) goto LAB
; BGTS REG1, REG2, goto, LAB
movf REG2, W
addlw 0x80
movwf TMP
movf REG1, W
addlw 0x80
subwf TMP, W
btfss STATUS, C
goto LAB
}}}
{{{
; si (REG1 <= REG2) goto LAB
; BLES REG1, REG2, goto, LAB
movf REG2, W
addlw 0x80
movwf TMP
movf REG1, W
addlw 0x80
subwf TMP, W
btfsc STATUS, C
goto LAB
}}}
{{{
; si (REG1 >= REG2) goto LAB
; BGES REG1, REG2, goto, LAB
movf REG1, W
addlw 0x80
movwf TMP
movf REG2, W
addlw 0x80
subwf TMP, W
btfsc STATUS, C
goto LAB
}}}
Comparaisons registre/immediat sur des nombres signés::
{{{
; si (REG < IMM) goto LAB
; BLTS REG, IMM, goto, LAB
movf REG, W
addlw 0x80
movwf TMP
movlw 0x80 | IMM
subwf TMP, W
btfss STATUS, C
goto LAB
}}}
{{{
; si (REG > IMM) goto LAB
; BGTS REG, IMM, goto, LAB
movf REG, W
addlw 0x80
sublw 0x80 | IMM
btfss STATUS, C
goto LAB
}}}
{{{
; si (REG <= IMM) goto LAB
; BLES REG, IMM, goto, LAB
movf REG, W
addlw 0x80
sublw 0x80 | IMM
btfsc STATUS, C
goto LAB
}}}
{{{
; si (REG >= IMM) goto LAB
; BGES REG, IMM, goto, LAB
movf REG, W
addlw 0x80
sublw 0x80 | IMM
btfss STATUS,Z
btfss STATUS, C
goto LAB
}}}
Comparaisons immediat/registre sur des nombres signés::
{{{
; si (IMM < REG) goto LAB
; BLTS IMM, REG, goto, LAB
movf REG,W
addlw 0x80
sublw 0x80 | IMM
btfss STATUS, C
goto LAB
}}}
{{{
; si (IMM > REG) goto LAB
; BGTS IMM, REG, goto, LAB
movf REG,W
addlw 0x80
sublw 0x80 | IMM
btfsc STATUS,Z
goto $+3
btfsc STATUS,C
goto LAB
}}}
{{{
; si (IMM <= REG) goto LAB
; BLES IMM, REG, goto, LAB
movf REG,W
addlw 0x80
movwf TMP
movlw 0x80 | IMM
subwf TMP,W
btfsc STATUS, C
goto LAB
}}}
{{{
; si (IMM >= REG) goto LAB
; BGES IMM, REG, goto, LAB
movf REG,W
addlw 0x80
sublw 0x80 | IMM
btfsc STATUS, C
goto LAB
}}}
== ` ` If condition then else == #if_then_else
Pour pouvoir faire une alternative, il faut savoir faire un branchement conditionnel. Nous supposons que
nous savons le faire. Dans une alternative ''si-alors-sinon'', nous allons mettre la séquence ''alors'' avant la séquence
''sinon''. Ce choix permet, a priori, une plus grande lisibilité du code, car il est plus proche du code écrit dans les
langages évolués de type C ou Pascal. Pour respecter cet ordre, il est souvent nécessaire d'inverser le test. En effet, si la
condition est fausse, il faut sauter à la séquence ''sinon'' et donc simplement continuer si la condition est vraie.
Quand les conditions sont plus complexes, qu'elles sont le résultat d'une expression Booléenne, il faut faire des séquences
de test. Le tableau suivant contient quelques exemples permettant de comprendre la méthode à suivre. Ce n'est pas une méthode générale
SI test ALORS sequence_alors SINON sequence_sinon FIN::
Cas général:
{{{
IFNOT test GOTO sinon
alors sequence_alors
goto fin
sinon sequence_sinon
fin
}}}
Exemple: si (reg1==reg2) alors sequence_alors SINON sequence_sinon FIN
{{{
si ; BNE reg1, reg2, goto, sinon
movf reg1,w
xorwf reg2,w
btfss STATUS, Z
goto sinon
alors sequence_alors
goto fin
sinon sequence_sinon
fin
}}}
SI test1 ET test2 ALORS sequence_alors SINON sequence_sinon FIN::
{{{
si IFNOT test1 GOTO sinon
IFNOT test2 GOTO sinon
alors sequence_alors
goto fin
sinon sequence_sinon
fin
}}}
SI test1 OU test2 ALORS sequence_alors SINON sequence_sinon FIN::
{{{
si IF test1 GOTO alors
IFNOT test2 GOTO sinon
alors sequence_alors
goto fin
sinon sequence_sinon
fin
}}}
SI (test1 OU test2) ET (test3 OU test4) ALORS sequence_alors SINON sequence_sinon FIN::
{{{
si IF test1 GOTO si2
IFNOT test2 GOTO sinon
si2 IF test3 GOTO alors
IFNOT test4 GOTO sinon
alors sequence_alors
goto fin
sinon sequence_sinon
fin
}}}
SI (test1 ET test2) OU (test3 ET test4) ALORS sequence_alors SINON sequence_sinon::
{{{
si IFNOT test1 GOTO si2
IF test2 GOTO alors
si2 IFNOT test3 GOTO sinon
IFNOT test4 GOTO sinon
alors sequence_alors
goto fin
sinon sequence_sinon
fin
}}}
== ` ` Faire n fois == #faire_n_fois
On traite deux cas, si N est sur 8 bits ou si N est sur 16 bits. Dans le cas 8 bits, la valeur 0 est en fait 256. Dans le
as 16 bits, la valeur 0 c'est vraiment 0 (c'est-à-dire qu'on n'exécute pas la séquence_corps).
Faire N fois (8 bits) sequence_corps FIN::
{{{
; Si N est une valeur immédiate.
movlw N
movwf REG
corps sequence_corps
decfsz REG
goto corps
}}}
{{{
; Si N est dans un registre.
movf REG_N, W
movwf REG
corps sequence_corps
decfsz REG
goto corps
}}}
Faire N fois (16 bits) sequence_corps FIN::
{{{
; Si N est une valeur immédiate
movlw (HIGH N) + 1
movwf REG+1
movlw LOW N
movwf REG
movf REG, F
btfsc STATUS, Z
goto testh
corps sequence_corps
decfsz REG
goto corps
testh decfsz REG+1
goto corps
fin
}}}
{{{
; Si N est dans une paire de registres successifs::
incf REG_N+1, W
movwf REG+1
movf REG_N, W
movwf REG
btfsc STATUS, Z
goto testh
corps sequence_corps
decfsz REG
goto corps
testh decfsz REG+1
goto corps
fin
}}}
== ` ` Branchement relatif au PC ==
On veut faire PC = PC+1+registre c'est-à-dire goto PC+1+reg
{{{
BRPC macro _reg_
movlw high($+7)
movwf PCLATH
movlw $+5
addwf _reg_,w
btfsc STATUS,C
incf PCLATH,f
movwf PCL
endm
}}}
== ` ` Switch case == #switchcase
Le switch case est un test à choix multiples. Il existe dans tous les langages évolués et permet entre autre de décrire les automates d'états finis particulièrement important dans la programmation en assembleur.
La forme générale du switch case est::
{{{
switch reg
case CST1 : sequence_cst1; break;
case CST2 : sequence_cst2; break;
case CST3 : sequence_cst3; break;
default : sequence_default
end
}}}
Les valeurs CST peuvent être quelconque. Dans ce cas, il n'y a d'autres choix que de faire les tests les uns après les
autres en séquence. Dans le code qui suit, on suppose que les constantes sont bien des valeurs immédiates mais en fait il peut
s'agir de registres. On peut même faire des tests différents. C'est très général mais évidemment ce n'est pas très effice et
tous les tests ne sont pas équivalent vis à vis du temps de traitement. Le premier est favorisé par rapport au dernier.
{{{
; BEQ reg, CST1, goto, label_cst1
movlw CST1
xorwf reg, W
btfsc STATUS, Z
goto label_cst1
; BEQ reg, CST2, goto, label_cst2
movlw CST2
xorwf reg, W
btfsc STATUS, Z
goto label_cst2
; BEQ reg, CST3, goto, label_cst3
movlw CST3
xorwf reg, W
btfsc STATUS, Z
goto label_cst3
; BEQ reg, CST4, goto, label_cst4
movf CST4
xorwf reg, W
btfsc STATUS, Z
goto label_cst4
}}}
{{{
; puis a la suite des tests
; les sequences a executer
sequence_default
label_cst1
sequence_cst1
goto fin
label_cst2
sequence_cst2
goto fin
label_cst3
sequence_cst3
goto fin
label_cst4
sequence_cst4
goto fin
fin
}}}
Pour être efficace, il est souhaitable de se contraindre dans le choix des valeurs. Le plus simple est alors de choisir les constantes consécutives à partir de 0, on peut alors faire une table de sauts.
{{{
cst_A EQU 0
cst_B EQU 1
cst_C EQU 2
; SWITCH reg
movlw high($+7)
movwf PCLATH
movlw $+5
addwf _reg_,w
btfsc STATUS,C
incf PCLATH,f
movwf PCL
goto label_cst_A
goto label_cst_B
goto label_cst_C
label_cst_A
sequence_cst1
goto fin
label_cst_B
sequence_cst2
goto fin
label_cst_C
sequence_cst3
goto fin
fin
}}}
== ` ` Tantque condition faire == #tantque
= Arithmétiques = #arith
== ` ` Entière 8 bits == #int9
* Addition
* Soustraction
* Multiplication
* Division
== ` ` Entière 16 bits == #int16
== ` ` Virgule fixe 16 bits == #vfixe16
== ` ` Flottante 16 bits == #float16
= Expressions = #expr
== ` ` Boolèennes == #exp_bool
== ` ` Arithmétiques == #exp_arith
= Structures de données = #struc_data
== ` ` Tableaux de données == #tables
* Dans les registres
* Dans le programme
== ` ` Liste chainée == #listes
== ` ` Pile == #piles
= Macroinstructions = #macros
= Automates = #automates
= Routines diverses (snippets) = #snippets
== ` ` Attentes actives == #snippets_wait
* Attente 10ms
{{{
wait10ms movlw D'66' ; il n'est pas utile d'initialiser le registre 0x70
movwf 0x71 ; attente d'environ 3*256*66*.2 = 10ms
decfsz 0x70
goto $-1
decfsz 0x71
goto $-3
return
}}}
== ` ` Mathématiques == #snippets_math
* Parité
* Moyenne glissante
* PGCD