[HN Gopher] Writing a Game Boy Emulator in OCaml
___________________________________________________________________
Writing a Game Boy Emulator in OCaml
Author : ibobev
Score : 205 points
Date : 2025-07-04 09:34 UTC (13 hours ago)
(HTM) web link (linoscope.github.io)
(TXT) w3m dump (linoscope.github.io)
| le-mark wrote:
| 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:
| rgbds-live is more-or-less this same idea, with an embedded
| version of RGBDS: https://gbdev.io/rgbds-live/
| noobcoder wrote:
| 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:
| It should already be possible to run CAMLBOY on WASM because of
| the new WASM backend of js_of_ocaml (wasm_of_ocaml).
| droolboy wrote:
| 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.
| jofzar wrote:
| https://youtu.be/a52p6ji1WZs maybe?
| wez470 wrote:
| https://nightshade256.github.io/2021/03/27/gb-sound-emulatio...
| is pretty good
| t0mek wrote:
| Not a tutorial per-se, but here are 2 slides describing how
| I've done it:
|
| https://www.slideshare.net/slideshow/emulating-game-boy-in-j...
|
| 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:
|
| https://gbdev.gg8.se/wiki/articles/Gameboy_sound_hardware
|
| 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:
|
| https://github.com/trekawek/coffee-gb/tree/master/src/main/j...
| brunojppb wrote:
| 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.
| mtlynch wrote:
| Excellent writeup and cool project.
|
| Needs a (2022).
| numlock86 wrote:
| 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:
| probably they are calling requestAnimationFrame() and then not
| accounting for deltaTime?
| derefr wrote:
| 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"?
| chickenzzzzu wrote:
| C89
| johnnyjeans wrote:
| 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.
| wk_end wrote:
| 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:
| > 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.
|
| https://baturin.org/code/mlton-overload/
|
| > 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] - http://www.mlton.org/Performance
|
| [2] - http://www.mlton.org/ForeignFunctionInterface
|
| [3] - http://www.mlton.org/MLNLFFIGen
|
| [4] - https://sss.cs.purdue.edu/projects/multiMLton/mML/Doc
| umentat...
| vkazanov wrote:
| 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:
| What are your thoughts on basis[1] and successorml[2]?
|
| [1] - http://www.mlton.org/MLBasis
|
| [2] - https://smlfamily.github.io/successor-ml/
| makeset wrote:
| 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.
| corysama wrote:
| Well, there's always https://pypi.org/project/rpython/
| foobiekr wrote:
| C is probably the best language for this.
| filleduchaos wrote:
| 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.
| IshKebab wrote:
| C isn't really the best language for anything anymore. Maybe
| as a compilation target for other languages.
| wk_end wrote:
| 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.
| grg0 wrote:
| 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.
| wk_end wrote:
| 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.
| materielle wrote:
| 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.
| runevault wrote:
| 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.
| kqr wrote:
| > 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:
| 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:
| 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:
| 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.
| whateveracct wrote:
| Haskell isn't a liability for that lol
| alaaalawi wrote:
| 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.
| UncleOxidant wrote:
| 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:
| Also another option for fun in the browser Elm. check out
| similar older project https://github.com/Malax/elmboy
___________________________________________________________________
(page generated 2025-07-04 23:00 UTC)