Here’s where things get really jargon-y. If you’ve not done a Raspberry Pi retro gaming project before, and specifically a PiTFT-based build, this might all be Greek. Prior experience is assumed.

This started with version 3.8.1 of RetroPie, a gaming-centric OS distribution for the Raspberry Pi. They’re up to version 4 now, but I specifically used this older release because it had advmame 0.94 (a specific version of a specific arcade machine emulator) already baked in…I just like how this version renders certain games and how the keyboard input is set up.

The software was loaded on a Raspberry Pi Model B+…an older board no longer made, not a Pi 2 or Pi 3. RetroPie has two separate builds, one for single-core boards, one for multi-core. The Pi Zero and the Model B+ are both single-core, so the card could be used interchangeably between the two. With only one USB port and funky mini HDMI output, the Pi Zero can be a challenge to load up…the Model B+ has an assortment of normal ports (including Ethernet networking), so getting it on the network to install additional software was no big thing.

From other PiTFT-based gaming projects, you might be familiar with three vital pieces of software:

  • A device tree overlay is the driver that makes an LCD screen appear as a “first class citizen” to the rest of the system — it’s now like having a second monitor attached, albeit a really dinky one. The Raspbian operating system (used by Retropie and many others) already includes overlays for several popular TFT displays…but not the tiny OLED we’re using here.
  • fbcp (framebuffer copy) is a utility that continually copies the contents of the main video display (e.g. HDMI) to the secondary display (e.g. LCD). This way, emulators don’t need to “know” about the LCD…they just work as they normally would on an HDMI monitor, while this other code handles the translation.
  • retrogame is a little Adafruit program that translates GPIO inputs (buttons, joystick) into virtual keyboard presses…again, so the emulator doesn’t need to know from GPIO hardware, it just goes along like there’s a keyboard attached.

This project does without the first two. Drivers in Linux are fiendishly tricky to write. Instead, a little user-space program called nanoscreen handles the task of copying the main framebuffer directly to the OLED display using SPI transfers. SPI must be enabled (via raspi-config) for this to work.

Because of the way nanoscreen works (more on that in a moment), the display must be configured for 4X the OLED resolution, or 384x256 pixels. Here’s what’s in my /boot/config.txt:

# Not all monitors can handle this resolution,
# but the framebuffer is there and correct and
# nanoscreen will work with it.
hdmi_cvt=384 256 60 1 0 0 0
# Optional, for 'portrait' video:

Not all HDMI monitors support this; you might get an “unsupported resolution” or “no signal” alert on the display. Thing to do then is set up and test at a more sensible resolution, then edit /boot/config.txt as a last step before unplugging HDMI and just using the OLED.

fbcp (which was very insightful in writing nanoscreen) uses the Dispmanx library to capture the screen and downsample this to the size of the alternate framebuffer. This scaling is handled on the GPU and is super efficient. However, it presented a problem with the degree of scaling required for this tiny OLED…

Dispmanx provides bilinear interpolation when scaling. This is normally a good thing, it smooths out the jaggies. But bilinear interpolation starts to fall apart when the reduction factor is greater than 2. Very small details can fall “between” each group of 4 pixels used in the interpolation. In some games this is just a visual annoyance, but in others it’s vital. In Pac Man for instance…some of the individual dots around the maze get lost this way, rendering the game unplayable as you can’t always tell which parts of the maze have been cleared.

nanoscreen does a software-based 1:4 scale using 4x4 pixel averaging, which preserves these smaller details…though blurry, the vital missing dots are at least visible. Vector games look better too.

This software-based scaling isn’t the most optimal route…I’m certain Dispmanx could handle it on the GPU using a 2-stage bilinear scale with an intermediate canvas of some sort…but nanoscreen was just an evening project and I couldn’t get into that depth of understanding with Dispmanx; software scaling was familiar and comparatively easy, and runs well enough for what it is.

Something else to improve legibility at this size was to increase all the font sizes in EmulationStation. I edited the default theme file /etc/emulationstation/themes/carbon/carbon.xml and roughly doubled the values accompanying any reference to “fontSize”.

This fixes the game list menu in MAME, but the exit menu and the RetroPie menu that’s used to shut down the system properly are both impossibly small and unreadable. I suspect there are settings for both, but I’ve MAMEd and RetroPie’d enough that these are just navigated from muscle memory.

Here’s the GPIO-and-key table used in retrogame:

   ioStandard[] = {
        // For Nanoscreen:
        // Input   Output (from /usr/include/linux/input.h)
        {  27,     KEY_UP    },   // Joystick (4 pins)
        {  22,     KEY_LEFT  }, 
        {  13,     KEY_DOWN  }, 
        {  26,     KEY_RIGHT }, 
        {  20,     KEY_Z     },   // A/Fire/jump/primary
        {  12,     KEY_X     },   // B/Bomb/secondary
        {   7,     KEY_1     },   // Start
        {  16,     KEY_5     },   // Select/credit
        {  -1,     -1        } }; // END OF LIST, DO NOT CHANGE

const unsigned long vulcanMask = (1L << 7) | (1L << 16);

When I’d found that I’d crossed some of the button wires, it was easier to change the pin numbers in this table than resolder those wires. Recompile as per usual.

RetroPie controls will need to be configured to match your final setup.

As in the earlier “Cupcade” project, the Start and Select buttons can be held to simulate pressing the ESC key to bring up the exit menu in MAME. Normally this menu expects an ENTER key when making a selection…I had to root around to enable using the “A” button for this instead.

In /opt/retropie/configs/mame-advmame/advmame-0.94.0.rc:

input_map[ui_select] keyboard[0,lcontrol] or keyboard[1,scan0]

In hindsight, it might’ve been easier to squeeze an extra button or two somewhere on the case.

nanoscreen and retrogame are launched automatically at startup by adding lines to /etc/rc.local, just before the final “exit 0” line:

/boot/Adafruit/Adafruit_Nanoscreen/nanoscreen &
/boot/Adafruit/Adafruit-Retrogame/retrogame &

I placed these files in /boot so they could more easily be edited or installed from other systems in a pinch. Working locally, this does appear to require editing and compiling as root though, just FYI.

The I2S audio amplifier was enabled using the Pimoroni script mentioned in the MAX98357 guide. Again this was done on a Model B+ to easily retrieve things from the network.

What is this, MAME for ants? Yes…it’s ant-y MAME.

So there you go, world’s smallest…this week, anyway. I’m throwing down the Gauntlet. And the Tempest, and Battlezone…

This guide was first published on Sep 14, 2016. It was last updated on Sep 14, 2016.

This page (Software) was last updated on Sep 14, 2016.

Text editor powered by tinymce.