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 BOUCLEC'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 BOUCLEMais 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,EOn 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 lebinaire... 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 ★ AMSTRAD CPC ★ A voir aussi sur CPCrulez , les sujets suivants pourront vous intéresser... |
|
|