Un semplice programma per sommare


Nota di Antonio: ho completamente rifatto ed ampliato questa lezione.

Oggi vedremo nel dettaglio un prototipo di programma che esegue la somma di due numeri di un numero anche molto grande di cifre.
Metteremo in pratica alcune delle istruzioni viste nel tutorial precedente.
Vi riporto subito il listato, in due versioni: nella prima i numeri da sommare sono costanti dichiarate nel prg.; la seconda è un po' più complessa e li legge da input. Segue una breve spiegazione del flusso del programma:


TUT9.COM TUT9.ASM ;TUT9.ASM - by Antonio 1999 ;Somma due numeri in BCD. SEG_A SEGMENT ASSUME CS:SEG_A, DS:SEG_A ORG 100H Somma PROC FAR INIT: JMP START ;salta a START ; costanti modificabili N_CIFRE EQU 8 ; numero di cifre dei numeri ; ricorda: metti zeri in testa Num1 DB 1,2,3,4,5,6,7,8 Num2 DB 8,7,6,5,4,3,2,1 Ris DB 0,0,0,0,0,0,0,0,0,'$' ;terminatore START: ; -- Somma dei due numeri mov cx,N_CIFRE ;numero di cifre da sommare mov di,cx ;punta alla cifra più a destra di Ris mov si,N_CIFRE-1 ;... di Num1 o Num2 clc ;il riporto è 0 all'inizio SommaCifra: mov al,Num1[si] adc al,Num2[si] ;somma due cifre col riporto aaa ;metto a posto le cifre mov Ris[di],al dec di ;non cambieranno il Carry Flag dec si loop SommaCifra mov Ris[di],0 jnc FineSomma inc Ris[di] ;Ris ora ha nove cifre significative FineSomma: ; -- asciizza il risultato (ehilà, ho inventato un nuovo termine) ; cioè prepara per la stampa mov cx,N_CIFRE+1 mov di,0 Ascii: add Ris[di],30h ;oppure: or Ris[di],30h inc di loop Ascii ; -- stampa risultato StampaRis: mov ah,09h mov dx,OFFSET Ris int 21h RETN Somma ENDP SEG_A ENDS END INIT
TUT9BIS.COM TUT9BIS.ASM ;TUT9BIS.ASM - by Antonio 1999 ;Legge due numeri di N_CIFRE cifre e li somma (in BCD). ;NOTA : 4+3 si scrive 04+03 !!! (se N_CIFRE Š 2) SEG_A SEGMENT ASSUME CS:SEG_A, DS:SEG_A ORG 100H Somma PROC FAR INIT: JMP START ;salta a START ; costanti modificabili N_CIFRE EQU 8 ; i numeri hanno N_CIFRE cifre Msg_Num1 DB "Inserisci il primo addendo : $" Msg_Num2 DB 13,10,"Inserisci il secondo addendo : $" Num1 DB N_CIFRE DUP(?) Num2 DB N_CIFRE DUP(?) Ris DB N_CIFRE+1 DUP(?), '$' START: ; -- Lettura del primo addendo (ricorda: metti 0 in testa) mov dx,OFFSET Msg_Num1 ;stampo il primo msg mov ah,09h int 21h mov ah,01h mov si,0 Leggi_1: int 21h ;leggo un carattere cmp al,0Dh ;Enter ? je Fine_1 sub al,30h mov BYTE PTR Num1[si],al ;lo metto in Num1 inc si jmp Leggi_1 Fine_1: ; -- Lettura del secondo addendo mov dx,OFFSET Msg_Num2 ;stampo il secondo msg mov ah,09h int 21h mov ah,01h mov si,0 Leggi_2: int 21h ;leggo un carattere cmp al,0Dh ;Enter ? je Fine_2 sub al,30h mov BYTE PTR Num2[si],al ;lo metto in Num2 inc si jmp Leggi_2 Fine_2: ; -- salta una riga mov ah,02h mov dl,13 int 21h mov dl,10 int 21h ; -- Somma dei due numeri mov cx,N_CIFRE ;numero di cifre da sommare mov di,cx ;punta alla cifra pi- a destra di Ris mov si,N_CIFRE-1 ;... di Num1 o Num2 clc ;il riporto Š 0 all'inizio SommaCifra: mov al,Num1[si] adc al,Num2[si] ;somma due cifre col riporto aaa ;metto a posto le cifre mov Ris[di],al dec di ;non cambieranno il Carry Flag dec si loop SommaCifra mov Ris[di],0 jnc FineSomma inc Ris[di] ;Ris ora ha nove cifre significative FineSomma: ; -- prepara per la stampa mov cx,N_CIFRE+1 mov di,0 Ascii: add Ris[di],30h ;oppure: or Ris[di],30h inc di loop Ascii ; -- stampa risultato StampaRis: mov ah,09h mov dx,OFFSET Ris int 21h RETN Somma ENDP SEG_A ENDS END INIT
Si inizia con la dichiarazione delle varibili che ci serviranno nel programma: due stringhe da visualizzare e tre varibili per contenere i due numeri e il risultato.
Le variabili Num1 e Num2 occupano N_CIFRE byte, 1 per ogni cifra in notazione BCD. Il totale Ris ne occupa N_CIFRE+2, perchè in generale la somma di due numeri di N_CIFRE cifre può produrre un numero di N_CIFRE+1 cifre e perchè volendo stampare il numero Ris con l' INT 21h/09h abbiamo dovuto inserire il terminatore '$'.
Il programma comincia visualizzando la prima stringa e leggendo il primo numero tramite l'interrupt 21h/01h. Viene letta una cifra per volta e devono essere lette sempre esattamente N_CIFRE cifre, quindi è importante inserire prima le cifre più significative, inserendo 0 se sono nulle, e poi quelle meno significative fino all'unità. In pratica dovete scrivere il numero in input normalmente, solo che in testa vanno messi tanti zeri non significativi fino a far sì che il numero abbia esattamente N_CIFRE cifre. Il programma non controlla la correttezza dell'input. Che ne dite di aggiungere questo controllo?
Lo stesso lavoro di lettura viene fatto per il secondo numero.

