/* Macchina virtuale. La classe puo' essere eseguita direttamente, tramite il metodo main, o istanziata a partire da un'altra classe. Revisione Maggio 2015 */ package lt.macchina; import java.io.*; public class Macchina { public static final String ESEG_DEFAULT = "eseguibile"; //nome di default del file eseguibile private String buffer = null; //buffer lettura caratteri private int bufferIndex; //puntatore al prox carattere del buffer //costanti per le istruzioni della macchina public static final int //istruzioni di trasferimento dati PUSH = 0, PUSHIND = 1, // PUSH* PUSHIMM = 2, // PUSH= POP = 3, POPIND = 4, // POP* POPIMM = 5, // POP= //istruzioni aritmetiche ADD = 10, SUB = 11, MUL = 12, DIV = 13, //istruzioni di salto JUMP = 20, JZERO = 21, JNZERO = 22, JGTZ = 23, JGEZ = 24, JLTZ = 25, JLEZ = 26, JUMPIND = 27, // JUMP* //altre istruzioni INPUT = 30, OUTPUT = 31, OUTPUTCH = 32, MOVESP = 33, PUSHPC = 34, PUSHSP = 35, PUSHSIZE = 36, INPUTCH = 37, HALT = 50; //definizione della memoria della macchina private static int DIM_CODICE = 10000; //lunghezza area codice private static int DIM_MEMO = 10000; //lunghezza area dati private int[] codice; //area codice private int[] memo; //area dati (stack) private int sp, pc; //stack pointer e program counter //variabile utile per il metodo visualizza private int lungCodice; //lunghezza codice //attivazione debug private boolean debug = false; /* Carica il file specificato nell'argomento per l'esecuzione. Se non viene specificato alcun argomento, viene usato il nome di default definito nella costante ESEG_DEFAULT. */ public static void main(String... args) { String nomeEseguibile = ESEG_DEFAULT; //opzioni boolean optionDebug = false; boolean optionList = false; boolean optionRun = true; //esame degli argomenti sulla linea di comando for (int i = 0; i < args.length; i++) if (args[i].charAt(0) == '-') for (int j = 0; j < args[i].length(); j++) switch(args[i].charAt(j)) { case 'd': optionDebug = true; break; case 'l': optionList = true; break; case 'L': optionList = true; optionRun = false; break; } else nomeEseguibile = args[i]; //esecuzione try { Macchina eseg = new Macchina(nomeEseguibile, optionDebug); if (optionList) eseg.visualizza(); if (optionRun) eseg.esegui(); } catch (IOException e) { System.err.println("errore di lettura"); } catch (RuntimeException e) { System.err.println(e.toString()); } } /* costruttore: predispone la macchina virtuale caricando il codice prelevato dal file specificato tramite il parametro */ public Macchina(String s) throws IOException { //predisponde aree codice e dati codice = new int[DIM_CODICE]; memo = new int[DIM_MEMO]; ObjectInputStream inFile = new ObjectInputStream(new FileInputStream(s)); //carica il codice int i = 0; try { while (true) codice[i++] = inFile.readInt(); } catch (EOFException e) { //l'eccezione in questo caso non segnala un errore, ma il raggiungimento //della fine di inFile } lungCodice = i - 1; } public Macchina(String s, boolean d) throws IOException { this(s); debug = d; } /* esecuzione del codice */ public void esegui() throws IOException { sp = -1; pc = 0; //predispone canale di lettura BufferedReader tastiera = new BufferedReader(new InputStreamReader(System.in)); boolean finito = false; do { if (debug) System.out.println(istrToString(pc)); switch (codice[pc++]) { case PUSH: memo[++sp] = memo[codice[pc++]]; break; case PUSHIND: memo[sp] = memo[memo[sp]]; break; case PUSHIMM: memo[++sp] = codice[pc++]; break; case POP: memo[codice[pc++]] = memo[sp--]; break; case POPIND: memo[memo[sp - 1]] = memo[sp]; sp = sp - 2; break; case POPIMM: sp--; break; case ADD: memo[sp - 1] = memo [sp - 1] + memo[sp]; sp--; break; case SUB: memo[sp - 1] = memo [sp - 1] - memo[sp]; sp--; break; case MUL: memo[sp - 1] = memo [sp - 1] * memo[sp]; sp--; break; case DIV: memo[sp - 1] = memo [sp - 1] / memo[sp]; sp--; break; case JUMP: pc = codice[pc]; break; case JZERO: if (memo[sp--] == 0) pc = codice[pc]; else pc++; break; case JNZERO: if (memo[sp--] != 0) pc = codice[pc]; else pc++; break; case JGTZ: if (memo[sp--] > 0) pc = codice[pc]; else pc++; break; case JGEZ: if (memo[sp--] >= 0) pc = codice[pc]; else pc++; break; case JLTZ: if (memo[sp--] < 0) pc = codice[pc]; else pc++; break; case JLEZ: if (memo[sp--] <= 0) pc = codice[pc]; else pc++; break; case JUMPIND: pc = memo[sp--]; break; case INPUT: System.out.flush(); buffer = null; // elimina un eventuale buffer di caratteri di INPUTCH memo[++sp] = Integer.parseInt(tastiera.readLine()); break; case INPUTCH: if (buffer == null) { //lettura stringa buffer = tastiera.readLine(); bufferIndex = 0; } if (bufferIndex == buffer.length()) { //tutti i caratteri gia' utilizzati memo[++sp] = 0; //carattere di codice 0 buffer = null; //elimina il buffer } else memo[++sp] = buffer.charAt(bufferIndex++); break; case OUTPUT: System.out.print(memo[sp--]); break; case OUTPUTCH: System.out.print((char)memo[sp--]); break; case MOVESP: sp = memo[sp]; break; case PUSHPC: memo[++sp] = pc; break; case PUSHSP: ++sp; memo[sp] = sp; break; case PUSHSIZE: ++sp; memo[sp] = DIM_MEMO; break; case HALT: finito = true; break; default: throw new RuntimeException("istruzione sconosciuta"); } } while (!finito); } /* Classe interna utilizzata per restituire due valori nel metodo getFineEStringa */ private class InteroEStringa { public int intero; public String stringa; public InteroEStringa (int i, String s) { intero = i; stringa = s; } } /* Restituisce una stringa corrispondente all'istruzione memorizzata a partire dall'indirizzo i. Se l'istruzione prevede un operando viene restituito anch'esso all'interno della stringa. Nel campo di tipo intero del risultato viene restituito l'indirizzo dell'istruzione successiva, che sara' i + 1 nel caso di istruzioni prive di operandi, i + 2 nel caso di istruzioni con operandi. */ private InteroEStringa getFineEStringa(int i) { String s = ""; //spazi iniziali per incolonnamento corretto for (int nspazi = String.valueOf(lungCodice).length() - String.valueOf(i).length(); nspazi >= 0; nspazi--) s = s + " "; switch (codice[i]) { case PUSH: s += i++ + " PUSH " + codice[i++]; break; case PUSHIND: s += i++ + " PUSH*"; break; case PUSHIMM: s += i++ + " PUSH= " + codice[i++]; break; case POP: s += i++ + " POP " + codice[i++]; break; case POPIND: s += i++ + " POP*"; break; case POPIMM: s += i++ + " POP="; break; case ADD: s += i++ + " ADD"; break; case SUB: s += i++ + " SUB"; break; case MUL: s += i++ + " MUL"; break; case DIV: s += i++ + " DIV"; break; case JUMP: s += i++ + " JUMP " + codice[i++]; break; case JZERO: s += i++ + " JZERO " + codice[i++]; break; case JNZERO: s += i++ + " JNZERO " + codice[i++]; break; case JGTZ: s += i++ + " JGTZ " + codice[i++]; break; case JGEZ: s += i++ + " JGEZ " + codice[i++]; break; case JLTZ: s += i++ + " JLTZ " + codice[i++]; break; case JLEZ: s += i++ + " JLEZ " + codice[i++]; break; case JUMPIND: s += i++ + " JUMP*"; break; case INPUT: s += i++ + " INPUT"; break; case INPUTCH: s += i++ + " INPUTCH"; break; case OUTPUT: s += i++ + " OUTPUT"; break; case OUTPUTCH: s += i++ + " OUTPUTCH"; break; case MOVESP: s += i++ + " MOVESP"; break; case PUSHPC: s += i++ + " PUSHPC"; break; case PUSHSP: s += i++ + " PUSHSP"; break; case PUSHSIZE: s += i++ + " PUSHSIZE"; break; case HALT: s += i++ + " HALT"; break; default: throw new RuntimeException("istruzione sconosciuta"); } return new InteroEStringa(i, s); } public String istrToString(int i) { return getFineEStringa(i).stringa; } public String toString() { String s = ""; int i = 0; while (i < lungCodice) { InteroEStringa u = getFineEStringa(i); i = u.intero; s = s + u.stringa + "\n"; } return s; } public void visualizza() { System.out.println(this.toString()); } }