III. Modèle de programmation▲
III-A. Syntaxe▲
Avant d'aller plus loin, il est nécessaire de présenter rapidement la syntaxe Motorola. Ceux qui ont déjà pratiqué l'assembleur savent que chaque constructeur a sa syntaxe propre qui est de plus susceptible de varier suivant le compilateur utilisé. Pour plus de détails, il faudra se référer à la documentation du programme d'assemblage choisi.
- Les mnémoniques sont suffixées par la taille de l'opérande
- La source précède la destination.
MOVE.L D0, D1 ; Place le mot long (32 bits) contenu dans D0 dans D1
MOVE.W D0, D1 ; Place le mot (16 bits de poids faible) contenu dans D0 dans D1
MOVE.B D0, D1 ; Place l'octet (8 bits de poids faible) contenu dans D0 dans D1
- # est l'opérateur d'immédiateté
- $ désigne un nombre hexadécimal
- % désigne un nombre binaire
MOVE.B #10,D0 ; place 10 (décimale) dans D0
MOVE.B #$10,D0 ; place 0x10 (hexadécimale) dans D0
MOVE.B #,D0 ; place 0b10 (binaire) dans D0
MOVE.B 10,D0 ; place ce qui est à l'adresse 10 dans D0
Les labels commencent en colonne 0 par une lettre. Ils terminent par " : " et contiennent uniquement des caractères alphanumériques ainsi que l'underscore " _ ". Ils sont sensibles à la casse.
label: nop
bra label
Certains assembleurs (pas très malins) exigent de suffixer la source pour lever l'ambiguïté entre différents opcodes. Cela n'a rien à voir avec la taille de l'opérande.
MULU.W D0.L,D1
III-B. Modes d'adressage▲
Avant d'attaquer les instructions attardons-nous sur les modes d'adressages. Ils sont au nombre de onze. Leur choix détermine souvent le succès d'un développement.
Absolu
MOVE.L $10,D0
Place ce qui est à l'adresse 10 dans D0
Immédiat
MOVE.L #$10,D0
Place la valeur 10 dans D0
Direct registre d'adresse
MOVEA.L A0,A1
Place ce qui est dans le registre A0 dans le registre A1
Direct registre de donnée
MOVE.L D0,D1
Place ce qui est dans D0 dans D1
Indirect registre d'adresse
MOVE.L (A0),D0
Place ce qui est désigné par l'adresse contenue dans A0 dans D0
Indirect registre d'adresse pré-décrémenté
MOVE.L -(A0),D0
Décrémente A0 de la taille de l'opérande puis comme indirect registre d'adresse
Indirect registre d'adresse post-incrémenté
MOVE.L (A0)+,D0
Comme indirect registre d'adresse puis A0 est incrémenté de la taille de l'opérande
Indirect registre d'adresse avec déplacement
MOVE.L (12,A0),D0
Place dans D0 ce qui est désigné par l'adresse contenue dans A0 +12. Le déplacement est sur 16 bits. L'addition est signée. A0 n'est pas modifié.
Indirect registre d'adresse avec déplacement, index et facteur d'échelle
MOVE.L (12,A0,D0,S)
L'adresse effective est obtenue en additionnant en signé les 32 bits de A0 au déplacement (ici 12) puis à l'index (D0) multiplié par le facteur d'échelle (S). S peut être égal à 1, 2 ou 4. A0 n'est pas modifié
Relatif
BRA $2A
On ajoutera 0x2A en signé au Program Counter. Le déplacement est sur 16 bits signé. Généralement on utilise un label.
Relatif compteur programme avec déplacement et relatif compteur programme avec déplacement, index et facteur d'échelle.
MOVE.W ($14,PC) MOVE.W ($14,PC,D3,S)
Voir indirect registre d'adresse avec déplacement et suivant. Interdit en destination
Pour écrire des modules translatables, il est important de définir une stratégie d'adressage souple. L'utilisation de pointeurs plutôt que d'adresses "dures" est vivement recommandé. On trouvera en annexe toutes les combinaisons possibles entre modes d'adressage et instructions. Le MCF est bien plus contraignant que le 68000 en ce domaine. Il est par exemple impossible d'écrire :
DIVU.W #1000,D3
Il faut passer la constante par un registre
MOVE.L #1000,D2
DIVU.W D2,D3
III-C. Jeu d'instruction▲
Une instruction est composée d'une mnémonique suffixée et de deux opérandes (une seule dans certains cas). Chaque opérande est elle-même composée d'un couple valeur / mode d'adressage. Lorsque l'instruction modifie une valeur le résultat est toujours placé dans la destination.
Mnémonique | Suffixe | Source | Destination |
---|---|---|---|
MOVE | .L | (A0) | D1 |
La source est A0 en adressage indirect registre d'adresse | La destination est D0 en adressage direct registre de donnée |
Certaines instructions ont une incidence sur le CCR et ne supportent
pas toutes les tailles d'opérandes. Le détail est fourni en annexe.
L'un des opérateurs est toujours un registre.
On a toujours envie de trouver une façon ludique de présenter les instructions. Malheureusement, je ne vois pas de meilleur moyen pour être relativement concis et exhaustif que d'utiliser des tableaux énumératifs.
Chaque mnémonique présentée correspond à un opcode particulier. Autrement dit, ADD ou ADDQ font tout les deux des additions, mais il s'agit d'instructions totalement différentes d'un point de vue physique. Les câbles utilisés ne sont pas les mêmes. Cela implique qu'il ne faut pas se tromper dans le choix des instructions, si l'on veut des performances optimales. Certains assembleurs sauront choisir entre, par exemple, ADDI, ADDQ ou ADD. Mais ce n'est pas toujours le cas.
Si certaines instructions apparaissent deux fois c'est qu'elles ont un comportement différent suivent les modes d'adressages utilisés.
On peut découper le jeu d'instruction en plusieurs sous catégories : arithmétique, logique, branchement, contrôle, déplacement ... Commençons par les plus populaires :
III-C-1. Les instructions de déplacement▲
Instruction | Explication | Exemple |
---|---|---|
MOVE | Déplace la source dans la destination | MOVE.L D0,D1 |
MOVEA | La source est transférée dans un registre d'adresse | MOVEA.L D0,A1 |
MOVEM | MOVE mémoire vers liste de registres et inversement |
MOVEM.L DO-7,(A0) MOVEM.L (A0),D0-7 |
MOVEQ | Placer une constante de 8bits dans un registre de donnée | MOVEQ.L #3,D0 |
III-C-2. Les instructions logiques▲
Instruction | Explication | Exemple |
---|---|---|
AND | Source ET Destination | AND.L DO,D1 |
ANDI | Constante ET destination | ANDI.L #$FF, D1 |
EOR | Source OU EXCLUSIF Destination | EOR.L DO,D1 |
EORI | Constante OU EXCLUSIF destination | EORI.L #$FF, D1 |
OR | Source OU Destination | OR.L DO,D1 |
ORI | Constante OU destination | ORI.L #$FF, D1 |
NOT | NON logique | NOT.L D0 |
III-C-3. Les instructions arithmétiques▲
Instruction | Explication | Exemple |
---|---|---|
ADD | Source + destination | ADD.L D0,D1 |
ADDA | La destination est une adresse | ADDA.L D0,A1 |
ADDI | La source est une constante | ADDI.L #$FF,D0 |
ADDQ | La source est une constante entre 1 et 8 | ADDQ.L #1,D0 |
ADDX | Source + Destination + bit X | ADDX.L D0,D1 |
SUB | Destination - Source | SUB.L D0,D1 |
SUBA | La destination est une adresse | SUBA.L D0,A1 |
SUBI | La source est une constante | SUBI.L #$FF,D0 |
SUBQ | La source est une constante entre 1 et 8 | SUBQ.L #1,D0 |
SUBX | Source -Destination - bit X | SUBX.L D0,D1 |
MULU.W | Multiplie les 2 opérandes de 16 bits résultat sur 32 dans la destination | MULU.W D0,D1 |
MULU.L | Multiplie les 2 opérandes de 32 bits. Poids faibles du résultat dans la destination | MULU.L D0,D1 |
MULS.W | Comme MULU.W mais en signé | MULU.W D0,D1 |
MULS.L | Comme MULU.L mais en signé | MULU.L D0,D1 |
DIVU.W |
Divise les 32 bits de la destination par les 16 bits de la source. Le résultat est dans le mot de poids faible de la destination. Le reste dans le poids fort. |
DIVU.W D0,D1 |
DIVU.L |
Divise les 32 bits de la destination par les 32 bits de la source. Résultat dans les 32 bits de la destination |
DIVU.L D0,D1 |
DIVS.W | Comme DIVU.W mais en signé | DIVS.W D0,D1 |
DIVS.L | Comme DIVU.L mais en signé | DIVS.L D0,D1 |
III-C-4. Décalages et permutations▲
Instruction | Explication | Exemple |
---|---|---|
LSL |
Décalage logique à gauche. Constante entre 1 et 8 |
LSL.L #3,D0 LSL.L D1,D0 |
LSR |
Décalage logique à droite Constante entre 1 et 8 |
LSR.L #3,D0 LSR.L D1,D0 |
ASL |
Décalage Arithmétique à gauche. Constante entre 1 et 8 |
ASL.L #3,D0 ASL.L D1,D0 |
ASR |
Décalage Arithmétique à droite. Constante entre 1 et 8. Extension de signe |
ASR.L #3,D0 ASR.L D1,D0 |
CLR | Force la valeur à zéro | CLR.B D0 |
EXT | Etend le signe d'un registre d'un octet à un mot ou d'un mot à un mot long | EXT.W D0 |
EXTB | Etend le signe d'un registre d'un octet à un mot long | EXT.L D0 |
NEG | L'opérande est soustrait à 0 | NEG.L D0 |
NEGX | L'opérande et le bit X sont retranchés à 0 | NEGX.L D0 |
SWAP | Permute le mot de poids fort et le mot de poids faible d'un registre | SWAP.W D0 |
III-C-5. Opérations sur les bits▲
Instruction | Explication | Exemple |
---|---|---|
BCHG |
Le bit est complémenté. La destination est un registre. Le numéro du bit est modulo 32. La source est une constante |
BCHG.L #31,D0 |
BCHG |
Le bit est complémenté. La destination est une adresse. Le numéro du bit est modulo 8. La source est un registre |
BCHG.B D0,$1000 |
BCLR |
Le bit est forcé à zéro La destination est un registre. Le numéro du bit est modulo 32. La source est une constante |
BCLR.L #31,D0 |
BCLR |
Le bit est forcé à zéro La destination est une adresse. Le numéro du bit est modulo 8. La source est un registre |
BCLR.B D0,$1000 |
BSET |
Le bit est forcé à un. La destination est un registre. Le numéro du bit est modulo 32. La source est une constante |
BSET.L #31,D0 |
BSET |
Le bit est testé est forcé à un. La destination est une adresse. Le numéro du bit est modulo 8. La source est un registre |
BSET.B D0,$1000 |
BTST |
Le bit est testé mais pas modifié. La destination est un registre. Le numéro du bit est modulo 32. La source est une constante |
BTST.L #31,D0 |
BTST |
Le bit est testé mais pas modifié. La destination est une adresse. Le numéro du bit est modulo 8. La source est un registre |
BTST.B D0,$1000 |
III-C-6. Tests, comparaisons, branchements▲
Instruction | Explication | Exemple |
---|---|---|
CMP | Soustrait la source à la destination. Le résultat n'est pas rangé dans la destination. Le mot d'état seul est affecté | CMP.L D0,D1 |
CMPA | Comme CMP pour les registres d'adresse | CMPA.L,D0,A0 |
CMPI | Comparaison avec une constante | CMPI.L #$FF,D0 |
TST | Compare l'opérande à 0 | TST.W,D0 |
Bcc | Voir ci-dessous | BEQ BOUCLE |
Scc | Voir ci-dessous | SEQ.B D0 |
BRA | L'exécution continue au label | BRA BOU |
BSR | PC est poussé dans la pile. L'exemption continue au label | BSR MONPROC |
RTS | Pousse le sommet de la pile dans PC | RTS |
JSR | Comme BSR mais l'adresse est codée en dur | JSR (A0) |
JMP | Comme BRA mais l'adresse est codée en dur | JMP (A0) |
JSR et JMP sont à éviter si l'on veut faire des modules translatables. Préférer BSR et BRA.
Bcc est une série d'instruction qui, si la condition est vérifiée poursuivent
l'exécution à un label précis.
cc peut avoir les valeurs suivantes :
Valeur de cc | Explication |
---|---|
CC ou HS | Retenue à zéro |
CS ou LO | Retenue à 1 |
EQ | Egal |
GE | Supérieur ou égal signé |
GT | Supérieur signé |
HI | Supérieur |
LE | Inférieur ou égal signé |
LS | Inférieur ou égal |
LT | Inférieur signé |
MI | Négatif signé |
NE | Différent |
PL | Positif signé |
VC | Pas de débordement signé |
VS | Débordement signé |
Comme Bcc, Scc est une série d'instructions.
Si la condition est vraie, l'octet de la destination est forcé à FF
sinon, il est mis à zéro.
cc peut avoir les valeurs suivantes :
Valeur de cc | Explication |
---|---|
CC | Retenue à zéro |
CS | Retenue à 1 |
EQ | Egal |
GE | Supérieur ou égal signé |
GT | Supérieur signé |
HI | Supérieur |
LE | Inférieur ou égal signé |
LS | Inférieur ou égal |
LT | Inférieur signé |
MI | Négatif signé |
NE | Différent |
PL | Positif signé |
VC | Pas de débordement signé |
VS | Débordement signé |
F | Jamais vrai |
T | Toujours vrai |
III-C-7. Instructions de contrôle et divers▲
Série des instructions génériques :
Instruction | Explication | Exemple |
---|---|---|
LEA | Calcule l'adresse effective et la range dans la destination. | LEA.L (12,A0),A1 |
PEA | Comme LEA mais pousse le résultat dans la pile | PEA.L (12,A0) |
LINK | Voir gestion des variables | |
ULNK | Voir gestion des variables | |
NOP | Ne rien faire | NOP |
TRAP | Voir gestion des exceptions | |
MOVE from CCR | Copie le CCR dans la destination | MOVE.WCCR,D0 |
MOVE to CCR | Copie la source dans le CCR | MOVE.W D0,CCR |
Série des instructions privilégiées :
Instruction | Explication | Exemple |
---|---|---|
MOVE from SR | Copie le registre d'état | MOVE.W SR,D0 |
MOVE to SR | Copie la source dans le registre d'état | MOVE.W D0,SR |
MOVEC | Transfère le contenu d'un registre dans un registre de contrôle. | MOVEC.L D0,VBR |
RTE | Restaure depuis la pile SR puis PC. | RTE |
STOP | Lance une exception de type RESET | STOP #$AF8E |
III-D. Exemples▲
Pour se familiariser avec le jeu d'instruction des MCF, il serait bon de regarder ces
quelques exemples/exercices classiques :
III-D-1. Le codage BCD▲
Ce module, donne en code BCD l'équivalent d'un mot de 16 bits
Le nombre de départ est dans les 16 bits de poids faible de D0
Le résultat est dans D1.
Exemple : D0 0000FFFF
donne D1 : 00065535
Cela permet de traduire directement le nombre vers un afficheur sans
passer par l'ASCII
Algorithme :
Un mot de 16 bit représente une valeur non signé comprise
entre 0 et 65535. On a donc cinq chiffres entre 0 et 9
a trouver pour exprimer la valeur en BCD
Entier N //valeur initiale sur 16 bits
Entier r <br/>
<br/>
On divise N par 10000 -> premier chiffre du résultat <br/>
r <- reste de la division <br/>
On divise r par 1000 -> second chiffre du résultat <br/>
r <- reste de la division <br/>
On recommence avec 100 puis 10 -> troisième et quatrième chiffres <br/>
Le reste donne le cinquième chiffre <br/>
<br/>
La question de la réentrance et de la sauvegarde du contexte sont volontairement ignorées.
org $10000
debut: nop ;ne rien faire
andi.L #$FFFF,D0 ;on efface les 16 bits de poids fort de D0
move.L D0,D2 ;copions D0 dans D2
clr.L D1 ;effaçons D1
move.L #10000,D3 ;plaçons 10000 dans D3. La division ne supporte pas l'adressage immédiat
divu.w D3,D2 ;divisons de D2 (sur 32 bits) par D3 (sur 16 bit).
;Le résultat sera dans les 16 bits de poids faible de D2
;Le reste dans les 16 bits de poids fort de D2
move.w D2,D1 ;on place le resultat dans D1
lsl.L #4,D1 ;on décale D1 de 4 bits vers la gauche
swap.w D2 ;on permute les 16 bits de pods faible et les 16 bits de poids fort de D2
fin_1: nop
Nous avons maintenant, notre première valeur dans la partie supérieure de l'octet de poids faible de D1
D1.B | |
15 | 8 7 6 5 4 3 2 1 0 |
. x x x x . . . . |
Passons à la seconde valeur
debut_2: nop
andi.L #FFFF,D2
move.L #1000,D3 ;Une division par 10 de D3 serait bien plus couteuse
divu.w D3,D2
move.b D2,D3 ;ce qui nous interesse est dans l'octet de poids faible de D2
andi.L #F,D3 ;plus précisement dans les 4 premier bits
or.L D3,D1
lsl.l #4,D1
swap.w D2
fin_2: nop
Maintenant que nous avons compris le mécanisme Terminons ou plutôt terminez ....... ;-)
faire des variantes sur 32 bits, en signé ...
III-D-2. Addition de deux mots de 64 bits▲
Ce module additionne deux mots de 64 bits.
Pour l'instant, nous ne gérons pas les variables.
Les opérandes se situerons donc à une adresse
mémoire précise.
Le résultat sera également placé en mémoire.
Nous réserverons donc un espace de 7x32 bits
d'origine mem répartie comme il suit :
label | exemple | opérande |
---|---|---|
mem | 1000 | Opérande 1 poids fort |
... | ||
1007 | Opérande 1 poids faible | |
1008 | Opérande 2 poids fort | |
... | ||
100F | Opérande 2 poids faible | |
memOp | 1010 | |
... | ||
1013 | Retenue dans le bit de poids faible | |
1014 | Résultat poids fort | |
... | ||
1017 | ||
1018 | ||
... | ||
101B | Résultat poids faible | |
memRes | 101C |
On réserve 32 bits pour
stocker la retenue. Pour une question de performance et
à moins d'être vraiment contraint par l'espace, c'est la
meilleure solution.
*Directives d'assemblages et définition de constantes *
mem_op equ $20010 ;définition de deux constantes
mem_res equ $2001C
org $10000 ; adresse à laquelle le programme sera chargé
cpu 5206e ; cela permet de gérer les versions des MCF
opt optimise=all; On demande au compilateur d'être intelligeant.
*dans cet entête, dans ce cas précis seul org est obligatoire. Le reste est donné à titre d'exemple*
debut: nop
movea.L #mem_op,A0 ;mise en place des pointeurs
movea.L #mem_res,A1
addi.L #0,D0 ;mise à zéro du bit x
move.L -(A0),D0 ;récupération des poids faibles de l'operande 2
move.L (-8,A0),D1 ;récuperation des poids faibles de l'operande 1
addx.L D0,D1 ;L'utilisation d'addx plustôt que d'add ne se justifiera que plus tard.
;En M68K on aurait pu aller chercher la source directement en mémoire
move.L D1,-(A1) ;On range les poids faibles du résultat
move.L -(A0),D0 ;Cette sequence est exactement identique à la précedente (d'où l'addx).
move.L (-8,A0),D1 ;Ne pourrait-on pas boucler ? Essayez (attention à la retenue)! est-ce interessant ?
addx.L D0,D1
move.L D1,-(A1)
clr.L D0 ;On efface D0
addx.L D0,D0 ;On récupère la retenue
move.L D0,-(A1) ;On la range
fin: nop
III-D-3. Parcours itératif d'un tableau d'octet▲
L'objet de ce module, sera de comprendre certains travers liés à l'absence de maîtrise du jeu d'instruction. Le but du jeu sera donc de comprendre en quoi ce code est nul et de l'améliorer.
Le tableau sera désigné par une adresse de départ dans A0 et un offset dans D0. La valeur recherchée sera dans D1.b Le résultat sera un pointeur placé dans A1.
org $10000
debut : nop
move.L D0,D2 ; sauvegardons l'offset de réference
bou: cmp.B (A0,D2),D1 ;La valeur pointé par A0 + offest est-elle la valeur cherchée ?
beq trouve ;Si oui on va à trouve
subi.L #1,D2 ;Sinon on décrémente l'offset
bne no_value ;Si l'offset est négatif on a pas trouvé
bra bou ;Sinon on recommence
no_value: clr.L A1 ;Si on n'a rien trouvé, on renvera un pointeur = 0
bra fin ;et on termine
trouve: movea.L A0,A1 ;Si on a trouvé, on calcule l'adresse du pointeur
adda.L D2,A1
fin: nop
Il faudrait regarder un peu du coté de l'instruction Scc, de LEA... Puis on doit pouvoir ne pas utiliser de BRA.