Sei sulla pagina 1di 36

Domande Calcoltori Elettronici

DOMANDA
Si descrivano i problemi relativi al passaggio dei parametri nel caso di richiamo di
sottoprogrammi e le convenzioni previste per le funzioni realizzate dai registri del processore
MIPS, anche in considerazione della necessità di preservare/non preservare le informazioni. In
particolare, nel caso di richiamo di sottoprogrammi, si presenti e si commenti il coinvolgimento
dello stack e dei registri ra, sp. Si indichi anche una soluzione al problema di riutilizzare nel
programma chiamante i registri t dopo aver richiamato il sottoprogramma, tenendo conto che i
registri t erano già in uso prima della chiamata.
Si indichi anche una soluzione al problema di passare al programma chiamante un numero di
risultati maggiore rispetto al numero dei registri valore.

RISPOSTA
Nel processore MIPS, la gestione delle subroutine avviene nel seguente modo.
Il programma chiamante mette i valori dei parametri da passare al sottoprogramma nei registri $a0-$a3
e utilizza l'istruzione JAL LABEL (Jump And Link, salto con ritorno) per saltare al sottoprogramma
LABEL, salvando nel registro $ra (return address) l'indirizzo dell'istruzione successiva al programma
chiamante (PC+4). Il programma chiamato LABEL esegue le proprie operazioni, memorizza i risultati
nei registri $v0 e $v1 e restituisce il controllo al programma chiamante con l'istruzione JR $ra.
Il MIPS usa le seguenti convenzioni per allocare i suoi 32 registri nelle chiamate a sottoprogrammi:
*$a0-$a3 sono i quattro registri argomento per il passaggio dei paramentri;
*$v0, $v1 sono i due registri valore per la restituzione dei valori;
*$ra che è appunto il registro di ritorno al programma chiamante;
Il MIPS offre due classi di registri:
 $t0-$t9: sono 10 registri temporanei che non sono preservati in caso di chiamata a
sottoprogramma;
 $s0-$7: sono 8 registri che devono essere preservati in caso di chiamata a sottoprogramma
(ossia, se utilizzati, devono essere salvati e ripristinati dal programma chiamato);

Finchè è possibile, la scelta ottimale è quella di dividere i registri in “s” e “t”: i primi vengono utilizzati
solo dal main program e gli altri dai sottoprogrammi. Tuttavia possono succedere due cose: il main
program necessita anche dell'utilizzo dei “t” o il sottoprogramma degli “s”.
In questi casi, qualunque registro utilizzato dal main program (o sottoprogramma) deve essere riportato
al valore che conteneva prima del suo utilizzo: è qui che entra in gioco la regione di memoria chiamata
stack (“pila”) che viene gestita con la logica LIFO (last in first out), ossia l'ultimo dato a entrare è il
primo a uscire.
Infatti, nel caso in cui sia il sottoprogramma a dover utilizzare anche gli “s”, prima di farlo ha il
compito di salvarne il continuto nello stack e una volta finito di prelevarne il contenuto e rimetterlo
nell'apposito registro; viceversa, se il main dovesse utilizzare anche i registri “t” deve prima assumersi
il compito di salvarne il contenuto e poi ripristinarlo.
Lo stack (che cresce da indirizzi di memoria alti verso indirizzi via via più bassi) fa uso del suo
puntatore detto Stack Pointer per svolgere il proprio compito attraverso i comandi PUSH
(memorizzazione di un dato nello stack) e POP (estrazione di un dato).
Il MIPS alloca un registro appositamento per lo stack: $sp, che salva l'indirizzo dell'elemento dello
stack allocato più di recente.
Nel caso in cui i valori del sottoprogramma che debbano ritornare al main siano più di 2, essi vengono
caricati in memoria in maniera consecutiva: nel registro $v0 viene inserito l’indirizzo di partenza del
primo parametro e in $v1 viene inserito il numero di parametri.
Inoltre, nel caso di chiamate annidate (il sottoprogramma chiama un altro sottoprogramma), il valore di
$ra non può essere sovrascritto: per questo, prima della seconda chiamata a sottoprogramma, esso viene
salvato nello stack, per poi essere recuperato e inserito nel PC dal primo sottoprogramma per tornare al
main.
Questo perchè Hennessy e Patterson, studiando le statistiche di chiamate a sottoprogramma annidate
dei software dell'epoca come GCC e SPICE, notarono che queste erano in numero ridotto e che fosse
quindi più conveniente far uso prima di una sorta di “PC OLD”. Questo rientra ovviamente sotto uno
dei punti cardine che è “rendere più veloce l'evento più frequente”.
DOMANDA
Si presentino le fasi dello sviluppo di un sistema a microprocessore confrontando in particolare
simulazione ed emulazione.

RISPOSTA
Le fasi di sviluppo di un sistema a microprocessore si dividono in due macro-categorie:
 Sviluppo Hardware: date le specifiche del problema, è necessario progettare il circuito, quindi
costruire una scheda di prova e realizzare il prototipo. Per verificare che il sistema funzioni
correttamente bisogna effettuare un debug dell'hardware, che, a causa della complessità del
progetto, del gran numero di componenti e piste, può non essere facile da completare;
 Sviluppo Software: date le specifiche del problema, viene deciso che linguaggio di
programmazione è più opportuno realizzare (es. quanta memoria ho a disposizione, quanto deve
essere veloce la scheda). Dopodichè, si rende necessaria la stesura del flow-chart, quindi del
programma sorgente: esso verrà poi tradotto dall'assemblatore o dal compilatore in linguaggio
macchina. Il file oggetto prodotto non può essere eseguito perchè fa riferimento a procedure o
dati contenuti in altri file: per questo motivo il linker ha il compito di riunire un insieme di file
oggetto e librerie condivise in un unico file eseguibile;

Un simulatore è un ambiente totalmente software (la componente hardware è solo quella del calcolatore
su cui è ospitato) che replica il funzionamento della CPU: esso ci dà l'opportunità di vedere cosa accade
all'interno dei registri della CPU istruzione per istruzione (esempi per MIPS: SPIM e MARS). In
questo modo, tuttavia, non riesco a collaudare i veri tempi di risposta e l'interazione con il mondo
esterno (ad esempio il valore resituito da un sensore), perchè non ho effettivamente una circuiteria
hardware. Anche nel momento in cui integro software e hardware, entrambi funzionanti se collaudati
individualmente, spesso accade che il sistema non funzioni correttamente: è difficle, durante il processo
di individuazione dell'errore (debugging), scoprire quale sia la causa, se hardware o software (o
entrambe).
Per questo si ricorre all'utilizzo dell'emulatore (Emulazione Real Time). Dal prototipo si estra la CPU
e si inserisce al suo posto un componente chiamato ICE (In Circuit Emulator) che si collega attraverso
un'interfaccia comune (es. un cavo) a un generico calcolatore, lasciando inalterato il circuito dal punto
di vista elettronico/meccanico: ora ho il pieno controllo del software che in questo caso può interagire
con il mondo. Riesco quindi a testare cosa accade all'interno del mio sistema globale (hardware e
software) e vedere eseguito il programma proprio come se nella scheda ci fosse la CPU vera, valutando
i tempi effettivi di esecuzione e le risposte agli stimoli esterni del circuito.
L'emulatore, a differenza del simulatore che replica il sistema, è una circuiteria vera e propria.
Talvolta può accadere di non avere la possibilità di montare una CPU su uno zoccolo, perchè molto
costosi o perchè sottoposti a forti accelerazioni (con il rischio che la CPU si stacchi), per cui non ho la
possibilità di estrarre la CPU per l'emulazione. In questi casi nell’hardware esistono dei pin a cui ci si
può collegare con l’ice, la cpu vedrà la connessione ed entrerà in fase di collaudo. Esaminando pro e
contro abbiamo:
Simulazione:
Vantaggio: posso testare il software anche se l'hardware è ancora in fase di progettazione;
Svantaggio: non ho la vera interazione del software col circuito, non posso quindi conoscere i tempi
effettivi di risposta della scheda e il suo funzionamento;
Emulazione:
Vantaggio: mi permette di vedere come gira il programma sulla vera circuiteria, valutando quindi i
tempi di risposta effettivi e l'interazioni con il mondo esterno (è un collaudo operativo, molto
affidabile);
Svantaggio: è una tecnica costosa, diversa da CPU a CPU;
DOMANDA
Come è noto nell’esecuzione di istruzioni di somma e sottrazione si può generare l’overflow. Si
discuta il comportamento del processore MIPS in presenza di tale situazione. Si confronti il
comportamento del MIPS con quello di altri processori che sono dotati di un flag ad hoc, che
viene interrogato dopo aver eseguito le istruzioni di somma e sottrazione.

RISPOSTA
Lavorando in complemento a due, è possibile che durante un'operazione aritmetica di somma o
sottrazione si possa generale overflow (“traboccamento”, ossia superamento del massimo/minimo
rappresentabile con n bit utilizzando la rappresentazione in complemento a 2). Tenendo conto che la
dimensione della word nel MIPS è abbastanza elevata (32 bit), non si verifica molto spesso il caso
dell'overflow. È in base a questa considerazione che i progettisti del MIPS hanno deciso che quando si
verifca overflow, la ALU genera un'eccezione, inviando un segnale di interrupt all'unità di controllo che
altera il normle flusso di di esecuzione delle istruzioni verso una particolare sottoprogramma
(subroutine).
Solitamete, quando si salta a una subroutine si salva l'indirizzo di rientro nel registo $ra.
Ma in questo caso non si può fare in quanto non ci è dato conoscere in anticipo in quale porzione di
codice si sia generato overflow: se questo dovesse verificarsi all'interno di un sottoprogramma,
sovrascriverei l'indirizzo di ritorno al programma chiamante e il programma non seguirebbe più il
flusso di istruzioni corretto. Per questo motivo esiste una sorta di PC ulteriore chiamato EPC (Exeption
Program Counter), nel quale viene memorizzato l''indirizzo a cui dobbiamo tornare dopo che è stata
gestita un'eccezione (PC+4).
Quando terminerà la routine di gestione dell'overflow, si ha la necessità di tornare all’istruzione
successiva rispetto quella in cui si era verificata l’eccezione.
Questa operazione non può essere effettuata tramite l'istruzione “jr $EPC” in quanto non esiste.
Per effettuare jr $EPC si usano due istruzioni :
1. MFC0 $K0,$EPC
2. JR $K0
La prima istruzione letteralmente vuol dire Move from Coprocessor 0, ossia copia il contenuto di EPC
in un registro riservato al sistema operativo (K0). La seconda istruzione fa semplicemente il
caricamento nel program counter del contenuto di k0, e si ripartirà dall’istruzione successiva
all’eccezione.
Il vantaggio che ne deriva da questa procedura è evidente: non perdo tempo dopo ogni operazione
aritmetica a chiedermi se si sia verificato overflow o meno, come accade in altre architetture: quando
questo si verifica, viene generata una eccezione.
Lo svantaggio è che la chiamata a sottoprogramma richiede una maggior perdita di tempo rispetto al
caso in cui l'overflow venisse gestito all'interno del programma, ma come abbiamo detto, questo ha
poche probabilità di verificarsi vista la dimensione delle word: questo è un chiaro esempio della
filosofia “rendere più veloce il caso più frequente”.
Non sempre però abbiamo la necessità di individuare l’overflow, infatti se lavorassimo con numeri in
valori assoluto, non si può generare la condizione di overflow e quindi non avremmo bisogno di creare
una routine per gestirlo.
Sono state introdotte delle istruzioni che effettuano l’overflow detection e istruzioni che non lo
effettuano: per esempio ADD effettua l’overflow detection mentre ADDU (add unsigned) non effettua
la rilevazione dell’overflow, in quanto questa istruzione utilizza numeri in valore assoluto.

Lavorando con i numeri in valore assoluto la condizione che si può verificare durante un'operazione
artimetica non è l'overflow, ma il il riporto: nel MIPS non esiste né un flag dedicato nè viene generata
un’eccezione quando esso si verifica.
È compito del programmatore verificare il corretto risultato.
L’individuazione del riporto può essere effettuata con una routine: nel caso di una somma con ADDU,
per esempio, se il risultato è minore di uno dei due addendi si è verificato il riporto.

DOMANDA
Si presenti e si descriva l’architettura e il principio di funzionamento della ALU a 1 bit che esegue
le operazioni di AND, OR, somma, sottrazione e la funzione Less. Si evidenzi e si descriva come
tale architettura si modifichi per rendere possibile l’esecuzione delle istruzioni slt e beq. Per
ciascuna delle operazioni si propongano i valori assunti dalle linee di controllo della ALU.

RISPOSTA
L'ALU (Unità Logico-Aritmetica) è una componente fondamentale della CPU che si contraddistingue
per essere preposta all'esecuzione di operazioni aritmentico (somma, sottrazione, prodotto..) e logiche
(and, or, not..).
Nel realizzare una ALU a un bit (con ingressi a e b) ci serviamo di un multiplexer, ossia un selettore di
uscita: esso è un “interruttore” che permette di scegliere, tra due ingressi, quello la cui codifica è uguale
al segnale di comando.
Per realizzare le funzioni di AND e OR, si considera un singolo AND e un singolo OR in ingresso a un
multiplexer: le uscite di queste due porte logiche rappresentano 2 degli ingressi del multiplexer.
Il bit di controllo del multiplexer seleziona l'uscita: nel nostro esempio, se tale bit è 0, il risultato è
l'operazione di AND, vicerversa se è 1 quella di OR.
Innanzitutto un sommatore deve avere due ingressi per gli operandi ed una uscita di un bit per la
somma. Inoltre, deve tenere conto di un eventuale CarryIn in entrata dalla ALU adiacente e un
CarryOut per la ALU successiva.
Per l'operazione differenza si sfrutta il fatto che utilizzando la notazione di complemento a 2, somme e
sottrazioni possono essere effettuate con lo stesso circuito. In binario vale infatti:
A+B=A+ B+1
Si ricordi infatti che la scorciatoia per negare un numero in complemento a due consiste nell'invertire
ciascun bit e nel sommare poi il valore 1. Per invertire ciascun bit è sufficiente aggiungere un
multiplexere 2:1 che scelga tra b e b, a seconda del valore BInvert: se BInvert è 0 b resta naturale, se
binvert è 1 b viene negato e di conseguenza verrà fatta la sottrazione.
Per far sì che la ALU esegue istruzioni come slt, si deve innanzitutto espandere il multiplexer a 3
ingressi, aggiungendo un ingresso per il risultato della slt: il nuovo ingresso prende il nome di Less.
Si deve quindi forzare a 0 i bit di Less da 0 a 31, mentre per il bit meno significativo si riporta il bit di
segno della differenza A-B (se A-B>0, allora A>B, slt =0).

Avremo 5 configurazioni possibili, di conseguenza basta 3 bit per identificare le linee di controllo:
BINVERT OPA OPB OPERAZIONE
0 0 0 AND
0 0 1 OR
0 1 0 ADD
1 1 0 SUB
1 1 1 SLT

DOMANDA
Si descriva cosa si intenda per vincoli di allineamento, evidenziando l’ordinamento dei byte
previsto dal processore MIPS e indicando istruzioni del processore che ne fanno e non ne fanno
uso.

RISPOSTA
La memoria viene vista come un vettore (array) dove ogni elemento ha un indirizzo e questo indirizzo è
l'indice dell'array. La memoria del processore MIPS è suddivisa in byte (ossia 8 bit) e questa
architettura contempla due tipi di indirizzamento: indirizzamento a byte e a word.
Il primo prevede che si trattino i singoli byte a cui è associato l'indirizzo corrispondente: questo tipo di
indirizzamento è utile quando la dimensione dei dati da trattare è piccola.
Nel caso in cui 8 bit non bastino, si usano le word (“parole”): nel MIPS, le word sono costituite da 32
bit, ossia 4 byte. Quindi una word è un insieme di 4 celle di memoria ciascuna da 1 byte.
È evidente che, per questioni di velocità nel trasferimento dati, queste 4 celle di memoria debbano
essere consecutive una all'altra, ossia debbano rispettare il vincolo di allineamento (comune a molte
altre architetture): ogni word inizia sempre ad indirizzi multipli di 4.
In generale, quindi, le istruzioni che lavorano con le word come LW (Load Word) e SW (Store Word)
devono rispettare il vincolo di allineamento: se lo spiazzamento (offset) in queste due instruzioni non è
multiplo di 4 l'assemblatore segnale errore (non si possono leggere/scrivere dati a cavallo fra due
word).
Il MIPS prevede anche le istruzioni LH e SH (Store/Load Half Word), per le quali bisogna rispettare il
vincolo di allineamento sui multipli di 2, e istruzioni che lavorano coi singoli byte quali LB (Load
Byte) e SB (Store Byte).
In quest'ultimo caso l'offset non deve essere necessariamente un numero multiplo di 4: sarà
l'assemblatore a sacrificare delle celle vuote (1,2,3) in modo tale da fare coincidere l’indirizzo della
word con un multiplo di 4 (l’indirizzo che caratterizza la word è l’indirizzo del primo dei 4 byte che
formano la word).
Si pone quindi un problema: se memorizzo un byte da un registro alla memoria non ci sono problemi,
in quanto la memoria è suddivisa in byte. D'altro canto, però, se devo leggere un byte dalla memoria e
salvarlo in un registro (che è suddiviso in 4 byte), devo chiedermi come riempire gli altri 3 byte liberi.
Lasciari intatti com'erano prima non è una buona idea: mi trovo ad avere un dati tra loro totalmente
scorrelati, che generano solo confusione. La soluzione è dunque la seguente: se i dati in arrivo sono i
valore assoluto, i byte vengono settati a zero per non alternarne il valore; se i dati sono in complemento
a due si procede con l'estensione del segno, il cui senso è quello di mantenere inalterato il valore del
dato: se il numero è positivo i bit rimanenti vengono settati a 0, se il numero è negativo a 1.
Questo perchè la convenzione utilizzata dal MIPS è la BIG ENDIAN, ossia i byte più significativi
sono memorizzati nell'indirizzo di memoria più piccolo.

DOMANDA
Si presenti la rappresentazione in virgola mobile, secondo lo standard IEEE 754, discutendo in
particolare cosa si intenda per “hidden bit” (1 iniziale implicito) e i vantaggi e i problemi
derivanti dal suo impiego. Si discuta cosa si intenda per overflow e underflow.
I 2 numeri seguenti sono rappresentati in virgola mobile in doppia precisione, secondo lo
standard IEEE 754. Si indichi nella tabella seguente a quali numeri decimali essi corrispondano
commentando il risultato ottenuto.

Numero in virgola mobile Numero decimale corrispondente


0x6F10F000 00000000
0x80000000 00000007

Altro esercizio (singola precisione):

Numero S E M
+ 51544,62510 x 2-58
- 1652,810 x 2-141

RISPOSTA
Nel rappresentare numeri frazionari (es. 3,1416), molto piccoli (0,0000001) e molto grandi (6,7*10^9)
si ricorre a una notazione alternativa detta notazione scientifica, la quale prevede un'unica cifra a
sinistra della virgola moltiplicata per una potenza della base.
L'aritmetica dei cacolatori che supporta numeri in questa notazione viene detta virgola mobile, poiché
rappresenta numeri in cui la virgola non è in una posizione fissa.
La rappresentazione in virgola mobile avviene nel seguente modo:
Segno Esponente Mantissa
È chiaro che:
 Aumentando la dimensione della mantissa aumenta la precisione (perchè si può descrivere il
numero con più cifre);
 Aumentando la dimensione dell'esponente aumenta il range di numeri rappresentabili;

Il motivo della disposizione di segno, esponente e mantissa non è causale.


Il primo bit, quello di segno, ci permette di stabilire subito, se due numeri hanno segno diverso, quale
sia il maggiore/minore.
In caso di numeri con uguale segno, il posizionamento dell'esponente prima della mantissa fornisce una
risposta immediata nel confronto: quello con esposnte maggiore è più grande dell'altro.
Infine, se i primi due test dovessero fallire, si confrontano le mantisse.
Il MIPS si appoggia sullo standard IEEE 754 per la virgola mobile, che stabilisce le seguenti
convenzioni:
 1 bit segno, 8 bit esponente, 23 bit mantissa per la singola precisione (32 bit);
 1 bit segno, 11 bit esponente, 52 bit mantissa per la doppia precisione (64 bit);

Inoltre, per problemi legati all'ordinamento, la notazione migliore per l'esponente è la cosidetta tecnica
dell'eccesso (polarizzazione: 127 per la singola precisione, 1023 per la doppia).

Infine, poiché la prima cifra è sempre 1, questa cifra non viene memorizzata: di fatto la mantissa ha 24
cifre, di cui la prima sottointesa (hidden bit).
Per cui ricapitolando:
segno (0=+) esponente (forma polarizzata) 1+mantissa (complemento a due)

Il vantaggio dell'hidden bit è evidente: ho a disposizione una cifra in più nella mantissa e questo mi
permette una maggior precisione.
Tuttavia questo porta subito a un problema: la rappresentazione dello zero (0).
Tale numero dovrebbe essere infatti rappresentato da una mantissa di soli zeri, cosa che, con l'hidden
bit, non è possibile. La soluzione adottata è la seguente: nel caso in cui l'esponente sia rappresentato da
tutti 0 (-127), così come la mantissa, non applico la formula ma dico che tale numero è lo zero.
Anche nella rappresentazione in virgola mobile, così come in complemento a due, può accadere che si
presenti il problema dell'overflow: esso si verifica quando il numero ha mantissa superiore alla
massima mantissa rappresentabile e l'esponente è maggiore del massimo esponente rappresentabile,
ossia si è superato il massimo numero rappresentabile con quel numero di bit (massimo rappresentabile
senza overflow deve essere minore di 2-128).
In virgola mobile si presenta anche il problema dell'underflow, ossia quando si vogliono rappresentare
numeri inferiori al minimo numero positivo rappresentabile (diverso da zero) o superiori al massimo
numero negativo rappresentabile (ossia più piccoli di 2-126).
Inoltre, un grosso problema della virgola mobile è la precisione: avendo a disposizione solo un numero
limitato di bit, è necessario troncare il numero. Per avere minimi errori di rappresentazione, in realtà, il
processore utilizza, per svolgere i calcoli interni, due ulteriori bit (detti di guardia e di
arrotondamento), i quali rappresentano le 2 cifre dopo quelle messe a disposizione alla mantissa.
DOMANDA
Si descriva il funzionamento delle istruzioni sub, addiu e addi evidenziando anche le modalità di
indirizzamento coinvolte. Supponendo che s0 = 0x7782f113 e s1 = 0x80000a86 si dica quali valori
esadecimali assumerà il registro s4 se verranno eseguite le seguenti istruzioni, ben motivando la
risposta (-30018 = 8abe in complemento a 16):

sub $s4, $s1, $s0 addiu $s4, $s1, -30018 addi $s4, $s1, -30018

RISPOSTA
L'operazione aritmentica sub serve per effettuare la differenza tra due numeri.
Gli operandi sono entrambi contenuti in un registro e il risultato viene memorizzato in un terzo registro.
L'istruzione ha formato R, che prevede 6 bit per il codice operativo, 5 per il registro sorgente, 5 per il
registro target, 5 per il registro destinazione, 5 per lo shift amount e 6 per il campo function.
La modalità di indirizzamento per il 3 operandi è quella a registro.
overflow

L’operazione aritmetica addiu serve per effettuare la somma con operando immediato. L’operando è
contenuto nel campo costante dell’istruzione I (16 bit). Con questa istruzione è possibile effettuare sia
somme che sottrazioni in quanto la costante può essere sia positiva che negativa. Addiu fa l’estensione
del segno della costante nel caso in cui essa sia più piccola di 32 bit, ed inoltre non effettua l’overflow
detection perché lavora in valore assoluto. La modalità di indirizzamento prevista è a registro per il
primo operando e la destinazione, immediato per la costante.
Nel nostro caso si è generato un riporto (infatti ottieniamo 7fff9544 con il riporto di 1), che dovrà
essere gestito dal programmatore.

L’operazione aritmetica addi serve per effettuare la somma con un operando immediato contenuto nel
campo costante dell’istruzione I (16 bit). Questa operazione effettua l’estensione del segno della
costante nel caso in cui sia più piccola di 32 bit, ed effettua la somma tramite complemento a 2, quindi
compie l’overflow detection, in pratica se si verifica rileva la condizione di overflow. La modalità di
indirizzamento prevista è quella di indirizzamento immediato, la costante immediata è contenuta nel
campo dell’istruzione. Nel nostro caso si è verificata una situazione di overflow infatti la somma tra
due numeri negativi (la cui costante ha il segno esteso) genera un numero negativo troppo
grande(negativamente parlando) da essere rappresentato con n bit e quindi andremo fuori dal range di
rappresentazione.
DOMANDA
Si descriva il funzionamento delle istruzioni sub, addiu e addi evidenziando anche le modalità di
indirizzamento coinvolte. Supponendo che s0 = 0x57f11382 e s1 = 0x80002a06 si dica quali valori
esadecimali assumerà il registro s4 se verranno eseguite le seguenti istruzioni, ben motivando la
risposta (-24540 = a024 in complemento a 16):

sub $s4, $s1, $s0 addiu $s4, $s1, -24540 addi $s4, $s1, -24540

Si descriva il funzionamento delle istruzioni sub, addiu e addi evidenziando anche le modalità di
indirizzamento coinvolte. Supponendo che s0 = 0x67f11382 e s1 = 0x8000600a si dica quali valori
esadecimali assumerà il registro s4 se verranno eseguite le seguenti istruzioni, ben motivando la
risposta (-28195 = 91dd in complemento a 16):

sub $s4, $s1, $s0 addiu $s4, $s1, -28195 addi $s4, $s1, -28195

RISPOSTA
DOMANDA
Nella stesura di un programma per il processore MIPS oltre alle istruzioni i programmatori
possono utilizzare anche le pseudoistruzioni. Si spieghi cosa sono e si presentino i vantaggi e gli
svantaggi del loro uso. Si indichi come venga tradotta la pseudo istruzione lw $t0, 0x10010800
(carica nel registro dalla cella di memoria il cui indirizzo è indicato) e ulteriori due esempi
corredati dalle vere istruzioni del processore MIPS.
Altri casi:
1. sb $t5, 0x1001081f (carica il byte meno significativo del registro nella cella di memoria il
cui indirizzo è indicato)
2. beq $t0, 1421, cic2b (salta all’etichetta cic2b se il confronto fra registro e costante ha dato
esito positivo)
3. sw $t2, 0x10010644 (carica il contenuto del registro nella cella di memoria il cui indirizzo
è indicato)
4. sle $t1, $t2, $t3 (forza a 1 il valore di $t1 se $t2 è minore o uguale a $t3)
5. abs $t1, $t2 (scrive nel registro $t1 il valore assoluto di quanto è contenuto in $t2)

RISPOSTA
Le pseudoistruzioni sono particolari istruzioni che non sono implementate nell'hardware del MIPS ma
vengono tradottore in istruzioni “reali” del MIPS dall'assemblatore quando vengono chiamate.
Esempio di pseudoistruzioni sono addi, li, mv.
Il vantaggio delle pseudoistruzioni è che il programmatore ha a che fare con un set di istruzioni più
ricco di quello implementato effettivamente via hardware, permettendogli di utilizzare istruzioni più
complesse senza doverle scrivere ogni volta come set di istruzioni elementari del MIPS (esempio: addi
si compone di lui + ori).
Tuttavia comportano anche uno svantaggio: la perdita d'utilizzo di un registro da parte del
programmatore.
Durante la fase di traduzione delle pseudo-istruzioni, infatti, l'assemblatore andrebbe a modificare dei
registri potenzialmente in uso dal programmatore. Siccome non è in alcun modo possibile, da parte
dell'assemblatore, capire se un registro sia in utilizzo o meno, i progettisti del MIPS sono giunti a un
compromesso: dedicare il registro 1 ($at) solo all'assemblatore per poter svolgere il suo compito di
traduzione delle pseudoistruzioni, precludendone l'utilizzo al programmatore.
Inoltre, è bene ricordare che quando bisogna tenere conto delle prestazione del processore (tempo di
esecuzione, spazio di memoria occupato) si deve far riferimento alla istruzione reali del MIPS e non
alle pseudoistruzioni.
Load Immediate.
li $t0, 0x12345678 diventa:
lui $at,0x1234
ori $t0,$at,0x5678

Move.
mv $t0,$t1 diventa
add $t0,$zero,$t1

Altri casi:
 sb $t5, 0x1001081f (carica il byte meno significativo del registro nella cella di memoria il cui
indirizzo è indicato)

lui $at,0x1001
sb $t5,2079($at)

 beq $t0, 1421, cic2b (salta all’etichetta cic2b se il confronto fra registro e costante ha dato esito
positivo)

ori $at,$zero,1421
beq $t0,$at,cic2b

 sw $t2, 0x10010644 (carica il contenuto del registro nella cella di memoria il cui indirizzo è
indicato)

lui $at,0x1001
sw $t2,1604($at)

 sle $t1, $t2, $t3 (forza a 1 il valore di $t1 se $t2 è minore o uguale a $t3)

bne $t2,$t3,12
ori $t1,$zero,1
beq $zero,$zero,8
slt $t1,$t2,$t3

 abs $t1, $t2 (scrive nel registro $t1 il valore assoluto di quanto è contenuto in $t2)

