CODINGANTOINE ★ ASSEMBLEUR Z80 - La Multiplication ★

Z80: La MultiplicationCoding Antoine
Eh oui ce mois-ci marque le début d'un nouveau dossier sur l'arithmétique. A propos d'arithmétique, cette discipline o combien noble est en train d'etre réinventée par Amstrad 100 %; prenez le numéro 42 et lisez un peu la page 31 ... 2+2+1+1=8 !!! Etonnant, non ?? Pour revenir à notre dossier, il ne s'agit pas d'apprendre l'addition à Poum, mais de faire en langage machine des opérations un peu plus complexes que celles directement réalisables avec les mnémoniques du Z80.

Mais pourquoi chercher à faire ses propres routines alors qu'on pourrait utiliser celles qui sont en ROM ? Pour cela il faut se souvenir que le système peut traiter deux types de nombres: les entiers 16 bits et les nombres à virgule flottante codés sur 5 octets, dont 4 pour le nombre lui-meme, et un pour la mantisse, c'est-à-dire l'exposant en puissance de deux.

En pratique, on constate que ces deux types de nombres sont assez peu adaptés à nos besoins. En effet les opérations sur les entiers sont rapides mais ceux-ci sont limités de -32768 à 32767. D'autre part, les nombres à virgule flottante permettent un champ extremement vaste, mais les opérations sur ces nombres sont très complexes et très longues... De plus, pour le programmeur en assembleur, si les opérations à virgule flottante sont accessibles par les vecteurs système, il n'en est pas de meme pour l'arithmétique entière qui se situe dans la ROM Basic, et donc il faut faire un saut directement dans cette Rom, ce qui demande tout d'abord de repérer les adresses exactes de ces routines, et de plus cela supprime la compatibilité entre les différents modèles de CPC.

Nous allons donc commencer ce mois-ci par la multiplication. Bien sur il vient en premier à l'esprit l'idée d'additionner n fois le nombre à lui-meme, ce qui donnera bien comme résultat le produit des deux :

LD B,MULTI
LD DE,NOMBRE
LD HL,0
BOUCLE ADD HL,DE
DJNZ BOUCLE

C'est en effet la définition meme de la multiplication. On s'aperçoit cependant vite qu'à partir d'un certain nombre (B=16 environ), la routine est trop lente. Avec un multiplicateur sur 8 bits, c'est déjà long, alors je ne vous conseille pas d'essayer une multiplication 32 bits par cette méthode !!

Il faut alors penser à certains cas particuliers... en effet que fait-on par exemple pour multiplier un nombre par 2,4,8 ou toute autre puissance de deux en assembleur ? Vous me répondrez bien sur que vous faites un truc du genre ADD A,A ou ADD HL,HL et ceci 1,2,3 fois ou plus selon l'exposant n de la puissance de deux 2^n.

Pour généraliser il nous faut un peu d'algèbre... Par exemple pour multiplier l'accu par 9, on serait tenté de faire:

LD C,A
LD B,9
BOUCLE ADD A,C
DJNZ BOUCLE

Mais si on cherche autre chose, on trouve 9=8a+a... J'espère que vous comprenez ça !! Or, la multiplication par 8 et par 1, on sait faire...

LD E,A
ADD A,A
ADD A,A
ADD A,A
ADD A,E

On voit bien que les trois ADD A,A reviennent à faire une rotation de 3 bits vers la gauche, ou encore à une multiplication par 8... Ensuite on ajoute l'ancien contenu de A, ce qui fait bien une multiplication par 9 !! Je me répète mais c'est pour etre sur que vous ayez bien compris... Maintenant la question est de savoir si tout nombre, quel qu'il soit, peut etre converti en une somme de puissances de deux... C'est là qu'il va falloir vous souvenir de vos premiers cours d'assembleur, que dis-je, de votre premier contact avec le

