Revenir au plan du site
La multiplication
Vous avez peut-être cherché dans la liste des instructions si une opération DIV ou MUL existait, afin de diviser ou multiplier. Comme la quasi totalité des processeurs 8 bits, le Z80
ne sait pas faire de multiplications nativement. Il faut soit ajouter plusieurs fois de suite (le + simple, mais lent), soit utiliser une suite de décalages et additions (mieux). Il
reste la possibilité de précalculer des résultats de multiplication et d'utiliser des tables en mémoire, sachez que ça existe, mais nous verrons ça plus tard.
La multiplication permet par exemple d'adresser une case dans un tableau à deux dimensions. Avec un tableau de 10 cases sur 10, si on souhaite le deuxième élément de la 3è ligne, il faut
multiplier le nombre de lignes par 10 et ajouter 2. Autrement dit, l'index de la case qu'on souhaite est index = largeur x rangée + colonne
; D=rangée
; E=colonne
largeur=10 ; variable contenant la largeur de notre tableau
LD HL,tableau ; on part de la première case, le début du tableau
; ajouter les lignes (ou rangées)
LD A,D
OR A
JR Z,pasDeRangee ; si rangée vaut zéro, on n'additionne rien
LD BC,largeur
compteRangees
ADD HL,BC
DEC D ; on décompte le nombre de rangées
JR NZ,compteRangees ; on recommence l'ajout tant que notre compteur ne vaut pas zéro
pasDeRangee
LD D,0
ADD HL,DE ; ajouter la colonne à l'index
RET ; terminé, on renvoie l'adresse de la cellule dans HL
|
Cette routine est très simple et peu optimisée. Comme nos nombres sont binaires (base 2), cela signifie qu'un décalage des bits multiplie ou divise le nombre par deux. Pour de petites multiplications, il est plus
simple de conserver cette boucle ou de faire quelques additions mais pour des nombres plus grands, la boucle devient trop lourde. On peut tester chaque bit de notre valeur et ajouter les puissances de 2 respectives,
en fonction que ce bit soit présent ou non.
; E=rangée
; C=colonne
largeur=10 ; variable contenant la largeur de notre tableau
LD HL,0 ; on initialise notre accumulateur 16 bits à zéro
LD D,H ; on va utiliser DE pour la valeur de la rangée, on met la partie haute à zéro
LD B,8 ; on va multiplier par une valeur 8 bits
multiply
rrca ; on décale à droite pour mettre le bit le plus faible dans la carry
jr nc,.noAdd ; si la carry est vide, pas d'addition
add hl,de
.noAdd
sla de ; meta-instruction équivalente à [ sla e : rl d ], on multiplie DE par 2
djnz multiply
ld b,0 ; on complète la partie haute de BC pour utiliser la valeur de la colonne toujours dans C
add hl,bc ; on ajoute la colonne
ld bc,tableau
add hl,bc ; on ajoute l'offset du tableau
RET ; terminé, on renvoie l'adresse de la cellule dans HL
|
Un déroulé vaut mieux qu'on long discours :
Si nous multiplions par exemple, par la valeur 10, il faut considérer la valeur binaire 10 (8+2) soit %1010. Admettons qu'on veuille multiplier 23 par 10.
On décale A vers la droite => %101 C=0, pas de carry, on ne fait pas l'addition.
On décale DE d'un bit vers la gauche, on multiplie par deux donc.
On décale A vers la droite => %10 C=1, carry, on fait l'addition.
On décale A vers la droite => %1 C=0, carry, on ne fait pas l'addition.
On décale A vers la droite => %0 C=1, carry, on fait l'addition.
A vaut zéro, on ne fera aucune autre addition.
HL contient 230 soit 23x10.
Cette routine est générique et peut multiplier par n'importe quelle valeur 8 bits, mais si vous voulez multiplier par dix, comme on vient de le voir en faisant le déroulé ici, il suffit de deux additions et 3 décalages. Une routine dédiée sera plus rapide, plus courte et aussi plus facile à comprendre.
; DE=valeur à multiplier par 10
ld hl,0
sla de ; DE = DE x 2
add hl,de ; HL = DE x 2
sla de ; DE = DE x 2 (donc x 4 rapport à la valeur de départ)
sla de ; DE = DE x 2 (donc x 8 rapport à la valeur de départ)
add hl,de ; HL = DE x 2 + DE x 8
; HL = DE x 10
ret
|
Dans l'article suivant, nous allons mettre en pratique des multiplications pour afficher un point à l'écran.