ISA e Linguaggio Assemby MIPS

Da Wikiversità, l'apprendimento libero.
lezione
lezione
ISA e Linguaggio Assemby MIPS
Tipo di risorsa Tipo: lezione
Materia di appartenenza Materia: Architetture degli elaboratori

Architettura MIPS[modifica]

Microprocessore MIPS R4400 prodotto da Toshiba.

L'architettura MIPS (originariamente acronimo di Microprocessor without Interlocked Pipeline Stages) è un'architettura RISC e load-store sviluppata da John Hennessy nel 1981. L'architettura MIPS è attualmente usata in molti sistemi embedded come nei router Cisco e Fonera. In passato è stata impiegata, ad esempio, nelle console per videogiochi come il Nintendo 64 o la PlayStation, PlayStation 2 e PlayStation Portatile di Sony.

Registri[modifica]

Il processore MIPS possiede 32 registri general purpose a 32 bit.

  • Il registro $zero contiene il valore 0.
  • Il registro $at (o Assembler Temporary) è riservato dall'assemblatore per permettere l'utilizzo delle pseudoistruzioni. Non è indirizzabile.
  • I registri $v0 e $v1 (Registri Valore) sono utilizzati per restituire valori dalle funzioni.
  • I registri da $a0 a $a3 (Registri Argomento) sono utilizzati per passare argomenti alle funzioni.
  • I registri da $t0 a $t7, $t8 e $t9 (registri temporanei) sono utilizzati nelle funzioni. Il contenuto non viene preservato al termine della funzione in quanto utile solo all'esecuzione della stessa.
  • I registri da $s0 a $s7 (Registri Salvati) sono utilizzati nel main program ed è quindi necessario preservarne il contenuto. Qualora i registri temporanei non fossero sufficienti per la corretta esecuzione di una procedura, è necessario memorizzare temporaneamente il contenuto di un registro s nello stack e usare il registro nella funzione. Al termine della funzione è però necessario ripristinare il contenuto del registro s impiegato.
  • I registri $k0 e $k1 sono registri riservati al kernel del sistema operativo. Ad esempio, per riprendere l'esecuzione di un programma (o funzione) che ha generato overflow si utilizzano le istruzioni mfc0 $k0 $epc (move from coprocessor 0) per salvare in k0 (o k1) il contenuto dell'exception program counter e saltare all'istruzione che ha generato overflow tramite jr $k0 (jr $k1) per riprendere l'esecuzione.
  • Il registro $sp (Stack Pointer) contiene l'indirizzo della cima dello stack ed è usato per operazioni di Push (addiu $sp, $sp, -4 seguita da sw $t0, 4($sp)) e Pop (lw $t0, 4($sp) seguita da addi $sp, $sp, 4) rispettivamente per inserire o estrarre elementi dallo Stack.
  • Il registro $ra (Return Address) contiene l’indirizzo di rientro da chiamata a sottoprogamma. È impiegato nell'istruzione jal (Jump And Link) per memorizzare l'indirizzo dell'istruzione successiva (PC + 4) e permettere di riprendere la regolare esecuzione del programma chiamante tramite jr $ra (Jump to Register).

Oltre ai 32 registri sopraelencati, sono previsti ulteriori registri non indirizzabili, ma impiegati dal processore per eseguire alcune istruzioni.

  • I registri HI e LO sono utilizzati per accedere al risultato delle operazioni di moltiplicazione e divisione. Sono accessibili solo tramite le istruzioni mfhi $_ (move from high) e mflo $_ (move from low) nelle quali è necessario indicare solo il registro di destinazione nel quale salvare il contenuto di HI o LO.
  • Il registro PC contiene l’indirizzo della prossima istruzione da eseguire.

Di seguito una panoramica delle convenzioni usate per i registri.

Registri
Nome Numero Utilizzo Chiamante deve preservare?
$zero $0 costante 0 N/A
$at $1 Assembly Temporary No
$v0–$v1 $2–$3 Values for function returns and expression evaluation No
$a0–$a3 $4–$7 Argomenti delle funzioni No
$t0–$t7 $8–$15 Registri Temporanei No
$s0–$s7 $16–$23 Registri Salvati
$t8–$t9 $24–$25 Registri Temporanei No
$k0–$k1 $26–$27 Riservati al kernel del S.O. No
$gp $28 Global Pointer
$sp $29 Stack Pointer
$fp $30 Frame Pointer
$ra $31 Return Address N/A