addu $t1,$zero,$t1
bgez $t1 8
sub $t0,$zero,$t1
DOMANDA
Nella stesura dei programmi si utilizzano, oltre alle istruzioni del processore e alle
pseudoistruzioni, anche le direttive per l’assemblatore. Se ne presenti l’utilità, e si elenchino e si
commentino quelle note. Per ciascuna delle direttive proposte si dica se interferisce con la
memoria e, se sì, di quale tipo si tratti (a sola lettura o a lettura e scrittura).

RISPOSTA
Gli assemblatori hanno una varietà di funzioni aggiuntive che aiutano a scrivere programmi in modo
più veloce e agevole senza modificare in alcun modo il linguaggio assembleativo: un esempio sono le
direttive per la codifica dati, che consentono al programmatore di descrivere i dati in modo più conciso
e naturale rispetto alla rappresentazione binaria. Queste hanno la caratteristica di iniziare tutte con il “.”
e solitamente si trovano a inizio programma. Analizziamone alcune:
 .ascii stringa
Consente di caricare in memoria (ROM) la stringa specificata. È l'assemblatore che si preoccupa di
collocare in memoria la stringa codificata in byte delle singole lettere. Si tratta di memoria ROM
perchè il messaggio che il programmatore salva in memoria non deve essere modificato dall'utente (per
esempio la scritta “Temperatura:” nel display di un'auto);
 .asciiz stringa
Si differenzia dalla precedente in quanto questa prevede alla fine della stringa il carattere di fine stringa
NULL, utile per verificare quando la stringa è terminata;
 .byte b1, bn
Scrive i singoli byte b1, bn direttamente in celle di memoria (ROM) contigue;
 .word w1, wn
Scrive word (costanti di 32 bit) in celle di memoria consecutive (ROM). L'assembleatore ci viene in
aiuto: si occupa da solo di scrivere i dati all'inizio di una word, rispettando quindi il vincolo di
allineamento (nessun problema di accavallamento tra due parole);
 .data <indirizzo>
Specifica l'indirizzo di partenza dell'area di memoria che voglio contenga dei dati (non colloca nessun
valore in memoria);
 .text <indirizzo>
Specifica l'indirizzo di partenza dell'area di memoria che voglio contenta le istruzioni (non colloca
nessun valore in memoria);
 .float f1, fn
Memorizza i numeri in virgola mobile in singola precisione (32 bit) scritti in area di memoria (ROM)
contigue;
 .double d1, dn
Memorizza i numeri in virgola mobile in doppia precisione (64 bit) scritti in area di memoria (ROM)
contigue;
 .space <num>
Riserva uno spazio (in byte) in memoria (RAM) dell'entità specificata da <num>. Fa riferimento alla
RAM perchè riservo uno spazio in cui durante l'evoluzione del programma srivo dei risultati o dei dati;
 .global label
Dichiara l'etichetta “label” globale, ossia è possibile far riferimento ad essa da altri file oltre a quello in
cui è definita. Questo perchè, nello sviluppo di progetti complessi, è buona regola suddividere il
programma in diverse porzioni di codice, ognuna delle quali si occupa di assolvere un solo compito, in
modo che sia più facilmente modificabile;
DOMANDA
Nel progetto di sistemi di calcolo estremamente sofisticati la principale tecnica utilizzata si basa
su livelli gerarchici, o astrazioni. Si spieghi il significato di tale tecnica, mettendo in particolare
evidenza il ruolo svolto e l’importanza del livello che prende il nome di architettura del set delle
istruzioni.

RISPOSTA
Un tema comune nella progettazione hardware/software è che scavando in profondità nell'Hardware e
nel Software si scoprono sempre maggiori informazioni e i dettagli di livello inferiore sono nascosti al
fine di offrire un modello più semplice ai livelli superiori.
L'uso di tali livelli, o astrazioni, è la principali tecnica utilizzata nel progetto di sistemi di calcolo
estremamente sofisticati, in cui lavorano diversi team.
Uno dei livelli di astrazione più importanti è quello corrispondente all'interfaccia tra Hardware e
Software di più basso livello: questo livello viene denominato archietettura del set di istruzioni.
I progettisti di calcolatori distinguono l'architettura dall'implementazione dell'architettura, utilizzando
lo stesso approccio: un'implementazione è una struttura fisica, un'organizzazione hardware che realizza
la astrazione prevista dall'archietettura.
Sia l'Hardware che il Software consistono di diversi livelli gerarchici, all'interno dei quali ogni livello
nasconde alcuni dettagli al livello superiore.
Questo principio di astrazione è il modo in cui sia i progettisti dell'hardware che quelli del software
affrontano la complessità dei sistemi di calcolo.
Il software viene diviso in software di base e software applicativi, mentre la parte dell'hardware
comincia dall'analisi del silicio per arrivare alla costruzione del circuito.
Il vantaggio che ne deriva è che ci sono diverse implementazioni della stessa architettura, ma questo
non porta a innovazioni tecnologiche se il linguaggio non lo prevede.
Il MIPS appartiene alla famiglia dei processori RISC (Riduced Instruzion Set Computer), ossia
vengono implementate a livello hardware solo poche istruzioni fondamentali (al contrario del CISC).
DOMANDA
Come è noto, conclusa la fase di simulazione di un programma, bisogna valutare quali requisiti di
memoria debbano essere rispettati affinché il programma possa essere messo in esecuzione su
una scheda reale basata sul processore MIPS, da progettare o acquisire dal mercato. Si spieghi
come si possano identificare le aree di cui si compone il programma e la relativa mappa di
memoria (per ogni area, indirizzo di inizio, di fine, dimensione della memoria risultante e
tipologia di contenuto e di memoria richiesta).

RISPOSTA
Conclusa la fase di simulazione si ha la piena visione delle aree di memoria che andremo ad utilizzare
con il nostro programma: dai dati alle costanti, ai messaggi e ai risultati.
Un passaggio fondamentale è quello della scelta delle tipologie di memoria più adatte per la soluzione
che vogliamo realizzare.
Possiamo identificare due modelli di memoria:
 Ideale (fornito dal software di collaudo);
 Reale;

Il modello ideale ha una struttura di questo tipo:

7fffffffhex
Stack segment

Dynamic data
Data segment
Static data
10000000hex
Text segment
400000hex
Reserved

Possiamo identificarre all'interno di esso diverse aree:


 Una parte riservata al sistema operativo;
 L'area del programma (.text);
 L'area dei dati statitici (es. messaggi .ascii e operandi .data) con la quale termina la ROM;

Successivamente comincia la memoria di tipo RAM (Random Access Memory), che comprende tutti
quei dati di cui non è necessario salvare il valore: verranno inseriti dati dinamici come i risultati
dell'operazioni logico/aritmetiche e lo stack.
Questa struttura è però, appunto, ideale: sul mercato ci sono memorie con tagli predefiniti, ed è
impossibile trovare trovare un dispositvo ROM che abbia esattamente il numero di caselle di mio
interesse. In un modello reale non avrò mai ROM e RAM consecutive, ma vi sarà un spazio lasciato
libero adibito per scopi futuri.
DOMANDA
Si descriva il formato J e l’esecuzione dell’istruzione “j dopo”; si indichi la massima distanza a
cui si deve trovare l’istruzione da raggiungere, a cui è associata l’etichetta “dopo”. Si indichino
altre istruzioni che permettano di raggiungere tale etichetta, quando essa si trovi al di fuori
dell’intervallo di raggiungibilità.

RISPOSTA
L'istruzione JUMP utilizza il formato J previsto dal MIPS, che si compone di:
 6 bit per il codice operativo;
 26 bit per per il campo indirizzo;
L'istruzione “j dopo” è un salto incondizionato all'etichetta “dopo”: è chiaro quindi che la massima
distanza che a cui si deve trovare l'istruzione da raggiungere è 2^26 word.
In sè, 26 bit non bastano per definire un indirizzo.
Tuttavia, siccome questi indirizzi puntano sempre a word, i due bit meno significativi sono sempre
uguali a zero (se un numero binario è multiplo di 4 finisce con 00).
Inoltre, per il principio di località degli accessi, la distribuzione degli indirizzi generati durante
l'esecuzione di un programma non è del tutto casuale: esiste un'elevata probabilità che a partire da un
indirizzo ne venga poi generato uno simile, cioè a pochi accessi di memoria. Per questo motivo i 4 bit
più significativi rimangono invariati rispetto a quelli del PC all'atto dell'esecuzione dell'istruzione: in
questo modo abbiamo i 32 bit necessari a comporre un indirizzo di memoria.
Questo metodo, in cui l'indirizzo a cui saltare è ottenuto concatendando i 26 (28) bit dell'istruzione con
i 4 più significativi del Program Counter, è detto indirizzamento pseudodiretto e ha il vincolo che i
salti possono avvenire solo all'interno di blocchi di memoria che hanno i 4 bit alti del PC in comune.
Se bisogna saltare a un'altra macro-area (cioè 26 bit non bastano) è necessario utilizzare l'istruzione JR
$s0 (formato istruzione J), ossia “Jump to Register”: preliminarmente, l'indirizzo a cui si vuole saltare
viene caricato nel registro indicato dall'istruzione ($s0); quindi, con il comando JR $s0, l'indirizzo
salvato nel registro diventa il nuovo contenuto del PC.
Questi problemi di “salti” sono direttamente gestiti dall’assemblatore in fase di traduzione, quindi il
programmatore non deve preoccuparsi se un salto sia fattibile o meno, ma sarà l’assemblatore, nel
caso, ad effettuare le dovute sostituzioni per poterlo eseguire.

