sabato 6 febbraio 2010

Imparare il linguaggio MIPS con l'Emulatore MARS

Sarò breve. MARS (MIPS Assembler and Runtime Simulator) è un validissimo strumento per fare pratica nella programmazione su architetture MIPS (e molto altro ancora!). Il sito del progetto è:

http://courses.missouristate.edu/KenVollmar/MARS/

Per utilizzarlo su Ubuntu (io lo sto usando sul 9.04) ho installato OpenJDK Java 6 Runtime

sudo apt-get install openjdk-6-jre

(non ricordo se necessita di altri pacchetti, ma è facile installare la Java Runtime...esistono varie guide).

Scaricate quindi dal sito ufficiale il file "Mars.jar" e avviatelo cliccando col tasto destro e cliccando su "Apri con OpenJDK Java 6 Runtime"....e si aprirà la seguente interfaccia.

In (4) abbiamo la barra dei menu e degli strumenti; in (1) possiamo selezionare due schede: "Edit" e "Execute"...inizialmente posizioniamoci su "Edit", dove scriveremo il nostro codice MIPS; in (2) avremmo i messaggi che l'assemblatore ci comunicherà; in (3) c'è una comoda interfaccia che ci fa vedere in tempo reale il contenuto di ogni registro.


Passiamo nella scheda "Execute" (5):


In (6) abbiamo i "Text segment" ovvero la traduzione del linguaggio da noi scritto (magari corredato da pseudoistruzioni) che viene tradotto in linguaggio assembler e poi macchina; in (8) vi è una finestra che ci da informazioni relative alle etichette utilizzate; in (7) vi la finestra "Data segment", che ci permette (selezionaldoli dal menu a tendina sottostante) di visualizzare il contenuto della memoria, dello stack, dell'heap, ecc...
Naturalmente questa è una descrizione del programma molto breve e poco specifica. Si rimanda all'Help e al sito ufficiale per una conoscenza appropriata di tutte le funzionalità di MARS.
Quello che mi interessava realizzare in questo articolo è un Tutorial di primo impatto (in perfetto stile di Linux Beginner).
Quello che vogliamo realizzare è una semplice procedura MIPS, che in C ha questa forma:













Questa è una semplice funzione C che implementa il prodotto scalare di due vettori puntati rispettivamente dai puntatori *a e *b di dimensione "dim". Alla variabile P, inizializzata a 0, tramite un ciclo for, viene addizionato iterativamente il prodotto delle componenti dei vettori, ottenendo così alla fine il prodotto scalare. Vediamo come ottenere lo stesso risultato in linguaggio MIPS (naturalmente la versione che segue è in alcune cose diversa da quella che produrrebbe un compilatore C, ma certamente risulta più scolastica):

la fuzione ha 3 parametri, quindi sappiamo che alla procedura verranno passati gli indirizzi di memoria di questi 3 parametri nei registri $a0, $a1, $a2. Per poter verificare il funzionamento corretto del nostro codice dobbiamo in qualche modo caricare la memoria con i valori che un eventuale main scritto in C scriverebbe in memoria prima della chiamata della funzione "prod_scal". Faremo questo andando in nella scheda "Edit" di (5) e scrivendo in fondo alla text box:

.data
veta: .word 1,2,1,2
vetb: .word 1,2,3,4
dim: .word 4


Abbiamo indicato a MARS l'esistenza di 3 elementi : vettore a e vettore b di 4 elementi, dim un elemento pari a 4 (dimensione dei vettori).
Ora partendo da sopra possiamo cominciare a scrivere il programma:

.text
.gobl main

main: la $a0,veta
la $a1,vetb
la $a2,dim


Con queste istruzioni abbiamo inizializzato il main e con le istruzioni "la" abbiamo caricato i registri $a0, $a1, $a2 con gli indirizzi di memoria degli operandi passati alla funzione, quindi ora la memoria contiene i dati come se una funzione C gli avesse caricati in precedenza e possiamo verificare questo nella finestra "Data segment" (7). Possiamo ora scrivere il nostro programma. Non spiego come realizzarlo in dettaglio, ma mi limito a riportarne la soluzione funzionante da me realizzata:

.text
.globl main
main: la $a0,veta
la $a1,vetb
la $a2,dim

add $fp,$sp,$zero #pongo frame point pari allo stack point
addi $sp,$sp,-20 #faccio posto a una parola per mem il vecchio valore di $s0
sw $s0,16($sp) #backup di $s0
sw $s1,12($sp) #backup di $s1
sw $s2,8($sp) #backup di $s2
sw $s3,4($sp) #backup di $s3
sw $s4,0($sp) #backup di $s4

add $s0,$zero,$zero #s0=0, considero s0 come P
add $t0,$zero,$zero #t0=0, considero t0 come i
lw $s4,($a2) #$s4=a2 (s4 acquisisce il valore di dim)

L1: sll $t4,$t0,2 #t4= t0*4 (i=i*4) per considerare un'incremento di un intera parola

add $t2,$a0,$t4 #$t2=$a0(base)+$t4(shift) (=$a[i])
lw $s1,($t2) #$s1=a[i]

add $t2,$a1,$t4 #s2=a1+t0 (=b[i])
lw $s2,($t2) #$s2=b[i]

mult $s1,$s2 #Hi,Lo=$s1*$s2 (=a[i]*b[i])
mflo $s3

add $s0,$s0,$s3 #s0=s0+s3 (P=P+a[i]*b[i]

addi $t0,$t0,1 #t0=t0+1

slt $t1,$t0,$s4 #t1=1 se t0


Per poter controllare il corretto funzionamento del programma basta dare il comando di "assemble" con F3, se tutto va a buon fine in fase di assemblaggio (lo verifichiamo nella finestra (2)) possiamo scegliere se far girare il programma per intero con F5 o se controllare lo svolgimento di ogni singola istruzione con F7 (molto utile!). Detto questo, non resta che provare. Alla prossima!