Istruzioni MIPS[modifica]

Tabella assegnazioni registri[modifica]

Categoria Nome Istruzione Sintassi Istruzione Significato Formato/opcode/funct Notes/Encoding
Aritmetica Add add $d,$s,$t $d = $s + $t R 0 Somma il contenuto dei registri $s e $t. Lancia un'eccezione in caso di overflow.
000000ss sssttttt ddddd--- --100000
Add unsigned addu $d,$s,$t $d = $s + $t R 0 Come la precedente. Ignora l'overflow.
000000ss sssttttt ddddd--- --100001
Subtract sub $d,$s,$t $d = $s - $t R 0 Sottrae il contenuto dei registri $s e $t. Lancia un'eccezione in caso di overflow.
000000ss sssttttt ddddd--- --100010
Subtract unsigned subu $d,$s,$t $d = $s - $t R 0 Come la precedente. Ignora l'overflow.
000000ss sssttttt ddddd000 00100011
Add immediate addi $t,$s,C $t = $s + C (signed) I - Somma la costante C al contenuto del registro $s. Estende il segno della costante C. Lancia un'eccezione in caso di overflow.
001000ss sssttttt CCCCCCCC CCCCCCCC
Add immediate unsigned addiu $t,$s,C $t = $s + C (unsigned) I - Come la precedente. Ignora l'overflow. Estende il segno della costante C.
001001ss sssttttt CCCCCCCC CCCCCCCC
Multiply mult $x,$y LO = (($x * $y) << 32) >> 32;
HI = ($x * $y) >> 32;
R 0 Moltiplica il contenuto dei registri $x e $y. Salva il risultato a 64 bit nei registri HI (32 bit più significativi) e LO (32 bit meno significativi).
Divide div $x, $y LO = $x / $y     HI = $x % $y R 0 1A Divide il contenuto di due registri. Salva in LO il risultato intero a 32 bit e in in HI il resto della divisione.
Divide unsigned divu $x, $y LO = $x / $y     HI = $x % $y R 0 1B Come la precedente.
Trasferimento dati Load double word ld $x,C($y) $x = Memory[$y + C] I - Carica la word memorizzata in MEM[$y + C] e i successivi 7 byte nel registro $x e quello successivo.
Load word lw $x,C($y) $x = Memory[$y + C] I - Carica la word memorizzata in MEM[$y + C] e i successivi 3 byte nel registro $x.
Load halfword lh $x,C($y) $x = Memory[$y + C] (signed) I - Carica una halfword memorizzata in MEM[$y + C] e il successivo byte nel registro $x. Il segno viene esteso della grandezza del registro.
Load halfword unsigned lhu $x,CONST($y) $x = Memory[$y + C] (unsigned) I - Come la precedente. Non estende il segno.
Load byte lb $x,C($y) $x = Memory[$y + C] (signed) I - Carica il byte memorizzato in MEM[$y + C] nel registro $x.
Load byte unsigned lbu $x,C($y) $x = Memory[$y + C] (unsigned) I - Come la precedente. Non estende il segno.
Store double word sd $x,C($y) Memory[$y + C] = $x I - Memorizza due word contenute nel registro $x e nel successivo in MEM[$y + C] e nei successivi 7 byte.
Store word sw $x,C($y) Memory[$y + C] = $x I - Memorizza una word contenuta nel registro $x in MEM[$y + C] e nei successivi 3 byte.
Store halfword sh $x,C($y) Memory[$y + C] = $x I - Memorizza una halfword in MEM[$y + C] e nel successivo byte.
Store byte sb $x,C($y) Memory[$y + C] = $x I - Memorizza un byte in MEM[$y+C].
Load upper immediate lui $x,C $x = C << 16 I - Carica una costante a 16 bit nei 16 bits più significativi del registro $x. Il valore massimo della costante è 216-1
Move from high mfhi $x $x = HI R 0 Salva il valore di HI nel registro $x.
Move from low mflo $x $x = LO R 0 Salva il valore di LO nel registro $x.
Move from Coprocessor Z mfcZ $x, $y $x = Coprocessor[Z].ControlRegister[$y] R 0 Salva il valore a 4 byte da un registro del Coprocessore Z in un registro general purpose. Estende il segno.
Move to Coprocessor Z mtcZ $x, $y Coprocessor[Z].ControlRegister[$y] = $x R 0 Salva il valore a 4 byte in un registro del Coprocessore Z da un registro general purpose. Estende il segno.
Logiche And and $d,$s,$t $d = $s & $t R 0 And bit a bit.
000000ss sssttttt ddddd--- --100100
And immediate andi $t,$s,C $t = $s & C I - 001100ss sssttttt CCCCCCCC CCCCCCCC
Or or $x,$y,$z $x = $y | $z R 0 Or bit a bit.
Or immediate ori $x,$y,C $x = $y | C I -
Exclusive or xor $x,$y,$3 $x = $y ^ $z R 0
Nor nor $x,$y,$z $x = ~ ($y | $z) R 0 Nor bit a bit.
Set on less than slt $x,$y,$z $x = ($y < $z) R 0 Verifica se il contenuto del registro $y è minore del contenuto del registro $z.
Set on less than immediate slti $x,$y,C $x = ($y < C) I - Verifica se il contenuto del registro $y è minore della costante C.
Bitwise Shift Shift left logical sll $x,$y,C $x = $y << C R 0 0 Shift logico a sinistra di un numero C di bit (multiplies by )
Shift right logical srl $x,$y,C $x = $y >> C R 0 Shift logico a destra di un numero C di bit (divides by ).
Shift right arithmetic sra $x,$y,C
R 0 Shift aritmetico a destra di un numero C di bit (divisione in complemento a due di )
Salti condizionati Branch on equal beq $s,$t,C if ($s == $t) go to PC+4+4*C I - Salta all'indirizzo dell'istruzione C se il contenuto dei registri $s e $t è uguale.
000100ss sssttttt CCCCCCCC CCCCCCCC
Branch on not equal bne $x,$y,C if ($x != $y) go to PC+4+4*C I - Salta all'indirizzo dell'istruzione C se il contenuto dei registri $s e $t non è uguale.
Salti incondizionati Jump j C PC = PC+4[31:28] . C*4 J - Salto incondizionato all'indirizzo dell'istruzione C.
Jump register jr $x goto address $x R 0 Salto all'indirizzo contenuto nel registro $x.
Jump and link jal C $31 = PC + 4; PC = PC+4[31:28] . C*4 J - Per chiamata a sottoprogramma. Nel registro $ra è memorizzato l'indirizzo di ritorno. Rientro da sottoprogramma tramite jr $ra.

