Sicuramente saprete come sia strutturato un computer, Scheda Madre, CPU ed altro. In questo articolo ci occuperemo di scrivere, nella maniera più semplice possibile, un piccolo emulatore software in Java di un processore, l’articolo nasce con l’intento di spiegare come è possibile realizzare questo dispositivo emulato con due semplici classi. Il problema può essere così schematizzato: il processore esegue un’istruzione alla volta, la richiede alla memoria, capisce di cosa si tratta e la esegue. Il processore non è l’unico chip ad eseguire le operazioni, ma ci sono altri con lavori più specializzati, inseriti nel chipset della scheda madre. Per scrivere un emulatore occorre conoscere esattamente il funzionamento di un processore, non solo, ma anche degli altri chip inseriti nella scheda madre.
Il funzionamento di un processore è estremamente semplice, ripete in continuazione le seguenti operazioni: legge un’istruzione dalla memoria e la esegue. All’interno del processore sono disponibili dei registri di determinate grandezze, in ognuno di questi sono contenute le informazioni. Il processore legge le informazioni dalla memoria a partire da un determinato indirizzo che viene tenuto in un registro particolare, il program counter (PC). L’istruzione viene recuperata dalla memoria, successivamente vengono presi gli operandi dell’operazione ed infine viene eseguita l’istruzione vera e propria.
Per semplicità rappresenteremo la memoria come un vettore di parole di 16 bit e gli indirizzi della memoria indicano la parola e non il byte. La classe MotherBoard è un vettore della memoria che offre i metodi peek e poke rispettivamente per ottenere una parola in un certo indirizzo di memoria e per scriverla in un altro. Per caricare in memoria le istruzioni si utilizza il metodo loadMemory che prende le istruzioni da un file di input, quindi viene inserita la prima istruzione all’indirizzo zero e si compie l’esecuzione. Arrivati al termine viene eseguita l’istruzione HALT e viene invocato il metodo printMemory che stampa il contenuto dei registry del processore ed il contenuto della memoria nel file di output. La classe CPU implementa il processore che contiene un riferimento alla scheda madre per leggere e scrivere la memoria, inoltre abbiamo dei metodi per estrarre i comandi dalle parole di 16 caratteri, ad esempio getR1Code estrae il primo pezzo, getR2Code il secondo e via dicendo. Infine troviamo il metodo run che implementa il ciclo di esecuzione del processore, la variabile istr contiene l’istruzione da eseguire. Ecco la parte in cui si effettuano le istruzioni nel nostro emulatore:
... public void run() { boolean halt = false; while (!halt) { int istr = mb.peek(programCounter++); int addIstr; int res; switch (getOpCode(istr)) { case ADDRR: res = registers[getR1Code(istr)] + registers[getR2Code(istr)]; registers[getR3Code(istr)] = res; isZero = res == 0; break; case ADDNR: addIstr = mb.peek(programCounter++); res = addIstr + registers[getR2Code(istr)]; registers[getR3Code(istr)] = res; isZero = res == 0; break; case ADDMR: addIstr = mb.peek(programCounter++); res = mb.peek(addIstr) + registers[getR2Code(istr)]; registers[getR3Code(istr)] = res; isZero = res == 0; break; case ADDMRR: res = mb.peek(registers[getR1Code(istr)]) + registers[getR2Code(istr)]; registers[getR3Code(istr)] = res; isZero = res == 0; break; case SUBRR: res = registers[getR1Code(istr)] - registers[getR2Code(istr)]; registers[getR3Code(istr)] = res; isZero = res == 0; break; case ZERO: registers[getR1Code(istr)] = 0; isZero = true; break; case HALT: halt = true; break; case JMP: addIstr = mb.peek(programCounter++); programCounter = addIstr; break; case JZ: case JNZ: addIstr = mb.peek(programCounter++); if (isZero) programCounter = addIstr; break; case STORE: addIstr = mb.peek(programCounter++); mb.poke(addIstr, registers[getR1Code(istr)]); break; } } } ...
Per provare il nostro emulatore in Java occorre inserire il codice da eseguire nel file di input ed avviarlo. Ad esempio per contare i numeri fino a cento è possibile scrivere in codice:
ZERO R1 ZERO R2 ADD #100, R1, R1 loop: ADD #1, R2, R2 SUB R1, R2, R3 JNZ Loop STORE R2, @20 HALT
Lo scopo di questo articolo è quello di mostrare, più in linea teorica che pratica, la possibilità di creare un emulatore in Java o in qualsiasi altro linguaggio. Volendo potete creare l’emulatore completo, non è difficile, la parte più complicata (il metodo run) l’ho inserita in questo articolo, comunque per maggiori informazioni potete contattarmi tramite forum nella sezione dedicata al linguaggio Java.