binaire... En effet de quoi est constitué un nombre binaire, hein ? Mais de puissances de deux bien sur !! Rappelez-vous... chaque bit d'un octet est associé à une puissance de deux.

Ainsi le bit 0 correspond à 2^0=1, le bit 1 à 2^1=2, ainsi de suite jusqu'au bit n qui correspond à 2^n. Tout devient ainsi limpide, il nous suffira de tester tous les bits, du bit de poids faible au bit de poids fort, et d'ajouter si ce bit est à un, 2^n au résultat, n étant le 'numéro' du bit. Tout ceci sera effectué très simplement à l'aide de rotations et d'additions.

A titre d'exemple, voici une routine qui effectue la multiplication d'un nombre 16 bits par un multiplicateur 8 bits, avec un résultat stocké lui aussi sur 2 octets:

10 ORG &A000 Explications: à chaque parcours de la
20 LD HL,(NOMBRE) boucle LOOP, la rotation charge dans le
30 LD A,(MULTI) Carry l'ancien état du bit 0 de
40 LD DE,0 ;l'accumulateur. Si le carry est à un, on
50 LD B,8 ajoute à DE le nombre à multiplier HL. Puis
60 LOOP RRA on multiplie HL par deux pour tenir compte
70 JR NC,LOOP_SUI1 du rang des différents bits.
80 EX DE,HL Après avoir parcouru la boucle 8 fois (car
90 ADD HL,DE le multiplicateur est codé sur 8 bits), on
100 EX DE,HL charge le résultat à l'adresse mémoire
110 LOOP_SUI1 ADD HL,HL et c'est fini.
120 DJNZ LOOP ;Vous pouvez bien sur tester cette routine
130 LD (RESULT),DE sous Basic après l'assemblage. A cet effet
140 RET n'oubliez pas que les nombres 16 bits
150 NOMBRE DW 0 doivent etre sous le format octet
160 MULTI DB 0 faible-octet fort et que cette routine ne
170 RESULT DW 0 tient pas compte des retenues.

Cette méthode est très rapide puisque la boucle est executée un nombre de fois correspondant au nombre de bits du multiplicateur: 8 fois pour un multiplicateur 8 bits, etc... Elle est appelée la méthode égyptienne (ne me demandez pas pourquoi !) et c'est elle qui est utilisée, par exemple, par l'arithmétique entière du Basic.

Mais bien sur, vous aurez surement besoin un jour de manipuler des nombres plus grands. Comme le Z80 n'a que peu de registres, on ne pourra donc pas travailler directement sur ces registres mais sur des emplacements de la mémoire. Voici donc une routine qui effectue une multiplication d'un 'long word'(32 bits) par un mot (16 bits), avec un résultat sur 32 bits également...