Note Sugli Operandi in Memoria[modifica]

  • Il MIPS indirizza un singolo byte (8 bit).
  • Gli indirizzi delle word sono multipli di 4, poiché composte da 4 byte. Gli indirizzi di due word consecutive differiscono di 4 unità e le parole iniziano sempre ad indirizzi multipli di 4, cioè 0, 4, 8, 12, ... rispettando il Vincolo di Allineamento.

Realizzazione di procedure mediante assembly MIPS[modifica]

Per eseguire una procedura, devono essere effettuati i seguenti passi:

  1. Il programma chiamante salva i parametri della procedura in un luogo accessibile alla procedura chiamata. Per convenzione sono utilizzati a tal scopo i registri $a0-$a3.
  2. Trasferisce il controllo alla procedura chiamata tramite un'istruzione di salto.
  3. La procedura chiamata acquisisce le risorse ed esegue il compito richiesto salvando il risultato (qualora previsto) in un luogo accessibile al programma chiamante. Per convenzione sono utilizzati a tal scopo i registri $v0 e $v1.
  4. Restituisce il controllo al programma chiamante tramite un'istruzione di salto.

In particolare per la chiamata di una procedura si utilizza l'apposita istruzione jal Label (jump and link), che salta a un indirizzo dell'etichetta specificata di seguito (in questo caso Label) e contemporaneamente salva l'indirizzo dell'istruzione successiva nel registro $ra. In pratica jal salva il valore di PC+4 nel registro $ra, creando un “collegamento” all'indirizzo di ritorno dalla procedura. L'indirizzo di ritorno è necessario perché la stessa procedura può essere richiamata in più parti del programma. Per il ritorno da una procedura al programma chiamante viene utilizzata l'istruzione jr $ra (jump to register), che permette il salto all'indirizzo contenuto in $ra.

Le convenzioni[modifica]

Sono necessarie delle convenzioni poiché le procedure dei linguaggi di alto livello possono essere compilate separatamente e anche perché i programmatori assembler possono implementare/chiamare procedure, realizzate da compilatori o da altri programmatori.

