uxngb icon indicating copy to clipboard operation
uxngb copied to clipboard

uxngb

A highly experimental port of the uxn virtual machine to the Game Boy and Game Boy Color gaming handheld. I knew this was a fairly ridiculous project from the start, but wanted to see how it might perform and was pleased to see compudanza's pong tutorial run at slow-motion playable speeds.

Some effort has gone into optimizing for performance. The MUL and DIV instructions are obviously very slow due to the lack of hardware support for those operations. They have been unrolled for speed, but additional optimizations are likely possible. In addition, as the Game Boy lacks a bitmap graphics mode, the background layer is used to mimic one for "background" pixel/sprite writes (which is slow), and the hardware objects (sprites) are used for foreground sprites (which leads to quite a few limitations).

You can download a binary build here. Binaries with a variety of UXN ROMs appended are also available there.

Screenshots

uxnemu_catclock uxnemu_cube3d uxnemu_hello-pong-1 uxnemu_mandelbrot-1 uxnemu_mandelbrot uxnemu_screen

Note: Changes to sprite tile/OAM cycling in 0.1.2 will yield slightly different results than the Screen.tal results shown here.

Performance

The mandelbrot ROM currently takes ~1h24m to render fully on an original Game Boy, and ~42min on a Game Boy Color (using double-speed mode).

Running your own ROMs

The emulator is contained in the base uxnemu.gbc ROM, and the UXN ROM to be executed must be appended to it. To create a properly formed GB/GBC ROM, it will then need to be padded and have the header fixed, which can be performed using rgbfix from RGBDS. This is an example of combining the emulator with a ROM and fixing it from a Linux command prompt:

cat uxnemu.gbc dvd.rom > uxnemu_dvd.gbc
rgbfix -O -v -p 0xFF -t dvd uxnemu_dvd.gbc

Implemented

  • All 253 uxn CPU instructions
  • Controller device
  • Screen device:
    • Background pixel drawing
    • Background sprite drawing
    • Auto byte for all supported drawing operations
    • Foreground sprites are limited to 16 unique tile/blend combinations, and will begin to overwrite old tiles once this is exceeded (sprites are flipped in hardware, so flip variations don't count toward this limit)
    • Foreground sprites are limited by the 10 sprites/line limit of the hardware (no attempt is made to overcome this via flickering)
  • Very basic Datetime device (fixed startup date/time, HH:MM:SS will advance)
  • Limited console output (only when built in CLI mode to run CPU instruction test ROM)

Unsupported

  • UXN ROMs larger than ~8 KiB, or UXN memory beyond $2000
    • I originally intended to support the full 64 KiB memory space, using 8 banks of swapped external cartridge RAM, but as the performance I was going to actually end up with revealed itself that dropped in priority
  • Screen resizing (fixed at 160x144 pixels)
  • Setting the System RGB bytes has no effect on Game Boy
  • Foreground pixel drawing operations aren't currently supported
  • Certain blending combinations may not render correctly, and the opaque lookup is not applied
  • Stack over/underflow and divide-by-zero are not detected
  • No keyboard/mouse/audio/midi/file device support
  • No support for relocatable stacks (seen in uxn11)

Tools Used for Development

  • RGBDS (https://rgbds.gbdev.io/)
  • Emulicious (https://emulicious.net/)
  • uxn32 (https://github.com/randrew/uxn32)
  • Visual Studio Code (https://code.visualstudio.com/)