DOMANDA
Si discuta il funzionamento delle istruzioni che permettono di scrivere in memoria numeri in
virgola mobile in singola e doppia precisione, mettendo anche in evidenza come possano essere
identificati i registri all’interno delle istruzioni, senza modificare né i formati delle istruzioni che
lavorano su numeri interi, né il numero di bit dedicati all’identificazione degli stessi.
Si proponga un’analisi comparativa con le istruzioni di scrittura in memoria di numeri interi; si
dica cosa cambia se il dato da scrivere è un intero in valore assoluto o in complemento a due.

RISPOSTA
Come per i numeri interi, anche per i numeri in virgola mobile è possibile effettuare la scrittura in
memoria di un numero contenuto in un registro.
Nel caso in cui il numero sia contenuto in un registro f (ossia registri in virgola mobile) in singola
precisione (32 bit), la scrittura in memoria viene effettuata con swc1 $f0,0($s2).
Si noti che in questo tipo di istruzioni convivono sia registri interi che decimali in quanto il registro $f0
conterrà il numero in virgola mobile singola precisione da scrivere alla locazione di memoria il cui
indirizzo è contenuto nel registro intero $s2 (l'indirizzo di memoria è un numero intero).
Per i numeri in virgola mobile in doppia precisione (che sono memorizzati in due registri consecutivi,
avendo bisogno di 64 bit) devono essere effettuate due scritture in memoria in due locazioni di
memoria adiacenti.
Se mettiamo il numero in virgola mobile in $f0 e $f1, le scritture in memoria saranno :
sw $f0,0($s1) e sw $f1,4($s1) in questo modo avremo le due locazioni di memoria consecutive che
conterranno il numero in virgola mobile in doppia precisione, come prima si include il registro intero in
quanto gli indirizzi sono numeri interi.
I formati di lettura/scrittura non cambiano da interi a virgola mobile: se il codice operativo identifica
un'istruzione che lavora con numeri interi, i registri utilizzati saranno i 32 dedicati agli interi; viceversa,
se il codice operativo identifica un'istruzione che opera con numeri in virgola mobile verrano utilizzati i
32 dedicati alla virgola mobile. Entrambe le tipologie di registri (interi e virgola mobile), infatti,
necessitano di 5 bit per essere indirizzati.

Nel caso in cui si volesse memorizzare in un registro un numero in valore assoluto o in complemento a
due, nulla cambia dal punto di vista del formato dell'istruzione.
Tuttavia, bisogna prestare attenzione al tipo di dato che viene immagazzinato, ossia distinguere tra le
istruzioni di lb/lh e lbu/lhu: infatti, qualore il numero fosse composto da meno di 32 bit, è necessario
stabilire come riempire i bit alti rimasti liberi.
Lasciare tali bit intatti è una pessima idea perchè creerebbe solo confusione tra il dato immagazzinato e
bit che non c'entrano con esso. Per questo si può procedere in due maniere distinte:
 Se il dato è espresso in valore assoluto, tali bit liberi vengono settati a 0;
 Se il dato è espresso in complemento a due, si procede con l'estensione del segno: se il numero
è positivo vengono settati a 0, se il numero è negativo a 1;

Queste operazioni vengono attuate per non alterare il valore del dato ricevuto, rispettandone la codifica.
Le istruzioni lb/lh effettuano l'operazione di estensione del segno, mentre le istruzioni di lbu/lhu
(unsigned) no. Altri casi sono la gestione delle stringhe e delle operazioni logiche.
Per entrambe non è necessaria l'estensione del segno: per caricare dati come caratteri si usa lbu (la
tabella ASCII è in valore assoluto), mentre nelle operazioni logiche quali “and” e “or” (andi e ori) si
lavora con singoli bit e non numeri.

DOMANDA:
Si enuncino e si descrivano i principi fondamentali di progetto che hanno ispirato il processore
MIPS e l’architettura del suo set delle istruzioni.
RISPOSTA:
I principi di progetto sono 4:
 Semplicità e regolarità sono strettamente correlate; Un esempio sta nel fatto di
richiedere che tutte le istruzioni abbiano esattamente tre operandi (il numero più
naturale: due numeri su cui lavorare il riferimento alla locazione in cui memorizzare il
risultato), non uno di più non uno di meno ed è conforme alla filosofia di mantere
l'hardware semplice. Questa è una notazione rigida, nel senso che ciascuna istruzione
aritmetica MIPS esegue una solo operazione alla volta e contiene esattamente 3
variabili.
 Minori sono le dimensioni, maggiore è la velocità; Un esempio sta nella limitazione ai
32 registri interni alla CPU. È conveniente utilizzare i registri interni perchè più veloci
rispetto all'andare in memoria e quindi è logico pensare che maggiore è il numero di
registri, maggiore è la velocità di esecuzione delle istruzioni. Tuttavia è vero solo in
parte: infatti, un numero di registri molto elevato aumenta la durata del ciclo di clock,
semplicmente perchè i segnali elettrici impiegano un tempo maggiore se devono
comprire una distanza maggiore. Principi come questo non sono però validi in assoluto:
31 registri possono anche non essere meglio di 32, per cui i progettisti devono trovare un
compromesso fra la possibilità di usare un discreto numero di registri e la necessità di
rendere veloce il ciclo di clock;
 Un buon progetto richiede buoni compromessi; Un esempio è il conflitto fra il
desiderio di mantenere la stessa lunghezza per tutte le instruzioni e quello di avere un
unico formato: il compromesso scelto dal MIPS è quello di mantere uguale la lunghezza
di tutte le istruzioni, predisponendo però formati diversi per tipi di istruzioni diversi
(formati I, R, J);
 Rendere più veloce l'evento più frequente; Un esempio è il metodo di indirizzamento
imediato previsto dal MIPS. Le istruzioni che prevendono molto spesso l'utilizzo di
costanti. Prevedere registri interni dedicati per ogni costante è semplicemente assurdo, e
andare ogni volta in memoria a prelevare la costante rallenta le istruzioni. Il MIPS offre
particolari versioni delle istruzioni aritmentiche in cui uno degli operandi è una costante,
con la novità data dal fatto che la costante è contenuta dentro l'istruzione stessa (tutte le
istruzioni che terminano con I (di immediate) come addi, lui). Il campio contenente la
costante è lungo 16 bit, che solitamente sono sufficiente ma possono non esserlo: per
questo motivo le pseudo-istruzioni come addi si dividono i due istruzioni: lui (load
upper immediate) che caria i 16 bit più significativi della costante nella parte alta del
registro, e ori che carica gli altri 16 bit della parte bassa della costante;

DOMANDA
Si discuta il funzionamento delle istruzioni seguenti, mettendo anche in evidenza come possano
essere identificati i registri in virgola mobile all’interno delle istruzioni, senza modificare né i
formati delle istruzioni che lavorano su numeri interi, né il numero di bit dedicati
all’identificazione degli stessi.

add.d $f0, $f6, $f8 bc1t poi

RISPOSTA
Il MIPS fornisce la possibilità di lavorare con i numeri in virgola mobile sia in singola che in doppia
precisione, mettendo a disposizione 32 registri virgola mobile identificati con $f.
Le istruzioni per le operazioni in virgola mobile differiscono da quelle per i numeri interi solo per il
codice operativo, a cui è affidato il compito di identificare se l'istruzione utilizza registri interi o in
virgola mobile: essendo entrambi i tipi di registri a 32 bit, bastano sempre 5 bit per poterli indirizzare.
Il MIPS fa uso dello standard IEEE 754, il quale permette di lavorare in singola (.s) e in doppia (.d)
precisione, mettendo a disposizione rispettivamente 32 e 64 bit.
Per la singola precisione si può utilizzare qualsiasi dei 32 registri $f, mentre per la doppia si utilizzano
solo quelli con numeri pari, poiché vengono uniti al dispari successivo al fine di contenere tutti e 64 i
bit del numero.
Con l’istruzione add.d $f0,$f6,$f8 stiamo effettuando la somma in virgola mobile tra due numeri in
doppia precisione: il primo è contenuti in $f6 e $f7 mentre il secondo in $f8 e $f9; il risultato sarà
inserito in $f0 e $f1.
La funzione “bc1t poi” solitamente segue una istruzione di compare: l'struzione di comparazione a
seconda dell’esito settano a 1 o a 0 un flag definito come fp (Frame Pointer).
La funzione bc1t se fp =1 viene effettuato il salto a POI altrimenti il programma prosegue.

DOMANDA
Si discutano le modalità di indirizzamento seguenti, previste dal set di istruzioni del processore
MIPS, per quanto concerne il modo di operare, il formato delle istruzioni che ne fanno uso e l’uso
dell’operando eventualmente presente in tali istruzioni, con almeno due esempi di istruzioni per
ciascun indirizzamento proposto:

 Immediate addressing
 PC-relative addressing
 Base addressing
 Pseudo direct addressing
 Register addressing

RISPOSTA
 Immediate addressing
L'operando è una costante specificata nell'istruzione. Formato utilizzato: I.
Nei 16 bit riservati all'indirizzo troviamo l'operando di nostro interesse.
Esempio: addi, subi, slti, andi, ori (in generale le istruzioni che finiscono con “i” di immediate).

 Register addressing
Il registro specificato nell'istruzione contiene l'operando. Formato utilizzato: R.
Esempio: add, sub

 Base addressing
Il byte, half word o word è in una locazione di memoria individuata dalla somma del contenuto di un
registro e di una costante specificata nell'istruzione. Formato utilizzato: I.
Esempio: lb, sb, lw, sw

 PC-relative addressing
L'indirizzo è dato dalla somma tra l'offset specificato nell'istruzione moltiplicato per 4 (per rispettare
vincolo di allineamento) e il contenuto del Program Counter.
Formato utilizzato: I.
Esempio: beq, bnq

 Pseudo direct addressing


L'indirizzo è ottenuto concatenando i 26 bit dell'istruzione con i 4 bit più significativi del PC.
Formato utilizzato: J.
Esempio: j;

Alcune istruzioni hanno operandi che usano diversi indirizzamenti: addi, per esempio, usa
l'indirizzamento a registro per identificare la destinazione e il primo operando e quello immediato per
la costante.