NOTA (sulle procedure e sul ruolo dell'assembly oggi):
Notate che c'è del codice ripetuto (che rende il prg. molto noioso oltre che più lungo). Nel prossimo tutorial imparete a fare le procedure per risolvere questo problema.
Dopo che avrete letto il prossimo tutorial provate a riscrivere in forma procedurale questo programma. Un'altro vantaggio delle procedure, come nei linguaggi ad alto livello, è di permettere una più facile riusabilità del codice. Questo vale anche in Assembly, ma solo se le procedure sono ben progettate (l'interfaccia di una procedura verso l'esterno infatti siete voi a stabilirla!).
Tutti questi vantaggi hanno un costo:
c'è un po' di overhead nella chiamata di una procedura, che in assembly può essere notevolmente ridotto utilizzando i registri per passare i parametri se possibile. Anche se usate la progettazione modulare in assembly, mettendo in pratica tutti i trucchi per fare ottimizzazioni, è probabile che riuscirete ad essere più veloci di un compilatore, anche se oggi esistono dei compilatori molto intelligenti, in grado di ottimizzare molto bene il codice in modo automatico.
Perchè allora stiamo imparando l'assembly? Per capire come funziona il computer e per poter accedere a tutte le sue funzioni. Oggi non scriverete mai un grosso programma completamente in assembly, però l'assembly è ancora utile per fare delle ottimizzazioni in un programma scritto in un linguaggio di alto livello. Come potete vedere la programmazione in Assembly richiede molto sforzo, percui si useranno pezzi di codice Assembly nei punti più critici, che sono fonte di maggiore complessità di un programma, dove lo sforzo fatto per crearsi delle procedure super-ottimizzate in assembly vale i benefici in termini di velocità che si ottengono. A parte questo solo con l'assembly si può lavorare direttamente a contatto con l'hardware. Ad esempio lavorando con l'hardware grafico di un calcolatore potete aggiungere la gestione della grafica in un compilatore C solo standard, richiamando le efficienti routine in assembler della libreria che avete creato e che linkerete al vostro programma. In questo tutorial illustremo solo nell'ultima lezione l'interfacciamento tra Assembler e un linguaggio ad alto livello; non sarà il C, ma il (Turbo) Pascal, però le cose sono analoghe.

La somma vera e propria viene effettuata son l'operazione ADC seguita da AAA per sistemare il risultato in notazione BCD per facilitare la visualizzazione a video. La ADC viene usata invece di una semplice ADD per tenere conto del riporto generato dalla somma di cifre precedente.
Notate che alle cifre del risultato ho sommato 30h che è il codice ASCII dello zero, questo mi serve per convertire il risultato in un carattere da visualizzare e in questo caso lo converto nel carattere della cifra corrispondente.

Non mi sono dilungato molto sulla spiegazione del programma vorrei che ognuno di voi se lo studi per proprio conto e magari cerchi di modificarlo cercando magari di risolvere il problema che bisogna sempre indicare gli zeri non significativi (eventuali zeri non significativi non introdotti dovrebbero essere aggiunti automaticamente; la soluzione più semplice è di gestire l'input a stack e aggiungere poi in testa eventuali zeri non significativi).
Questo programma serve solo come base per capire come si possono eseguire le operazioni matematiche in Assembly. Provate a farne uno che esegue una sottrazione o una moltiplicazione... ovviamente se apportate miglioramenti al prg. o ne scrivete altri non dimenticate di mandarmeli, così posso aggiungerli a questo tutorial. ciao !


Assembly Page di Antonio
<< Indice >>