It runs even slower than in MRI and is far from polished, but works. To watch it, just click the button below and wait until the black lines get replaced by Pitfall Harry slooooowly running to the left (sorry, no key bindings for now).
Keep reading if you want the gory technical details!
Ruby2600 is fairly portable across Ruby interpreters (JRuby, for example, doubles its speed) and graphic libraries. A typical port consists on a script that:
- Calls the
Bus#framemethod and renders the resulting array of pixels into an image (translating Atari 2600 NTSC colors into native ones); and
- Triggers actions accordingly (e.g., sets
trueto move the Player 0’s joystick to the right).
HTML5 Canvas is the ideal place to render those frames, not only for its ability of addressing individual bits, but also for allowing horizontal stretch, which compensates Atari 2600’s rectangular pixels. So I wrote a minimal HTML, some rendering code and…
Generating a frame is CPU-intensive, and browsers don’t like functions that run for too long. The fix: expose a
Bus#scanline method and render scanline-by-scanline chaining calls to
setTimeout(..., 0), giving the browser some breathing space.
On my first run, the emulated game crashed straight after a few hundred CPU instructions. It would be a nightmare to debug, if ruby2600 did not have extensive automated tests (which I ended up migrating to RSpec 3 to get rid of deprecated syntax that did not please opal-rspec).
The specs revealed Opal missed bit indexing and had issues with peculiar cases of array expansion and binary shift. Those operations are seldom found in typical Ruby code, but are essential to ruby2600.
Open-source to the rescue: a few patches to Opal fixed these problems (one of the tests even made its way into Rubyspec), leaving me with one last problem: integer division.
3 / 2 (=1) apart from
3.0 / 2 (=1.5). Had to wrap such divisions in
( ).to_i - a bit of noise, but a small price for the awesomeness of cross-compilation.
Another minor change:
Cartnow can read the binary data of a cartdrige from an array. Ideally, I’d make it use an uploaded ROM file (since I don’t intend to distribute those), but I’m not sure if that is feasible and just wanted to move on.
Firefox renders at least 50% faster than Chrome - at least it did when I watched both running at the same time. I know, I know, this is as scientific as Brazilian Bozo’s Horse Race (and not nearly as fun), but you can check by yourself.
(Chrome also crashed before the inevitable fall into the crocodile lake, while Firefox kept going. Your mileage may vary.)