wiki:doc-assembleur-pic16

Page d'aide assembleur

Préambule

La difficulté de la programmation en assembleur PIC se résume en deux points:

  • seulement 35 instructions,
    mais pas de structures de contrôle, pas de fonctions...
  • et quelques centaines d'octets de mémoire,
    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

Instructions sur les registres octet
Mnémotique argumentsCommentaire drapeauxcycles
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 argumentsCommentaire drapeauxcycles
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 argumentsCommentaire drapeauxcycles
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 argumentsCommentaire drapeauxcycles
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

Branchements conditionnels

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

Pour pouvoir faire une alternative, il faut savoir faire un <a href="#test">branchement conditionnel</a>. 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

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

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

Arithmétiques

Entière 8 bits

  • Addition
  • Soustraction
  • Multiplication
  • Division

Entière 16 bits

Virgule fixe 16 bits

Flottante 16 bits

Expressions

Boolèennes

Arithmétiques

Structures de données

Tableaux de données

  • Dans les registres
  • Dans le programme

Liste chainée

Pile

Macroinstructions

Automates

Routines diverses (snippets)

Attentes actives

  • 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

  • Parité
  • Moyenne glissante
  • PGCD
Last modified 11 years ago Last modified on Jan 4, 2014, 7:19:31 PM