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.7-desktop/cache
Page créée en 110 millisecondes et consultée 2093 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.