TITLE mulascpC.asm: dal compito 27/1/23 comment * Moltiplicazione di interi positivi GRANDI rappresentati in formato ASCII decimale e contenuti in stringhe di memoria. Il risultato P=AxB e' anch'esso posto in una stringa del medesimo formato. NON si fa uso dell'istruzione mul. I prodotti parziali sono costruiti facendo uso di una "tabellina della moltiplicazione" preallocata in memoria in forma di LOOK-UP TABLE. L'accesso alla tabellina e' effettuato attraverso una PROCEDURA. I parametri di ingresso e di uscita vengono passati attraverso lo STACK. Per la somma dei prodotti parziali si adopera la politica dell'ACCUMULAZIONE dei numeri contenuti nelle stringhe di memoria. Per l'addizione, si ricorre al CALCOLO (istruzione ADD) anziche' alla MEMORIZZAZIONE (con look-up table). Data creazione: 5 febbraio 2023 Data ultima modifica: 8 febbraio 2023. * ;----------------------------------------------------------------- ; Definizione costanti CR EQU 13 ; carriage return LF EQU 10 ; line feed DOLLAR EQU '$' ;----------------------------------------------------------------- ; due M A C R O (per stampa a video) ;----------------------------------------------------------------- ; N.B. Con int 21h (servizio 9) ogni stringa deve terminare con '$' display macro stringa push dx push ax mov dx,offset stringa mov ah,9 int 21h pop ax pop dx endm ;----------------------------------------------------------------- ; Converte una stringa di cifre decimali in una stringa ASCII ; N.B. Il vettore decimale deve terminare con '$' per sapere quando fermarsi ASCIIfy macro dec_str,asc_str local converti_carattere,esci push ax push bx xor bx,bx converti_carattere: mov al,dec_str[bx] cmp al,'$' je esci add al,'0' mov asc_str[bx],al inc bx jmp converti_carattere esci: pop bx pop ax endm ;----------------------------------------------------------------- ; PILA SEGMENT STACK 'STACK' ; definizione del segmento di stack DB 64 DUP('STACK') ; lo stack e' riempito con la stringa ripetuta 'stack' ; per identificarlo meglio in fase di debug PILA ENDS ;----------------------------------------------------------------- ; DATI SEGMENT PUBLIC 'DATA' ; definizione del segmento dati A db '24756978',DOLLAR lenA equ $-A-1 ; N.B. Il -1 esclude dal conteggio della lunghezza della stringa il carattere '$' B db '5347726379',DOLLAR lenB equ $-B-1 lenP equ lenA+lenB PP db lenP dup (0),DOLLAR ; stringa ausiliaria per l'accumulazione dei prodotti parziali PPASC db lenP dup ('a'),DOLLAR ; versione ASCII di PP P db lenP dup (0),DOLLAR PASC db lenP dup ('p'),DOLLAR ; versione ASCII di P riporto_mul db 0 riporto_sum db 0 ; N.B. La seguente tabellina della moltiplicazione non considera le moltiplicazioni per 0 (banali), ; e tiene il risultato (p) della moltiplicazione delle cifre a e b separato dal riporto (c) Tabellina label byte ; --------j-------- ; 0,1,2,3,4,5,6,7,8 a x b = cp , con a,b,p \in {0,1,2,3,4,5,6,7,8,9}, c \in {0,1,2,3,4,5,6,7,8} Tab0 db 1,2,3,4,5,6,7,8,9 ; 0 | Tab1 db 0,4,6,8,0,2,4,6,8 ; 1 | a==0 v b==0: c = p = 0 Tab2 db 0,0,9,2,5,8,1,4,7 ; 2 | else : p = Tabellina(n,m), c = (Tabellina(m,n-1) if n>0) v (0 if n==0) Tab3 db 0,1,1,6,0,4,8,2,6 ; 3 | Tab4 db 1,1,2,2,5,0,5,0,5 ; 4 i con: m = max(a,b)-1, n = min(a,b)-1 Tab5 db 1,1,2,3,3,6,2,8,4 ; 5 | Tab6 db 1,2,2,3,4,4,9,6,3 ; 6 | esempio 1: a=7,b=5 => m=6,n=4>0 => p = Tabellina(4,6) = 5, c = Tabellina(6,3) = 3 => 7 x 5 = 35 Tab7 db 1,2,3,4,4,5,6,4,2 ; 7 | esempio 2: a=3,b=8 => m=7,n=2>0 => p = Tabellina(2,7) = 4, c = Tabellina(7,1) = 2 => 3 x 8 = 24 Tab8 db 1,2,3,4,5,6,7,8,1 ; 8 | esempio 3: a=6,b=1 => m=5,n==0 => p = Tabellina(0,5) = 6, c = 0 => 6 x 1 = 06 ; vettore dei puntatori (N.B. sono word!) alle righe di Tabellina Tabellina_row_pointers dw 9 dup (?) ; N.B. I puntatori vanno inseriti nel vettore nella sezione di codice ; per formattazione output A_msg db "A = ",DOLLAR B_msg db "B = ",DOLLAR P_msg db "P = ",DOLLAR comma db ", ",DOLLAR PP_msg db "PP = ",DOLLAR AxB_msg db "A x B = ",DOLLAR CRLF db CR,LF,DOLLAR DATI ENDS ;================================================================= CSEG SEGMENT PUBLIC 'CODE' MAIN proc far ASSUME CS:CSEG,DS:DATI,SS:PILA,ES:NOTHING; MOV AX,DATI MOV DS,AX ; stampa_stringhe_iniziali: fattori A e B display crlf display A_msg display A display crlf display B_msg display B display crlf display crlf ; display del prodotto parziale inizializzato a 0 ASCIIfy PP,PPASC display PP_msg display PPASC display crlf ; RIEMPIMENTO DEL VETTORE CON I PUNTATORI ALLE RIGHE DI Tabellina ;-------------------------------------------------------------------------------------------------------------------------------- xor di,di ; indice nel vettore Tabellina_row_pointers: incrementato ogni volta di due byte mov bx, offset Tabellina ; N.B. offset Tabellina == offset Tab0, mentre offset Tabi = offset Tab0 + 9*i, ; dove 9 e' il numero di colonne di ogni riga mov Tabellina_row_pointers[di],bx mov cx,8 ; la tabellina ha nove righe, la prima delle quali e' gia' stata inizializzata qui sopra fill_tab_pointers: add di,2 ; indice dell'elemento successivo nel vettore di puntatori (sono word) add bx,9 ; puntatore alla prossima riga mov Tabellina_row_pointers[di],bx loop fill_tab_pointers ; CALCOLO PRODOTTO ;=============================================================================================================================== ; ciclo esterno: accumulazione dei prodotti parziali mov cx,lenB ; il ciclo esterno si ripete per il numero di cifre del moltiplicatore mov si,cx dec si ; indice della cifra meno significativa del moltiplicatore accumula_prodotti_parziali: mov al,B[si] ; lettura della prossima cifra del moltiplicatore sub al,'0' ; conversione ASCII -> binario push cx ; si salva il CX del ciclo esterno, dato che CX sara' utilizzato anche nel ciclo interno mov bp,lenA add bp,si ; in BP si pone lenA+SI: il numero di zeri da porre a destra del prodotto parziale (indicizzato da DI) e' (lenB-1)-SI, ; con (lenB-1) valore di inizializzazione di SI e (lenP-1) == lenA + (lenB-1) valore di inizializzazione di DI. ; Per questo motivo DI (decrementato ad ogni iterazione) si confrontera' con BP, fermandosi quando sono uguali. ;--------------------------------------------------------------------------------------------------------------- ; ciclo interno: calcolo dei prodotti parziali mov riporto_mul,0 mov cx,lenA ; il ciclo interno si ripete per il numero di cifre del moltiplicando mov bx,offset A ; in bx l'indirizzo della stringa contenente il moltiplicando add bx,cx dec bx ; qui bx punta alla cifra meno significativa del moltiplicando mov di,lenP-1 ; indice della cifra meno significativa del prodotto parziale PP append_zeros: cmp di,bp je calcola_prodotto_parziale mov PP[di],0 dec di jmp append_zeros calcola_prodotto_parziale: mov ah,[bx] ; lettura della prossima cifra del moltiplicando sub ah,'0' ; conversione ASCII -> binario push ax ; passaggio dei parametri alla procedura call near ptr accedi_tabellina ; ah=a, al=b -> dh=c, dl=p tali che axb = cp (vedi commento alla tabellina) pop dx ; prelievo dei risultati dalla procedura add dl,riporto_mul cmp dl,9 jle salva_cifra_prodotto_parziale sub dl,10 inc dh ; c <- c+1: si somma a c il riporto della somma precedente, che qui non puo' essere che 1 salva_cifra_prodotto_parziale: mov riporto_mul,dh mov PP[di],dl ; si salva in binario, non in ASCII dec di dec bx dec cx jz esci_dal_ciclo_interno jmp calcola_prodotto_parziale esci_dal_ciclo_interno: mov dl,riporto_mul ; l'ultimo riporto e' la cifra piu' significativa del prodotto parziale mov PP[di],dl ;--------------------------------------------------------------------------------------------------------------- ; display del prodotto parziale calcolato ASCIIfy PP,PPASC display PP_msg display PPASC display comma ; accumulazione del prodotto parziale calcolato nella stringa (decimale) del prodotto ;----------------------------------------------------------------------- mov cx,lenP sub cx,di ; in cx il numero di caratteri da sommare: vale lenP-di mov di,lenP-1 ; indice della cifra meno significativa del prodotto mov riporto_sum,0 ciclo_accumulazione: mov al,PP[di] mov ah,P[di] add ah,al add ah,riporto_sum cmp ah,9 jg sottrai_dieci mov riporto_sum,0 jmp scrivi_in_P sottrai_dieci: sub ah,10 mov riporto_sum,1 ; N.B. Oltre a 0, l'unico possibile riporto nella somma di due cifre decimali e' 1 scrivi_in_P: mov P[di],ah dec di loop ciclo_accumulazione ; display dell'accumulatore ASCIIfy P,PASC display P_msg display PASC display crlf ; aggiornamento del ciclo esterno e check della condizione di terminazione dec si pop cx dec cx jz print_result jmp accumula_prodotti_parziali ;======================================================================================================================================== print_result: ; display del prodotto A x B display crlf display AxB_msg ASCIIfy P,PASC display PASC display crlf exit: MOV AH,4CH ; ritorno al DOS INT 21H main endp ;================================================================================================= ; procedura che consente di accedere alla look-up table della moltiplicazione e di ottenere ; le cifre decimali c,p tali che a x b = cp (a e b cifre decimali in ingresso) ;================================================================================================= accedi_tabellina proc near ; salvataggio in stack dei registri usati nella procedura push ax push bx push cx ; cl e' usato nello swap, cx per aggiornare si push dx push si push bp ; prelievo dei parametri a -> ah, b -> al (N.B. l'8086 e' little-endian!) mov bp,sp mov ah,[bp+15] mov al,[bp+14] xor bh,bh ; si usera' bx come indice, con bx = [0,bl] xor dx,dx ; inizializzazione a 0 di c (=dh) e p (=dl) ; se a oppure b vale zero, il prodotto a x b = cp viene 0 cmp ah,0 je torna cmp al,0 je torna ; calcolo di a-1 e b-1: vedi definizione di m e n nel commento alla tabellina dec ah dec al ; calcolo max tra ah e al cmp ah,al jge ah_equ_m ; se a >= b allora m = a-1, n = b-1... swap_m_and_n: ; ...altrimenti n = a-1, m = b-1 mov cl,ah mov ah,al mov al,cl ; prelievo dati dalla tabellina ah_equ_m: cmp al,0 ; qui e' comunque ah == m, al == n je compute_p ; n==0: si calcola solo p (=dl), lasciando c (=dh) a zero compute_c: ; n>0: si calcola anche c (=dh) dec al ; n -> n-1 mov bl,ah ; N.B. c = Tabellina[m][n-1] = Tabm[n-1] shl bl,1 ; si indicizzano word nel vettore, dunque l'indice va moltiplicato per due mov si,Tabellina_row_pointers[bx] xor ch,ch mov cl,al add si,cx mov dh,[si] inc al ; ripristino n-1 -> n per il calcolo di p compute_p: mov bl,al ; N.B. p = Tabellina[n][m] = Tabn[m] shl bl,1 ; si indicizzano word nel vettore, dunque l'indice va moltiplicato per due mov si,Tabellina_row_pointers[bx] xor ch,ch mov cl,ah add si,cx mov dl,[si] torna: ; salvataggio in stack dei risultati, al posto dei parametri a e b: c = dh, d = dl (N.B. l'8086 e' little-endian!) mov [bp+15],dh mov [bp+14],dl ; ripristino dei registri usati nella procedura pop bp pop si pop dx pop cx pop bx pop ax ret ; ritorno dalla procedura accedi_tabellina endp cseg ends END MAIN ; il programma comincia all'indirizzo di MAIN