Le principali convenzioni, non imposte dall'hardware ad eccezione di $ra, sono:

  • $a0 - $a3: 4 registri argomento per il passaggio dei parametri alle funzioni.
  • $v0 - $v1: 2 registri valore per la restituzione dei valori dalle funzioni.
  • $ra: registro di ritorno per tornare al punto di origine.
  • Il programma chiamante mette i valori dei parametri da passare alla procedura nei registri $a0-$a3 e utilizza l'istruzione jal Label per saltare alla procedura Label.
  • Il programma chiamato esegue le operazioni richieste, memorizza i risultati nei registri $v0-$v1 e restituisce il controllo al chiamante con l'istruzione jr $ra.

L'uso dello stack[modifica]

Spesso una procedura necessita di salvare i valori di alcuni registri, perché chiama un'altra procedura che li cambia o perché non deve alterarli per il programma chiamante. Come soluzione viene utilizzato lo stack (o pila) che è un'area della memoria gestita secondo la logica LIFO (Last-In First-Out) dove è possibile memorizzare il valore di un registro, tramite le operazioni dette di Push e recuperare l'ultimo valore inserito con le operazioni di Pop. Per la gestione dello stack si utilizza un puntatore allo stack (o stack pointer) che contiene l'indirizzo dell'ultimo dato introdotto nello stack. Lo stack si evolve secondo indirizzi decrescenti (cresce a partire da indirizzi di memoria alti verso indirizzi di memoria bassi), perciò quando vengono inseriti dei dati nello stack il valore dello stack pointer diminuisce, e viceversa aumenta. Il registro dal MIPS impiegato come stack pointer è $sp.

Riempimento dello Stack[modifica]

Per effettuare operazioni di Push viene utilizzata l'istruzione sw (o store word).

Esempio: È necessario memorizzare nello Stack il contenuto dei registri $t0, $t1, $t2.

  addiu $sp, $sp, -12  #Decremento il valore dello Stack Pointer di 12, allocando spazio per 12 / 4 = 3 word.
  sw $t0, 8($sp)       #Memorizzo il valore di $t0 all'indirizzo $sp + 8.
  sw $t1, 4($sp)       #Memorizzo il valore di $t1 all'indirizzo $sp + 4.
  sw $t2, 0($sp)       #Memorizzo il valore di $t2 all'indirizzo $sp + 0.

Svuotamento dello stack[modifica]

Per effettuare operazioni di Pop viene utilizzata l'istruzione lw (o load word).

Esempio: È necessario rimuovere dallo stack i dati memorizzati nell'esempio precedente.

 lw $t0, 8($sp)      #Carico il valore all'indirizzo $sp + 8 in $t0.
 lw $t1, 4($sp)      #Carico il valore all'indirizzo $sp + 4 in $t1.
 lw $t2, 0($sp)      #Carico il valore all'indirizzo $sp + 0 in $t2.
 addiu $sp, $sp, 12  #Incremento il valore dello Stack Pointer di 12, deallocando lo spazio occupato dalle 12 / 4 = 3 word.

Il compito di salvare i registri[modifica]

Con l'utilizzo delle procedure si può affrontare il problema del compito di salvare i registri in due modi diversi:

  1. Salvataggio da parte del chiamato: la procedura ha la responsabilità di non alterare nessun registro ed il chiamante quindi si aspetta che nessun registro sia modificato. C'è però l'inconveniente che i registri salvati dalla procedura potrebbero non essere usati dal chiamante e quindi vengono salvati e ripristinati inutilmente.
  2. Salvataggio da parte del chiamante: la procedura può alterare qualunque registro ed è compito del chiamante salvare i registri che desidera non siano modificati nello stack. Anche in questo caso si ha un inconveniente, per il quale se la procedura non usa i registri che il chiamante utilizza (ad esempio, usa un solo registro) il chiamante salva inutilmente tutti i registri che usa.

Per ovviare agli incovenienti visti si usa un approccio ibrido mediante specifiche convenzioni che determinano quali registri sono preservati e quali non lo sono; riduce la necessità di salvare registri in memoria.

  • $t0-$t9: registri temporanei che non sono preservati in caso di chiamata di procedura.
  • $s0-$s7: registri che devono essere preservati, se utilizzati devono essere salvati e ripristinati dal programma chiamato.
  • $a0-$a3: registri che non sono preservati.

Procedure annidate[modifica]

