CODINGANTOINEGAMESLIST ★ Z80: LA DIVISION ★

ASSEMBLEUR Z80 - La division

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 meme 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 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

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

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