La Divisione Binaria

by

Chessa Salvatore


In microprocessori dove la divisione e' parte delle istruzioni la cosa e molto semplice. Il dividendo e' caricato in un registro (spesso specializzato), il divisore in un'altro registro,il risultato ed il resto della divisione finiscono in altri registri ancora. Il tutto risulta estremamente semplificato. 4 righe di programma e il gioco e' fatto. Ad esempio per i processori della INTEL tipo 80x86 e sucessivi (compreso il PENTIUM) questo e' il sorgente per una divisione con un dividendo da 32 bit ed un divisore a 16 Bit



80x86

 
    MOV DX,10    ;  In DX i 16 Bit alti
    MOV AX,100   ;  In AX i 16 Bit bassi
    MOV BX,1000  ;  In BX il divisore
    DIV BX       ;  Esegui la divisione


Tutto qua. A questo punto troveremo il risultato in AX ed il resto in DX. Come potete vedere e' breve, semplice, conciso. Diverso e' il discorso quando si usano processori che non hanno la divisione tra le istruzioni. La faccenda si complica e il programma si allunga. Non dobbiamo mai dimenticare che un processore e' in ultima analisi solo un grosso stupidone incapace di fare alcunche' se non istruito con precisione. Di seguito vediamo una routine di divisione per Z80 sempre con dividendo da 32 Bit e divisore da 16 Bit esattamente come sopra.

Z80

   DE = Contiene i 16 bit alti del dividendo
   HL = Contiene i 16 bit bassi del dividendo
   BC = Contiene i 16 bit del divisore
        -----------------------------------------
   DE = Contiene i 16 bit alti del risultato
   HL = Contiene i 16 bit bassi del risultato
   BC = Contiene il resto

  ; inizializzazione dei registri 

     LD      DE,0AAAH        ; byte alto del dividendo
     LD      HL,0AAAAH       ; byte basso del dividendo
     LD      BC,0CH          ; divisore
     CALL    DIVIDE          ; esegui la divisione
     JP      100H            ; ritorna a capo

DIVIDE:

     LD      IX,DIVIDENDO    ; punta ai 32 bit del dividendo
     LD      (IX+0),D        ; carico (D) nel (MSB)
     LD      (IX+1),E        ; proseguo con i byte decrescenti
     LD      (IX+2),H        ; proseguo con i byte decrescent
     LD      (IX+3),L        ; carico (L) nel (LSB)

     LD      H,B             ; --------------------------
     LD      L,C             ;
     LD      B,16            ; In questa parte determino di
     LD      D,16            ; quanti loop e' necessaria la
PK1: LD      A,D             ; divisione in base al valore
     CP      H               ; del divisore in proporzione
     JR NC,  PK2             ; al dividendo.
     JP      START           ;

PK2: LD      E,B             ; Sono necessari 2 diversi loop.
     LD      B,4             ; uno per la divisione ed uno 
PK3: SLA     L               ; per estrarre il resto dalla 
     RL      H               ; coda del dividendo. 
     INC     E               ; aumentando il primo 
     DEC     D               ; deve diminuire il secondo 
     DJNZ    PK3             ; 
     LD      B,E             ; 
     JP      PK1             ; -------------------------- 


START: 

     CALL    RESET_RISULTATO ; azzera i 4 byte del risultato 
PK4: LD      C,0             ; (C) e' usato come flag 
     CALL    SHIFT_DIVIDENDO ; shift a sinistra del dividendo 
     CALL    SHIFT_RISULTATO ; shift a sinistra del risultato 
     CALL    COMP_SUB        ; compara e sottrai se necessario 
     DJNZ    PK4             ; ------------------------------- 

     LD      B,D             ; carica in (B) il totale dei loop 
     LD      D,(IY+0)        ; carica in (D) il (MSB) 
     LD      E,(IY+1)        ; del risultato 
     LD      H,(IY+2)        ; e carica in (L) il (LSB) 
     LD      L,(IY+3)        ; 
PK5: SLA     (IX+1)          ; eseguo uno shift a sinistra 
     RL      (IX+0)          ; sui 2 byte del resto 
     RL      (IX+3)          ; e li deposito momentaneamente 
     RL      (IX+2)          ; su 2 byte di appoggio 
     DJNZ    PK5             ; 
     LD      B,(IX+2)        ; in (B) il byte alto del resto 
     LD      C,(IX+3)        ; in (C) il byte basso del resto 
     RET                     ; -------------------------------- 


SHIFT_DIVIDENDO:  

     LD      IX,DIVIDENDO    ;  -------------------- 
     SLA     (IX+3)          ; rotazione a sinistra dei 
     RL      (IX+2)          ; 4 byte del risultato con 
     RL      (IX+1)          ; riporto del carry nel 
     RL      (IX+0)          ; bit 0 del byte che segue 
     RL      C               ; Se c'e' carry e' posto in C 
     RET                     ;  -------------------- 

