Un altro esempio per approfondire l’utilizzo dei cicli in asm. Questa volta vediamo un altro classico problema 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. Si presta molto bene anche per approfondire asm e i cicli.
Lettura
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. Ancora prima dell’esecuzione del nostro programma, tali registri possono aver ospitato valori, indirizzi o altro di processi andati in esecuzione prima. Ecco perché, come con qualsiasi linguaggio di programmazione ad alto livello, bisogna sempre inizializzare i registri o le variabili ai valori utili, nella maggior parte dei casi con lo zero. Qui è da 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.
Ciclo
Il ciclo vero e proprio è molto semplice. Ci serve una etichetta esplicativa per contrassegnare il frammento di codice da ripetere per ogni passaggio del ciclo. Il ciclo, di fatto, è il classico do-while. Ho il contatore da incrementare, il registro rdx che accumula le somme dei 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
Media
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 posto in un registro qualsiasi. 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
Ultima modifica 14 Febbraio 2022