Esercizi di programmazione Assembler MIPS

Esercizi da esame
di Programmazione Assembler MIPS
Versione 1.04 (16 Gennaio 2004)
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. Basta
poco: (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.
10.
11.
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
Esame scritto del 10/9/2003............................................................................................................................8
Esame scritto del 29/9/2003 (*).......................................................................................................................8
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
Ciclo:
addi $11, $0, 0
addi $12, $0, 4
addi $18, $0, 99
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, $18, Ciclo
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.
Cont:
5.
# spiazzamento i vale 0
# spiazzamento i+1 vale 4
# caricamento costante 99
# 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]
4
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.
Traduciamo questo frammento di codice C in pseudocodice, vale a dire un linguaggio che usa istruzioni che sono più
vicine alle istruzioni assembler.
i=0;
if (A[i] < B[i]) then goto ScriviB;
C[i] = A[i];
goto Cont;
ScriviB: C[i] = B[i];
Cont:
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
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;
5
while (A[i] != 0) {
sum = sum + A[i];
}
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
10.
Esame scritto del 10/9/2003
Dato un vettore A di 100 interi memorizzati a partire dalla locazione 2000, scrivere un programma in assembler MIPS
che conti quante volte il primo elemento è ripetuto nell’array (compreso la posizione 0) e lo scriva nella locazione 3000.
Ad esempio (su un array di 10 elementi), se il vettore è A[0]=7, A[1]=3, A[2]=4, A[3]=7, A[4]=8, A[5]=15, A[6]=18,
A[7]=19, A[8]=13, A[9]=7 allora il risultato sarà 3 in quanto l’elemento A[0] (cioè 7) viene ripetuto 3 volte (in
posizione 0, 3 e 9). 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.
11.
Esame scritto del 29/9/2003 (*)
Dato un vettore A di 100 interi memorizzati a partire dalla locazione 2000, scrivere un programma in assembler MIPS
che scriva nella locazione 4000 il valore 1 se tutti gli interi nel vettore A sono uguali e scrive nella locazione 4000 il
valore 0 altrimenti (cioè se non sono tutti uguali). 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.
Il programma richiesto deve controllare che in tutto il vettore sia presente lo stesso elemento. L’output del programma
(1 se tutti gli elementi sono uguali e 0 altrimenti) viene messo nella locazione 4000. E’ importante notare, innanzitutto,
che i due possibili output non presentano situazioni simmetriche: per poter dare in output 1 si deve controllare che
gli elementi del vettore siano uguali tra loro mentre per poter dare in output 0 basta che
degli elementi sia
diverso da un elemento precedentemente letto. Quindi, per facilitare le cose, confronteremo tutti gli elementi con il
primo elemento del vettore e, in caso troviamo un elemento diverso da A[0], potremo terminare il ciclo ed concludere
che esiste almeno un elemento diverso dagli altri, e quindi dare in output 0 nella locazione 4000. Qualora, invece
terminiamo il ciclo “naturalmente”, vale a dire perchè abbiamo scorso tutti gli elementi da A[0] ad a[99], allora questo
significa (in maniera ovvia! ☺) che non siamo mai usciti dal ciclo stesso in maniera prematura, incontrando cioè un
elemento diverso dagli altri e (finalmente!) possiamo concludere che tutti gli elementi sono uguali al primo e scrivere il
valore 1 in locazione 4000.
Quindi il nostro pseudocodice è il seguente:
tutti
uno solo
for (i = 1; i < 100; i++) {
// per ogni elemento del vettore
if (A[i] != A[0])
// se abbiamo trovato un elemento diverso da A[0]...
goto Esci
//
allora possiamo saltare (fuori dal ciclo) a Esci
}
Esci: if (i != 100) {
// si esce dal ciclo per due motivi: se i != 100 allora siamo usciti con il goto
// scrivi 0 nella locazione 4000 perchè evidentemente c’è un elemento diverso dagli altri
else
// altrimenti siamo usciti perchè i ha raggiunto il valore 100 e quindi
// scrivi 1 nella locazione 4000 perchè tutti gli elementi sono uguali al primo elemento di A
Va notato che il programma dell’appello precedente (10/9/2003) chiedeva di contare il numero di elementi che in un
vettore risultassero uguali all’elemento A[0] ed è quindi chiaro che si può usare la stessa soluzione, contando il numero
di elementi uguali ad A[0] e poi scrivere il risultato nella locazione 4000 a seconda se il numero di elementi uguali a
A[0] fossero 99 (tutti tranne A[0]) oppure no.
Comunque, in uno pseudocodice più vicino all’Assembler, il programma diventa:
i=1
if (A[i] != A[0] )
goto Esci;
i = i +1
if (i != 100) goto Cont;
Esci:
if (i != 100) goto Scrivi0
// scrivi il valore 1 in locazione 4000
goto Fine:
Scrivi0: // scrivi il valore 0 in locazione 4000
Fine:
// qualsiasi istruzione
Cont:
Giusto per evitare una label aggiuntiva si può utilizzare un trucchetto: alla fine del for viene scritto nella locazione 4000
uno dei due risultati (diciamo che scriviamo un 1) e l’if lo usiamo solamente per riscrivere, se necessario, il valore 0
nella locazione 4000. Così il programma diventa:
Fine:
i=1
if (A[i] != A[0] )
goto Esci;
i = i +1
if (i != 100) goto Cont;
// scrivi il valore 1 in locazione 4000
if (i != 100) goto Fine
// avevamo ragione! andava scritto 1 in 4000, termina!
// scrivi il valore 0 in locazione 4000 // in caso contrario scriviamo 0 in 4000
// qualsiasi istruzione
$10
$11
$12
$13
indice i
spiazzamento dell’indice i
registro che contiene il valore di A[0] da confrontare man mano
costante 1
Cont:
Esci:
Passiamo ora alla descrizione dei registri:
8
$14
registro temporaneo per caricare A[i]
Infine, ecco il programma in Assembler:
Ciclo:
Esci:
Fine:
addi $10, $0, 1
addi $11, $0, 4
lw $12, ASTART($0)
addi $13, $0, 1
addi $15, $0, 100
lw $14, ASTART($11)
bne $14, $12, Esci
addi $10, $10, 1
addi $11, $11, 4
bne $10, $15, Ciclo
sw $13, 4000 ($0)
bne $10, $15, Fine
sw $0, 4000 ($0)
add $0, $0, $0
#i=1
# spiazzamento di i a 4
# carico A[0]
# costante 1 in $13
# costante 100
# carica A[i]
# if (A[i] != A[0]) goto Esci
#i=i+1
# spiazzamento = spiazzamento + 4
# if (i != 100) goto Ciclo
# scriviamo 1 come output
# if (i != 100) goto Fine
# scriviamo 0 come output
# istruzione fittizia per poter avere una etichetta Fine
9