IJVM è un linguaggio assembly creato (a scopo didattico) da Andrew Stuart Tanenbaum implementato sulla microarchitettura MIC-1 (anch'essa ideata da quest'ultimo). Tale linguaggio è adottato per l'insegnamento base nel suo libro 'Architettura dei calcolatori'.
IJVM è una semplificazione del linguaggio JVM (Integer JVM) usato nella Java platform. Il linguaggio è semplificato a tal punto da rendere molto difficile la scrittura di programmi molto complessi (per esempio, non sono previste operazioni di shift o in virgola mobile).
Organizzazione della memoria in IJVM
[modifica | modifica wikitesto]La memoria in IJVM viene concepita come un array di 4.294.967.296 byte o 1.073.741.824 parole da 4 byte l'una. Tale memoria viene gestita ed indicizzata tramite dei puntatori appositi,cioè dei registri della microarchitettura MIC-1, che delimitano 4 aree di memoria diverse:
Porzione costante di memoria (Constant Pool)
[modifica | modifica wikitesto]Quest'area non permette la scrittura da parte del programma IJVM. I dati qui caricati (costanti, puntatori e stringhe), possono essere scritti solamente quando il programma è portato in memoria. L'indirizzo alla prima parola di questa porzione di memoria è contenuto nel registro CPP (Constant Pool Pointer).
Blocco delle variabili locali (Local Variable Frame)
[modifica | modifica wikitesto]In questo blocco di memoria risiedono le variabili locali dei metodi richiamati, oltre ai parametri con i quali sono stati invocati. L'indirizzo della prima locazione di memoria di quest'area è registrato nel registro implicito LV (Local Variable).
Stack degli operandi (Operand Stack)
[modifica | modifica wikitesto]Implementato immediatamente sopra il Variable Frame, troviamo un blocco di memoria (che non può superare una certa dimensione) che contiene gli operandi delle varie operazioni eseguite all'interno di un metodo. Sebbene lo Stack degli operandi sia diviso dal Variable Frame, i due blocchi vengono pensati come un'unica pila. L'indirizzo della parola in cima a questo stack è puntato dal registro SP (Stack Pointer) che tiene quindi conto degli operandi inseriti o rimossi.
Area dei metodi (Method Area)
[modifica | modifica wikitesto]In questa area risiede il programma IJVM da eseguire. L'indirizzo della seguente istruzione da prelevare (fetch) è contenuto nel registro PC (Program Counter). Quest'area, invece di essere trattata come un array di parole come le altre aree di memoria, è pensata come un array di byte.
Data la struttura a parole della Constant Pool, del Variable Frame e dello Stack degli operandi, e quella a byte della Method Area, occorre puntualizzare che gli offset indicati dai registri CPP,LV e SP si riferiscono a numeri di parole, mentre quelli indicati da PC si riferiscono a numeri di byte. Ad esempio, con la notazione 'LV + 3' si indica la terza parola del Variable Frame, mentre 'PC + 4' indica il 4 byte a partire dal byte puntato da PC.
Istruzioni IJVM
[modifica | modifica wikitesto]Le istruzioni IJVM sono composte da un codice operativo e in alcuni casi un operando, che può essere una costante o uno spiazzamento.
La prima colonna rappresenta il nome mnemonico in linguaggio assembly, la seconda gli operandi e la terza una breve descrizione dell'istruzione.
Nome | Operandi | Descrizione |
---|---|---|
BIPUSH | byte | Scrive un byte in cima allo stack |
DUP | N/A | Legge la prima parola sulla stack e compie push duplicandola |
ERR | N/A | Stampa un messaggio di errore e arresta il simulatore |
GOTO | nome etichetta | Salto incondizionato |
HALT | N/A | Interrompe il simulatore |
IADD | N/A | Sostituisce le due parole in cima allo stack con la loro somma |
IAND | N/A | Sostituisce le due parole in cima allo stack con il loro AND logico |
IFEQ | nome etichetta | Estrae la parola in cima allo stack ed esegue un salto se ha valore zero |
IFLT | nome etichetta | Estrae la parola in cima allo stack ed esegue un salto se ha valore negativo |
IF_ICMPEQ | nome etichetta | Estrae le due parole in cima allo stack ed esegue un salto se sono uguali |
IINC | nome variabile byte | Somma una costante a una variabile locale |
ILOAD | nome variabile | Scrive una variabile locale in cima allo stack |
IN | N/A | Legge un carattere dal buffer della tastiera e lo scrive in cima allo stack. Se il carattere non è disponibile scrive il valore di 0 |
INVOKEVIRTUAL | nome metodo | Invoca un metodo |
IOR | N/A | Sostituisce le due parole in cima allo stack con il loro OR logico |
IRETURN | N/A | Termina un metodo restituendo un valore intero |
ISTORE | nome variabile | Estrae la parola in cima allo stack e la memorizza in una variabile locale |
ISUB | N/A | Sostituisce le due parole in cima allo stack con la loro differenza |
LDC_W | nome costante | Scrive una costante proveniente dalla constant pool in cima allo stack |
NOP | N/A | Nessuna operazione |
OUT | N/A | Estrae la prima parola in cima allo stack e la scrive sulla periferica standard di output |
POP | N/A | Estrae una parola dalla cima dello stack |
SWAP | N/A | Scambia la posizione delle due parole in cima allo stack |
WIDE | N/A | Istruzione Prefisso: l'istruzione seguente ha un indice a 16 bit |
Descrizione operandi
- byte: Un numero in ottale, decimale o esadecimale.
- nome etichetta: Una stringa univoca.
- nome variabile: Una stringa univoca.
- nome costante: Una stringa univoca.
- nome metodo: Una stringa univoca.
- N/A: Istruzione senza operandi.
Alcune tra queste istruzioni consentono di inserire nello stack una parola proveniente da diverse fonti, come ad esempio il blocco delle variabili locali(ILOAD),la constant pool(LDC_W) e l'istruzione stessa(BIPUSH).
Una qualsiasi variabile può essere estratta dalla cima dello stack e memorizzata nel blocco delle variabili locali(ISTORE). È consentito anche eseguire, utilizzando come operandi le due parole che si trovano alla cima dello stack, operazioni logiche cioè booleane(IAND,IOR) e operazioni aritmetiche(IADD,ISUB).In tutte le operazioni logiche e aritmetiche sono estratte, quindi cancellate, dalla cima dello stack due parole e sostituite con il loro risultato.
Esistono anche quattro istruzioni per i salti,una incondizionata(GOTO) e tre condizionate(IFEQ,IFLT,IF_ICMPEQ).Tutte queste istruzioni,se accettate, cambiano il valore del PC in base alla grandezza del loro spiazzamento.
Infine c'è l'istruzione INVOKEVIRTUAL per invocare un nuovo metodo e l'istruzione IRETURN per terminare il metodo e restituire il controllo a quello che l'aveva invocato.
Esempio di istruzione: INVOKEVIRTUAL
[modifica | modifica wikitesto]Il procedimento per l'invocazione dei metodi è il seguente: innanzitutto il metodo chiamante immette sullo stack un puntatore chiamato OBJREF che si riferisce all'oggetto da chiamare, in seguito inserisce sullo stack i parametri del metodo ed infine è eseguita l'istruzione INVOKEVIRTUAL. Questa istruzione include uno spiazzamento che rappresenta una locazione di memoria all'interno della Constant Pool contenente l'indirizzo dell'area dei metodi in cui inizia il metodo che si sta invocando.
I primi 4 byte nell'area dei metodi contengono dati speciali che sono così composti:
I primi 2 byte sono interpretati come un numero intero a 16 bit indicante il numero dei parametri del metodo(OBJREF è considerato il parametro 0). Questo intero a 16 bit fornisce insieme al valore di SP la locazione di OBJREF.
I successivi 2 byte sono sempre interpretati come un intero a 16 bit che però indica la dimensione del blocco delle variabili locali del metodo invocato. Questo valore è basilare dato che verrà creato un nuovo stack per il metodo immediatamente sopra il blocco delle variabili locali.
infine il quinto byte contiene il primo OPCODE da eseguire.
La reale sequenza di operazioni che avvengono quando viene eseguita l'istruzione INVOKEVIRTUAL è la seguente.
- I 2 byte senza segno che seguono L'OPCODE sono utilizzati per costruire un indice riguardante la Constant Pool. L'istruzione calcola l'indirizzo base del nuovo blocco delle variabili sottraendo al puntatore dello stack il numero dei parametri e impostando il registro LV in modo che punti a OBJREF.
- Viene sovrascritto OBJREF con l'indirizzo di memoria del vecchio PC. Questo indirizzo è calcolato sommando l'indirizzo contenuto in LV con la dimensione del blocco delle variabili locali(variabili locali+parametri).
- Subito sopra l'indirizzo in cui memorizzare il vecchio PC c'è l'indirizzo in cui deve essere memorizzato il vecchio LV.
- Ancora sopra questo indirizzo inizia lo stack per il metodo che è stato chiamato.SP è impostato in modo da puntare al vecchio LV che contiene l'indirizzo immediatamente sotto la prima locazione vuota dello stack. Da ricordare che SP punta sempre alla parola in cima allo stack, se lo stack è vuoto allora punta alla prima locazione sotto la fine dello stack.
- L'ultima operazione è impostare PC in modo che punti al quinto byte nell'area del codice del metodo, quindi terminare l'esecuzione di INVOKEVIRTUAL.
L'istruzione IRETURN inverte la sequenza delle operazioni effettuate da INVOKEVIRTUAL,dealloca lo spazio utilizzato dal metodo chiamato con INVOKEVIRTUAL riportando lo stack al suo stato precedente tranne per il fatto che:
- OBJREF e tutti i parametri sono stati estratti dallo stack
- Il valore restituito dal metodo è inserito in cima allo stack, nella locazione occupata in precedenza da OBJREF
Affinché l'istruzione IRETURN memorizzi il vecchio stato, deve essere in grado di riportare ai loro precedenti valori i registri PC e LV. Essa accede al puntatore di collegamento(Link pointer),cioè alla parola puntata dal registro LV attuale. Questa parola e quella immediatamente superiore vengono recuperate e usate per restituire i loro vecchi valori ai registri PC e LV.
- SP viene reimpostato in modo da puntare alla cima dello stack in cui si trova il valore restituito dal metodo.
- Il controllo è restituito all'istruzione successiva rispetto a INVOKEVIRTUAL.
Implementazione di IJVM su Mic-1
[modifica | modifica wikitesto]La tabella sottostante mostra il microprogramma in Mic-1 che permette di interpretare IJVM. Si può facilmente notare quanto esso sia breve, dato che conta in totale solo 112 istruzioni. Ciascuna delle istruzioni riportate in tabella è divisa in 3 colonne, che ne descrivono l'etichetta, l'effettivo codice da usare e un breve commento.
Etichetta | Operazioni | Commenti |
Main1 | PC = PC + 1; fetch; goto(MBR) | MBR contiene il codice operativo; prelievo del byte successivo; diramazioni |
nop1 | goto Main1 | Non esegue nulla |
iadd1 | MAR = SP = SP - 1; rd | Legge la seconda parola in cima allo stack |
iadd2 | H = TOS | H = Cima dello stack |
iadd3 | MDR = TOS = MDR + H; wr; goto Main1 | Somma le due parole in cima allo stack; scrive in cima allo stack |
isub1 | MAR = SP = SP -1; rd | Legge la seconda parola in cima allo stack |
isub2 | H = TOS | H = Cima dello stack |
isub3 | MDR = TOS = MDR - H; wr; goto Main1 | Esegue la sottrazione; scrive in cima allo stack |
iand1 | MAR = SP = SP -1; rd | Legge la seconda parola in cima allo stack |
iand2 | H = TOS | H = Cima dello stack |
iand3 | MDR = TOS = MDR AND H; wr; goto Main1 | Esegue l'AND; scrive nella nuova cima dello stack |
ior1 | MAR = SP = SP -1; rd | Legge la seconda parola in cima allo stack |
ior2 | H = TOS | H = Cima dello stack |
ior3 | MDR = TOS = MDR OR H; wr; goto Main1 | Esegue l'OR; scrive nella nuova cima dello stack |
dup1 | MAR = SP = SP + 1 | Incrementa SP e lo copia in MAR |
dup2 | MDR = TOS; wr; goto Main1 | Scrive la nuova parola dello stack |
pop1 | MAR = SP = SP -1; rd | Legge la seconda parola in cima allo stack |
pop2 | Attende che il nuovo TOS sia letto dalla memoria | |
pop3 | TOS = MDR; goto Main1 | Copia la nuova parola in TOS |
swap1 | MAR = SP -1; rd | Imposta MAR a SP - 1; legge la seconda parola dello stack |
swap2 | MAR= SP | Imposta Mar con la parola in cima alla stack |
swap3 | H = MDR; wr | Salva TOS in H; scrive la seconda parola in cima allo stack |
swap4 | MDR = TOS | Copia il vecchio TOS in MDR |
swap5 | MAR = SP -1; wr | Imposta MAR a SP -1; scrive la seconda parola nello stack |
swap6 | TOS = H; goto Main1 | Aggiorna TOS |
bipush1 | SP = MAR = SP + 1 | MBR = byte da inserire nello stack |
bipush2 | PC = PC + 1; fetch | Incrementa PC, preleva il successivo codice operativo |
bipush3 | MDR = TOS = MBR; wr; goto Main1 | Estende il segno della costante e la inserisce nello stack |
iload1 | H = LV | MBR contiene l'indice; copia LV in H |
iload2 | MAR = MBRU + H; rd | MAR = indirizzo della variabile locale da inserire nello stack |
iload3 | MAR =SP = SP + 1 | SP punta alla nuova cima dello stack; prepara la scrittura |
iload4 | PC = PC + 1; fetch; wr | Incrementa PC ottiene il nuovo codice operativo; scrive la cima dello stack |
iload5 | TOS = MDR; goto Main1 | Aggiorna TOS |
istore1 | H = LV | MBR contiene l'indice; copia LV in H |
istore2 | MAR = MBRU + H | MAR = indirizzo della variabile locale da memorizzare |
istore3 | MDR = TOS, wr | Copia TOS in MDR; scrive la parola |
istore4 | SP = MAR = SP - 1; rd | Legge la nuova parola in cima alla stack |
istore5 | PC = PC + 1; fetch | Incrementa PC; preleva il successivo codice operativo |
istore6 | TOS = MDR; goto Main1 | Aggiorna TOS |
wide1 | PC = PC + 1; fetch | Preleva il byte dall'operando o il successivo codice operativo |
wide2 | goto (MBR OR 0x100) | Diramazione a più destinazioni con il bit alto impostato a 1 |
wide_iload1 | PC = PC + 1; fetch | MBR contiene il primo byte dell'indice; preleva il secondo |
wide_iload2 | H = MBRU << 8 | H = primo byte dell'indice traslato a sinistra di 8 bit |
wide_iload3 | H = MBRU OR H | H = indice a 16 bit della variabile locale |
wide_iload4 | MAR = LV + H; rd; goto iload3 | MAR = indirizzo della variabile locale da inserire nello stack |
wide_istore1 | PC = PC + 1; fetch | MBR contiene il primo byte dell'indice; preleva il secondo |
wide_istore2 | H = MBRU << 8 | H = primo byte dell'indice traslato a sinistra di 8 bit |
wide_istore3 | H = MBRU OR H | H = indice a 16 bit della variabile locale |
wide_istore4 | MAR = LV + H; rd; goto istore3 | MAR = indirizzo della variabile locale da memorizzare |
ldc_w1 | PC = PC + 1; fetch | MBR contiene il primo byte dell'indice; preleva il secondo |
ldc_w2 | H = MBRU << 8 | H = primo byte dell'indice traslato a sinistra di 8 bit |
ldc_w3 | H = MBRU OR H | H = indice a 16 bit della variabile locale |
ldc_w4 | MAR = H + CPP; rd; goto iload3 | MAR = indirizzo della costante nella porzione costante |
iinc1 | H = LV | MBR contiene l'indice; copia LV in H |
iinc2 | MAR = MBRU + H; rd | Copia LV + indice in MAR; legge variabile |
iinc3 | PC = PC + 1; fetch | Preleva la costante |
iinc4 | H = MDR | Copia la variabile in H |
iinc5 | PC = PC + 1; fetch | Preleva il successivo codice operativo |
iinc6 | MDR = MBR + H; wr; goto Main1 | Inserisce in MDR la somma; aggiorna la variabile |
goto1 | OPC = PC - 1 | Salva l\'indirizzo del codice operativo |
goto2 | PC = PC +1; fetch | MBR contiene il primo byte dell'indice; preleva il secondo |
goto3 | H = MBR <<8 | Trasla e salva in H il primo byte con segno |
goto4 | H = MBRU OR H | H = spiazzamento a 16 bit della diramazione |
goto5 | PC = OPC + H; fetch | Aggiunge lo spaziamento a OPC |
goto6 | goto Main1 | Attende il prelievo del successivo codice operativo |
iflt1 | MAR= SP = SP - 1; rd | Legge la seconda parola in cima allo stack |
iflt2 | OPC = TOS | Salva temporaneamente TOS in OPC |
iflt3 | TOS = MDR | Inserisce in TOS la nuova cima dello stack |
iflt4 | N = OPC; if(N) goto T; else goto F | Diramazione in base al bit di N |
ifeq1 | MAR= SP = SP - 1; rd | Legge la seconda parola in cima allo stack |
ifeq2 | OPC = TOS | Salva temporaneamente TOS in OPC |
ifeq3 | TOS = MDR | Inserisce in TOS la nuova cima dello stack |
ifeq4 | Z = OPC; if(Z) goto T; else goto F | Diramazione in base al bit di N |
if_icmpeq1 | MAR= SP = SP - 1; rd | Legge la seconda parola in cima allo stack |
if_icmpeq2 | MAR= SP = SP - 1 | Imposta MAR per leggere la nuova cima dello stack |
if_icmpeq3 | H = MDR; rd | Copia in H la seconda parola dello stack |
if_icmpeq4 | OPC = TOS | Salva temporaneamente TOS in OPC |
if_icmpeq5 | TOS = MDR | Inserisce in TOS la nuova cima dello stack |
if_icmpeq6 | Z = OPC - H; if (Z) goto T; else goto F | Se le due parole in cima allo stack sono uguali, goto T; altrimenti, goto F |
T | OPC = PC - 1; goto goto2 | Uguale a goto1; necessario per l'indirizzo destinazione |
F | PC = PC + 1 | Salta il primo byte dello spiazzamento |
F2 | PC = PC + 1; fetch | PC punta ora al nuovo metodo |
F3 | goto Main1 | Attende il prelievo del codice operativo |
invokevirtual1 | PC = PC + 1; fetch | MBR = byte 1 dell'indice; incrementa PC; ottiene il secondo byte |
invokevirtual2 | H = MBRU << 8 | Trasla e salva in H il primo byte |
invokevirtual3 | H = MBRU OR H | H » spiazzamento del puntatore al metodo rispetto a CPP |
invokevirtual4 | MAR = CPP + H; rd | Ottiene dall'area CPP un puntatore al metodo |
invokevirtual5 | OPC = PC + 1 | Salva temporaneamente il PC di ritorno in OPC |
invokevirtual6 | PC = MDR; fetch | PC punta ora al nuovo metodo; ottiene il numero di parametri |
invokevirtual7 | PC = PC + 1; fetch | Preleva il secondo byte del numero di parametri |
invokevirtual8 | H = MBRU « 8 | Trasla e salva in H il primo byte |
invokevirtual9 | H = MBRU OR H | H = numero di parametri |
invokevirtual10 | PC = PC + 1; fetch | Preleva il primo byte del numero di variabili locali |
invokevirtual11 | TOS = SP - H | TOS = indirizzo di OBJREF - 1 |
invokevirtual12 | TOS = MAR = TOS + 1 | TOS = indirizzo di OBJREF (nuovo LV) |
invokevirtual13 | PC = PC + 1; fetch | Preleva il secondo byte del numero di variabili locali |
invokevirtual14 | H = MBRU << 8 | Trasla e salva in H il primo byte |
invokevirtual15 | H = MBRU OR H | H = numero dì variabili locali |
invokevirtual16 | MDR = SP + H + 1; wr | Sovrascrive OBJREF con il puntatore di collegamento |
invokevirtual17 | MAR = SP = MDR | Imposta SP e MAR con la locazione in cui memorizzare il vecchio PC |
invokevirtual18 | MDR = OPC; wr S | Salva il vecchio PC sopra le variabili locali |
invokevirtual19 | MAR = SP = SP + 1 | SP punta alla locazione in cui salvare il vecchio LV |
invokevirtual20 | MDR = LV; wr | Salva il vecchio LV sopra il PC salvato |
invokevirtual21 | PC = PC +1; fetch | Preleva ii primo codice operativo del nuovo metodo |
invokevirtual22 | LV = TOS; goto Main1 | Imposta LV per puntare al blocco LV |
ireturn1 | MAR = SP = LV; rd | Reimposta SP e MAR per ricevere il puntatore di collegamento |
ireturn2 | Attende che si completi la lettura | |
ireturn3 | LV = MAR = MDR; rd | Imposta LV con il puntatore di collegamento; ottiene il vecchio PC |
ireturn4 | MAR= LV + 1 | Imposta MAR per leggere il vecchio LV |
ireturn5 | PC = MDR; rd;fetch | Ripristina PC; preleva il successivo codice operativo |
ireturn6 | MAR = SP | Imposta MAR per scrivere TOS |
ireturn7 | LV = MDR | Ripristina LV |
ireturn8 | MDR = TOS; wr; goto Main 1 | Salva il valore di ritorno sulla cima originale dello stack |
I registri CPP, LV e SP vengono utilizzati per memorizzare, rispettivamente, i puntatori alla porzione costante di memoria, al blocco delle variabili locali e alla cima dello stack.
- PC mantiene l'indirizzo del successivo byte da prelevare dal flusso dell'istruzione.
- MBR è un registro a 1 byte che mantiene in modo sequenziale i byte dello stream dell'istruzione proveniente dalla memoria per poterli interpretare.
- TOS contiene il valore puntato da SP, ovvero la parola che si trova in cima allo stack.
- OPC è un indirizzo temporaneo, di solito viene usato per salvare il vecchio PC (Old Program Counter).
Ognuno di questi registri contiene sempre un particolare valore. Ad esempio, all'inizio e alla fine di ogni istruzione, TOS contiene il valore puntato in SP, ovvero la parola che si trova in cima allo stack. In realtà questa parola può essere letta in qualsiasi momento dalla memoria, ma avendola a disposizione in un registro permette di risparmiare un riferimento alla memoria. Per poche istruzioni mantenere TOS implica un impiego maggiore di operazioni di memoria, come ad esempio l'istruzione POP, che deve leggere dalla memoria la nuova parola in cima allo stack prima di poterla copiare all'interno di TOS.
Come tutti gli interpreti, anche questo microprogramma ha un suo ciclo principale che preleva, decodifica ed esegue le istruzioni del programma da interpretare, che in questo particolare caso sono le istruzioni di IJVM. Il suo ciclo inizia con la linea etichettata Main1. Perché il ciclo abbia inizio, deve essere verificata la condizione che il registro PC sia già stato caricato con un indirizzo di locazione di memoria che contenga del codice operativo, il quale a sua volta deve già essere stato memorizzato in MBR.
All'inizio di ogni istruzione viene eseguita la stessa sequenza di operazioni, ed è importante che la lunghezza di questa sequenza sia più breve possibile. Dopo numerosi studi, si è arrivati alla conclusione che il ciclo principale può essere ridotto ad una singola istruzione. Ogni volta che questa istruzione viene eseguita il codice operativo IJVM da eseguire si trova già all'interno di MBR.
A questo punto, è necessario fare un salto nel microcodice per eseguire l'istruzione IJVM, oltre a iniziare il prelievo del byte che segue il codice operativo.
Bisogna però ricordare che le istruzioni non vengono eseguite in maniera sequenziale, ma ciascuna di loro è obbligata a indicare in modo esplicito il proprio successore. Tutti gli indirizzi della memoria corrispondenti ai codici operativi devono essere riservati per la prima parola della corrispondente istruzione dell'interprete, quindi ad esempio il codice che interpreta POP inizia a 0x57, mentre quello di DUP inizia a 0x59. Il codice di POP, però, è lungo 3 microistruzioni, e quindi, se fosse memorizzato in parole consecutive, interferirebbe con l'inizio di DUP. All'interno di ciascuna sequenza le microistruzioni che seguono quella iniziale devono essere memorizzate nelle locazioni di memoria ancora libere, non riservate per i codici operativi. Per questo è necessario effettuare molti salti da una locazione di memoria all'altra.
Per vedere come funziona l'interprete, assumiamo che MBR contenga il valore 0x60, ovvero il codice operativo di IADD. Nel ciclo principale compaiono 3 azioni:
- Incrementiamo PC, in modo che contenga l'indirizzo del primo byte dopo il codice operativo.
- Preleviamo il byte successivo da portare all'interno di MBR.
- All'inizio di Main1 effettuiamo una diramazione verso l'indirizzo contenuto in MBR.
Il prelievo del byte successivo inizia in questo punto in modo che sia disponibile all'inizio della terza microistruzione. Se il byte contenuto in MBR è formato da soli 0, allora identifica il codice operativo dell'istruzione NOP. In questo caso l'istruzione non esegue nulla, ma effettua un salto all'inizio del ciclo principale, dove viene ripetuta la sequenza utilizzando il nuovo codice memorizzato in MBR. Ricordiamo infine che le microistruzioni in tabella non sono memorizzate consecutivamente nella memoria, quindi Main1 non si trova nell'indirizzo 0 della memoria. È compito del microassemblatore posizionare le microistruzioni negli indirizzi adatti e collegarli in brevi sequenze usando il campo NEXT_ADDRESS.
Bibliografia
[modifica | modifica wikitesto]- Andrew S. Tanenbaum, Architettura dei calcolatori. Un approccio strutturale, Amsterdam, Pearson, 2013, ISBN 978-88-7192-962-0.
Voci correlate
[modifica | modifica wikitesto]Collegamenti esterni
[modifica | modifica wikitesto]- Mic IJVM Simulator Simulatore che implementa IJVM sulla macchina virtuale Mic-1, scritto in C++ robusto e semplice da installare (non richiede Java)
- emuIJVM emulatore open source di IJVM sviluppato da studenti di informatica dell'università di Catania
- IJVM Simulator Simulatore IJVM scritto in C++ robusto e semplice da installare (non richiede Java)