L'algoritmo usato nelle routine di moltiplicazione e' praticamente
identico a quello a quello che noi umani abbiamo imparato nelle
elementari. Si scansiona il moltiplicatore partendo dalle unita'
(cioe' da destra a sinistra) si moltiplica per il digit scansionato
e lo si eleva per la posizione decimale. Si ottengono cosi' dei risultati
parziali che al temine verranno sommati tra loro per fornire il risultato.
Nei microprocessori analogamente, si scansiona il moltiplicatore partendo
dal bit piu' basso, non si moltiplica perche' il valore e' al massimo 1
e quindi il risultato parziale e' il valore assunto dal moltiplicando ed
infine si eleva il moltiplicando per la posizione binaria del bit.
L'unica differenza e' che non ci sono risultati parziali dato che e' piu'
comodo,piu' veloce e richiede meno passi di programma eseguire la somma
immediata dei vari parziali. Questo e' il sorgente di una moltiplicazione
con moltiplicando e moltiplicatore a 16 Bit. Il risultato e' a 32 Bit.
Il processore e' del tipo 80x86 e seguenti. Ha la moltiplicazione tra le
istruzioni ma non accetta operandi superiori a 16 bit. Pertanto se si
vuole moltiplicare 100.000x100 ad esempio e' necessario preparare una
routine ad hoc.
80x86
MOV AX,100 ; In AX i 16 Bit del moltiplicando
MOV BX,1000 ; In BX i 16 Bit del moltiplicatore
MUL BX ; Esegui la moltiplicazione
A questo punto troveremo il risultato in DX:AX Breve, semplice, conciso.
Diverso e' il discorso quando si usano processori che non hanno la
moltiplicazione tra le istruzioni. Di seguito vediamo una routine di
moltiplicazione per Z80 questa volta pero' il moltiplicando accetta fino
a 31 Bit cioe' fino ad oltre 2 miliardi mentre il moltiplicatore e' sempre
da 16 Bit come sopra cioe' 65565.
Z80
; Moltiplicazione 16x32 bit con risultato a 32 bit
; Domanda: Perche' fare una moltiplicazione a 32 bit
; e magari bastava da 8 bit?
; Risposta: E se per caso salta fuori un numero superiore
; a 255 che facciamo ? Un'altra routine !!
-----------------------------------------------------
DE = Contiene i 16 bit alti del moltiplicando
HL = Contiene i 16 bit bassi del
BC = Contiene i 16 bit del moltiplicatore
---------------------------------------------
DE = Contiene i 16 bit alti del risultato
HL = Contiene i 16 bit bassi del risultato
-----------------------------------------------------
; Questa moltiplicazione richiede per il moltiplicatore
; da caricare in BC un massimo di 16 bit cioe' 65535.
; Per il moltiplicando da caricare in DE:HL il massimo
; e' di 31 bit pari cioe a piu' di 2 miliardi.
; Sotto inizializzo i registri per eseguire la routine
ORG 100H
LD SP,1000H
LD BC,1000
LD DE,0
LD HL,1000
CALL MUL32X16
JP 100H
; Qui' inizia la routine di moltiplicazione
MUL32X16:
; Inizializzo la RAM e azzero il risultato
CALL INIT
; Dopo aver copiato DE:HL in RAM copio BC in DE per
; poter utilizzare B come contatore di loop
PUSH BC
POP DE
; Inizializzo i puntatori IX e IY
TP1: LD IX,MOLTIPLICANDO
LD IY,RISULTATO
; Eseguo uno shift a destra del moltiplicatore
; contenuto in DE e se dal bit 0 esce un uno
; (che testo tramite il carry) sommo al risultato
; il contenuto del moltiplicando
SRL D
RR E
JR NC, TP3
; Nel carry c'e' uno e pertanto eseguo una somma
; sui 4 byte del risultato col moltiplicando
AND A
LD B,4
TP2: LD A,(IX+0)
ADC A,(IY+0)
LD (IY+0),A
INC IX
INC IY
DJNZ TP2
; Ad ogni ciclo controllo se il moltiplicatore
; e' a zero nel qual caso termina la routine
; dato che non c'e' piu' niente da moltiplicare
TP3: LD A,D
OR E
JR Z, FINE
; Ad ogni ciclo viene raddoppiato il valore del
; moltiplicando eseguendo uno shift a sinistra
; dei 32 bit. Questo perche' il valore da sommare
; al risultato deve essere elevato per la posizione
; binaria del bit in uscita dal moliplicatore
LD IX,MOLTIPLICANDO
SLA (IX+0)
RL (IX+1)
RL (IX+2)
RL (IX+3)
JP TP1
; Inizializzo la RAM con il valore del
; moltiplicando e azzero il risultato
INIT:
LD IX,MOLTIPLICANDO
LD (IX+0),L
LD (IX+1),H
LD (IX+2),E
LD (IX+3),D
LD (IX+4),0
LD (IX+5),0
LD (IX+6),0
LD (IX+7),0
RET
; La routine e' terminata. Ora carico nei
; registri DE:HL il risultato finale
FINE:
LD IY,RISULTATO
LD D,(IY+3)
LD E,(IY+2)
LD H,(IY+1)
LD L,(IY+0)
RET
; -------------- area dati ram -----------------
MOLTIPLICANDO: DB 0,0,0,0
RISULTATO: DB 0,0,0,0,0
LOGO: DB 'By Tore ... (c) Ottobre 1999 '
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 ed 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. Sempre con 31 Bit di moltiplicando e 16 Bit di moltiplicatore.
Ovviamente e' piu' lunga e complessa
ST6
; Sezione variabili (084H ..0BFH)
MUL_0 .DEF 084H ; MSB del moltiplicando
MUL_1 .DEF 085H ;
MUL_2 .DEF 086H ;
MUL_3 .DEF 087H ; LSB del moltiplicando
MULT_H .DEF 088H ; MSB del moltiplicatore
MULT_L .DEF 089H ; LSB del moltiplicatore
RES_0 .DEF 08AH ; MSB del risultato
RES_1 .DEF 08BH ;
RES_2 .DEF 08CH ;
RES_3 .DEF 08DH ; LSB del risultato
TABELLA .DEF 08EH ; tabella uso controllo bit
; Questa moltiplicazione richiede che il moltiplicando
; ed il moltiplicatore siano posti in memoria.
; Al ritorno dalla routine l'intero si trovera' nei 4 byte
; del risultato.
; inizializzazione del sistema
RESET LDI WDOG,0FEH
LDI TSCR,0
LDI ADCR,0H
LDI IOR,00H
RETI
; Inizializzazione delle variabili
; e azzeramento del risultato
CALL INIT
NOP
; Questa e' la chiamata per la routine
; di moltiplicazione
CALL MUL32X16
NOP
JP 0880H
MUL32X16:
; azzero il risultato da precedenti operazioni
CALL CLEAR
; Per la moltiplicazione e' necessario addizionare
; il moltiplicando elevato per la posizione del bit
; del moltiplicatore al risultato. Pertanto sarebbe
; utile far scorrere i bit del moltiplicatore da
; sinistra verso destra e testarli nel carry oppure
; nel bit 0. Purtroppo per questo processore non e'
; disponibile uno shift da sinistra verso destra e
; quindi li faccio scorrere da destra verso sinistra
; li raccolgo nel carry e li deposito in una tabella
; creata in RAM per testarli successivamente.
CALL CREA_TABELLA
; Qui' punto alla tabella e ne testo il bit ivi contenuto.
; Se e' 1 sommo al risultato il moltiplicando, altrimenti
; raddoppio solo il valore del moltiplicando
CLR W
SW1: LDI X,TABELLA
LD A,W
ADD A,X
LD X,A
LD A,(X)
CPI A,0
JRZ SW2
CALL ADDIZIONA
NOP
SW2: CALL RADDOPPIA
INC W
LD A,W
CPI A,15
JRZ SW3
JP SW1
SW3: RET
; Questa parte di programma esegue una addizione
; su valori da 32 bit
ADDIZIONA:
LD A,RES_3
ADD A,MUL_3
LD RES_3,A
LDI A,0
RLC A
ADD A,RES_2
ADD A,MUL_2
LD RES_2,A
LDI A,0
RLC A
ADD A,RES_1
ADD A,MUL_1
LD RES_1,A
LDI A,0
RLC A
ADD A,RES_0
ADD A,MUL_0
LD RES_0,A
RET
; Questa parte di programma esegue una rotazione
; da destra a sinistra su 32 bit che equivale
; a raddoppiare il valore del dato.
RADDOPPIA:
LD A,MUL_3
SLA A
LD MUL_3,A
LD A,MUL_2
RLC A
LD MUL_2,A
LD A,MUL_1
RLC A
LD MUL_1,A
LD A,MUL_0
RLC A
LD MUL_0,A
RET
; Prima di iniziare la routine e' necessario
; azzerare il risultato da precedenti operazioni
; e la tabella del moltiplicatore
CLEAR: LDI Y,RES_0
LDI W,20
SW4: CLR A
LD (Y),A
INC Y
DEC W
LD A,W
CPI A,0
JRNZ SW4
RET
; Creo una tabella con tutti i bit del moltiplicatore
CREA_TABELLA:
LDI A,TABELLA
ADDI A,15
LD X,A
LDI V,16
SW5: LD A,MULT_L
SLA A
LD MULT_L,A
LD A,MULT_H
RLC A
LD MULT_H,A
JRNC SW6
INC (X)
SW6: DEC X
DEC V
LD A,V
CPI A,0
JRZ SW7
JP SW5
SW7: RET
; Questa parte serve ad inizializzare le variabili
; e non fa parte della routine
INIT: LDI Y,MUL_0
LDI A,0
LD (Y),A
INC Y
LDI A,0
LD (Y),A
INC Y
LDI A,027H
LD (Y),A
INC Y
LDI A,010H
LD (Y),A
INC Y
LDI A,027H
LD (Y),A
INC Y
LDI A,010H
LD (Y),A
RET
|