Trying to implement a gameboy emulator in a bunch of languages for my own amusement and education; also giving people an opportunity to compare the same code written in different languages, similar to Rosetta Code but with a non-trivial codebase :)
The main goals are:
- Readability of the code
- Consistency across langauges
- Idiomatic use of language features
- Basic playability
Notably, 100% accuracy is not a goal - if Tetris works perfectly then I'm happy, if other games require more obscure hardware features, then I'll weigh up whether or not the feature is worth the complexity.
Also yes, "consistent across languages" and "idiomatic" can be at odds - there are subjective compromises to be made, but for the most part that doesn't seem to be a huge problem.
So far all the implementations follow a fairly standard layout, with each module teaching me how to do a new thing. In fact they're all so similar, I wrote one copy of the documentation for all the implementations:
- main: argument parsing
- cpu: CPU emulation
- gpu: graphical processing
- apu: audio processing
- buttons: user input
- cart: binary file I/O and parsing
- clock: timing / sleeping
- consts: lists of constant values
- ram: array access where some array values are special
Pull requests to translate into new languages, or fleshing out existing languages, are very welcome :)
Feature | Python | C++ | Rust | Go |
---|---|---|---|---|
gblargh's CPU test suite | ✓ | ✓ | ✓ | ✓ |
silent / headless | ✓ | ✓ | ✓ | ✓ |
scaled output | ✓ | ✓ | ✓ | ✗ |
CPU logging | ✓ | ✓ | ✓ | ✓ |
keyboard input | ✓ | ✓ | ✓ | ✓ |
gamepad input | ✗ | ✗ | ✓ | ✗ |
turbo button | ✗ | ✓ | ✗ | ✓ |
audio | ✗ | off-key | glitchy | ✗ |
memory mapping | ✓ | ✓ | ✓ | ✓ |
scanline rendering | ✗ | ✓ | ✓ | ✗ |
bank swapping | ? | ? | ? | ✗ |
CPU interrupts | ✓ | ✓ | ✓ | ✓ |
GPU interrupts | ✗ | ✓ | ✓ | ✓ |
Warning: These implementations aren't fully in-sync, so take numbers with a large grain of salt. For example: the Python implementation uses native code to blit whole 8x8 sprites in one go, while the other languages do one pixel at a time (which is more correct, and necessary for things like parallax effects), which means that the python version is unfairly fast.
Measurements are FPS, done with --turbo --profile 3600 opus5.gb
(measuring
how much physical time it takes to emulate 60 seconds of game time), on a
Macbook Pro 2019 (2.4 GHz 8-Core Intel Core i9). If somebody knows how to
measure CPU instructions instead of clock time, that seems fairer; especially
if we can get the measurement included automatically via github actions. Pull
requests welcome :)
I also wonder if there's something happening in SDL which is capping frame rates to 60fps when a window is displayed...
Feature | Python | C++ | Rust | Go |
---|---|---|---|---|
release, silent, headless | 5 | 1000 | 1800 | ✗ |
debug, silent, headless | 5 | 220 | 80 | ✗ |
release, silent | 5 | 60 | 60 | ✗ |