Esercizi da esame di Programmazione Assembler MIPS Versione 1.02 (24 Luglio 2003) Corso di Architettura Corso di Laurea in Informatica Università di Salerno Vittorio Scarano Premessa: questa è una raccolta di esercizi di esame di programmazione Assembler. Alcuni di questi (contrassegnati con un asterisco) presentano anche una soluzione, descritta in maniera tale da illustrare chiaramente tutto lo svolgimento. Ovviamente non si pretende dallo studente per l’esame scritto la stessa precisione e prolissità per quanto riguarda la descrizione del programma. Bastano poche righe di descrizione seguite da (giusto per ricordarlo) “(1) la descrizione dell’algoritmo usato (in pseudocodice), (2) la descrizione dell’uso dei registri e (3) il programma in assembler commentato linea per linea”. Buon lavoro! Indice 1. 2. 3. 4. 5. 6. 7. 8. 9. Seconda prova intercorso 2001-2002...................................................................................................................2 Esame scritto del 3/7/2002 (*).............................................................................................................................2 Esame scritto del 23/7/2002 (*) ...........................................................................................................................3 Esame scritto del 12/9/2002 (*) ...........................................................................................................................3 Esame scritto del 25/9/2002 ................................................................................................................................4 Esame scritto del 20/2/2003 ................................................................................................................................4 Seconda prova intercorso 2002-2003 (*)..............................................................................................................4 Esame scritto del 3/7/2003 (*).............................................................................................................................5 Esame scritto del 24/7/2003 (*) ...........................................................................................................................7 1 1. Seconda prova intercorso 2001-2002 Dato un vettore A di 100 interi memorizzati a partire dalla locazione 2000, scrivere un programma in assembler MIPS che scriva in un array B di 3 posizioni il numero di valori di A minori di 68 (da scrivere in B[0]), il numero di quelli maggiori o uguali a 68 ma minori di 68752 (da scrivere in B[1]), ed il numero di quelli maggiori o uguali di 68752 (da scrivere in B[2]). Si richiede la descrizione dell’algoritmo usato (in pseudocodice), la descrizione dell’uso dei registri ed il programma in assembler commentato linea per linea. Non si può assumere la inizializzazione di nessun registro. 2. Esame scritto del 3/7/2002 (*) Dato un vettore A di 100 interi memorizzati a partire dalla locazione 2000, scrivere un programma in assembler MIPS che copii in un vettore B (memorizzato a partire dalla locazione 3000) gli elementi di A che sono minori di 15 ed in un vettore C (memorizzato a partire dalla locazione 4000) tutti gli altri elementi di A (cioè quelli maggiori o uguali a 15). Si richiede (1) la descrizione dell’algoritmo usato (in pseudocodice), (2) la descrizione dell’uso dei registri (con la indicazione di eventuali valori già memorizzati) e (3) il programma in assembler commentato linea per linea. Non si può assumere la inizializzazione di nessun registro. In questo programma si richiede di scorrere tre array ma in maniera non sincronizzata, nel senso che avremo bisogno di un indice per scorre l’array A (chiamiamolo i) di un indice per scorrere l’array B (chiamiamolo j) e di un indice per scorrere l’array C (chiamiamolo k). Mentre è chiaro che l’indice i deve essere incrementato “automaticamente” da un ciclo (per poter esaminare tutti gli elementi di A) gli indici j e k devono essere incrementati solamente quando si va ad aggiungere un elemento all’interno (rispettivamente) dell’array B e dell’array C. Quindi, ad esempio, quando andiamo ad aggiungere un elemento A[i] all’array B (perchè A[i] risulta essere minore di 15) dovremo compiere due operazioni: B[j] = A[i]; j = j + 1; // inserisco l’elemento nel vettore B // incremento l’indice del vettore B che indica il primo elemento “libero” del vettore In effetti, quindi, l’indice j indica quale è la prossima posizione libera nell’array B, dove può essere inserito un elemento. Una volta inserito un elemento, si deve incrementare l’indice j in modo che esso indichi la locazione successiva come locazione libera per il prossimo inserimento. Lo stesso vale per l’indice k e l’array C. Quindi, il nostro algoritmo in codice C è: j = 0; k = 0; for (i = 0; i < 100; i++) { if (A[i] < 15) { B[j] = A[i]; j = j +1; } else { C(k) = A[i] k = k + 1; } } // inizializzo l’indice di B // inizializzo l’indice di C // per ogni elemento A[i] del vettore A // se A[i] è minore di 15 allora.. // ... scrivi A[i] nel vettore B // ... ed incrementa l’indice j // altrimenti // ... scrivi A[i] nel vettore C // ... ed incrementa l’indice k Traduciamo questo frammento di codice C in pseudocodice, vale a dire un linguaggio che usa istruzioni che sono più vicine alle istruzioni assembler. j=0; k = 0; i=0; Ciclo: if ( ! (A[i] < 15)) goto ScriviC; B[j] = A[i]; j = j +1; goto Con t; ScriviC: C[k]=A[i]; k = k +1; Cont: i = i +1; if (i != 100) goto Ciclo; // inizializza indice del ciclo // se la condizione non è vera salta alla parte else // ... inserisci A[i] nel vettore B // ... j indica il primo elemento libero di B // ... salta all’incremento di i // altrimenti .. inserisci A[i] nel vettore C // ... k indica il primo elemento libero di C // incrementa l’indice // se non abbiamo scorso tutto l’array, torna a inizio ciclo A questo punto passiamo alla implementazione in Assembler, indicando, innanzitutto, l’utilizzo dei registri che si farà nel programma. Un commento è necessario: per l’indice i abbiamo bisogno sia dell’indice (per confrontarlo con la costante 100) sia dello spiazzamento corrispondente (per accedere all’elemento A[i]); invece per gli indici j ek abbiamo bisogno solamente dello spiazzamento, in quanto dobbiamo solamente accedere in scrittura a B[j] ed a C[k]. Quindi non dedichiamo nessun registro a memorizzare il valore di j e di k, memorizzando solamente gli spiazzamenti corrispondenti, badando bene ad incrementarli di 4 in 4. 1 $10 $11 $12 $13 indice i spiazzamento relativo all’indice i spiazzamento relativo all’indice j spiazzamento relativo all’indice k 1 A rigor di termini, non è necessario neanche per l’indice i di un for mantenere sia l’indice che lo spiazzamento all’interno del programma. Infatti basta eliminare del tutto l’indice ed usare lo spiazzamento, passando ad effettuare i controlli di fine ciclo e gli incrementi sullo spiazzamento stesso. In pratica si tratta di passare da un ciclo tipo: for (i=0; i < 100; i++) ad un ciclo: for (spiazz=0; spiazz < 400; spiazz=spiazz+4) . 2 $20 $21 $22 valore letto da A[i] costante 100 risultato della slt Passiamo, finalmente al programma in Assembler. addi addi addi addi addi Ciclo: lw slti beq sw addi j ScriviC: sw addi Cont: addi addi bne 3. $21, $0, 100 $10, $0, 0 $11, $0, 0 $12, $0, 0 $13, $0, 0 $20, 2000($11) $22, $20, 15 $22, $0, ScriviC $20, 3000($12) $12, $12, 4 Cont $20, 4000($13) $13, $13, 4 $10, $10, 1 $11, $11, 4 $10, $21, Ciclo # costante 100 in $21 # inizializzazione i a 0 # inizializzazione spiazzamento di i a 0 # inizializzazione spiazzamento di j a 0 # inizializzazione spiazzamento di k a 0 # leggo A[i] in $20 # se A[i] non è minore di 15... # ... vai a scrivere A[i] in C # scrivi A[i] in B[j] # incrementa lo spiazzamento di j # salta all’incremento di i # scrivi A[i] in C[k] # incrementa lo spiazzamento di k # incrementa l’indice i # incrementa lo spiazzamento relativo a i # se non abbiamo scorso tutto l’array, torna a inizio ciclo Esame scritto del 23/7/2002 (*) Dati due vettori A e B di 100 interi memorizzati, rispettivamente, a partire dalla locazione 2000 e 3000, scrivere un programma in Assembler MIPS che crei un vettore C (memorizzato a partire dalla locazione 4000) tale che in ogni posizione C[i] venga memorizzato il minimo tra A[i] e B[i]. Si richiede (1) la descrizione dell’algoritmo usato (in pseudocodice), (2) la descrizione dell’uso dei registri e (3) il programma in assembler commentato linea per linea. Non si può assumere la inizializzazione di nessun registro. Vedi la soluzione della seconda prova intercorso del 2002-2003, che riguarda il massimo e non il minimo. 4. Esame scritto del 12/9/2002 (*) Dato un vettore A di 100 interi memorizzati (in ordine crescente) a partire dalla locazione 2000, scrivere un programma in assembler MIPS che calcoli la posizione del vettore in cui si trova l’elemento più distante dall’elemento successivo. Cioé si vuole calcolare la posizione 0≤ i ≤ n-2 tale che sia massima la differenza A[i+1]-A[i]. Ad esempio, se il vettore è A[0]=1, A[1]=3, A[2]=4, A[3]=6, A[4]=8, A[5]=15, A[6]=18, A[7]=19) allora il risultato sarà 4 in quanto in posizione 4 si trova l’elemento 8 che è distante 7 dall’elemento successivo (cioè 15). Si richiede (1) la descrizione dell’algoritmo usato (in pseudocodice), (2) la descrizione dell’uso dei registri e (3) il programma in assembler commentato linea per linea. Non si può assumere la inizializzazione di nessun registro. L’esercizio è meno complicato di quanto sembri. In effetti, si tratta di calcolare il massimo di un insieme di valori, che rappresentano la differenza tra ogni elemento e quello precedente, partendo dall’elemento 1. Quindi, in effetti, si tratta di una semplice modifica da effettuare al semplice programma che calcola il massimo (che si presenta a lezione). Una osservazione è necessaria: si vuole calcolare la posizione dove la differenza tra un elemento e quello successivo è massima, quindi dovremo mantenere, ad ogni passo, non solamente il valore massimo trovato finora (per confrontarlo con le successive differenza) ma anche la posizione dove questo massimo è stato ottenuto. Ricordiamo brevemente il tipo di soluzione per il calcolo del massimo. Innanzitutto si deve inizializzare il valore corrente del massimo come il valore di A[1] – A[0] e la posizione a 0; poi, si deve scorrere il vettore, partendo dalla posizione 1 fino ad arrivare alla posizione 98. Quindi il programma in C che risolve l’esercizio è: massimo = A[1] – A[0]; posizione = 0; for (i=1; i < 99; i++) { diff = A[i+1] – A[i]; if (diff > massimo) { massimo = diff; posizione = i; } } // inizializzo il valore del massimo con quello ottenuto per i = 0 // stessa cosa per la posizione // per tutti gli altri elementi... // calcola la differenza tra l’elemento successivo e l’elemtno i-mo // se questa differenza rappresenta un nuovo massimo allora ... // ... aggiorno il massimo // ... aggiorno la posizione A questo punto possiamo passare alla soluzione in pseudocodice: Ciclo: Cont: massimo = A[1] – A[0]; posizione = 0; i = 1; diff = A[i+1] – A[i]; if ( ! diff > massimo) goto Cont; massimo = diff; posizione = i; i = i + 1; if (i !=99) goto Ciclo; Ora descriviamo i registri che ci servono: $10 $11 $12 $13 i spiazzamento relativo all’indice i spiazzamento relativo all’indice i+1 massimo 3 $14 $15 $16 $17 $18 $20 posizione diff valore A[i] valore A[i+1] costante 99 risultato della slt Una osservazione è necessaria: per poter accedere sia all’elemento A[i] che all’elemento A[i+1] si deve mantenere due spiazzamenti, uno relativo al valore i (=4i) e l’altro relativo al valore i+1 (=4i+4). Procediamo alla stesura del programma in Assembler. Ciclo: Cont: 5. addi $11, $0, 0 addi $12, $0, 4 lw $16, 2000 ($11) lw $17, 2000 ($12) sub $13, $17, $16 addi $14, $0, 0 addi $10, $0, 1 addi $11, $11, 4 addi$12, $12, 4 lw $16, 2000 ($11) lw $17, 2000 ($12) sub $15, $17, $16 slt $20, $13, $15 beq $20, $0, Cont add $13, $0, $15 add $14, $0, $10 addi $10, $10, 1 addi $11, $11, 4 addi$12, $12, 4 bne $10, $20, Ciclo # spiazzamento i vale 0 # spiazzamento i+1 vale 4 # carico A[0] # carico A[i] # massimo = A[1] – A[0] # posizione = 0 #i=1 # incremento spiazzamento relativo a i di 4 # incremento spiazzamento relativo a i+1 di 4 # carico A[i] # carico A[i+1] # diff= A[i+1] – A[i] # controlla se massimo < diff... # ... se ! diff > massimo salta a Cont # aggiorna il massimo (massimo = diff) # .. e la posizione (posizione = i) # i = i +1 # incremento spiazzamento relativo a i di 4 # incremento spiazzamento relativo a i+1 di 4 # if (i != 99) goto Ciclo Esame scritto del 25/9/2002 Dati due vettori A e B di 50 interi memorizzati (rispettivamente) a partire dalla locazione 2000 e a partire dalla locazione 3000, scrivere un programma in assembler MIPS che scriva a partire dalla locazione 4000 un array C di 100 elementi che corrispondono a A[0], B[0], A[1], B[1], …, A[49], B[49], cioè inframezzando elementi di A e di B. Si richiede (1) la descrizione dell’algoritmo usato (in pseudocodice), (2) la descrizione dell’uso dei registri e (3) il programma in assembler commentato linea per linea. Non si può assumere la inizializzazione di nessun registro. 6. Esame scritto del 20/2/2003 Dati due vettori A e B di 50 interi memorizzati (rispettivamente) a partire dalla locazione 2000 e a partire dalla locazione 3000, scrivere un programma in assembler MIPS che scriva a partire dalla locazione 4000 un array C di 50 elementi che corrispondono a A[0]+B[0], A[1]+B[1], …, A[49]+B[49], cioè la somma degli elementi di A e di B. Si richiede (1) la descrizione dell’algoritmo usato (in pseudocodice), (2) la descrizione dell’uso dei registri e (3) il programma in assembler commentato linea per linea. Non si può assumere la inizializzazione di nessun registro. 7. Seconda prova intercorso 2002-2003 (*) Dati due vettori A e B di 100 interi memorizzati, rispettivamente, a partire dalla locazione 2000 e 3000, scrivere un programma in assembler MIPS che crei un vettore C (memorizzato a partire dalla locazione 4000) tale che in ogni posizione C[i] venga memorizzato il massimo tra A[i] e B[i]. Si richiede (1) la descrizione dell’algoritmo usato (in pseudocodice), (2) la descrizione dell’uso dei registri e (3) il programma in assembler commentato linea per linea. Non si può assumere la inizializzazione di nessun registro Per poter creare il vettore C è necessario scorrere i vettori A e B in maniera sincronizzata, vale a dire che possiamo usare lo stesso indice (e lo stesso spiazzamento) per il vettore A ed il vettore B. Infatti, si deve accedere agli elementi A[0] e B[0] per poterne calcolare il massimo, poi agli elementi A[1] e B[1], poi agli elementi A[2] e B[2], etc. etc. Inoltre, si può usare lo stesso indice (e lo stesso spiazzamento) anche per il vettore C, in quanto il massimo tra A[0] e B[0] va scritto in C[0], il massimo tra A[1] e B[1] va scritto in C[1], il massimo tra A[2] e B[2] va scritto in C[2], etc. A questo punto è chiaro che il programma deve essere realizzato attraverso un ciclo che scorrerrà in maniera sincrona gli array A, B e C in maniera da andare a scrivere in posizione C[i] il massimo tra A[i] e B[i]. L’algoritmo espresso in codice C è: for (i = 0; i < 100; i++) { if (A[i] < B[i]) C[i] = B[i]; else C[i] = A[i]; } // per ogni elemento dell’array A // se il massimo è B[i] allora... // ...scrivilo in C[i] // altrimenti ... // ... scrivi A[i] in C[i] Un commento è necessario se A[i]=B[i]: in questo caso, non importa che cosa andiamo a scrivere in C[i]. Infatti il valore massimo è ottenuto da entrambi gli elementi e, nel codice appena esaminato, questo caso viene trattato dall’ else. 4 Traduciamo questo frammento di codice C in pseudocodice, vale a dire un linguaggio che usa istruzioni che sono più vicine alle istruzioni assembler. Ciclo: ScriviB: Cont: i=0; if (A[i] < B[i]) then goto ScriviB; C[i] = A[i]; goto Cont; C[i] = B[i]; i = i + 1; if (i !=100) goto Ciclo; // inizializzazione indice del ciclo for // salta a ciò da eseguire se il confronto è vero // else il massimo è A[i] e va scritto in C[i] // salta all’incremento dell’indice // il massimo è B[i] e va scritto in C[i] // incrementa l’indice // se non abbiamo scorso tutto l’array, torna a inizio ciclo A questo punto possiamo passare alla implementazione in Assembler. Dobbiamo innanzitutto specificare per quale scopo utilizziamo i registri all’interno del nostro programma. Quindi: $10 $11 $12 $13 $14 $20 costante 100 indice i spiazzamento relativo all’indice i valore letto da A[i] valore letto da B[i] risultato della slt addi add add Ciclo: lw lw slt bne sw j ScriviB: sw addi addi bne $10, $0, 100 $11, $0, $0 $12, $0, $0 $13, 2000($12) $14, 3000($12) $20, $13, $14 $0, $20, ScriviB $14, 4000($12) Cont $13, 4000($12) $11, $11, 1 $12, $12, 4 $11, $10, Ciclo La implementazione in Assembler MIPS sarà, quindi 8. # costante 100 in $10 #i=0 # spiazzamento = 0 # carico A[i] in $13 # carico B[i] in $14 # se A[i] < B[i] ... # ....salta a ScriviB # C[i] = B[i] # salta all’incremento dell’indice # C[i] = A[i] # incrementa l’indice # incrementa lo spiazzamento di 4 # se non abbiamo scorso tutto l’array, torna a inizio ciclo Esame scritto del 3/7/2003 (*) Sia dato un vettore A memorizzato a partire dalla locazione 1000, di dimensione non nota, che contiene i voti riportati agli esami da uno studente. I voti sono valori positivi (tra 18 e 30) ed il valore 0 indica la fine del vettore. Scrivere un programma in assembler MIPS che scriva nella locazione 2000 il numero di esami e nella locazione 2004 la media ottenuta Si richiede (1) la descrizione dell’algoritmo usato (in pseudocodice), (2) la descrizione dell’uso dei registri (con la indicazione di eventuali valori già memorizzati) e (3) il programma in assembler commentato linea per linea. Non si può assumere la inizializzazione di nessun registro. Innanzitutto alcuni commenti. Per prima cosa, è ovvio che la media che si richiede è la parte intera della media non avendo studiato nel programma istruzioni che permettano di fare la divisione con un risultato in virgola mobile. Poi, dobbiamo anche definire cosa intendiamo per media qualora lo studente non abbia fatto alcun esame e quindi il suo vettore di voti risulti vuoto (A[0] = 0): in questo caso, assumiamo che la sua “media” valga 0. Per poter calcolare la media aritmetica è necessario (ovviamente) calcolare il numero di esami fatti e fare la somma di tutti i voti. Per semplificare la presentazione spieghiamo, separatamente, come effettuare in C questi due compiti, che possono essere (comunque) effettuati insieme nello stesso ciclo. Iniziamo, quindi, con il problema: “dato un vettore di voti, memorizzato dalla locazione 1000 e di dimensione non nota (il valore 0 indica la fine del vettore) calcoliamo il numero di elementi presenti nel vettore”. Per poter scorrere questo vettore di dimensione non nota non possiamo utilizzare il ciclo for e, piuttosto, utilizziamo un ciclo di tipo while che controlla di non avere ancora incontrato il segnale di “fine vettore” vale a dire che l’elemento A[i] non valga 0. Vediamo prima il codice in C e poi lo commentiamo. i = 0; while (A[i] != 0) { i = i +1; } count = i; Utilizziamo esclusivamente una variabile indice i che viene inizializzata a 0 ed incrementata ad ogni passo. Quando si esce dal ciclo, vale che la condizione controllata dal while non è vera e quindi il valore dell’elemento A[i] è zero. A questo punto, i indica il numero di elementi presenti nel vettore e viene copiato in una variabile count. Attenzione a questo passo: in effetti i indica la posizione dove si trova lo zero, il che significa che nel vettore ci sono gli elementi non vuoti A[0], A[1], A[2], ..., A[i-2], A[i-1] (infatti A[i] vale 0). Il numero di questi elementi non vuoti è proprio il valore di i quando siamo usciti dal ciclo. Adesso passiamo al secondo problema, vale a dire quello di “dato un vettore di voti, memorizzato dalla locazione 1000 e di dimensione non nota (il valore 0 indica la fine del vettore) calcoliamo la somma degli elementi presenti nel vettore”. Il programma corrispondente in C è alquanto semplice, ora che abbiamo visto come scorrere l’array (nel frammento di codice precedente) ed è: i = 0; sum = 0; while (A[i] != 0) { sum = sum + A[i]; } 5 Quindi possiamo mettere insieme i due programmi, aggiungendo la operazione per calcolare la media (controllando prima che il numero di esami count sia diverso da 0) ed ottenere: i = 0; while (A[i] != 0) { i = i +1; } count = i; i = 0; sum = 0; while (A[i] != 0) { sum = sum + A[i]; } media = 0; if (count != 0 ) media = sum/count; Da notare che alla fine abbiamo di fatto assegnato 0 a media e poi, solamente se count non vale 0, scriviamo il valore effettivo della media. Quindi se count è uguale a 0 allora non si effettua alcuna operazione e la media rimane 0 come richiesto. Spero che sia chiaro che abbiamo separato i due problemi solamente per semplicità e che, quindi, possiamo chiaramente unirli ottenendo un solo ciclo che calcola allo stesso tempo sia il numero di elementi che la loro somma. sum = 0; i = 0; while (A[i] != 0) { i = i +1; sum = sum + A[i]; } count = i; media = 0; if (count != 0 ) media = sum/count; Ora possiamo scrivere questo programma in pseudocodice (utilizzando istruzioni vicine all’Assembler): End: sum = 0; i = 0; if (A[i] == 0) goto ExitW i = i +1; sum = sum + A[i]; goto While; count = i; media = 0; if (count == 0 ) goto End: media = sum/count; ... $10 $11 $12 $13 $14 $15 indice i spiazzamento indice i sum count media valore letto da A[i] While: ExitW: A questo punto possiamo passare alla codifica in Assembler. Descriviamo dapprima il ruolo dei registri. Ora passiamo alla codifica: While: ExitW: End: addi $12, $0, 0 addi $10, $0, 0 addi $11, $0, 0 lw $15, ASTART($11) beq $15, $0, ExitW addi $10, $10, 1 addi $11, $11, 4 addi $12, $12, $15 j While add $13, $0, $10 addi $14, $0, 0 beq $13, $0, End div $12, $13 mflo $14 ... # sum = 0 #I=0 # spiazzamento = 0 # carico A[i] # if (A[i] == 0) goto ExitW #i=i+1 # spiazzamento + 4 # sum = sum + A[i] # goto While # count = i # media = 0 # if (count == 0) goto End # dividi sum per count # muovi il risultato (parte intera) in $14 Alcuni commenti finali. Ovviamente, riflettendo un attimino, si può chiaramente vedere che la variabile count non serve veramente. Basta usare la variabile i così come “esce” dal ciclo while, senza doverla copiare. In questa maniera si risparmia un registro ($13) e la istruzione che assegna i a count e si sostituisce il registro $10 al registro $13 nelle due istruzioni che usano $13 dopo lea etichetta ExitW. 6 9. Esame scritto del 24/7/2003 (*) Sia dato un vettore A di 20 elementi, memorizzato a partire dalla locazione 1000, che contiene i voti riportati agli esami da uno studente. Si può assumere che i voti sono tutti valori positivi tra 18 e 30. Scrivere un programma in assembler MIPS che scriva un vettore B di 20 elementi, memorizzato a partire dalla locazione 2000 in modo che B[i] = 0 se A[i] è compreso tra 18 e 24, B[i] = 1 se A[i] è compreso tra 25 e 27 e B[i]=2 se A[i] è compreso tra 28 e 30. Si richiede (1) la descrizione dell’algoritmo usato (in pseudocodice), (2) la descrizione dell’uso dei registri (con la indicazione di eventuali valori già memorizzati) e (3) il programma in assembler commentato linea per linea. Non si può assumere la inizializzazione di nessun registro. La descrizione del programma è abbastanza chiara. Si tratta di creare un nuovo vettore che contenga per ogni esame un valore dipendente dal voto ottenuto all’esame. Una cosa che può semplificare la scrittura del programma è la assunzione che tutti i valori presenti nell’array A sono compresi tra 18 e 30 e quindi non servono altri controlli. Passiamo, ora, alla stesura del programma in codice C. Il programma consiste di un for (che deve scorrere il vettore dalla posizione 0 alla posizione 19) al cui interno si trovano una serie di if che permettono di assegnare all’elemento corrispondente del vettore B il valore richiesto. for (i = 0; i < 20; i++) { if (A[i] < 25) B[i] = 0; else if (A[i] < 28) B[i] = 1; else B[i] = 2; } // per ogni elemento del vettore // se il voto è <= 24 (e si assume che sia ≥ 18)... // .... allora B[i] vale 0 // altrimenti (quindi è >=25)... se A[i] è ≤ 27... // .... allora B[i] vale 1 // altrimenti (vuol dire che A[i] >= 28 e si assume che sia ≤ 30) // .... allora B[i] vale 2 Innanzitutto, notiamo come la assunzione che tutti i valori A[i] siano compresi tra 18 e 30 ci facilita la stesura del codice. Il primo if, infatti, si basa sul fatto che tutti i voti A[i] ≥ 18 così come l’ultimo else si basa sul fatto che A[i] ≤ 30. Inoltre, si deve notare come abbiamo scritto il codice C in maniera da riflettere il tipo di test che abbiamo a disposizione con l’Assembler. Infatti, piuttosto che effettuare il test A[i] ≤ 24 abbiamo preferito effettuare il test A[i]< 25 (per usare la slt) sapendo tutti i valori A[i] sono interi. La stessa cosa capita per il test A[i] ≤ 27 che diventa A[i] < 28. Un altro commento è necessario per l’indice i che viene usato sia per accedere all’elemento di A sia per scrivere il corrispondente elemento di B, quindi useremo anche lo stesso spiazzamento per accedere sia ad A che a B. Ora passiamo alla scrittura in pseudocodice, utilizzando istruzioni più vicine all’Assembler. Ciclo: Else1: Else2: Cont: i = 0: if (! A[i] < 25) goto Else1; B[i] = 0; goto Cont; if (! A[i] < 28) goto Else2; B[i] = 1; goto Cont; B[i] = 2 i = i +1 if (i !=20) goto Ciclo; Passiamo ora alla descrizione dei registri: $10 $11 $12 $13 $14 $15 indice i spiazzamento dell’indice i registro che contiene il valore da scrivere in B[i] registro per A[i] registro per il confronto (slt) costante 20 Infine, ecco il programma in Assembler: Ciclo: Else1: Else2: add $10, $0, $0 add $11, $0, $0 addi $15, $0, 20 lw $13, ASTART($11) slti $14, $13, 25 beq $14, $0, Else1 addi $12, $0, 0 sw $12, BSTART($11) j Cont slti $14, $13, 28 beq $14, $0, Else2 addi $12, $0, 1 sw $12, BSTART($11) j Cont addi $12, $0, 2 sw $12, BSTART($11) addi $10, $10, 1 addi $11, $11, 4 bne $10, $15, Loop #i=0 # spiazzamento di i a 0 # costante 20 # carica A[i] # controlla se A[i] < 25 ... # ... se ! A[i] < 25 goto Else1 # scrivi 0 in ... # ... B[i] # goto Cont # controlla se A[i] < 28 ... # ... se ! A[i] < 28 goto Else2 # scrivi 1 in ... # ... B[i] # goto Cont # scrivi 2 in ... # ... B[i] #i=i+1 # spiazzamento = spiazzamento +4 # if (i != 20) goto Ciclo; 7