Writing a Game Boy Emulator in OCaml
linoscope.github.io
linoscope.github.io
I have some free time this weekend...I might take this repo and port it over to clojure to get the hang of things.
If you look at the examples, you'll find that writing a Chip8 emulator with JimTCL it's a breeze.
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.
Still, the reason I want to do it online is specifically because I want an excuse to really understand low-level hardware. I (and probably a majority of people on HN who do software) really only have "real" experience with "Java or Higher" with maybe hitting C if I have the rare occasion of having to worry about saving a few milliseconds. An emulator would force me understand low level hardware, or at least I think it would.
Also, read the book Code by Charles Petzold.
Yeah everyone will comment after me that x86 gets way more complex over time but if you just want to get a quick intro and you aren't a perfectionist x86 is kind of compelling.
And recently I wanted to try emulating MSP341 at someone's recommendation because it has a very simple architecture. But just getting a C compiler for it isn't easy! When I've tried to cross-compile C to other archs like RISC-V it also wasn't straightforward and I just got stuck in this toolchain spot.
If it were easier to cross-compile simple languages like C then writing emulators for other environments would be easier.
If you're on a new Mac, my whole argument applies for aarch64 instead of x86.
https://notes.eatonphil.com/emulating-amd64-starting-with-el...
I don't think 808x is such an unpopular machine for approaching emulation.
Some people use Space invaders (or contemporary titles), which is based on an 8080, as stepping stone between CHIP-8 and more modern (complex) platforms.
But I'm not arguing everyone must do it. Just trying to share why one unintuitive approach (the "complex" x86 architecture) might actually be easier and cooler than you think.
https://multigesture.net/articles/how-to-write-an-emulator-c...
https://wiki.xxiivv.com/site/uxn.html
There is also the LC3 virtual virtual machine, with a fairly great guide on creating it. https://justinmeiners.github.io/lc3-vm/Anyhow, I found the CPU part a lot of fun, and there's public domain test roms with expected registers after each instruction that you can use to test and confirm. Interaction with other hardware and emulation of that hardware gets a lot more challenging. The gameboy is certainly different than the NES, but I suspect there's a lot of similarity at the high level. It's certainly reasonable to stop (or pause, perhaps), when it stops being so fun, too.
I'm also not sure porting an emulator is going to give you the same understanding as writing one, but maybe.
Getting the graphics and sound in sync with the CPU is a lot more challenging if you're looking to support many games, but if you just want to play some of the super simple early NES games, like Donkey Kong, then you can get away with a lot of inaccuracies.
I wrote a Gameboy Color emulator[1] earlier this year, but stopped as soon as I realized that I wasn't learning anything new. As a result, my emulator has a few issues and does not support sound, but it is able to run quite a few games. I also got it running in a browser via WASM, which was fun.
I wrote my first emulator a while ago, for the Gigatron. People recommend CHIP-8 a lot but I just thought it looked too trivial and uninteresting. The Gigatron is still incredibly simple, but it's also a really elegant little beast worth studying for its own sake.
I'd also like to add that it seems like a missed opportunity here. It could have been named OBoy.
- 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
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.
Inb4 piracy, thanks to CBR's I've got more physical mangas and comic books than ever.
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.
I am but a fool
Based on your description it might as well have been scrapped regardless of the programming language used?
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?).
Sounds like the real issue is you wanted to write your own log tool.
It is like reading the first Turkish works of an English poet, who decided to learn Turkish by writing poems.
Yeah, that is the wrong reason to pick a new language to build an internal tool.
A "python dev" left the company. A different python dev was assigned to maintain his projects. They were surprised to find out all of ex-employee's "python" projects over the past 2 years were actually written in Rust. There was absolutely no code review in his department so no one was aware of this! No one else at the company knew Rust and they were debating rewriting it all, or hiring a Rust dev.
My old coworkers were basically putting all the blame on that one guy. And sure, that's shitty and unprofessional of him. But how could someone write Rust for 2 years and no one else notices?? I didn't understand how they could blame the guy and also not feel supremely embarrassed for even getting into that situation.