Vai al contenuto

IJVM

Da Wikipedia, l'enciclopedia libera.

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.

  1. 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.
  2. 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).
  3. Subito sopra l'indirizzo in cui memorizzare il vecchio PC c'è l'indirizzo in cui deve essere memorizzato il vecchio LV.
  4. 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.
  5. 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
Main1PC = PC + 1; fetch; goto(MBR)MBR contiene il codice operativo; prelievo del byte successivo; diramazioni
nop1goto Main1Non esegue nulla
iadd1MAR = SP = SP - 1; rdLegge la seconda parola in cima allo stack
iadd2H = TOSH = Cima dello stack
iadd3MDR = TOS = MDR + H; wr; goto Main1Somma le due parole in cima allo stack; scrive in cima allo stack
isub1MAR = SP = SP -1; rdLegge la seconda parola in cima allo stack
isub2H = TOSH = Cima dello stack
isub3MDR = TOS = MDR - H; wr; goto Main1Esegue la sottrazione; scrive in cima allo stack
iand1MAR = SP = SP -1; rdLegge la seconda parola in cima allo stack
iand2H = TOSH = Cima dello stack
iand3MDR = TOS = MDR AND H; wr; goto Main1Esegue l'AND; scrive nella nuova cima dello stack
ior1MAR = SP = SP -1; rdLegge la seconda parola in cima allo stack
ior2H = TOSH = Cima dello stack
ior3MDR = TOS = MDR OR H; wr; goto Main1Esegue l'OR; scrive nella nuova cima dello stack
dup1MAR = SP = SP + 1Incrementa SP e lo copia in MAR
dup2MDR = TOS; wr; goto Main1Scrive la nuova parola dello stack
pop1MAR = SP = SP -1; rdLegge la seconda parola in cima allo stack
pop2Attende che il nuovo TOS sia letto dalla memoria
pop3TOS = MDR; goto Main1Copia la nuova parola in TOS
swap1MAR = SP -1; rdImposta MAR a SP - 1; legge la seconda parola dello stack
swap2MAR= SPImposta Mar con la parola in cima alla stack
swap3H = MDR; wrSalva TOS in H; scrive la seconda parola in cima allo stack
swap4MDR = TOSCopia il vecchio TOS in MDR
swap5MAR = SP -1; wrImposta MAR a SP -1; scrive la seconda parola nello stack
swap6TOS = H; goto Main1Aggiorna TOS
bipush1SP = MAR = SP + 1MBR = byte da inserire nello stack
bipush2PC = PC + 1; fetchIncrementa PC, preleva il successivo codice operativo
bipush3MDR = TOS = MBR; wr; goto Main1Estende il segno della costante e la inserisce nello stack
iload1H = LVMBR contiene l'indice; copia LV in H
iload2MAR = MBRU + H; rdMAR = indirizzo della variabile locale da inserire nello stack
iload3MAR =SP = SP + 1SP punta alla nuova cima dello stack; prepara la scrittura
iload4PC = PC + 1; fetch; wrIncrementa PC ottiene il nuovo codice operativo; scrive la cima dello stack
iload5TOS = MDR; goto Main1Aggiorna TOS
istore1H = LVMBR contiene l'indice; copia LV in H
istore2MAR = MBRU + HMAR = indirizzo della variabile locale da memorizzare
istore3MDR = TOS, wrCopia TOS in MDR; scrive la parola
istore4SP = MAR = SP - 1; rdLegge la nuova parola in cima alla stack
istore5PC = PC + 1; fetchIncrementa PC; preleva il successivo codice operativo
istore6TOS = MDR; goto Main1Aggiorna TOS
wide1PC = PC + 1; fetchPreleva il byte dall'operando o il successivo codice operativo
wide2goto (MBR OR 0x100)Diramazione a più destinazioni con il bit alto impostato a 1
wide_iload1PC = PC + 1; fetchMBR contiene il primo byte dell'indice; preleva il secondo
wide_iload2H = MBRU << 8H = primo byte dell'indice traslato a sinistra di 8 bit
wide_iload3H = MBRU OR HH = indice a 16 bit della variabile locale
wide_iload4MAR = LV + H; rd; goto iload3MAR = indirizzo della variabile locale da inserire nello stack
wide_istore1PC = PC + 1; fetchMBR contiene il primo byte dell'indice; preleva il secondo
wide_istore2H = MBRU << 8H = primo byte dell'indice traslato a sinistra di 8 bit
wide_istore3H = MBRU OR HH = indice a 16 bit della variabile locale
wide_istore4MAR = LV + H; rd; goto istore3MAR = indirizzo della variabile locale da memorizzare
ldc_w1PC = PC + 1; fetchMBR contiene il primo byte dell'indice; preleva il secondo
ldc_w2H = MBRU << 8H = primo byte dell'indice traslato a sinistra di 8 bit
ldc_w3H = MBRU OR HH = indice a 16 bit della variabile locale
ldc_w4MAR = H + CPP; rd; goto iload3MAR = indirizzo della costante nella porzione costante
iinc1H = LVMBR contiene l'indice; copia LV in H
iinc2MAR = MBRU + H; rdCopia LV + indice in MAR; legge variabile
iinc3PC = PC + 1; fetchPreleva la costante
iinc4H = MDRCopia la variabile in H
iinc5PC = PC + 1; fetchPreleva il successivo codice operativo
iinc6MDR = MBR + H; wr; goto Main1Inserisce in MDR la somma; aggiorna la variabile
goto1OPC = PC - 1Salva l\'indirizzo del codice operativo
goto2PC = PC +1; fetchMBR contiene il primo byte dell'indice; preleva il secondo
goto3H = MBR <<8Trasla e salva in H il primo byte con segno
goto4H = MBRU OR HH = spiazzamento a 16 bit della diramazione
goto5PC = OPC + H; fetchAggiunge lo spaziamento a OPC
goto6goto Main1Attende il prelievo del successivo codice operativo
iflt1MAR= SP = SP - 1; rdLegge la seconda parola in cima allo stack
iflt2OPC = TOSSalva temporaneamente TOS in OPC
iflt3TOS = MDRInserisce in TOS la nuova cima dello stack
iflt4N = OPC; if(N) goto T; else goto FDiramazione in base al bit di N
ifeq1MAR= SP = SP - 1; rdLegge la seconda parola in cima allo stack
ifeq2OPC = TOSSalva temporaneamente TOS in OPC
ifeq3TOS = MDRInserisce in TOS la nuova cima dello stack
ifeq4Z = OPC; if(Z) goto T; else goto FDiramazione in base al bit di N
if_icmpeq1MAR= SP = SP - 1; rdLegge la seconda parola in cima allo stack
if_icmpeq2MAR= SP = SP - 1Imposta MAR per leggere la nuova cima dello stack
if_icmpeq3H = MDR; rdCopia in H la seconda parola dello stack
if_icmpeq4OPC = TOSSalva temporaneamente TOS in OPC
if_icmpeq5TOS = MDRInserisce in TOS la nuova cima dello stack
if_icmpeq6Z = OPC - H; if (Z) goto T; else goto FSe le due parole in cima allo stack sono uguali, goto T; altrimenti, goto F
TOPC = PC - 1; goto goto2Uguale a goto1; necessario per l'indirizzo destinazione
FPC = PC + 1Salta il primo byte dello spiazzamento
F2PC = PC + 1; fetchPC punta ora al nuovo metodo
F3goto Main1Attende il prelievo del codice operativo
invokevirtual1PC = PC + 1; fetchMBR = byte 1 dell'indice; incrementa PC; ottiene il secondo byte
invokevirtual2H = MBRU << 8Trasla e salva in H il primo byte
invokevirtual3H = MBRU OR HH » spiazzamento del puntatore al metodo rispetto a CPP
invokevirtual4MAR = CPP + H; rdOttiene dall'area CPP un puntatore al metodo
invokevirtual5OPC = PC + 1Salva temporaneamente il PC di ritorno in OPC
invokevirtual6PC = MDR; fetchPC punta ora al nuovo metodo; ottiene il numero di parametri
invokevirtual7PC = PC + 1; fetchPreleva il secondo byte del numero di parametri
invokevirtual8H = MBRU « 8Trasla e salva in H il primo byte
invokevirtual9H = MBRU OR HH = numero di parametri
invokevirtual10PC = PC + 1; fetchPreleva il primo byte del numero di variabili locali
invokevirtual11TOS = SP - HTOS = indirizzo di OBJREF - 1
invokevirtual12TOS = MAR = TOS + 1TOS = indirizzo di OBJREF (nuovo LV)
invokevirtual13PC = PC + 1; fetchPreleva il secondo byte del numero di variabili locali
invokevirtual14H = MBRU << 8Trasla e salva in H il primo byte
invokevirtual15H = MBRU OR HH = numero dì variabili locali
invokevirtual16MDR = SP + H + 1; wrSovrascrive OBJREF con il puntatore di collegamento
invokevirtual17MAR = SP = MDRImposta SP e MAR con la locazione in cui memorizzare il vecchio PC
invokevirtual18MDR = OPC; wr SSalva il vecchio PC sopra le variabili locali
invokevirtual19MAR = SP = SP + 1SP punta alla locazione in cui salvare il vecchio LV
invokevirtual20MDR = LV; wrSalva il vecchio LV sopra il PC salvato
invokevirtual21PC = PC +1; fetchPreleva ii primo codice operativo del nuovo metodo
invokevirtual22LV = TOS; goto Main1Imposta LV per puntare al blocco LV
ireturn1MAR = SP = LV; rdReimposta SP e MAR per ricevere il puntatore di collegamento
ireturn2Attende che si completi la lettura
ireturn3LV = MAR = MDR; rdImposta LV con il puntatore di collegamento; ottiene il vecchio PC
ireturn4MAR= LV + 1Imposta MAR per leggere il vecchio LV
ireturn5PC = MDR; rd;fetchRipristina PC; preleva il successivo codice operativo
ireturn6MAR = SPImposta MAR per scrivere TOS
ireturn7LV = MDRRipristina LV
ireturn8MDR = TOS; wr; goto Main 1Salva 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.

  • 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)
  Portale Informatica: accedi alle voci di Wikipedia che trattano di informatica