DOMANDA
Per l’immagazzinamento dei dati, oltre alla memoria, si possono usare i registri, sia interi che in
virgola mobile. Si discuta quanti e quali sono i registri del processore MIPS e le convenzioni
previste per le loro funzioni. Si spieghi come possano essere identificati i registri all’interno delle
istruzioni, sia per gli interi che per la virgola mobile, senza modificare né i formati delle
istruzioni, né il numero di bit dedicati all’identificazione dei registri. Si spieghi il significato
dell’operazione chiamata riversamento dei registri (spilling).

RISPOSTA
Il processore MIPS mette a disposizione 32 registri per la virgola mobile, identificabili con $f e il
numero del registro, da 1 a 31. Essi richiedono, come quelli interni, 5 bit per essere indirizzati.
Le istruzioni per la virgola mobile sono di due tipi:
 .s se si lavora con numeri in singola precisione (32 bit);
 .d se si lavora con numeri in doppia precisione (64 bit);

La sintassi è uguale a quella degli interi (es: add.s $f0, $f1, $f2).
I numeri in doppia precisione richiedono 64 bit e quindi 2 registri.
La convenzione prevede che si utilizzino nelle istruzioni solo registri identificati con un numero pari:
tali registri in realtà sottointendono anche l'utilizzo del registro successivo.
Per cui si avrà, ad esempio: add.d $f0, $f2, $f4.
Ossia in doppia precisione, un registro è composto in realtà da due registri ed è identificato dal nome
del registro pari.
I registri totali sono quindi 32 per lavorare con numeri interi e 32 per lavorare con numeri in virgola
mobile: è il codice operativo a identificare se un'operazione utilizza numeri interi o in virgola mobile (e
quindi i registri per interi o per virgol mobile), senza dover modificare né formati delle istruzioni (che
sono gli stessi) né il numero di bit dedicati all'identificazione dei registri (servono sempre 5 bit).
Siccome i programma, in genere, possiedono più variabili dei regitri della CPU, il MIPS cerca di
mantenere le variabili usate più frequentemente nei registri (interni alla CPU, quindi più veloci) mentre
le altre le riversa in memoria, utilizzando le istruzioni di load/store per il trasferimento dei dati: questa
tecnica viene chimata Register Spilling e rientra sotto uno dei principi cardine “rendere più veloce
l'evento più frequente”.

DOMANDA
Si progettino due sommatori a 4 bit, il primo in grado di produrre con la massima rapidità il
riporto in uscita (sommatore con hardware infinito), il secondo basato sul principio
dell’anticipazione del riporto (carry look-ahead) e si stimino i tempi necessari a produrre il
riporto in uscita c4. Con i sommatori a 4 bit realizzati si progettino due sommatori a 16 bit
collegando 4 blocchi di somma proposti, in serie. Quanto tempo si richiederebbe per produrre il
carry in uscita dagli ultimi sommatori (c 16) nelle due ipotesi di progetto presentate? Si commenti
la complessità dei progetti risultanti. Motivare la risposta.

RISPOSTA
Per il teorema delle reti combinatorie, ogni rete può essere semplificata con una rete a 2 livelli logici.
Dal momento che i soli ingressi esterni sono costituiti dai due operandi e dal CarryIn entante nello
stadio meno significativo, si potrebbero in teoria calcolare i valori di CarryIn per tutti gli alti stadi del
sommatore in due soli livelli di logica: sia Δ il tempo di passaggio attraverso un gate logico, allora
avremmo 2 Δ.
Le espressioni dei riporti sono infatti:
c1 = b0c0 + a0c0 +a0b0
c2 = a1b1 + a1c1 +a1b1 etc
Sostituendo c1 in c2 e così via via, posso ottenere la somma in un tempo di 2Δ con il sommatore a
hardware infinito.

L'implementazione di un sommatore con principio di carry look-ahead (ossia di anticipazione del


riporto) si basa su diversi livelli di astrazione. Utilizzando i termini di genera e propaga:
gi= ai*bi
pi=ai+bi
Si possono esprimere i segnali CarryIn in maniere più conveniente e quello che si ottiene è che il tempo
per produrre c4 sarà dato da 2 Δ della somma dei prodotto sommati a 1Δ per la creazione dei termini
genera e propaga, ossia in totale il sommatore CLA richiede 3 Δ.

Collegando questi 4 blocchi di somma, con il sommatore ad Hardware Infinito per produrre c16
impiegherei 8 Δ.
Per quanto riguarda invece il CLA, ci vorrebbero 3 Δ per il primo stadio e 2 per ogni successivo, ossia
in totale 9Δ.

Per quanto concerne la complessità: si può immaginare quando si espandano le equazioni per i riporti
mano a mano che si passa ai bit di ordine più elevato nel sommatore ad Hardware Infinito.
Tale complessità si riflette sul costo dell'hardware per il riporto veloce, rendendo questo schema
proibitivamente costoso per i sommatori di grandi dimensioni.

Per il CLA, seppure si sia introdotto il primo livello di astrazione, la logica risulta comunque
considerevolmente complesssa anche per un sommatore a 16 bit. Si cercherà quindi di procedere
all'adozione di un secondo livello di astrazione.

Implementado il secondo livello di astrazione si ottiene un c16 a un tempo di 5Δ.


DOMANDA
Si progetti un sommatore a 1 bit e con questo un sommatore a 16 basato sul principio della
propagazione del riporto (ripple carry adder) e si stimi il tempo necessario a produrre il riporto
in uscita c16. Si progetti un sommatore a 8 bit basato sul principio dell’anticipazione del riporto
(carry look-ahead) e si stimi il tempo necessario a produrre il riporto in uscita c 8. Con il
sommatore a 8 bit realizzato si progetti un sommatore a 16 bit collegando 2 blocchi di somma
proposti, in serie. Quanto tempo si richiederebbe per produrre il carry in uscita dall’ultimo
sommatore (c16) nell’ipotesi di progetto presentata? Motivare la risposta.

RISPOSTA
Nel sommatore a propagazione del riporto (ripple carry adder) ogni singolo sommatore impiega 2Δ per
produrre ogni risultato e ogni CarryOut. Infatti:
sum = a xor b xor CarryIn
CarryOut = ab + aCarryIn + bCarryIn
Essendo somme di prodotti, devono appunto passare attraverso due livelli di logica.
Se i bit da sommare sono 16 e ogni risultato viene prodotto in 2 Δ, il tempo totale per produrre il
riporto in uscita c16 è di 32Δ.

L'implementazione di un sommatore con principio di carry look-ahead (ossia di anticipazione del


riporto) si basa su diversi livelli di astrazione. Utilizzando i termini di genera e propaga:
gi= ai*bi
pi=ai+bi
Si possono esprimere i segnali CarryIn in maniere più conveniente e quello che si ottiene è che il tempo
per produrre c4 sarà dato da 2Δ della somma dei prodotto sommati a 1Δ per la creazione dei termini
genera e propaga, ossia in totale il sommatore CLA richiede 3Δ.

Collegando i 2 blocchi di somma proposti in serie, per produrre il carry in uscita dall'ultimo sommatore
c16 impiegherei 5Δ (1 Δ per creare i termini genera e propaga e 2 Δ per ciascun sommatore).
DOMANDA
Si progetti un sommatore a 1 bit e con questo un sommatore a 15 basato sul principio della
propagazione del riporto (ripple carry adder) e si stimi il tempo necessario a produrre il riporto
in uscita c15. Si progettino due sommatori a 5 bit, il primo basato sul principio dell’anticipazione
del riporto (carry look-ahead, CLA), il secondo capace di produrre con la massima rapidità il
riporto in uscita (hardware infinito); si stimino i tempi necessari a produrre i riporti in uscita c 5.
Con i sommatori a 5 bit realizzati si progettino due sommatori a 15 bit collegando 3 blocchi di
somma proposti, in serie. Quanto tempo si richiederebbe per produrre il carry in uscita
dall’ultimo sommatore (c15) nelle ipotesi di progetto presentate? Motivare la risposta.

RISPOSTA
Nel sommatore a propagazione del riporto (ripple carry adder) ogni singolo sommatore impiega 2Δ per
produrre ogni risultao e ogni CarryOut. Infatti:
sum = a xor b xor CarryIn
CarryOut = ab + aCarryIn + bCarryIn
Essendo somme di prodotti, devono appunto passare attraverso due livelli di logica.
Se i bit da sommare sono 15 e ogni risultato viene prodotto in 2 Δ, il tempo totale per produrre il
riporto in uscita c16 è di 30Δ.

Dal momento che i soli ingressi esterni sono costituiti dai due operandi e dal CarryIn entante nello
stadio meno significativo, si potrebbero in teoria calcolare i valori di CarryIn per tutti gli alti stadi del
sommatore in due soli livelli di logica: sia Δ il tempo di passaggio attraverso un gate logico, allora
avremmo 2 Δ.
Le espressioni dei riporti sono infatti:
c1 = b0c0 + a0c0 +a0b0
c2 = a1b1 + a1c1 +a1b1 etc
Sostituendo c1 in c2 e così via via, posso ottenere la somma in un tempo di 4Δ con il sommatore a
hardware infinito.

L'implementazione di un sommatore con principio di carry look-ahead (ossia di anticipazione del


riporto) si basa su diversi livelli di astrazione. Utilizzando i termini di genera e propaga:
gi= ai*bi
pi=ai+bi
Si possono esprimere i segnali CarryIn in maniere più conveniente e quello che si ottiene è che il tempo
per produrre c4 sarà dato da 2Δ della somma dei prodotto sommati a 1Δ per la creazione dei termini
genera e propaga, ossia in totale il sommatore CLA richiede 3Δ.
Essendo due sommatori a 5 bit, il tempo totale per produrre in uscita c5 è di 5Δ.

Collegando i 3 blocchi in serie, con il sommatore ad hardware infinito produco c15 in un tempo di 6Δ,
mentre con il sommatore a propagazione di riporto impiegherei 7Δ.

