L'emulazione è un'area multiforme. Ecco le idee di base e i componenti funzionali. Lo spezzerò in pezzi e poi riempirò i dettagli tramite le modifiche. Molte delle cose che descriverò richiederanno la conoscenza del funzionamento interno dei processori: è necessaria la conoscenza dell'assemblaggio. Se sono un po 'troppo vago su certe cose, ti preghiamo di porre domande in modo da poter continuare a migliorare questa risposta.
Idea base:
L'emulazione funziona gestendo il comportamento del processore e dei singoli componenti. Costruisci ogni singolo pezzo del sistema e quindi connetti i pezzi proprio come fanno i fili nell'hardware.
Emulazione del processore:
Esistono tre modi per gestire l'emulazione del processore:
- Interpretazione
- Ricompilazione dinamica
- Ricompilazione statica
Con tutti questi percorsi, hai lo stesso obiettivo generale: eseguire un pezzo di codice per modificare lo stato del processore e interagire con "hardware". Lo stato del processore è un conglomerato di registri del processore, gestori di interrupt, ecc. Per un determinato target del processore. Per il 6502, si avrebbe un numero di interi a 8 bit che rappresentano i registri: A
, X
, Y
, P
, e S
; avresti anche un PC
registro a 16 bit .
Con l'interpretazione, si inizia dal IP
(puntatore dell'istruzione - chiamato anche PC
, contatore del programma) e si leggono le istruzioni dalla memoria. Il codice analizza queste istruzioni e utilizza queste informazioni per modificare lo stato del processore come specificato dal processore. Il problema principale con l'interpretazione è che è molto lento; ogni volta che gestisci una determinata istruzione, devi decodificarla ed eseguire l'operazione richiesta.
Con la ricompilazione dinamica, si scorre il codice in modo molto simile all'interpretazione, ma invece di eseguire solo codici operativi, si crea un elenco di operazioni. Una volta raggiunta un'istruzione di succursale, compili questo elenco di operazioni in codice macchina per la tua piattaforma host, quindi memorizzi nella cache questo codice compilato ed eseguilo. Quindi, quando premi di nuovo un determinato gruppo di istruzioni, devi solo eseguire il codice dalla cache. (A proposito, la maggior parte delle persone in realtà non crea un elenco di istruzioni ma le compila al codice macchina al volo - questo rende più difficile l'ottimizzazione, ma non rientra nell'ambito di questa risposta, a meno che non siano interessate abbastanza persone)
Con la ricompilazione statica, fai la stessa cosa della ricompilazione dinamica, ma segui i rami. Alla fine si crea un blocco di codice che rappresenta tutto il codice nel programma, che può quindi essere eseguito senza ulteriori interferenze. Questo sarebbe un ottimo meccanismo se non fosse per i seguenti problemi:
- Il codice che non è nel programma per cominciare (ad esempio, compresso, crittografato, generato / modificato in fase di esecuzione, ecc.) Non verrà ricompilato, quindi non verrà eseguito
- È stato dimostrato che trovare tutto il codice in un dato binario equivale al problema di Halting
Questi si combinano per rendere completamente impossibile la ricompilazione statica nel 99% dei casi. Per ulteriori informazioni, Michael Steil ha fatto delle ottime ricerche sulla ricompilazione statica, la migliore che abbia mai visto.
L'altro lato dell'emulazione del processore è il modo in cui interagisci con l'hardware. Questo ha davvero due lati:
- Tempistica del processore
- Interrompere la gestione
Tempistica del processore:
Alcune piattaforme, in particolare le console più vecchie come NES, SNES, ecc., Richiedono che il tuo emulatore abbia un tempismo rigoroso per essere completamente compatibile. Con il NES hai la PPU (unità di elaborazione dei pixel) che richiede che la CPU metta i pixel nella sua memoria in momenti precisi. Se usi l'interpretazione, puoi facilmente contare i cicli ed emulare i tempi corretti; con la ricompilazione dinamica / statica, le cose sono molto / molto / più complesse.
Interrompere la gestione:
Gli interrupt sono il meccanismo principale che la CPU comunica con l'hardware. In generale, i componenti hardware diranno alla CPU ciò che interrompe. Questo è piuttosto semplice: quando il tuo codice genera un determinato interrupt, dai un'occhiata alla tabella del gestore di interrupt e chiami il callback corretto.
Emulazione hardware:
Esistono due lati per emulare un determinato dispositivo hardware:
- Emulazione della funzionalità del dispositivo
- Emulazione delle attuali interfacce del dispositivo
Prendi il caso di un disco rigido. La funzionalità viene emulata creando la memoria di supporto, le routine di lettura / scrittura / formattazione, ecc. Questa parte è generalmente molto semplice.
L'interfaccia effettiva del dispositivo è un po 'più complessa. In genere si tratta di una combinazione di registri mappati in memoria (ad esempio parti di memoria che il dispositivo rileva per apportare modifiche) e interrompe. Per un disco rigido, è possibile disporre di un'area mappata in memoria in cui si inseriscono comandi di lettura, scritture, ecc., Quindi si rileggono questi dati.
Andrei più nel dettaglio, ma ci sono un milione di modi in cui puoi seguirlo. Se hai domande specifiche qui, sentiti libero di chiedere e io aggiungerò le informazioni.
risorse:
Penso di aver fatto un'introduzione abbastanza buona qui, ma ci sono un sacco di aree aggiuntive. Sono più che felice di aiutarti con qualsiasi domanda; Sono stato molto vago in gran parte di questo semplicemente a causa dell'immensa complessità.
Link obbligatori a Wikipedia:
Risorse generali di emulazione:
- Zophar - È qui che ho iniziato con l'emulazione, scaricando prima gli emulatori e infine saccheggiando i loro immensi archivi di documentazione. Questa è la miglior risorsa in assoluto che tu possa avere.
- NGEmu - Non molte risorse dirette, ma i loro forum sono imbattibili.
- RomHacking.net - La sezione documenti contiene risorse relative all'architettura della macchina per console popolari
Progetti di emulatore per riferimento:
- IronBabel - Questa è una piattaforma di emulazione per .NET, scritta in Nemerle e ricompila il codice in C # al volo. Disclaimer: questo è il mio progetto, quindi scusate la spina spudorata.
- BSnes - Un fantastico emulatore SNES con l'obiettivo di un'accuratezza perfetta per il ciclo.
- MAME - L' emulatore arcade. Ottimo riferimento
- 6502asm.com - Questo è un emulatore JavaScript 6502 con un piccolo forum interessante.
- dynarec'd 6502asm - Questo è un piccolo trucco che ho fatto in un giorno o due. Ho preso l'emulatore esistente da 6502asm.com e l'ho modificato per ricompilare dinamicamente il codice in JavaScript per aumenti di velocità massicci.
Riferimenti di ricompilazione del processore:
- La ricerca sulla ricompilazione statica condotta da Michael Steil (citata sopra) è culminata in questo documento e puoi trovare la fonte e simili qui .
Addendum:
È passato molto più di un anno da quando è stata inviata questa risposta e con tutta l'attenzione che sta ricevendo, ho pensato che fosse tempo di aggiornare alcune cose.
Forse la cosa più eccitante dell'emulazione in questo momento è libcpu , iniziata dal già citato Michael Steil. È una libreria destinata a supportare un gran numero di core della CPU, che usano LLVM per la ricompilazione (statica e dinamica!). Ha un enorme potenziale e penso che farà grandi cose per l'emulazione.
emu-docs è stato anche portato alla mia attenzione, che ospita un grande archivio di documentazione di sistema, che è molto utile ai fini dell'emulazione. Non ho trascorso molto tempo lì, ma sembra che abbiano molte grandi risorse.
Sono contento che questo post sia stato utile e spero di potermi staccare dal culo e finire il mio libro sull'argomento entro la fine dell'anno / all'inizio del prossimo anno.