SHIFT_RISULTATO:  

     LD      IY,RISULTATO    ;  -------------------- 
     SLA     (IY+3)          ; rotazione a sinistra dei 
     RL      (IY+2)          ; 4 byte del risultato con 
     RL      (IY+1)          ; riporto del carry nel 
     RL      (IY+0)          ; bit 0 del byte che segue 
     RET                     ;  -------------------- 

RESET_RISULTATO:  

     LD      IY,RISULTATO    ;  -------------------- 
     LD      (IY+0),0        ; 
     LD      (IY+1),0        ; azzera i 4 byte 
     LD      (IY+2),0        ; del risultato 
     LD      (IY+3),0        ; 
     RET                     ;  -------------------- 

COMP_SUB:  

     XOR     A               ;  -------------------- 
     OR      C               ; 
     JR NZ,  PK7             ; confronto il byte 
     LD      A,H             ; alto del divisore 
     CP      (IX+0)          ; con il byte alto del dividendo 
     JR Z,   PK6             ; se e' piu' grande ritorno 
     JR C,   PK7             ; se e' uguale 
     RET                     ;  -------------------- 

PK6: LD      A,L             ; confronto il byte 
     CP      (IX+1)          ; basso del divisore 
     JR Z,   PK7             ; con il byte basso del dividendo 
     JR C,   PK7             ; se e' piu' grande ritorno 
     RET                     ; 

PK7: LD      A,(IX+1)        ; essendo il divisore piu' 
     SUB     L               ; piccolo del dividendo 
     LD      (IX+1),A        ; lo sottraggo e..... 
     LD      A,(IX+0)        ; 
     SBC     H               ; 
     LD      (IX+0),A        ; 

     LD      A,(IY+3)        ;-------------------------- 
     ADD     A,1             ; 
     LD      (IY+3),A        ; 
     LD      A,(IY+2)        ; incremento i 4 byte 
     ADC     A,0             ; del risultato 
     LD      (IY+2),A        ; 
     LD      A,(IY+1)        ; 
     ADC     A,0             ; 
     LD      (IY+1),A        ; 
     LD      A,(IY+0)        ; 
     ADC     A,0             ; 
     LD      (IY+0),A        ;--------------------------- 
     RET     



   ;  -------------------   AREA DATI IN RAM   -------------- 


 DIVIDENDO:  DB 0,0,0,0 

 RISULTATO:  DB 0,0,0,0 


   ;   ------------------------------------------------------ 
 



Quella che segue e' la routine per il microcontrollore ST62x10. Questo microcontrollore della SGS ha un solo registro abilitato a svolgere operazioni logiche e aritmetiche.E' l'accumulatore A che di conseguenza preleva dalla RAM o dai registri i dati, li elabora e li rimette da dove li ha presi. La routine e' analoga a quella per lo Z80.




ST6

