CODINGANTOINE ★ ASSEMBLEUR Z80 - La division ★

Z80: La divisionCoding Antoine

Pour que notre Z80 puisse diviser un  nombre  binaire  par  un  autre,  nous allons  utiliser  la  meme  méthode  que  pour  la  multiplication:  la  méthode égyptienne ! Si vous vous souvenez bien, le principe de base consistait  à  dire que multiplier un nombre par un autre revient à le multiplier  par  chacune  des puissances de deux qui composent le multiplicateur,  et  tout  était  réglé  par quelques décalages et additions. Ainsi, avec la méthode égyptienne,  l'opération A*10 se décomposait en A*8+A*2. On serait donc tenté de faire de  meme  pour  la division... Cependant, il y a un hic, car on ne peut pas développer une division n'importe comment: A/10 n'est pas égal à A/8+A/2 !!!

Revenons donc un peu en arrière: résoudre une division revient à  trouver  X égal à A/B (pour ceux qui ne comprendraient pas, continuez à regarder Le  Bebete Show...), c'est-à-dire tel que A=B*X. Donc ce n'est pas le diviseur B  que  l'on va décomposer, mais le dividende A !!! En effet, plusieurs fois de suite, on  va multiplier  B  par  des  puissances   de   deux   décroissantes   (par   exemple 16,8,4,etc...) d'exposant C, puis, si on peut soustraire B à A (B

  10           ORG  &A000                 Il y a quelques  différences  avec  la
  20           LD   A,(NUMBER2)        méthode théorique: tout d'abord, au  lieu
  30           OR   A                  de multiplier à chaque fois  le  diviseur
  40           RET  Z                  par une puissance de deux différente,  on
  50           LD   B,0                calcule tout d'abord sa  valeur  maximale
  60 LOOP      ADD  A,A                (dans la boucle LOOP), puis on le  divise
  70           INC  B                  par deux (RR C) à chaque parcours  de  la
  80           JR   NC,LOOP            boucle principale DIV_LOOP.
  90           RRA                        Par ailleurs, les différents  bits  du
 100           LD   C,A                résultat sont modifiées  à  l'aide  d'une
 110           LD   A,(NUMBER1)        simple   rotation   (RL   E),   car   les
 120           LD   E,0                instructions de manipulation de  bits  du
 130 DIV_LOOP  CP   C                  Z80 (RES et SET) ne désignent les numéros
 140           CCF                     de bits que par adressage immédiat et non
 150           PUSH AF                 par    adressage    implicite     (phrase
 160           RL   E                  volontairement incompréhensible  pour  le
 170           POP  AF                 commun   des    programmeurs...).    Vous
 180           JR   NC,NOT             remarquerez qu'à la fin,  A  contient  le
 190           SUB  C                  reste  de  la  division  entière   (aussi
 200 NOT       RR   C                  appelé modulo).
 210           DJNZ DIV_LOOP              Notez également que, si cette  routine
 220           LD   (REST),A           est prete à l'emploi, elle n'en pas  pour
 230           LD   A,E                autant  optimisée  pour  des  raisons  de
 240           LD   (RESULT),A         clarte... Notamment, les PUSH et POP  AF,
 250           RET                     qui servent ici à sauvegarder  l'état  du
 260 NUMBER1   DEFB 0                  Carry, mais font quand meme, à eux  deux,
 270 NUMBER2   DEFB 0                  7   cycles   machine   (en    clair,    7
 280 RESULT    DEFB 0                  microsecondes), auraient pu  etre  évités
 290 REST      DEFB 0                  sans beaucoup de difficultes.

D'autre part vous remarquerez qu'il y a deux boucles, dont  la  première  ne sert qu'à préparer le contenu du diviseur  (le  registre  C)  et  pourrait  etre supprimée. C'est ce que nous allons voir avec la routine suivante...

 

 10 DIV       LD   D,A                 En effet, cette routine est  une  routine
 20           XOR  A              de division 16 par 8 bits avec résultat sur  8
 30           LD   E,A            bits également. On profite donc de la phase de
 40           SRL  D              conversion du diviseur de 8 à 16 bits pour  le
 50           RR   E              multiplier par 256, évitant ainsi une première
 60           LD   C,8            boucle de préparation comme  dans  la  routine
 70 DIV_LOOP  OR   A              précédente. Ensuite on le divise une  première
 80           SBC  HL,DE          fois par 2 avant de  rentrer  dans  la  boucle
 90           JR   NC,DIV_YES     elle-meme. Pourquoi effectuer  cette  division
100           ADD  HL,DE          avant la boucle me  direz-vous,  puisqu'on  la
110 DIV_YES   CCF                 retrouve  ensuite  à  l'intérieur   de   cette
120           RLA                 fameuse boucle DIV_LOOP ? Oui bon c'est que je
130           SRL  D              suis très flemmard et, quand je me  suis  posé
140           RR   E              la meme question  que  vous,  je  n'avais  pas
150           DEC  C              vraiment envie de retoucher à ma routine... Si
160           JR   NZ,DIV_LOOP    vous voulez absolument gagner 4 microsecondes,
170           RET                 vous devrez déplacer les SRL D/RR E  au  début
180           END                 de la boucle  (juste  avant  le  OR  A).

(Paramètres: HL=dividende, A=diviseur; résultat: A=quotient, HL=reste (modulo)).

Maintenant voici le temps pris par ces 2 premières routines :

  • Division 8 bits :        mini.  35 cycles          maxi. 210 cycles
  • Division 16/8 bits :     mini. 155 cycles          maxi. 171 cycles

Au  niveau  rapidité  pur,  la  première  routine  est  donc   légèrement   plus avantageuse, mais la seconde est beaucoup plus stable  et  régulière  quels  que soient les paramètres, ce qui peut etre très utile dans  le  cas  de  programmes nécessitant une synchronisation assez précise.

Voici maintenant une troisième routine, qui elle vous réalisera une division sur 32 bits purs. Les paramètres sont  à  transmettre  aux  adresses  suivantes: NUMBER1 pour le dividende, NUMBER2 pour le diviseur. A la fin de la routine,  le quotient et le modulo seront contenus respectivement aux labels RESULT et  REST. Les 4 octets qui codent chacun de ces entiers seront  stockés,  comme  pour  les mots 16 bits, sous le format octet faible-octet fort.
 

  10           ORG  &A000             :: 350           CCF
  20           DI                     :: 360           JR   C,YES
  30           LD   HL,(NUMBER2)      :: 370 NO        ADD  HL,DE
  40           EXX                    :: 380           EXX
  50           PUSH BC                :: 390           ADC  HL,DE
  60           PUSH DE                :: 400           EXX
  70           PUSH HL                :: 410           OR   A
  80           LD   HL,(NUMBER2+2)    :: 420 YES       RL   C
  90           LD   A,H               :: 430           RL   B
 100           OR   L                 :: 440           EXX
 110           EXX                    :: 450           RL   C
 120           OR   H                 :: 460           RL   B
 130           OR   L                 :: 470           SRL  D
 140           JR   Z,OVER            :: 480           RR   E
 150           LD   A,1               :: 490           EXX
 160 PREP      INC  A                 :: 500           RR   D
 170           ADD  HL,HL             :: 510           RR   E
 180           EXX                    :: 520           DEC  A
 190           ADC  HL,HL             :: 530           JR   NZ,LOOP
 200           EXX                    :: 540           LD   (RESULT),BC
 210           JP   P,PREP            :: 550           LD   (REST),HL
 220           EXX                    :: 560           EXX
 230           EX   DE,HL             :: 570           LD   (RESULT+2),BC
 240           LD   HL,(NUMBER1+2)    :: 580           LD   (REST+2),HL
 250           LD   BC,0              :: 590 OVER      POP  HL
 260           EXX                    :: 600           POP  DE
 270           EX   DE,HL             :: 610           POP  BC
 280           LD   HL,(NUMBER1)      :: 620           EXX
 290           LD   BC,0              :: 630           EI
 300 LOOP      OR   A                 :: 640           RET
 310           SBC  HL,DE             :: 650 NUMBER1   DEFS 4,0
 320           EXX                    :: 660 NUMBER2   DEFS 4,0
 330           SBC  HL,DE             :: 670 RESULT    DEFS 4,0
 340           EXX                    :: 680 REST      DEFS 4,0

Je vous conseille fortement de décortiquer cette routine, ça  sera  surement très  instructif.  Si  vous  vous  demandez  pourquoi  j'utilise  les  registres alternatifs - qui, rappelons-le, obligent à sauvegarder les anciennes valeurs et à inhiber les interruptions -, sachez que cette routine est  à  peu  près  trois fois plus rapide qu'une routine équivalente qui  utiliserait  les  registres  16 bits comme pointeurs sur des cases mémoires. N'hésitez  jamais  à  utiliser  les registres alternatifs si vous n'avez pas assez d'un seul jeu de registres et que vous avez des contraintes de rapidité à respecter.

C'est enfin terminé pour cette fois-ci ! De toute façon  c'en  est  fini  de l'arithmétique qui commencait à devenir très très chiante. La prochaine fois  je vous ferai peut-etre un cours sur l'optimisation et tout ça... En attendant vous pouvez toujours m'écrire si vous avez des problèmes de programmation (assembleur mais aussi Basic), des questions sur le hard ou tous autres genres de trucs.

Bye...

Antoine / POW pour MM&PF

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

CPCrulez[Content Management System] v8.75-desktop/c
Page créée en 088 millisecondes et consultée 1470 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.