dal 2015 - visita n. 2424
StruttureControllo
StruttureControllo

 

Le strutture di controllo ASM


Introduciamo adesso alcune strutture fondamentali della programmazione utili in tantissime situazioni. In ogni programma, degno di tale nome, capita spesso di dover ripetere le stesse operazioni (un lavoro elementare) per un certo numero di volte. Un ciclo (loop) è una struttura di controllo di un programma che esegue per molte volte lo stesso insieme di istruzioni. Si dice anche che il ciclo è una struttura iterativa.

In un ciclo si possono distinguere quattro fasi: inizializzazione, lavoro, aggiornamento e controllo.

  1. Con l'inizializzazione si effettua la fase di preparazione del ciclo, nella quale si fa in modo che il lavoro venga eseguito correttamente per la prima volta.
  2. Il lavoro è l'insieme delle istruzioni che devono essere eseguite tante volte (in modo iterativo).
  3. La fase di aggiornamento prepara la struttura ad eseguire il lavoro per un'altra volta.
  4. La fase di controllo, o test, serve per valutare se si deve eseguire un'altra iterazione del ciclo o se è giunto il momento di smettere (uscita dal ciclo).

Esistono due possibilità di base per impostare correttamente un ciclo, entrambe fanno riferimento alla posizione del modulo di controllo rispetto agli altri moduli. Le figure indicano le due situazioni tipiche: con controllo in testa (prima di cominciare il blocco di lavoro), o con controllo in coda (dopo aver eseguito il lavoro per la prima volta).


Nei diagrammi delle figure si vede facilmente come il programma giri dentro al ciclo (parte rossa) fino a che la condizione di controllo non lo fa uscire (parte verde). Un giro di un ciclo è ciò che si intende con il termine iterazione.

I puristi indicano molti motivi per utilizzare cicli con controllo in testa (sicurezza, integrità, etc.), ma programmando in Assembly x86 questa indicazione viene spesso disattesa, perché il programma risulta di lettura più semplice se il controllo è in coda ed anche perché l'istruzione specifica per realizzare i cicli (LOOP) usa un controllo in coda. In ogni caso sarà poi l'esperienza a guidare il programmatore nella scelta migliore a seconda delle situazioni.

Di norma si conosce a priori il numero di iterazioni, ma non è sempre così. Come primo esempio supponiamo di voler eseguire 7 volte un certo lavoro e troviamo delle soluzioni intuitive che sfruttano i 2 diagrammi visti in precedenza. In questo tipo di cicli nell'inizializzazione si deve impostare un contatore, CX, che terrà traccia in ogni istante del numero di giri che sono stati compiuti. Chiameremo questo contatore "contatore di controllo". La fase di aggiornamento modifica il contatore per segnalare che è stata eseguita un'altra iterazione. Il test stabilisce se il ciclo è stato percorso il giusto numero di volte e se è giunto il momento di uscirne.


Vediamo ora la realizzazione in Assembly dei due cicli illustrati dai diagrammi di flusso.


         .....
         mov   cx,1   ;Inizial. 
 iteraz: cmp   cx,7
         ja    uscita
         .....
         .....        ;Lavoro
         .....
         inc   cx
         jmp   iteraz
 uscita: .....        
ciclo con logica di controllo in testa

         .....
         mov   cx,1   ;Inizial. 
 iteraz: .....
         .....        ;Lavoro
         .....
         inc   cx
         cmp   cx,7
         ja    uscita
         jmp   iteraz
 uscita: .....        
ciclo con logica di controllo in coda

Dopo aver esaminato i due casi classici, vediamo come sia possibile arrivare ad una soluzione ottimizzata con l'uso di vari accorgimenti. Il modo più efficiente per fare i cicli in Assembly utilizza un contatore a decremento ed un controllo in coda.

prima versione

         .....
         mov   cx,7   ;Inizial. 
 iteraz: .....
         .....        ;Lavoro
         dec   cx
         jnz   iteraz
 uscita: .....        
seconda versione

         .....
         mov   cx,7   ;Inizial. 
 iteraz: .....
         .....        ;Lavoro
         loop  iteraz
 uscita: .....        

ciclo a decremento
con logica di controllo in coda

Nella prima versione, come si vede, manca la cmp finale che non è più necessaria perché il ciclo, andando a rovescio, finisce sempre con lo zero e la condizione di zero viene rilevata direttamente dalla dec. Con questa versione si risparmia una cmp per ogni iterazione del ciclo. Il risparmio di un'istruzione può sembrare un'inezia, ma diventa significativo se fatto in cicli che si ripetono per centinaia/migliaia di volte, come accade per esempio in moduli che fanno parte del sistema operativo o di applicazioni grafiche.

Nella seconda versione è stata utilizzata l'istruzione "loop  iteraz" che semplifica ulteriormente la scrittura del ciclo in quanto raggruppa la "dec  cx" e la "jnz  iteraz". E' importante notare che l'istruzione LOOP presuppone l'uso esclusivo del registro CX con funzione di contatore. Anche se non è esplicito nella scrittura dell'istruzione il registro CX viene sempre decrementato dalla LOOP. Ogni volta che stiamo per usare una LOOP dobbiamo chiederci cosa c'è attualmente in CX e se quello che c'è è il valore giusto per il contatore del ciclo, altrimenti i risultati possono essere imprevedibili. L'uso della loop rende certamente più compatto il codice anche se con l'evoluzione delle CPU x86, dal 486 in avanti, il risultato non è più veloce delle altre istruzioni equivalenti.



Menù
Introduzione
I registri del Pentium
Il modello della memoria
Interazione CPU-RAM
I Servizi Linux - Int 80h
I File in Linux
Schema programma NASM
Esempi Write/Read
Esercizi
lods-stos-movs
gcc + nasm
Gestione Command Line
Stack
Stack-Esercizi
Libreria Funzioni Base
Esercizi con la Libreria
Libreria Macro Base
Test 5 esercizi


Sistemi di numerazione
Elementi Bistabili
Strutture di Controllo
Istruzioni JMP
Hello World
I/O di Testo
Command Line
Colori e Macro
Introduzione I/O
Porta Parallela


AppuntiAsm-386.html
Linux ELF Howto
Linux Assembly Howto
Il linguaggio PC Assembly
Assembly di Paul A.Carter
Assembly di Claudio Daffra



Chi non viene perdonato non si scorda facilmente.
Dudek

Valid CSS!
pagina generata in 0.001 secondi