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<A), on le fait et on met le bit C du résultat à 1, sinon on met juste ce même bit à  0.  Enfin, dans les deux cas, on décrémente C et on boucle jusqu'à C=0. Ca, c'est  dans  la théorie. Dans la pratique, ça donne ça:

 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 même 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-être 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

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

Lien(s):
» Coding » Cours et Initiation par Antoine / POW
» Coding » Clefs pour Amstrad 2 : 50 - Amsdos - Ouverture Fantome d'un Fichier
» Coding » Z80: Les Codes Speciaux (2/2)
» Coding » Z80: Les Codes Speciaux (1/2)
» Coding » Z80: La Multiplication
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.732-desktop/c
Page créée en 739 millisecondes et consultée 5030 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.