1

I want to write an 8086 CPU emulator in javascript, functional style. How would one conceptualise / design an 8086 emulator, or any CPU emulator that has registers and realmode memory access in a functional way?

I can't wrap my head around how an emulator that is pure / mostly side effect free in the core parts, like memory IO and registers, would work.

I have seen this Code Golf: Emulate an Intel 8086 CPU - In it, someone gave an answer in Haskell, so it seems possible.

5
  • What makes you say Monads are non-functional?
    – KChaloux
    Commented Nov 19, 2014 at 14:45
  • 1
    Referring specifically to Haskell's Monads, I've read over many tutorials that they're used for all side-effect code (as well as other things, of course)
    – wildeyes
    Commented Nov 19, 2014 at 14:46
  • So, is the real question here “how can monads be functional if they're used for side effects?” or “how do I program a CPU emulator using FP?” (which would be far too broad a question to be answerable here). I'd also like to note that besides some dogmatic Haskellers few think that immutability and side effect free functions are absolutely necessary for functional programming – I and many FP languages such as Lisp or ML treat those as a “nice to have”, not as a necessity.
    – amon
    Commented Nov 19, 2014 at 15:11
  • The latter. I can't wrap my head around how to start writing an emulator that is pure / mostly side effect free in the core parts, like memory IO and registers.
    – wildeyes
    Commented Nov 19, 2014 at 15:14
  • @wie Purity, the way Haskell deals with it, isn't about never having side effects. It's about tracking and isolating side-effecting code from non-side-effecting code. This is accomplished with the IO Monad in Haskell, but Monads are a much more abstract and general concept that are applied to a lot of other things that are completely unrelated to side-effects (such as modelling Lists).
    – KChaloux
    Commented Nov 19, 2014 at 15:33

1 Answer 1

6

You have a couple of stateful properties. Those are the registers and memory. And we have a transition between states. This is each CPU cycle. We can therefore model this architecture in a straightforward fashion:

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

Our cycle function will treat the input state as an immutable object, and create a new state to return. This involves copying most data over to the new state. This is not a problem until you start caring about performance.

The CPU cycles until some halting state is reached. This could be expressed as a loop:

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

which could of course also be expressed as infinite recursion.

Inside our cycle function, we locate, decode, and perform the current instruction. All instructions will at least mutate the instruction pointer. In JavaScript (which is not a pure functional language), the approach would be:

  1. Make a copy of the old registers
  2. Execute the current instruction, which possible mutates the current registers
  3. Treat the registers as immutable and add them to the next state
  4. Return the next state.

Unlike registers, memory (= an array of memory cells, e.g. bytes) will not necessarily be mutated during each instruction. If memory was not touched, we don't have to perform a copy and can simply reference the old memory in the next state. If we do change any cell, we have to create an updated copy for use in the new state. The impact of this can be reduced by dividing memory into pages, and only replacing pages that were actually touched. Effectively, this approach turns the array into an n-ary tree of arrays of fixed depth.

I would model each instruction as a function that calculates a delta from the current state. This way, the code for applying the delta to the current state and returning a new state in an effective manner does not have to be repeated throughout the code. A delta could be a small object such as { registers: { eax: 0x42}, memory: {0x1234: 0x00} }.

3
  • This would work well, and has an additional benefit - on modern (2 GHz+) CPUs, it'd come quite close to emulating the performance of a (5 to 10 MHz) 8086. ;-)
    – Brendan
    Commented Nov 19, 2014 at 20:50
  • Actually, since ECMAScript now has Proper Tail Calls (yay!), it would be quite easy to get rid of that ugly imperative while loop. Commented Nov 20, 2014 at 3:26
  • Seems like my answer is too broad for programmers (though I thought programmers was the place for quite-broad questions!), could you point towards some reading material to stimulate my thinking about the subject?
    – wildeyes
    Commented Nov 23, 2014 at 14:22

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.