Programmazione dello stile funzionale dell'emulatore della CPU [chiuso]

1

Voglio scrivere un emulatore di CPU 8086 in javascript, stile funzionale. Come si può concettualizzare / progettare un emulatore 8086 o qualsiasi emulatore di CPU che abbia registri e accesso alla memoria realmode in modo funzionale?

Non riesco a capire come un emulatore che è puro / principalmente privo di effetti collaterali nelle parti principali, come la memoria IO e i registri, funzionerebbe.

Ho visto questo codice Golf: Emulazione di una CPU Intel 8086 - In esso, qualcuno ha dato una risposta in Haskell, quindi sembra possibile.

    
posta Cat Boss 19.11.2014 - 15:39
fonte

1 risposta

6

Hai un paio di proprietà stateful. Questi sono i registri e la memoria. E abbiamo una transizione tra stati. Questo è ogni ciclo della CPU. Possiamo quindi modellare questa architettura in modo semplice:

function cycle(state: State) -> State { ... }

La nostra funzione cycle tratterà l'input state come un oggetto immutabile e creerà un nuovo stato da restituire. Ciò implica copiare la maggior parte dei dati nel nuovo stato. Questo non è un problema finché non inizi a preoccuparti delle prestazioni.

La CPU esegue un ciclo fino al raggiungimento di uno stato di arresto. Questo potrebbe essere espresso come un loop:

while(! state.isHaltingState()) {
    state = cycle(state);
}

che ovviamente potrebbe anche essere espresso come ricorsione infinita.

All'interno della nostra funzione cycle , localizziamo, decodifichiamo ed eseguiamo l'istruzione corrente. Tutte le istruzioni modificheranno almeno il puntatore dell'istruzione. In JavaScript (che non è un puro linguaggio funzionale), l'approccio sarebbe:

  1. Crea una copia dei vecchi registri
  2. Esegui l'istruzione corrente, che può mutare i registri correnti
  3. Tratta i registri come immutabili e aggiungili allo stato successivo
  4. Restituisce lo stato successivo.

Diversamente dai registri, la memoria (= una matrice di celle di memoria, ad esempio byte) non verrà necessariamente modificata durante ciascuna istruzione. Se la memoria non è stata toccata, non dobbiamo eseguire una copia e possiamo semplicemente fare riferimento alla memoria precedente nello stato successivo. Se cambiamo una cella, dobbiamo creare una copia aggiornata da utilizzare nel nuovo stato. L'impatto di questo può essere ridotto dividendo la memoria in pagine e sostituendo solo le pagine che sono state effettivamente toccate. In effetti, questo approccio trasforma la matrice in un albero n-ary di matrici di profondità fissa.

Modellerei ogni istruzione come una funzione che calcola un delta dallo stato corrente. In questo modo, il codice per applicare il delta allo stato corrente e restituire un nuovo stato in modo efficace non deve essere ripetuto in tutto il codice. Un delta potrebbe essere un piccolo oggetto come { registers: { eax: 0x42}, memory: {0x1234: 0x00} } .

    
risposta data 19.11.2014 - 16:57
fonte

Leggi altre domande sui tag