Questa divisione richiede che il dividendo e il divisore siano posti in memoria. al ritorno dalla divisione l'intero si trovera' su 4 byte nel risultato ed il resto su 2 byte si trovera' al posto del divisore.

        ;        sezione variabili (084H ..0BFH)  



 DVD_1      .DEF  084H       ; 1 byte msb dividendo 
 DVD_2      .DEF  085H       ; 2 byte 
 DVD_3      .DEF  086H       ; 3 byte 
 DVD_4      .DEF  087H       ; 4 byte lsb dividendo 


 DVS_1      .DEF  088H       ; 1 byte msb divisore 
 DVS_2      .DEF  089H       ; 2 byte lsb divisore 


 RESULT_1   .DEF  08AH       ; 1 byte msb risultato 
 RESULT_2   .DEF  08BH       ; 2 byte 
 RESULT_3   .DEF  08CH       ; 3 byte 
 RESULT_4   .DEF  08DH       ; 4 byte lsb risultato 

 FLAG       .DEF  08EH       ; variabile ad uso flag 


       ;--------------------------------------------------- 



      CALL    DIVISIONE    ; routine di divisione 
      JP      0880H        ; ritorna da capo 

 DIVISIONE:  


      LDI     V,16         ;----------------------------- 
      LDI     Y,16         ; 
 SK1: LD      A,DVD_1      ; 
      CP      A,DVS_1      ; 
      JRNC    SK2          ; In questa prima parte di programma 
      JP      BEGIN        ; adatto il numero dei loop in base 
 SK2: LDI     W,4          ; alla proporzione tra dividendo e 
 SK3: INC     V            ; divisore. Il loop parte inizialmente 
      DEC     Y            ; da 16 e viene incrementato di 4 ogni 
      LD      A,DVS_2      ; volta che non soddisfa la richiesta 
      SLA     A            ; E' necessario trovare il valore per 
      LD      DVS_2,A      ; 2 loop. Il primo (V) esegue lo shift 
      LD      A,DVS_1      ; sul dividendo ed il risultato mentre 
      RLC     A            ; il secondo (Y) al termine della 
      LD      DVS_1,A      ; divisione esegue lo shift sul 
      DEC     W            ; dividendo per estrarre il resto 
      LD      A,W          ; 
      CPI     A,0          ; 
      JRZ     SK4          ; 
      JP      SK3          ; 
 SK4: JP      SK1          ;----------------------------- 

 BEGIN:  

      CALL    CLEAN_RESULT ; azzera le variabili del risultato 
 SK5: RES     1,FLAG       ; resetto il bit 1 dei flag 
      CALL    SHIFT_DVD    ; shift a sinistra del dividendo 
      CALL    SHIFT_RST    ; shift a sinistra del risultato 
      CALL    COMP_SUB     ; comparazione e sottrazione 
      DEC     V            ; 
      LD      A,V          ; 
      CPI     A,0          ; ripeti fino a che (V)=0 
      JRNZ    SK5          ;----------------------------- 

 SK6: LD      A,DVD_2      ;----------------------------- 
      SLA     A            ; 
      LD      DVD_2,A      ; 
      LD      A,DVD_1      ; Terminata la divisione, il resto 
      RLC     A            ; si trova nei due byte alti del 
      LD      DVD_1,A      ; dividendo. Lo estraggo tramite 
      LD      A,DVS_2      ; shift e lo passo ai 2 byte del 
      RLC     A            ; divisore con un numero di loop 
      LD      DVS_2,A      ; caricato su (Y) determinato 
      LD      A,DVS_1      ; dalla proporzione tra dividendo 
      RLC     A            ; e divisore 
      LD      DVS_1,A      ; 
      DEC     Y            ; 
      LD      A,Y          ; 
      CPI     A,0          ; 
      JRZ     SK7          ; 
      JP      SK6          ;----------------------------- 
 SK7: RET     

 SHIFT_DVD:   

      LD      A,DVD_4      ; --------------------------- 
      SLA     A            ; 
      LD      DVD_4,A      ; 
      LD      A,DVD_3      ; 
      RLC     A            ; Eseguo lo shif a sinistra 
      LD      DVD_3,A      ; dei 4 byte del dividendo 
      LD      A,DVD_2      ; 
      RLC     A            ; 
      LD      DVD_2,A      ; Se c'e ulteriore riporto 
      LD      A,DVD_1      ; dal (MSB) 
      RLC     A            ; setto il bit 1 dei flag 
      LD      DVD_1,A      ; 
      JRNC    SK8          ; 
      SET     1,FLAG       ; --------------------------- 
 SK8: RET      

 SHIFT_RST:   

      LD      A,RESULT_4   ;  --------------------------- 
      SLA     A            ; 
      LD      RESULT_4,A   ; Eseguo lo shif a sinistra 
      LD      A,RESULT_3   ; dei 4 byte del risultato 
      RLC     A            ; 
      LD      RESULT_3,A   ; 
      LD      A,RESULT_2   ; 
      RLC     A            ; 
      LD      RESULT_2,A   ; 
      LD      A,RESULT_1   ; 
      RLC     A            ; 
      LD      RESULT_1,A   ; --------------------------- 
      RET      

 COMP_SUB:   

      JRS     1,FLAG,SX2   ;  Se e' alto il bit salta 
      LD      A,DVS_1      ; 
      CP      A,DVD_1      ;  Confronto tra dividendo 
      JRZ     SX1          ;  e divisore 
      JRC     SX2          ; 
      RET       

 SX1: LD      A,DVS_2      ;  finche' il divisore 
      CP      A,DVD_2      ;  e' maggiore,ritorna 
      JRZ     SX2          ; 
      JRC     SX2          ; 
      RET       

 SX2: LD      A,DVD_2      ; ---------------------------- 
      SUB     A,DVS_2      ; 
      LD      DVD_2,A      ;   Sottraggo il divisore 
      JRNC    SX3          ;   al dividendo 
      DEC     DVD_1        ;   e.... 
 SX3: LD      A,DVD_1      ; 
      SUB     A,DVS_1      ; 
      LD      DVD_1,A      ; ---------------------------- 

      LDI     W,4          ; ---------------------------- 
      LDI     X,RESULT_4   ; 
 SX4: LD      A,(X)        ; 
      ADDI    A,1          ; 
      LD      (X),A        ; 
      JRNC    SX5          ; 
      DEC     X            ;  Incremento il risultato 
      DEC     W            ; 
      LD      A,W          ; 
      CPI     A,0          ; 
      JRNZ    SX4          ; ---------------------------- 
 SX5: RET      


 CLEAN_RESULT:  

      LDI     W,4          ; ---------------------------- 
      LDI     X,RESULT_1   ; 
 SX6: CLR     A            ; 
      LD      (X),A        ; Azzera le variabili 
      INC     X            ; che conterranno 
      DEC     W            ; il risultato 
      LD      A,W          ; 
      CPI     A,0          ; 
      JRNZ    SX6          ; Azzera la variabile dei flag 
      CLR     FLAG         ; ---------------------------- 
      RET     


Top  |  80x86  |  Z80  |  ST6  |  Home