DOMANDA
Si progetti un sommatore a 1 bit e con questo un sommatore a 16 basato sul principio della
propagazione del riporto (ripple carry adder) e si stimi il tempo necessario a produrre il riporto
in uscita c16. Si progetti un sommatore a 16 bit basato sul principio dell’anticipazione del riporto
(carry look-ahead) e si stimi il tempo necessario a produrre il riporto in uscita c 16, commentando
la complessità del progetto risultante. Quanto tempo si richiederebbe per produrre il carry in
uscita dall’ultimo sommatore (c16) nell’ipotesi di progetto presentata? Motivare la risposta

RISPOSTA
Sommatore a 16 bit su CLA = 3 Δ ma hardware molto piu complicato.
Si potrebbe passare a 4 sommatori da 4 bit con secondo livello di astrazione

DOMANDA
Si commenti il ruolo svolto dallo spostamento (offset) all’interno delle istruzioni swc1,
commentando l’intervallo di spostamento reso possibile. Si descriva il formato I previsto dal
processore MIPS e utilizzato dalle istruzioni swc1. In un programma sia presente l’istruzione
swc1 e questa sia codificata da 0xe735ff22 (altro caso: 0xe6cfff18). Si interpreti il significato della
codifica dell’istruzione, evidenziando i campi di cui essa si compone; si dica quali sono i registri
coinvolti nella swc1, quale sia il valore della costante presente nell’istruzione e come possa essere
identificato il registro in virgola mobile all’interno dell’istruzione, senza modificare né i formati
delle istruzioni che lavorano su numeri interi, né il numero di bit dedicati all’identificazione degli
stessi.

RISPOSTA
L’offset, per qualsiasi istruzione di trasferimento dati, è lo spiazzamento che deve essere sommato
all’indirizzo contenuto nel registro specificato dall'istruzione per ottenere l'indirizzo della cella di
memoria in cui andare a scrivere/leggere.
La sua funzione non cambia tra operazione in virgola mobile e numeri interi: infatti, poiché gli indirizzi
della memoria sono numeri interi, anch'esso sarà sempre un numero intero.
Poichè l'istruzione swc1 utilizza il formato istruzione I:

OP 6 bit RS 5 bit RT 5 bit 16 bit number


Codice operativo registro sorgente registro target costante a 16 bit

L’intervallo di spostamento reso possibile è sempre da -215 a +215-1 (in quanto la costante è un numero a
16 bit): infatti, il formato dell’istruzione non cambia che si lavori in virgola mobile o con numeri interi.
La differenza sono i registri utilizzati, che saranno in virgola mobile o decimali a seconda del codice
operativo: è l'OP CODE a specificare il tipo di registro da utilizzare, in quanto sia i 32 registri dedicati
agli interi sia i 32 dedicati alla virgola mobile sono indirizzabili sempre con 5 bit.

Se la codifica è 0xe735ff22 il valore in binario associato (la costante è in complemento a due):


111001|11001|10101|1111111100100010
Codice operativo|$25|$f21|-222.

Il numero in virgola mobile contenuto nella cella di memoria specificata dal registro $25 meno l'offset
(222) viene memorizzato nella cella di memoria che ha indirizzo contenuto in $f21.

Altro caso:

0xe6cfff18 il valore binario è:


111001|10110|01111|1111111100011000
Codice operativo|$22|$f15|-232

Il numero in virgola mobile contenuto nella cella di memoria specificata dal registro $22 meno l'offset
(232) viene memorizzato nella cella di memoria che ha indirizzo contenuto in $f15.

DOMANDA
Supponendo che a1 = 0x4c22ef3d e s3 = 0x8c09a2b8 si dica quali valori assumerà il registro v0 se
verranno eseguite le seguenti istruzioni, motivando la risposta (-12853 = cdcb in complemento a
16). Si descriva il funzionamento delle istruzioni slt, slti e sltiu, evidenziando anche le modalità di
indirizzamento coinvolte:

sltiu $v0, $a1, -12853 slt $v0, $s3, $a1 slti $v0, $a1, -12853

RISPOSTA
L’istruzione sltiu effettua il confronto in valore assoluto tra il contenuto del registro a1 e la costante con
il segno esteso. Se a1< costante v0 =1 altrimenti v0=0.
In questo caso -12853=cdcb che equivale a 1100 1101 1100 1011, a questi 16 bit,per essere confrontati
con il valore all’interno del registro a1,deve essere esteso il segno in modo da confrontare due numeri a
32 bit.
Avremo quindi il confronto tra 4c22ef3d e ffffcdcb questo confronto produrrà il valore 1 all’interno di
v0 in quanto trattando il valore assoluto un numero con ff all’inizio è maggiore 4c22.

L’istruzione slt effettua il confronto in complemento a 2 tra il contenuto del registro s3 e il contenuto
del registro a1. Se s3<a1 allora v0=1 altrimenti 0. In questo caso 4c22ef3d è un numero positivo se
trattato in complemento a 2 mentre 8c09a2b8 è negativo e quindi s3 < a1 di conseguenza v0=1.

L’istruzione slti effettua il confronto in complemento a 2 tra il contenuto del registro a1 e la costante
con il segno esteso. Se a1< costante v0 =1 altrimenti v0=0.
In questo caso a1 è un numero positivo in complemento a 2 mentre la costante con il segno esteso in
complemento a 2 è un numero negativo, di conseguenza a1>costante e quindi v0=0

DOMANDA
In un programma sia presente l’istruzione addiu e sia codificata da 0x2734a460. Si interpreti il
significato della codifica, evidenziando e descrivendo il formato previsto per l’istruzione; si dica
quali sono i registri coinvolti e quale sia il valore della costante presente nell’istruzione. Si
commenti l’intervallo di variabilità della costante e il suo uso.

RISPOSTA
Il numero esadecimale 0x2734a460 corrisponde al binario 0010 0111 0011 0100 1010 0100 0110 0000.
lL'istruzione addiu utilizza il formato I, per cui avremo 6 bit per il codice operativo, 5 per il registro
sorgente, 5 per il registro destinazione e 16 per la costante da caricare.
Nel caso specifico RS = 25 e RT = 20.
Alla costante viene effettuata l'operazione di estensione del segno, per cui sarà:
1111 1111 1111 1111 1010 0100 0110 0000
Che in complemento a due codifica un numero negativo, ma essendo l'operazione di tipo unsigned
dovremo trattarlo come un numero in valore assoluto.
L'istruzione prevede infatti che tale numero, sommato al quello contenuto nel registro RS, venga
memorizzato nel registro 20.
I 16 bit a disposizione per la costante permetto di avere un intervallo di variabilità che varia da -215 a
215-1.

DOMANDA
In un programma sia presente l’istruzione sltiu e sia codificata da 0x2cb98524. Si interpreti il
significato della codifica, evidenziando e descrivendo il formato previsto per l’istruzione; si dica
quali sono i registri coinvolti e quale sia il valore della costante presente nell’istruzione. Si
commenti l’intervallo di variabilità della costante e il suo uso. Si descriva infine a cosa porta
l’esecuzione dell’istruzione proposta.

RISPOSTA

L’istruzione sltiu effettua il confronto in valore assoluto tra il contenuto di un registro e l’operando
immediate contenuto nell’istruzione in cui verrà esteso il segno.
Il registro specificato nell’istruzione varrà 1 o 0 a seconda che il registro sia minore o maggiore
dell’operando.
Il formato previsto è il formato I:
OP 6 bit RS 5 bit RT 5 bit 16 bit number
6 bit codice operativo,5 bit registro sorgente, 5 bit registro target e 16 bit per la costante che è
rappresentata in complemento a 2 e quindi rappresenta sia numeri positive che negative con un
intervallo di rappresentazione che va da -215 a 215-1.
Convertendo l’istruzione in binario otteniamo:
001011|00101|11001|1000010100100100

Viene effettuata l'operazione di estensione del segno sul numero a 16 bit, per cui diventa:
1111 1111 1111 1111 1000010100100100
Essendo un'operazione che tratta gli unsigned, questo numero va inteso in valore assoluto.
Se il contenuto del registro 5 è minore di questa costante, il contenuto del registro 15 viene settato a 1.

DOMANDA
In un programma sia presente l’istruzione sll e sia codificata da 0x00199c00. Si interpreti il
significato della codifica, evidenziando e descrivendo il formato previsto per l’istruzione; si dica
quali sono i registri coinvolti e quale sia il valore della costante presente nell’istruzione. Si
commenti l’intervallo di variabilità della costante e il suo uso.

RISPOSTA
L’istruzione sll effettua lo shift logico verso sinistra (shift logical left) , mette in un registro specificato,
il contenuto di un altro registro shiftato del numero di cifre indicato nell’istruzione.
Il formato dell’istruzione è il formato R:
Op-6 bit Rs-5bit Rt-5bit Rd-5bit Shamt-5bit Funct-6bit
6 bit cod op, 5 bit registro sorgente, 5 bit registro target, 5 bit registro destinazione, 5 bit shift amount
ossia il campo che conterrà lo shift nel caso in cui l’operazione da effettuare sia una sll altrimenti è 0 ed
infine 6 bit per il campo funct ossia il campo che specifica l’operazione (per esempio add e sub hanno
stesso cod op ma funct diversi).
La variabile shift amount ha un intervallo di variabilità che va da 0 a 31 ossia è possibile effettuare lo
shift fino a 31 cifre.
Nel nostro caso la codifica binaria corrisponde a:
000000|00000|11001|10011|10000|000000
Cod op|$zero|$t25|$19|16|0
In pratica l’istruzione è sll $19, $25,16 in pratica mettiamo in 19 il contenuto del registro 25 shiftato di
16 bit a sinistra.

DOMANDA
In un programma siano presenti i due statement seguenti; ipotizzando che nel registro t4 sia
contenuto il numero 8, se ne commenti il significato evidenziando e descrivendo le differenze fra
le modalità di esecuzione e il risultato ottenuto nel registro t7:

