Introduction
Introduction Statistics Contact Development Disclaimer Help
_______ __ _______
| | |.---.-..----.| |--..-----..----. | | |.-----..--.--.--..-----.
| || _ || __|| < | -__|| _| | || -__|| | | ||__ --|
|___|___||___._||____||__|__||_____||__| |__|____||_____||________||_____|
on Gopher (inofficial)
Visit Hacker News on the Web
COMMENT PAGE FOR:
Writing a Game Boy Emulator in OCaml (2022)
derefr wrote 23 hours 0 min ago:
Would anyone here assert that there's any particular programming
language that's better for writing emulators, virtual machines,
bytecode interpreters, etc?
Where, when I say "better", I'm not so much talking about getting
results that are particularly efficient/performant; nor in making fewer
implementation errors... but more in terms of the experience of
implementing an emulator in this particular language, being more
rewarding, intuitive, and/or teaching you more about both emulators and
the language.
I ask because I know that this sort of language exists in other
domains. Erlang, for example, is particularly rewarding to implement a
"soft-realtime nine-nines-of-uptime distributed system" in. The
language, its execution semantics, its runtime, and its core libraries,
were all co-designed to address this particular problem domain. Using
Erlang "for what it's for" can thus teach you a lot about distributed
systems (due to the language/runtime/etc guiding your hand toward its
own idiomatic answers to distributed-systems problems — which usually
are "best practice" solutions in theory as well); and can lead you to a
much-deeper understanding of Erlang (exploring all its corners,
discovering all the places where the language designers considered the
problems you'd be having and set you up for success) than you'd get by
trying to use it to solve problems in some other domain.
Is there a language like that... but where the "problem domain" that
the language's designers were targeting, was "describing machines in
code"?
MangoToupe wrote 3 hours 5 min ago:
> Would anyone here assert that there's any particular programming
language that's better for writing emulators, virtual machines,
bytecode interpreters, etc?
No, absolutely not. Emulation is super easy to implement in any
language with arrays (constant-time lookup of arbitrary indices) and
bit operations. At least before considering JIT.
And even functional languages have arrays and bitwise operations.
phplovesong wrote 7 hours 15 min ago:
Ocaml is really good for this, and is usually "fast enough" for most
things. When you REALLY need speed, there is the oxcaml project by
jane street that gets you all sorts of perf improvements and close to
C speed, while still having a GC for things not critical for raw
speed.
alaaalawi wrote 16 hours 46 min ago:
Also another option for fun in the browser Elm. check out similar
older project
[1]: https://github.com/Malax/elmboy
MangoToupe wrote 3 hours 4 min ago:
I want to dissuade anyone possible from looking at Elm. I used it
professionally for about six months before I convinced my org to
switch away. It simply doesn't have the type system to support
complex programs, and the maintainers have a really noxious
relationship with the community.
UncleOxidant wrote 17 hours 54 min ago:
OCaml doesn't seem like a bad choice here. Haven't played with it
much, but I wonder if Racket might be a good choice as well?
alaaalawi wrote 21 hours 11 min ago:
one of the options for fast iterations would be Forth. in its
circles, it famous for generation targets and cross compiling between
archs.
seaech the net you shold find plenty.
grg0 wrote 21 hours 16 min ago:
Haskell excels at DSLs and the sort of data manipulation needed in
compilers. OCaml, Lisp, and really any language with support for ADTs
and such things do the trick as well. You can even try hard with
modern C++ and variant types and such, but it won't be as pretty.
Of course, if you actually want to run games on the emulator, C or
C++ is where the game is. I suppose Rust would work too, but I can't
speak much for its low-level memory manipulation.
MangoToupe wrote 2 hours 51 min ago:
I'm curious why you put ADTs ahead of basic arrays. The latter
seems a lot more relevant for writing hardware emulation.
cwslowcay wrote 6 hours 4 min ago:
I wrote a GBC emulator in Haskell ( [1] ). It's nice for modelling
the instruction set, decoding and dispatching instructions.
Optimization is tough though. To achieve playable performance,
everything has to go into the IO monad. Haskell is famous for lazy
evaluation. I found that occasionally useful, but mostly a source
of performance problems.
Ultimately, the hard thing in emulation was not decoding
instructions. It was synchronization, timing, and faithfully
reproducing all the hardware glitches (because many games will not
work without certain hardware bugs). Haskell doesn't help much for
those things. If I was doing another emulation project I'd choose
rust.
[1]: https://github.com/CLowcay/hgbc/tree/master
wk_end wrote 21 hours 3 min ago:
Haskell and OCaml are excellent for compilers, because - as you
suggest - you end up building, walking, and transforming tree data
structures where sum types are really useful. Lisp is an odd
suggestion there, as it doesn’t really have any built-in support
for this sort of thing.
At any rate, that’s not really the case when building an emulator
or bytecode interpreter. And Haskell ends up being mostly a
liability here, because most work is just going to be imperatively
modifying your virtual machine’s state.
pjmlp wrote 3 hours 53 min ago:
Lisp has one of the most powerful macro systems.
Also when people say Lisp in 2025, usually we can assume Common
Lisp, which is far beyond the Lisp 1.5 reference manual in
capabilities.
In fact, back when I was in the university, Caml Light was still
recent, Miranda was still part of programming language lectures,
the languages forbidden on compiler development assignments were
Lisp and Prolog, as they would make it supper easy assignment.
emmelaich wrote 12 hours 31 min ago:
I've heard Haskell described as the best imperative language.
kqr wrote 7 hours 38 min ago:
Indeed:
[1]: https://entropicthoughts.com/haskell-procedural-progra...
whateveracct wrote 18 hours 18 min ago:
Haskell isn't a liability for that lol
kqr wrote 19 hours 53 min ago:
> And Haskell ends up being mostly a liability here, because most
work is just going to be imperatively modifying your virtual
machine’s state.
That sounds odd to me. Haskell is great for managing state, since
it makes it possible to do so in a much more controlled manner
than non-pure languages.
grg0 wrote 19 hours 47 min ago:
Yeah, I don't understand what the "liability" here is. I never
claimed it was going to be optimal, and I already pointed out
C/C++ as the only reasonable choice if you actually want to run
games on the thing and get as much performance as possible. But
manipulating the machine state in Haskell is otherwise perfect.
Code will look like equations, everything becomes trivially
testable and REPLable, and you'd even get a free time machine
from the immutability of the data, which makes debugging easy.
wk_end wrote 18 hours 22 min ago:
If you're effectively always in a stateful monad, Haskell's
purity offers nothing. Code doesn't look like equations,
things aren't trivially testable and REPLable, you don't get
a free time machine, and there's syntactic overhead from
things like lifting or writes to deeply nested structures and
arrays, since the language doesn't have built-in syntactic
support for them.
kqr wrote 18 hours 8 min ago:
On the other hand, it does have support for things like
side-effectful traversals, folds, side effects conditional
on value existing, etc. In most other languages you have to
write lower-level code to accomplish the same thing.
materielle wrote 20 hours 6 min ago:
I’d also point out, that even in the compiler space, there are
basically no production compilers written in Haskell and OCaml.
I believe those two languages themselves self-host. So not saying
it’s impossible. And I have no clue about the technical merits.
But if you look around programming forums, there’s this ideas
that”Ocaml is one of the leading languages for compiler
writers”, which seems to be a completely made up statistic.
pjmlp wrote 3 hours 51 min ago:
We must be looking at different compilers.
phplovesong wrote 7 hours 12 min ago:
What are you on? Rust was written in ocaml, and Haxe is still
after 25 years going strong with a ocaml based compiler, and is
very much production grade.
elbear wrote 8 hours 42 min ago:
What do you define as a production compiler?
Two related languages have compilers built in Haskell:
PureScript and Elm.
Also, Haskell has parsers for all major languages. You can find
them on Hackage with the `language-` prefix: language-python,
language-rust, language,javascript, etc.
[1]: https://hackage.haskell.org/packages/browse?terms=lang...
beckford wrote 13 hours 37 min ago:
Obviously C is the ultimate compiler of compilers.
But I would call Rust, Haxe and Hack production compilers. (As
mentioned by sibling, Rust bootstraps itself since its early
days. But that doesn't diminish that OCaml was the choice
before bootstrapping.)
pjmlp wrote 3 hours 50 min ago:
Most C compilers are written in C++ nowadays.
beckford wrote 1 hour 44 min ago:
Yes, C and C++ have an odd symbiosis. I should have said
C/C++.
runevault wrote 20 hours 2 min ago:
I don't know that many production compilers are in them, but
how much of that is compilers tending towards self hosting once
they get far enough along these days? My understanding is early
Rust compilers were written in Ocaml, but they transitioned to
Rust to self-host.
wk_end wrote 21 hours 44 min ago:
Verilog?
...just kidding (maybe).
Assuming we're talking about a pure interpreter, pretty much anything
that makes it straightforward to work with bytes and/or arrays is
going to work fine. I probably wouldn't recommend Haskell, just
because most operations are going to involve imperatively mutating
the state of the machine, so pure FP won't win you much.
The basic process of interpretation is just: "read an opcode, then
dispatch on it". You'll probably have some memory address space to
maintain. And that's kind of it? Most languages can do that fine. So
your preference should be based on just about everything else: how
comfortable are you using it, how much do you like its abilities to
interface with your host platform, how much do you like type
checking, and so on.
foobiekr wrote 22 hours 22 min ago:
C is probably the best language for this.
MangoToupe wrote 2 hours 48 min ago:
The only thing C is interesting for at this point is as a target
for a compiler you can implement over a weekend.
pjmlp wrote 3 hours 43 min ago:
Only from a UNIX culture mindset.
MangoToupe wrote 2 hours 49 min ago:
As opposed to what?
pjmlp wrote 2 hours 24 min ago:
Everyone else that understands C is not the first systems
language to ever exist since FORTRAN in 1958, nor it will be
around forever outside anything tied to UNIX/POSIX.
MangoToupe wrote 1 hour 32 min ago:
C isn't even a systems language lol, it's too delicate to
rely on for systems.
pjmlp wrote 40 min ago:
UNIX authors saw it otherwise thought, as they created it
for UNIX System V rewrite.
IshKebab wrote 20 hours 30 min ago:
C isn't really the best language for anything anymore. Maybe as a
compilation target for other languages.
filleduchaos wrote 21 hours 42 min ago:
I quite frankly disagree. From personal experience I don't think
there's any mainstream programming language that in itself teaches
you anything much about emulating systems like the Game Boy or NES
- in fact, I'd go so far as to say that none of them even at least
yield elegant and accurate implementations.
People write "production-grade" emulators in C because it's fast,
not because it's uniquely suited to the domain as a language.
corysama wrote 22 hours 26 min ago:
Well, there’s always
[1]: https://pypi.org/project/rpython/
johnnyjeans wrote 22 hours 34 min ago:
sml, specifically the MLTon dialect. It's good for all the same
reasons ocaml is good, it's just a much better version of the
ML-language in my opinion.
I think the only thing that ocaml has that I miss in sml is
applicative functors, but in the end that just translates to slightly
different module styles.
makeset wrote 18 hours 30 min ago:
I remember working through Appel's compiler textbook in school, in
SML and Java editions side by side, and the SML version was of
course laughably more concise. It felt like cheating, because it
practically was.
pjmlp wrote 3 hours 44 min ago:
Nowadays you could make the Java version quite similar to the SML
one, if there would be a new edition.
I have been looking forward to ML like capabilities on mainstream
since using Caml Light.
Regarding those books, while we used the Java version, alongside
JavaCC, when time came to actually buy the book, I also got the
SML edition.
wk_end wrote 21 hours 52 min ago:
Can you expand on what makes SML better, in your eyes, than OCaml?
IMO: it's certainly "simpler" and "cleaner" (although it's been a
while but IIRC the treatment of things like equality and arithmetic
is hacky in its own way), which I think causes some people to
prefer SML over aesthetics, but TBH I feel like many of OCaml's
features missing in SML are quite useful. You mentioned applicative
functors, but there's also things like labelled arguments,
polymorphic variants, GADTs, even the much-maligned object system
that have their place. Is there anything SML really brings to the
table besides the omission of features like this?
johnnyjeans wrote 20 hours 57 min ago:
> the treatment of things like equality and arithmetic is hacky
in its own way
mlton allows you to use a keyword to get the same facility for
function overloading that is used for addition and equality. it's
disabled by default for hygienic reasons, function overloading
shouldn't be abused. [1] > labelled arguments
generally speaking if my functions are large enough for this to
matter, i'd rather be passing around refs to structures so
refactoring is easier.
> polymorphic variants
haven't really missed them.
> GADTs
afaik being able to store functors inside of modules would fix
this (and I think sml/nj supports this), but SML's type system is
more than capable of expressing virtual machines in a comfortable
way with normal ADTs. if i wanted to get that cute with the type
system, i'd probably go the whole country mile and reach for
idris.
> even the much-maligned object system that have their place
never used it.
> Is there anything SML really brings to the table besides the
omission of features like this?
mlton is whole-program optimizing (and very good at it)[1], has a
much better FFI[2][3], is much less opinionated as a language,
and the parallelism is about 30 years ahead[4]. the most
important feature to me is that sml is more comfortable to use
over ocaml. being nicer syntactically matters, and that increases
in proportion with the amount of code you have to read and write.
you dont go hiking in flip flops. as a knock-on effect, that
simplicitly in sml ends up with a language that allows for a lot
more mechanical sympathy.
all of these things combine for me, as an engineer, to what's
fundamentally a more pragmatic language. the french have peculiar
taste in programming languages, marseille prolog is also kind of
weird. ocaml feels quirky in the same way as a french car, and i
don't necessarily want that from a tool. [1] - [2] - [3] - [4]
[4] -
[1]: https://baturin.org/code/mlton-overload/
[2]: http://www.mlton.org/Performance
[3]: http://www.mlton.org/ForeignFunctionInterface
[4]: http://www.mlton.org/MLNLFFIGen
[5]: https://sss.cs.purdue.edu/projects/multiMLton/mML/Docume...
vkazanov wrote 20 hours 27 min ago:
I love, love, love StandardML.
I respect the sheer power of what mlton does. The language
itself is clean, easy to understand, reads better than anything
else out there, and is also well-formalised. I read (enjoyed!)
the tiger book before I knew anything about SML.
Sadly, this purism (not as in Haskell but as a vision) is what
probably killed it. MLTon or not, the language needed to
evolve, expand, rework the stdlib, etc.
But authors were just not interested in the boring part of
language maintenance.
johnnyjeans wrote 20 hours 8 min ago:
What are your thoughts on basis[1] and successorml[2]? [1] -
[1] [2] -
[1]: http://www.mlton.org/MLBasis
[2]: https://smlfamily.github.io/successor-ml/
vkazanov wrote 8 hours 28 min ago:
I don't think these change anything for the language. Too
little, too late.
chickenzzzzu wrote 22 hours 59 min ago:
C89
numlock86 wrote 23 hours 42 min ago:
Cool. The demo runs way too fast, though. The throttle checkbox doesn't
really change it. Unchecking it, if anything, makes it run slower. It
runs at 240 fps with throttle and at 180 fps without. With the throttle
checbox active one second are already about four seconds in the
emulator. I suspect this is related to the screen refresh rate, which
is 240Hz in my case.
chickenzzzzu wrote 23 hours 19 min ago:
probably they are calling requestAnimationFrame() and then not
accounting for deltaTime?
mtlynch wrote 1 day ago:
Excellent writeup and cool project.
Needs a (2022).
brunojppb wrote 1 day ago:
Beautiful write-up! Thanks for sharing this. I want to write a game boy
emulator in Rust and your blogpost really inspired me to kick this off.
I’m bookmarking this.
droolboy wrote 1 day ago:
I know it’s a long shot, but does anyone know of a tutorial for the
sound of a game boy emulator? Most of these tutorials never cover that
piece and when I try it on my own I find it hard to properly implement
or even understand the reference material well enough to implement on
my own.
t0mek wrote 1 day ago:
Not a tutorial per-se, but here are 2 slides describing how I've done
it: [1] Essentially, there are 4 channels, each providing a number
0-15 on every tick. Emulator should mix them together (arithmetic
average), scale up to 0-255 and feed to the sound buffer, adjusting
the tick rate (4.19MHz) to the sound output rate (e.g.: 22 kHz) -
taking every ~190 value (4.19MHz / 22 kHz) is a good start.
Now the 0..15 value that should be produced by each channel depends
on its characteristics, but it's well documented: [2] Channels 1 and
2 produce square waves, so a bunch of low (0) and high (15) values,
with optional volume envelope (gradually going down from 15 to 0 on
the "high" part of the square) and frequency sweep (alternating 0s
and 15s slower or faster).
Channel 3 allows an arbitrary waveform, read from the memory.
Channel 4 is a random noise, generated by the LSFR.
See SoundModeX.java for the reference:
[1]: https://www.slideshare.net/slideshow/emulating-game-boy-in-j...
[2]: https://gbdev.gg8.se/wiki/articles/Gameboy_sound_hardware
[3]: https://github.com/trekawek/coffee-gb/tree/master/src/main/j...
wez470 wrote 1 day ago:
[1] is pretty good
[1]: https://nightshade256.github.io/2021/03/27/gb-sound-emulatio...
jofzar wrote 1 day ago:
[1] maybe?
[1]: https://youtu.be/a52p6ji1WZs
noobcoder wrote 1 day ago:
ah nice ! great use of functors, GADTs
I wanna compare a CHIP 8 or NES emulator or port CAMLBOY to WASM using
ocaml-wasm
hydroxideOH- wrote 23 hours 23 min ago:
It should already be possible to run CAMLBOY on WASM because of the
new WASM backend of js_of_ocaml (wasm_of_ocaml).
le-mark wrote 1 day ago:
This is a very nice write up of not only Ocaml but also gameboy
emulator implementation. Great job and thank you to the author!
As an aside I’ve always thought it would be awesome to create a
single page app with an assembler editor and assembler/linker/loader to
enable doing gameboy homebrew in the browser. I think it would be a
great, accessible embedded development teaching opportunity.
binji wrote 23 hours 24 min ago:
rgbds-live is more-or-less this same idea, with an embedded version
of RGBDS:
[1]: https://gbdev.io/rgbds-live/
<- back to front page
You are viewing proxied material from codevoid.de. The copyright of proxied material belongs to its original authors. Any comments or complaints in relation to proxied material should be directed to the original authors of the content concerned. Please see the disclaimer for more details.