

Designing a DCPU-16 emulator in Haskell: on determinism and I/O - jaspervdj
http://jaspervdj.be/posts/2012-04-12-st-io-dcpu-16.html

======
CJefferson
I suspect this is why I don't "understand" Haskell programmers (I do
understand a reasonable amount of Haskell).

To me, it looks like you haven't "done" anything at all. You haven't emulated
a single instruction. This part of the emulator in C++ would be a couple of
structs and helper functions thrown at the top of the file.

As far as I can see, you've just written a bunch of very complicated code and
monads, to get you to place would would have started at in a non-functional
language. In that case, what exactly have you gained by using Haskell at all?

P.S. Sorry if this comment comes across as insulting / argumentative. I do not
have the time to write it in a more pleasing way.

~~~
jaspervdj
> To me, it looks like you haven't "done" anything at all. You haven't
> emulated a single instruction. This part of the emulator in C++ would be a
> couple of structs and helper functions thrown at the top of the file.

It seems there's a bit of a misunderstanding. The code in the blogpost
consists of incomplete snippets, taken from the actual implementation, found
in this repo [1], e.g. the emulator [2]. The reason why I have not included
the entire codebase in the blogpost is conciseness; I only wanted to include
what's necessary to illustrate the points I wanted to make. I realise this is
perhaps a bit unclear and I'll update the post to state this more clearly.

> As far as I can see, you've just written a bunch of very complicated code
> and monads, to get you to place would would have started at in a non-
> functional language. In that case, what exactly have you gained by using
> Haskell at all?

As much as you would gain by implementing a basic skeleton in any language. In
this case, what the blogpost focusses on, is that I have written two backends
ways, the ST and the IO one.

The advantage of the ST backend is that is guaranteed to be deterministic
(which is possible in Haskell): the code cannot perform any side effects
visible to the external world.

The IO backend does not offer this guarantee, and needs to be able to
communicate with the outside world in order to grab keyboard events and
display video.

The advantage of using the monadic abstraction is that the actual emulator
implementation is the same (completely shared code) for both these backends.
This allows you to e.g., get deterministic results for tests, while still
being able to support all features in the actual emulator binary.

The deterministic property is an advantage of using Haskell. I am sure an
equivalent of the monadic abstraction could also be implemented using some OOP
system.

> P.S. Sorry if this comment comes across as insulting / argumentative. I do
> not have the time to write it in a more pleasing way.

No insult taken.

[1]: <https://github.com/jaspervdj/dcpu16-hs> [2]:
[https://github.com/jaspervdj/dcpu16-hs/blob/master/src/Emula...](https://github.com/jaspervdj/dcpu16-hs/blob/master/src/Emulator.hs)

~~~
dan00
Hi Jasper,

well, I'm also guilty of a Haskell dcpu16 emulator ;).

The memory access using Memory should be pretty fast, but what is the overhead
of the MonadEmulator type class, of their load/store functions?

I used a Vector of unboxed Word16 for the ram and the ST monad to modfiy them.

    
    
       import qualified Data.Vector.Unboxed as V
       import qualified Data.Vector.Unboxed.Mutable as VM
       
       set :: Integral a => V.Vector Word16 -> a -> Word16 -> V.Vector Word16
       set vector index value = runST (setM vector (fromIntegral index) value)
          where
             setM vec i val = do
                mVec <- V.unsafeThaw vec
                VM.write mVec i val
                V.unsafeFreeze mVec 
    
    

Have you a feeling, can you estimate the performance difference of using a
Vector like this and your Memory?

Greetings, Daniel

~~~
jaspervdj
I'm pretty sure there will be no overhead from the type class when the
appropriate SPECIALIZE and INLINE pragmas are added, and compiled with -O2. I
haven't taken the time to optimize it yet because I haven't had performance
problems so far.

Your Vector implementation should offer roughly the same performance. It might
be an unnoticeable bit slower because Vector does bounds checking, and my
Memory module doesn't (out-of-bounds access is impossible because of the ADT I
added).

One thing I'd like to comment on is that I think you should revise your use of
unsafeThaw/unsafeFreeze: you don't easily get guarantees of determinism when
using these unsafe functions.

~~~
dan00
"One thing I'd like to comment on is that I think you should revise your use
of unsafeThaw/unsafeFreeze: you don't easily get guarantees of determinism
when using these unsafe functions."

Unfortunately that seems to be the only way to get a mutable Vector from a
immutable one without copying it. As long as the mutable version of the vector
isn't used anymore after calling set, it should be safe. But yes, guarantees
are always nicer.

Nice to learn about the SPECIALIZE pragma. :)

------
Drbble
Marketing is amazing. Why is she everyone doing DCPU emulators and not
universal machines from Cult of the Bound Variable?

~~~
zserge
Writing emulators is fun. Remember all those Chip8 emulators, Brainfck
machines, P-code machines? I think DCPU is a really nice RISC CPU, and without
marketing - how many people would start writing ANY emulators or diving into
assembler?