10 ORG &A000 La structure de départ est gardée, à la
20 LD IX,NOMBRE différence que j'ai séparé les sous-routines
30 LD IY,RESULT d'addition et de rotation 32 bits pour plus
40 CALL INIT ;de clarté.
50 LD HL,(MULTI)
60 LD B,16 ;La routine d'initialisation (label INIT) est
70 LOOP RR H nécessaire pour mettre les 4 octets (soit un
80 RR L mot long) du résultat à zéro.
90 CALL C,ADD32 Si le multiplicateur fait plus de 2 octets
100 CALL ROTATION (un mot), il faudra également le traiter en
110 DJNZ LOOP ;adressage indirect ou indexé.
120 RET
130 NOMBRE DS 4
140 MULTI DS 2 Pour l'addition et la rotation 32 bits j'ai
150 RESULT DS 4 choisi des routines linéaires car elles
160 INIT LD HL,RESULT nécessitent très peu de mise au point. Pour
170 LD DE,RESULT+1 des nombres plus grands (64 bits ?), il vaut
180 LD BC,3 ;mieux réserver les registres d'index pour le
190 LD (HL),B ;multiplicateur; on prendra alors pour
200 LDIR adresser le NOMBRE et le RESULTat en mémoire,
210 RET les registres HL et DE, et on effectuera
220 ADD32 LD A,(IY+0) l'addition et la rotation à l'aide de
230 ADD A,(IX+0) boucles.
240 LD (IY+0),A
250 LD A,(IY+1) Vous trouverez sur la disquette le code objet
260 ADC A,(IX+1) de cette routine sous le nom MULTI32.OBJ, en
270 LD (IY+1),A voici les adresses importantes:
280 LD A,(IY+2)
290 ADC A,(IX+2) DEBUT : &A000
300 LD (IY+2),A FIN : &A06A
310 LD A,(IY+3) EXECUTION: &A000
320 ADC A,(IX+3)
330 LD (IY+3),A LABELS: NOMBRE &A01D
340 RET MULTI &A021
350 ROTATION OR A RESULT &A023
360 RL (IX+0)
370 RL (IX+1) ;Vous pouvez parfaitement tester cette routine
380 RL (IX+2) ;sous Basic, pour cela un programme inclus sur
390 RL (IX+3) ;la disquette vous permettra, après que vous
400 RET avez chargé le code objet, d'entrer les
410 ;END paramètres en mémoire (MULTI32.BAS).

Cette routine, bien que traitant des nombres importants, reste rapide puisqu'elle prend au pire et à tout casser (&FFFFFFFF * &FFFF) 2,04 millisecondes !!! Maintenant si vous voulez travailler sur des nombres signés, il faut que votre routine détermine le signe du résultat d'après celui des deux termes, qu'elle appelle la routine de multiplication sans signe avec les valeurs absolues des nombres, puis qu'elle change le signe du résultat selon ce qu'elle a prévu auparavant.

Voilà, après ces quelques 10758 octets (!) de délire intellectuel, j'espère que vous avez tout compris parce que je crois que j'aurais eu du mal à faire plus dilué... La prochaine fois vous aurez droit soit à la division, soit au format BCD et tout le casse-tete (pour rester poli...) qu'il engendre; ça dépendra de l'état d'avancement de mes routines mais aussi et surtout de votre courrier. Ben oui c'est tout de meme vous qui lisez MMPF (Micro Mag & Press Fire bande d'ignares), c'est donc vous qui devez me dire que traiter dans le prochain numéro, alors écrivez-moi et n'oubliez pas le traditionnel timbre à 2,50 F. si vous désirez une réponse personnelle.

Antoine / POW pour MM&PF6

★ ANNÉE: ???
★ AUTEUR: ANTOINE

★ AMSTRAD CPC ★ A voir aussi sur CPCrulez , les sujets suivants pourront vous intéresser...

Lien(s):
» Coding » Z80: La division
» Coding » Clefs2 50 - Amsdos - Ouverture Fantome d'un Fichier
» Coding » Z80: Les Codes Speciaux (1/2)
» Coding » Z80: Les Codes Speciaux (2/2)
» Coding » Cours et Initiation par Antoine / POW
Je participe au site:

» Vous avez remarqué une erreur dans ce texte ?
» Aidez-nous à améliorer cette page : en nous contactant via le forum ou par email.

CPCrulez[Content Management System] v8.7-desktop/c
Page créée en 344 millisecondes et consultée 4418 fois

L'Amstrad CPC est une machine 8 bits à base d'un Z80 à 4MHz. Le premier de la gamme fut le CPC 464 en 1984, équipé d'un lecteur de cassettes intégré il se plaçait en concurrent  du Commodore C64 beaucoup plus compliqué à utiliser et plus cher. Ce fut un réel succès et sorti cette même années le CPC 664 équipé d'un lecteur de disquettes trois pouces intégré. Sa vie fut de courte durée puisqu'en 1985 il fut remplacé par le CPC 6128 qui était plus compact, plus soigné et surtout qui avait 128Ko de RAM au lieu de 64Ko.