La possibilità di avere delle procedure annidate ha come effetto collaterale la possibilità di conflitti. Per esempio nel caso in cui una procedura A chiama un'altra procedura B jal B altera $ra, che serve ad A per ritornare al programma chiamante. Anche in questo caso sono state create delle convenzioni in modo tale che:

  • $ra è un registro preservato.
  • Il programma chiamante memorizza nello stack i registri argomento ($a0 - $a3) o i registri temporanei ($t0 - $t9) di cui ha ancora bisogno dopo la chiamata.
  • Il programma chiamato salva nello stack il registro di ritorno $ra e gli altri registri ($s0 - $s7) che utilizza.

Queste convenzioni garantiscono la correttezza, perché sono applicate “ricorsivamente”. Infatti, sono a prova di ricorsione.

Creazione di variabili locali nelle procedure[modifica]

Le procedure possono definire variabili locali, che sono visibili solo internamente; e che vengono create all'inizio dell'esecuzione della procedura e distrutte al ritorno. Per memorizzare queste variabili locali si usa ancora lo stack.

Questo meccanismo presenta però un ulteriore problema: la dimensione dello stack può cambiare durante l'esecuzione della procedura e quindi diventa difficile riferire una variabile rispetto al registro $sp, poiché si avrebbero diversi offset nel corso del programma. Per ovviare si utilizza quindi un registro, per convenzione $fp(frame-pointer), per riferire l'inizio dello stack frame. Per cui all'inizio dell'esecuzione, la procedura deve:

       addi $sp, $sp, -dim    #Creare spazio nello stack per valori da salvare.
                              #Salvare nello stack i valori che serviranno, $fp compreso.
       addi $fp, $sp, dim-4   #Aggiornare $fp.

Passaggio di più di 4 parametri[modifica]

Se ci fosse la necessita di inviare più di 4 parametri essi devono esser posti in cima allo stack dal chiamante, in modo che la procedura chiamata li possa trovare appena sopra $fp.

Convenzioni nell'uso della memoria[modifica]

  • Il codice macchina MIPS è posto nel segmento text, a partire dall'indirizzo 0040 0000 hex.
  • Il segmento static data è utilizzato dal compilatore per oggetti:
    • la cui lunghezza non varia durante l'esecuzione, ed è conosciuta dal compilatore,
    • che durano per tutta l'esecuzione del programma.
  • Il registro $gp (global pointer) è posto a 1000 8000 hex e può essere utilizzato dall'istruzione lw (con un offset a 16 bit) per accedere al primo blocco di 64K che conterrà le variabili statiche di uso più frequente.
  • Il segmento dynamic data, detto anche heap contiene dati allocati dal programma durante l'esecuzione (es. in C tramite malloc) e quindi può espandersi, verso indirizzi più alti o ritirarsi.
  • Lo stack parte dall'indirizzo 7FFF FFFC hex e si espande verso indirizzi più bassi.

Tutto questo però è in teoria poiché nella realtà ci si riferisce ad uno spazio virtuale.

I formati delle istruzioni del MIPS[modifica]

Il MIPS adotta istruzioni a lunghezza costante di 32 bit, come per le parole di memoria ed i registri. Con 32 bit infatti è possibile esprimere tutte le istruzioni, suddivise in 3 categorie:

  • Istruzioni che devono indicare 3 registri (add, sub, and, slt, ecc.).
  • Istruzioni che devono indicare due registri e una costante, in particolare:
    • lw e sw
    • Istruzioni che riferiscono un operando immediato (addi, andi, ori, ecc.)
    • Salti condizionati (due registri per il confronto + costante per il salto)
  • Istruzioni di salto incondizionato, che non riferiscono alcun registro ma indicano una costante.

Formato-R[modifica]

Il formato-R (register) è utilizzato per istruzioni aritmetiche e logiche.

  • op: codice operativo
  • rs: primo operando sorgente (registro)
  • rt: secondo operando sorgente (registro)
  • rd: registro destinazione
  • shamt: shift amount (per operazioni di scalamento)
  • funct: seleziona una variante specifica dell'operazione base definita nel campo op. Per esempio: campo funct per le istruzioni add, sub, and, or, slt
                                        ADD:   100 000
                                        SUB:   100 010
                               op = 0   AND:   100 100
                                        OR:    100 101
                                        SLT:   101 010

Formato-I[modifica]

Il formato-I (immediate) è utilizzato per istruzioni di trasferimento, immediate e di branch.

|    6 bit    |    5 bit    |    5 bit    |	     16 bit	       |
 ----------------------------------------------------------------------