mul $t7, $t7, $t4 sll $t7, $t7, 3

RIPOSTA
L’istruzione mul effettua la moltiplicazione tra il contenuto del registro t7 e il contenuto del registro t4,
e il risultato va inserito all’interno del registro t7. In questo caso assumendo che il registro t4 contenga
il numero 8 in t7 viene messo il contenuto di t7*8.
L’istruzione di sll esegue lo shift logico verso sinistra di x posizioni indicate nell’istruzione e il risultato
viene messo in un registro specificato.
Nel nostro caso in s7 viene inserito il contenuto di s7 shiftato di 3 posizioni verso sinistra.
L’operazione di mul (che è una pseudoistruzione) è più lenta rispetto quella di sll, infatti per sll è
previsto un circuito logico diretto il indipendente infatti è molto più veloce effettuare uno shift che una
moltiplicazione.
Entrambe le due istruzioni effettuano la stessa identica operazione infatti effettuare una moltiplicazione
per una potenza di due equivale a fare uno shift logico verso sinistra pari all’esponenziale, in questo
caso moltiplicare per 8 equivale a fare uno shift di 3 posizioni.

DOMANDA
Supponendo che a3 = 0xb1240c48 e s4 = 0x662f5ab1 si dica quali valori assumerà il registro s5 se
verranno eseguite le seguenti istruzioni, motivando la risposta (-22543 = a7f1 in complemento a
16). Si descriva il funzionamento delle istruzioni sltu, slti e sltiu, evidenziando anche le modalità
di indirizzamento coinvolte:

slti $s5, $s4, -22543 sltu $s5, $s4, $a3 sltiu $s5, $s4, -22543

RISPOSTA

DOMANDA
Il simulatore SPIM per il processore MIPS mette a disposizione le chiamate di sistema,
richiamabili tramite lo statement syscall. Si descrivano le modalità d’uso e la loro utilità.

RISPOSTA
Uno dei difetti del simulatore è il fatto che non si può interagire con il mondo esterno (es. sensori).
Tuttavia, è possibile fare interagire in parte il simulatore SPIM con il mondo esterno in quanto esso è
installato su un calcolatore che ha come input la tastiera e come output un monitor (leggere dati
immessi dall'utente e stamparli a video).
Questo è possibile mediante l'utilizzo delle syscall (system call, chiamate a sistema), che non sono
istruzioni MIPS, ma comandi di SPIM analoghi ai servizi di un sistema operativo.
La syscall può essere vista come una chiamata a sottoprogramma, in cui il passaggio dei parametri per
la syscall avviene attraverso i registri $a0, $a1, $a2, $a3 se i parametri sono degli interi, e via $f12 se in
virgola mobile. Il codice della chiamata (esempio: print_string, read_int) viene caricato nel registro
$v0.
Le chiamate di sistema forniscono poi un valore in uscita che viene salavato in $v0 se intero e $f0 se in
virgola mobile.
Esistono diversi tipi di syscall e ad ognuna di esse è associato un codice (un numero).
Se in uscita abbiamo più valori, viene utilizzata la memoria.

DOMANDA
Alcune istruzioni del processore MIPS contengono al loro interno operandi da utilizzare
nell’esecuzione delle istruzioni. In alcuni casi si rende necessaria l’operazione denominata
“estensione del segno”. Si indichino e si descrivano istruzioni che ne fanno uso e non ne fanno
uso, descrivendo il significato di tale operazione e le motivazioni in ambo le situazioni.

RISPOSTA
Ciascuno dei 32 registri del MIPS è a 32 bit (ossia 4 byte = 1 word).
Ogni volta che una costante più piccola di una word viene caricata in un registro, siccome è necessario
rispettare il vincolo di allineamento, si rende necessaria un'operazione che riempia i byte (1,2,3) liberi:
lasciari intatti com'erano prima non è una buona idea perchè mi trovo ad avere un dati tra loro
totalmente scorrelati, che generano solo confusione.
Per cui si adotta questa soluzione per non alterare il valore del dato: poiché la convezione adottata dal
MIPS è la BIG ENDIAN, se il numero in arrivo è rappresentato in valore assoluto, i byte che lo
precedono vengono settati a 0 (101 e 0101 sono uguali); se il numero è in complemento a due si
procede con l'estensione del segno. Con questa operazione, se il numero è positivo i bit vengono settati
a 0 (010 e 0010 sono uguali), mentre se il numero è negativo a 1.
In questo modo la convenzione del dato trasferito viene rispettata.
Le istruzioni che operano l'estensione del segno sono, ad esempio: addi, addiu, sub, lb, beq, bnq, …
Le istruzioni che non operano l'estensione del segno sono, ad esempio: addu, subu, nor, or..
Altri casi sono la gestione delle stringhe e delle operazioni logiche.
Per entrambe non è necessaria l'estensione del segno: per caricare dati come caratteri si usa lbu (la
tabella ASCII è in valore assoluto), mentre nelle operazioni logiche quali “and” e “or” (andi e ori) si
lavora con singoli bit e non numeri.
DOMANDA
Come è noto nell’esecuzione di istruzioni di somma e sottrazione si può generare l’overflow. Si
descriva il comportamento delle istruzioni seguenti del processore MIPS in particolare in
presenza di tale situazione. Si dica in quali fra le istruzioni proposte si rende necessaria
l’operazione denominata “estensione del segno” descrivendo il significato di tale operazione:

addu sub addi addiu sltiu


RIPOSTA
Addu: non estende segno
Sub: estende segno
Addi: estende segno
Addiu: estende segno
Sltiu: estende segno
DOMANDA
Si discuta il funzionamento e il formato di tutte le istruzioni che permettono di leggere dalla
memoria e scrivere nella memoria numeri interi con e senza segno, mettendo in evidenza per
ciascuna l’utilità e le motivazioni che ne motivano la presenza nel set delle istruzioni del
processore Mips.

RISPOSTA
Il processore MIPS offre la possibilià di effettuare le operazione di trasferimento dati da un registro alla
memoria e viceversa. Al fine di rendere più veloce l'evento più frequente, infatti, è bene salvare le
variabili più utilizzate nei registri (i quali, essendo interni alla CPU, sono più veloce) e riversare le altre
in memoria, per essere recuperate al momento opportuno (spilling).
Essendo la memoria suddivisa in byte, il MIPS presenta 3 categorie di istruzioni di lettura/scrittura in
memoria:
1. Load Byte/Store Byte;
2. Load Half Word/Store Half Word;
3. Load Word/Store Word;

L'istruzione di caricamento generale di un dato dalla memoria a un registro ha la seguente sintassi:


lx $t0, 4($s0)
che vuole di caricare nel registro $t0 il contenuto della cella di memoria il cui indirizzo è dato dal
contenuto del registro $s0 a cui viene sommato un'offset (indicato prima della parentesi).
È necessario però fare attenzione a questo offset: poiché infatti bisogna rispettare il vincolo di
allineamento, esso dovrà essere:
 un multiplo di 4 nelle istruzioni di LOAD WORD;
 un multiplo di 2 nelle istruzioni di LOAD HALF WORD;
 un numero qualsiasi nelle istruzioni di LOAD BYTE;

Nel caso di LH e LB viene fatta una ulteriore distinzione: infatti, poiché entrambe trasferiscono un
numero di bit minore a quello del registro (32), bisogna stabilire come debbano essere trattati i restanti
bit liberi. Lasciarli intatti porterebbe solo a confusione tra dato copiato e bit privi di significato.
Si ha scelta allora su due possibilità:
1. LH e LB operano la cosidetta estensione del segno, ossia: se il numero è in valore assoluto, i bit
liberi vengono settati a 0; se il numero è in complemento a due ed è positivo vengono settati a 0,
se è negativo a 1. Questa operazione, infatti, non altera il valore del dato copiato, rispettando o
una o l'altra codifica (valore assoluto: 101 = 00101, complemento a due 101 = 1101, 01=001);
2. LHU e LBU non effettuano l'estensione del segno e bit alti vengono riempiti con degli 0;

Discorso analogo vale per le operazioni di scrittura da un registro alla memoria SB, SH e SW.
L'unica differenza è che per queste operazioni non si effettua mai l'operazione di estensione del segno:
sarà il programmatore a sapere cosa deve essere memorizzato, se si dovranno memorizzare anche i bit
utilizzati per riempire il registro oppure si dovrà memorizzare solo la parte “utile” del dato (quindi solo
l’ultimo byte o l’ultima half word)
DOMANDA
Tenendo presente il modo di indirizzamento relativo al PC, spiegare perché potrebbe essere
problematico per l’assemblatore implementare direttamente l’istruzione di salto condizionato
presente nel seguente frammento di codice. Mostrare come l’assemblatore dovrebbe riscrivere
questa sequenza di codice per risolvere il problema.

uno: beq $t1, $t2, dopo1


……
dopo1: add $t1, $t1, $t1

Altro caso:
dopo: add $s1, $a1, $t1
……
uno: bne $s1, $a2, dopo

dopo: ...
uno: beq $s1, $a2, vicino
j dopo
vicino:

RISPOSTA
Il qui presente frammento di codice potrebbe risultare problematico perchè i 16 bit per l'indirizzo messi
a disposizione dall'indirizzamente relativo al PC potrebbero non bastare per saltare all'etichetta
“dopo1”. Per risolvere il problema l'assembleatore dovrebbe riscrivere così la sequenza di codice:
bne $t1, $t2, dopo2
j dopo1
dopo2: ...
dopo1: …
In questo caso si utilizza l'istruzione j (Jump) che prevede il formato J, il cui campo indirizzi di 26 bit
mi permette uno spostamento più grande rispetto ai 16 bit del formato I.
L'assemblatore risolve questo problema automaticamente.
Nel caso in cui neanche 26 bit dovessero bastare, bisognerebbe riscrivere il codice in questo modo:
la $at, dopo1
jr $at