Esempio 17: media di n numeri da input con ciclo

Un altro esempio per approfondire l'utilizzo dei cicli in asm. Questa volta vediamo un altro classico problemino per i programmatori in erba: la media di n numeri inseriti da tastiera con n a sua volta scelto in input dall'utente in input.

 

Il problema in analisi è un classico per chi inizia a programmare con qualsiasi linguaggio di programmazione ad alto livello e si presta molto bene anche per approfondire asm e i cicli.

La prima parte di lettura del numero di elementi di cui fare la somma è molto semplici per chi è arrivato a questo punto del nostro percorso. Scegliamo un registro generico qualsiasi per ricevere il numero, ad es. rax. Vi ricordo la lettura GET_DEC col parametro 4 per leggere 32bit, 8 per leggere 64bit. Se inseriamo 4 con i registri a 64bit funziona lo stesso ma utilizzeremo meno bit del registro a 64bit.

    PRINT_STRING "Inserisci quanti numeri vuoi sommare"
    GET_DEC 8,rax  ; cin >>  n 

 Più interessante è ricordare sempre che i registri sono utilizzati dalla cpu per tutti gli scopi necessari al funzionamento del pc, quindi ancora prima del nostro programma, tali registri possono aver ospitato valori, indirizzi o altro di altri processi andati in esecuzione. Ecco perché, come con qualsiasi linguaggio di programmazione ad alto livello, bisogna sempre inizializzare i registri o le variabili ai valori corretti, nella maggior parte dei casi anche lo zero stesso. Qui per mettere zero nei registri rcx che ci servirà da contatore e rdx che ci servirà da accumulatore, invece di usare la opCode mov registro, 0 ho usato la opCode di xor registro, registro poichè ottiene lo stesso risultato ma con meno cicli di cpu. Lo studente che non gradisce le ottimizzazioni può certamente usare l'altra modalità più intuitiva.

Il ciclo vero e proprio è molto semplice. Una etichetta esplicativa che ci serve per contrassegnare il frammento di codice da ripete per ogni passaggio del ciclo. Il ciclo, di fatto, è il classico do-while, in cui ho il contatore da incrementare, il registro rdx che accumula come somme i numeri letti e posizionati sul registro rbx, la condizione per tornare sopra il ciclo con un salto o uscire qualora la condizione venisse meno. In questo caso dal ciclo dobbiamo uscire quando il contatore è arrivato al numero complessivo altrimenti se minore deve saltare all'inizio del ciclo e leggere un altro numero e così via. JL, jump less, è proprio la condizione while(contatore < numero){...}.

ciclo:    
    GET_DEC 8,rbx  ; cin >>  num
    inc rcx ; contatore elementi inseriti i++
    add rdx, rbx
    cmp rcx,rax 
    jl ciclo

Il frammento successivo si occupa di calcolare la media con la divisione intera. Come visto nelle altre lezioni, bisogna preparare i registri rax e rdx per accogliere il dividendo e il resto rispettivamente, mentre il divisore può essere un registro qualsiasi ed il quoziente sarà poi sovrascritto in rax stesso.

    mov rax, rdx
    xor rdx, rdx
    div rcx

 

Il codice completo:

%include "io64.inc"

section .text
global CMAIN
CMAIN:
    mov rbp, rsp; for correct debugging
    PRINT_STRING "Inserisci quanti numeri vuoi sommare"
    GET_DEC 8,rax  ; cin >>  n   
    
    ;inizializzo/pulisco a zero i registri che mi occorrono 
    xor rcx, rcx  ; equivale a mov ecx, 0 ovvero ecx=0
    xor rdx, rdx 
    
ciclo:    
    GET_DEC 8,rbx  ; cin >>  num
    inc rcx ; contatore elementi inseriti i++
    add rdx, rbx
    cmp rcx,rax 
    jl ciclo
    
    ;preparo i registri per la divisione es. 40 / 4
    ;il 40 che stava in dx lo muovo in ax
    ;sbianco dx per il resto
    ;i lrisultato quoziente sarà ancora in ax
    mov rax, rdx
    xor rdx, rdx
    div rcx
    
    NEWLINE
    PRINT_STRING "media= "
    PRINT_DEC 4,rax
    
   
    xor rax, rax
    ret

Stampa