| operation   |    rs       |    rt       |         address            |
  • rs:
    • nel caso di istruzioni immediate: registro sorgente
    • nel caso di lw e sw: registro base al cui contenuto va sommato address
  • rt:
    • nel caso di istruzioni immediate: registro destinazione
    • nel caso di lw e sw: primo registro che compare nell'istruzione (registro destinazione per lw e registro sorgente per sw)
  • const/address: costante da sommare a 16 bits (-2^15 ... +2^15 -1)

Indirizzamento immediato[modifica]

  • L'operando è una costante.
  • Usato dalle versioni “immediate” delle istruzioni aritmetiche (ad es. addi) che usano due operandi registro (sorgente e destinazione) e un operando immediato.
  • La costante è contenuta nel “immediate” nel formato-I
  • Essendo un intero a 16 bit, ha ancora valori da –2^15 a +2^15-1
  • Nelle versioni “unsigned” delle istruzioni esso viene interpretato come “unsigned” (da 0 a 2^16-1)

Indirizzamento tramite base o spiazzamento[modifica]

  • L'operando è in memoria.

P.es.

         lw       $t1, 32($t0)  # $t0 contiene un indirizzo base
                                # 32 è uno spiazzamento da sommare al contenuto di $t0
  • Lo spiazzamento è espresso come intero con segno a 16 bit, quindi può valere da –2^15 a +(2^15-1)


Codifica delle istruzioni di salto condizionato[modifica]

Anche per le istruzioni beq(Branch-on-equal) e bne(Branch-on-not-equal) si può usare il Formato-I, vediamo subito un semplice esempio:

           bne $s0, $s1, Esci # vai alla etichetta Esci se $s0 è diverso da $s1

Considerando che i campi op, rs ed rt sono rispettivamente di 6 bit il primo e 5 bit gli ultimi due, si hanno a disposizione i rimanenti 16 bit per la codifica dell'etichetta. Se però si codificasse direttamente l'indirizzo con 16 bit nessun programma potrebbe avere dimensioni maggiori di 2^16, per cui si utilizza un indirizzamento relativo al Program-Counter(PC-relative), secondo il quale si specifica un registro da sommare all'indirizzo di salto, quindi il Program Counter sarà pari alla somma del registro e dell' indirizzo di salto. Considerando che normalmente i salti condizionati vengono usati nei cicli e nei costrutti if; i salti sono in genere ad istruzioni vicine, conviene quindi usare il registro PC, che ha l'indirizzo dell'istruzione, successiva a quella corrente. Nonostante questa ultima miglioria si è comunque limitati sulla distanza delle istrazioni dei salti, quindi per sfruttare al meglio i 16 bit a disposizione, si può considerare l'indirizzo in istruzioni, moltiplicando per 4 la costante, effettuando uno shift di due bit; in modo tale che a partire da PC si può saltare fino a una distanza di ±2^15-1 istruzioni rispetto all'istruzione successiva a quella corrente e questo è sufficiente. Questa modalità d'indirizzamento è usato solo nelle istruzioni di salto condizionato, mentre le istruzioni jump e jump-and-link (chiamata di procedure) utilizzano un altro modo di indirizzamento.

Formato-J (jump) [per istruzioni di salto incondizionato j e jal][modifica]

Queste istruzioni non specificano alcun registro ed i salti della jump e jal non sono in genere “locali”; per cui si ha il bisogno di un formato che permette di specificare una costante con più di 16 bit.

                                       |   6 bit     |          26 bit                     |
                                       -----------------------------------------------------
                                       | operation   |          address                    |

Indirizzamento pseudo-diretto (per i salti incondizionati):

                  j 10000       # vai alla locazione 10000 (espresso come indirizzo di parola)

I 26 bit all'interno dell'istruzione jump contengono un indirizzo in parole, questi 26 bit vengono concatenati con i 4 bit più significativi del PC. L'indirizzo in byte si ottiene poi moltiplicando per 4 (ovvero, concatenando altri due bit 00) per ottenere un indirizzo a 32 bit.

Se l'indirizzo specificato in un salto condizionato è troppo lontano, l'assembler risolve il problema inserendo un salto incondizionato al posto di quello condizionato invertendo la condizione originale per cui l'istruzione

                  beq     $s0, $s1, L1

viene sostituita con

                  bne     $s0, $s1, L2
                  j       L1
               L2:

Note[modifica]