8 ms·
Writing a Game Boy Emulator in OCaml
- fareesh 5y agoOCaml I am but a fool
- anthk 5y agoFrom Ocaml I still use MLDonkey daily. I hope the multicore Ocaml brings a speed up for it. I am not saying MLDonkey is slow or unstable, but it could gain a performance boost on current machines.
- sandebert 5y agoLooks nice, well done. I'd also like to add that it seems like a missed opportunity here. It could have been named OBoy.
- ackfoobar 5y agoIt doesn't seem OCaml's object system is used there. That name would not be fitting. Sorry to ruin your joke. :P
- aliswe 5y agoA developer at our place did our log search tool in OCaml, the reason being "because I've never worked with it before". It had to be scrapped shortly after he left.
- fsloth 5y agoNaur's classic essay (and maybe my favorite essay in all of software engineering) "Programming as theory building" [0 ] comes to mind. If the theory of the program is lost then it's quite hard to continue work no matter what the language used. Based on your description it might as well have been scrapped regardless of the programming language used? [0] https://pages.cs.wisc.edu/~remzi/Naur.pdf https://pages.cs.wisc.edu/~remzi/Naur.pdf
- aliswe 5y agoNo, the problem is that hardly noone had even heard of OCaml, much less had any experience with any functional language, much less knew how to maintain this tool. My point is that it was very tone deaf to introduce a new technology onto the stack for this reason (so called resume-based development I think?).
- Kototama 5y agoI doubt it, ocaml has very little job offers. Didn't you have a process or better communication to avoid this situation?
- aliswe 5y agoYou doubt what exactly? Nah we hardly had any processes at all back then.
- Kototama 5y agoI doubt it's for the resume because there are very few ocaml jobs.
- hiptobecubic 5y agoYou don't need to find a job that wants ocaml, just a job that recognizes what it means that you've written it.
- padator 5y agoNot sure you really need GADTs for the instruction set. A separate arg8 and arg16 ADT types would be enough, so you would have | Add8 of arg8 * arg8
- linoscope 5y agoHi, author here. Yeah, I thought the same and tried that too, but it failed due to the same reason mentioned in the "Problem with the definition using variants" section. Namely, the return value type of `read_arg` would not be uniquely determinable. We CAN define separate `read_arg8` for `read_arg16` for the `arg8` and `arg16` respectively, but I thought the GADT solution was more clean.
- d3nj4l 5y agoGreat job! Really well written, easily one of the better technical posts I've seen on HN in a while. I loved messing around with the (homebrew?) ROMs on the online emulator, too!
- rixed 5y agoLooks very polished, but major disappointment that it's not showcasing OCaml as part of RosettaBoy (https://github.com/shish/rosettaboy https://github.com/shish/rosettaboy)
- Zababa 5y agoIt seems like the RosettaBoy project/repo is not well known at all. I've search information about gameboy emulators before and never saw it mentioned, the project has only one contributor that's active, a few language, not much stars or watchers. I'm surprised that you expected the author to know that project.
- rixed 5y agoTrue, yet it has been submitted in HN not so long ago (https://news.ycombinator.com/item?id=28800673 https://news.ycombinator.com/item?id=28800673) but didn't reach anywhere near the front page.
- Zababa 5y agoThat's true, though the author's HN account wasn't created at this time (but he may have been reading it still), and since the post wasn't that popular it's fair to expect that the author didn't read it. You could contact him about it I think.
- laputan_machine 5y agoReally nice write up! It would have been nice to see more on how you handled the display, on my emulator journey the two big "i have no idea how to solve this" problems I had were 1) synchronising cpu/gpu/sound (and I settled on the exact method you use, 'catching up'), and 2) writing the framebuffer to the screen. The resources I've used always seem to skip over it, so I assume it's obvious, but it isn't to me.
- linoscope 5y agoThanks! I ended up omitting the GPU since it didn't use much interesting OCaml features. But yes, the GPU was the most time consuming part for me too (both to understand and to implement), so I can relate to the "i have no idea how to solve this" feeling.
- tombert 5y agoAn emulator is still on my "bucket list as an engineer." I feel like it's a hurdle I still haven't crossed and I'm reasonably certain I could make one. I have some free time this weekend...I might take this repo and port it over to clojure to get the hang of things.
- laputan_machine 5y agoThe first emulator I wrote was for a virtual machine called the CHIP-8. I used CowGod's reference, it was a fun experience! http://devernay.free.fr/hacks/chip8/C8TECH10.HTM#2.1 http://devernay.free.fr/hacks/chip8/C8TECH10.HTM#2.1
- tombert 5y agoI forgot about CHIP-8. That might be a good starting point. Thanks for reminding me.
- anthk 5y agoTry JimTCL, it uses SDL2 for "GUI"'s instead of TK, but it's as compatible (at code level, not TCL extensions, Jimsh uses its own ones) as TCL 8.5 so most stuff will work as is. If you look at the examples, you'll find that writing a Chip8 emulator with JimTCL it's a breeze. https://github.com/msteveb/jimtcl https://github.com/msteveb/jimtcl
- deleted 5y ago[deleted]
- Gunax 5y agoI wrote (or tried writing) a gameboy emulator once. My only qarning is that there are a lot of edge cases in the specification--and the proper specs are wrong. I ended up spending most of the time searching through undocumented/missing CPU specifications. It became rather tedious. For a learning project, this made it poorly suited. There are so many good emulators out there, the only reason to write one is for the learning experience. I just felt that I stopped oearning after a while and was just debugging obscure combinations of cpu states.
- ngcc_hk 5y agoHow it compares with the 12 years old javascript (my most beloved) simulator with detail explanation I wonder : https://imrannazar.com/GameBoy-Emulation-in-JavaScript:-The-CPU https://imrannazar.com/GameBoy-Emulation-in-JavaScript:-The-...
- Zababa 5y agoGreat article, thanks for writing that. A small question for the author: in the Addressable_intf.S signature, read_byte has regular arguments and write_byte named arguments. Is there a reason for that?
- linoscope 5y agoThanks! I added labels based on the number of arguments. `write_byte` has two arguments (other than `t`), the `addr` and `data`, so I used labeled arguments to distinguish the two easily. On the other hand, `read_byte` only has one argument, so I didn't label the argument. I saw this approach used in some Jane Street libraries: for example, `Hashtbl.set` takes two arguments and is labeled, while `Hashtbl.find` only has one argument and is not labeled. But I was sometimes confused and wrongly provided labels to `read_byte`, so looking back at it now, maybe I should have just labeled both functions for consistency.
- Zababa 5y agoThanks for the explanation, that makes a lot of sense.
- shadowofneptune 5y agoOCaml has recently interested me for being one of the few languages that: - Compiles to a binary - Achieves good speed - Is functional, while accommodating imperative styles. - Has a static type In particular, it interests me because of how Forth or a threaded code-based VM could be implemented in it. You could implement the operations of such a machine with a bunch of individual function calls, but that's slow and rather unnecessary. A jump to the next VM instruction's code is what you want instead. Switch statements or pointers to labels are what are often used, but that means you end up with hundreds or even thousands of lines of code which cannot be split into modules. The solution OCaml and other functional languages provide involves tail-call optimization: let rec first x = print_endline "foo" ; second(x) and second x = print_endline "bar" ; first(x);; first 1;; In a usual compiled language, these two functions would only be able to print 'foobar' a few hundred or thousand times before causing a stack overflow. In a language like OCaml this can run forever, and quickly too. This allows for what this web page calls continuation-passing threaded code: https://www.complang.tuwien.ac.at/forth/threaded-code.html https://www.complang.tuwien.ac.at/forth/threaded-code.html I have mocked up the concept in lua, an imperative language with tail call optimization, and it was quite easy to implement a basic Forth while keeping the code legible. To take the idea further OCaml feels like a good choice. If anyone has experience with OCaml, or any warnings, it would be helpful.