ISA e Linguaggio Assemby MIPS
Architettura MIPS
modificaL'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
modificaIl 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.
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 | Sì |
$t8–$t9 | $24–$25 | Registri Temporanei | No |
$k0–$k1 | $26–$27 | Riservati al kernel del S.O. | No |
$gp | $28 | Global Pointer | Sì |
$sp | $29 | Stack Pointer | Sì |
$fp | $30 | Frame Pointer | Sì |
$ra | $31 | Return Address | N/A |
Istruzioni MIPS
modificaTabella assegnazioni registri
modificaCategoria | 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
modificaPer eseguire una procedura, devono essere effettuati i seguenti passi:
- 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.
- Trasferisce il controllo alla procedura chiamata tramite un'istruzione di salto.
- 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.
- 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
modificaSono 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
modificaSpesso 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
modificaPer 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
modificaPer 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
modificaCon l'utilizzo delle procedure si può affrontare il problema del compito di salvare i registri in due modi diversi:
- 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.
- 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
modificaLa 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
modificaLe 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
modificaSe 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
modificaIl 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
modificaIl 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
modificaIl 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
modificaAnche 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]
modificaQueste 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: