Jul 3, 2022
Looking for the code? — Skip straight to the GitHub repo.
Cat in front of monitor

Our modern, technological world is full of distractions. For this reason, when I really want to engage with a book but still take notes, I like to use a very old computer. It still needs to save to my NAS, have good word processing and printing functions, and provide font support and an IME for writing in Japanese but nothing else. It should be very fast at these tasks, but modern gaming and internet connectivity should be as impractical as possible. I find the sweet spot for this set of requirements to be a generously-clocked 486 running Windows 3.11, Microsoft Office 4.3, and Pacific Software Publishing‘s KanjiWORD.

None of us can remain fully focused, though, and building a period piece like this brings with it a whole world of fascinating distractions to explore while setting it up. My favorite may be the Turbo button.

Turbo power

PCs of this era were, believe it or not, blazing fast. So absurdly fast it was a problem.

If you had amassed a software library through the “IBM PC Compatible” ’80s, a lot of it was likely targeted at running on an Intel 8088 or 8086 processor or one of their numerous clones at 4.77MHz or 8MHz. This became an issue when compatible, but much quicker 286, 386, and 486 processors became the norm. You could still run your old programs, but if any human i/o was timed using clock cycles, it would now run with uncontrollable speed. In games, your character might zip across the screen faster than you could let off the arrow key; in word processors, your cursor might shoot to the end of the document when you were just trying to go down a few lines. It was too much of a good thing.

They weren’t kidding.

To combat this, you could temporarily engage Turbo mode by shorting a jumper on the motherboard, usually attached to a physical switch on the front of the computer. This would under-clock your fancy, light speed processor down to the old, slower rate so that your programs could run how they used to.

Some manufacturers would wire this up the other way around so that Turbo meant you were going faster, but Turbo was often, counter-intuitively, a speed limiter. LGR has a great video about this.

Stupid case mods, retro edition

My first experience case modding was chopping a side window with a hack saw and shoving cold cathodes from FrozenCPU.com into a beige 98 machine while chugging Bawls, but this degeneracy has an even longer and more storied legacy.

If you understood what a Turbo button did, then you already knew what speeds the clock was toggling between: Native and 808x. But what if you wanted it to look cool when you smashed that Turbo button and engaged the Turbo brakes? What if all you really wanted out of life were lights and bleep-bloops? Then you needed a clock speed display.

Clint from LGR’s famous wood grain PC at 66MHz

It’s worth noting that clock speed displays never had any idea what the actual clock speed was–they didn’t measure anything. The numbers were set with jumpers on the back of the display and they would just toggle back and forth with the state of the Turbo button. They were purely eye candy.

I love eye candy.

Give me the shiny

Until recently, my 486 has been in a newer ATX case waiting for a forever home. When I finally found the right case, I couldn’t tell from the listing photos if it had a clock speed display or not. It seemed to have a spot for one, but the front panel wasn’t hooked up and it was all behind a smoked lens, so I couldn’t tell for sure. Unfortunately it didn’t have one–just a spot where one could go. Hats off to Jefferson College, who bought this unit in March 1994 from Skywalker AV Supply, for not wasting their money on nonsense… but now it’s time to fix that.

eBay listing photo of 486 case
The empty spot got my hopes up.

You may have guessed that finding an obscure mod like this from 30 years ago is practically impossible, but this also presents opportunities. We now have three decades of technological progress with which to make our mods even stupider.

With that in mind, what if we could display not just a number, but a cool logo? Maybe even a tiny screen saver?

The hardware

Arduino with Molex power connector
Use red and black for 5V from a molex connector.

I had a 0.96″ SSD1306 panel and an Arduino Mega sitting around and didn’t feel like waiting on other parts, so that’s what I used to build this mod. It’s almost a perfect size to fit where a 2-digit clock speed display would have gone.

The wiring is dead simple:

  • 5V and GND from computer power supply to power the Arduino, which you can just steal from any molex cable. Hack up a spare case fan or something. (pinout)
  • Four jumper wires from Arduino to SSD1306 display (5V, GND, SDA, and SCK–check your specific Arduino for which pins are SDA and SCK)
  • One pull-down resistor between Arduino GND and a digital input of your choosing
  • One jumper wire from said digital input to the positive lead of the LED above the case’s Turbo button (measure with a voltmeter if it’s not obvious which lead is which)
Arduino with Turbo Button wiring attached
Yellow goes to Turbo LED positive. Note pull-down resistor to GND.

Connecting in this way allows you to view the current Turbo status without even bothering to look at the motherboard’s jumper polarity or trying to do some tiny, neat solder connection on your original jumper wires. If Turbo is engaged, it’s going to send voltage to your Turbo LED, and this big, easily-reversible soldering target can set your digital input HIGH. If Turbo’s disengaged, the pull-down resistor will set the input LOW. No need to get too fancy; these weren’t technically fancy to begin with. Just do your soldering with some haste so you don’t damage the LED or anything, and make sure your Arduino is either insulated somehow or mounted up off the case.

Photo of wiring installed in case
Neat enough I suppose!

In retrospect, a custom four-ended jumper cable with one female end, two male ends, and one male end with an inline pull-down resistor, paired with one straight-through female to male jumper would accomplish this same thing with no soldering on original components whatsoever so long as the pins are the same size as what’s on your motherboard. Maybe next time.

The software

To drive the display you can use Adafruit’s SSD1306 library and GFX library. All the hard work is already done with this display so there’s no point reinventing the wheel. You’ll need two bitmaps representing what you want to show when the Turbo is engaged or disengaged, and you can make them in GIMP, Paint, or whatever. Jasper van Loenen’s image2cpp will happily convert your monochrome 128×64 bitmap into an array that the Arduino can read and display with the drawBitmap function in the Adafruit GFX library. For the SSD1306, use the “Horizontal, 1 bit per pixel” draw mode.

Arduino with OLED showing a Cyrix logo

For a little extra fun I mashed Sinoia’s oled-starfield into my program so that ten seconds after pressing the Turbo button, the screen switches to the classic Starfield Simulation screensaver until you press it again. OLED burn-in isn’t really a significant concern if you don’t use the machine that much (and it’s not like these tiny OLEDs are expensive anyway), but mine idles a lot and I would like to get at least another few decades out of it with as little maintenance as possible.

Animated gif of Starfield Simulation screen saver

Here’s the code I’m using, which you can also view on GitHub. I recommend adapting this to your tastes, or just stealing relevant bits and pieces. I’ve also included a separate display centering sketch in the GitHub repo that you can load while you’re actually attaching the display into the case to ensure it’s centered and level.

/**************************************************************************
OLED Turbo Button
Turbo button display replacement for IBM PC Compatibles
Using Adafruit SSD1306 B/W OLED panel at 128x64px
Fits into your case where a Turbo display normally would

Shows an icon for your processor (or 8088 if Turbo activated) on startup,
and whenever you toggle the Turbo button. Changes to a star field
screensaver after 10 seconds.

Requires a jumper with a pulldown resistor from the positive
lead on the case's Turbo LED to the digital input of your
choice (default 2) on the Arduino.

Create a logo for your processor using image2cpp
128x64 in Paint, monochrome bitmap format
Draw mode Horizontal, 1 bit per pixel
http://javl.github.io/image2cpp/
 **************************************************************************/

#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels

// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
// The pins for I2C are defined by the Wire-library. 
// On an arduino UNO:       A4(SDA), A5(SCL)
// On an arduino MEGA 2560: 20(SDA), 21(SCL)
// On an arduino LEONARDO:   2(SDA),  3(SCL), ...
#define OLED_RESET     4 // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);


////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////// SETTINGS
////////////////////////////////////////////////////////////////

// Solder a male jumper wire with a pulldown resistor to ground from the positive lead of the Turbo LED.
// When Turbo light is on, this Arduino pin will be HIGH. When it is off, the pin will be pulled LOW.
int turboPin = 2;

// How long to show the icon when the Turbo button is toggled (approximation in seconds)
int showIconFor = 10;


////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////// PROCESSOR ICONS
////////////////////////////////////////////////////////////////

// TURBO OFF ICON - Swap this array with the icon to show when the turbo button is disabled (default i8088 icon)
const unsigned char turbo_off [] PROGMEM = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x3f, 0x01, 0xf8, 0x03, 0xe0, 0x1f, 0x80, 0x7e, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x3f, 0x03, 0xfe, 0x07, 0xf0, 0x3f, 0xe0, 0xff, 0x80, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x3f, 0x03, 0xff, 0x0f, 0xf8, 0x3f, 0xf0, 0xff, 0xc0, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x3f, 0x07, 0x8f, 0x0e, 0x38, 0x78, 0xf1, 0xe3, 0xc0, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x1e, 0x07, 0x07, 0x1c, 0x18, 0x70, 0x71, 0xc1, 0xc0, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x07, 0x1c, 0x1c, 0x70, 0x71, 0xc1, 0xc0, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x3f, 0x07, 0x07, 0x1c, 0x1c, 0x70, 0x71, 0xc1, 0xc0, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x3f, 0x07, 0x07, 0x1c, 0x1c, 0x70, 0x71, 0xc1, 0xc0, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x3f, 0x07, 0x8f, 0x1c, 0x1c, 0x78, 0xf1, 0xe3, 0xc0, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x3f, 0x03, 0xfe, 0x1c, 0x1c, 0x3f, 0xe0, 0xff, 0x80, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x3f, 0x01, 0xfc, 0x1c, 0x1c, 0x1f, 0xc0, 0x7f, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x3f, 0x03, 0xfe, 0x1c, 0x1c, 0x3f, 0xe0, 0xff, 0x80, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x3f, 0x07, 0x8f, 0x1c, 0x1c, 0x78, 0xf1, 0xe3, 0xc0, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x3f, 0x07, 0x07, 0x1c, 0x1c, 0x70, 0x71, 0xc1, 0xc0, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x3f, 0x07, 0x07, 0x1c, 0x1c, 0x70, 0x71, 0xc1, 0xc0, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x3f, 0x07, 0x07, 0x1c, 0x1c, 0x70, 0x71, 0xc1, 0xc0, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x3f, 0x07, 0x07, 0x1c, 0x1c, 0x70, 0x71, 0xc1, 0xc0, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x3f, 0x07, 0x07, 0x1c, 0x1c, 0x70, 0x71, 0xc1, 0xc0, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x3f, 0x07, 0x07, 0x0c, 0x1c, 0x70, 0x71, 0xc1, 0xc0, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x3f, 0x07, 0x8f, 0x0e, 0x38, 0x78, 0xf1, 0xe3, 0xc0, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x3f, 0x03, 0xfe, 0x0f, 0xf8, 0x3f, 0xe0, 0xff, 0x80, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x3f, 0x03, 0xfe, 0x07, 0xf0, 0x3f, 0xe0, 0xff, 0x80, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0xf8, 0x03, 0xe0, 0x0f, 0x80, 0x3e, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0xff, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x3f, 0xff, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x03, 0x9f, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x1f, 0xff, 0xff, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x03, 0xe3, 0xff, 0xff, 0xff, 0xfc, 0x0f, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x03, 0xfc, 0x7f, 0xff, 0xfe, 0x03, 0xff, 0xff, 0xff, 0xb6, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x03, 0xff, 0x9f, 0xff, 0x01, 0xff, 0xff, 0xff, 0xed, 0xb6, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x03, 0xff, 0xff, 0xc0, 0xff, 0xff, 0xff, 0xfb, 0x6d, 0x92, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x01, 0xff, 0xfc, 0x3f, 0xff, 0xff, 0xfe, 0xdb, 0x64, 0x92, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xb6, 0xc9, 0x24, 0x92, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x9f, 0xff, 0xff, 0xff, 0x6d, 0xb6, 0x49, 0x24, 0x92, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x97, 0xff, 0xff, 0xdb, 0x6c, 0x92, 0x49, 0x24, 0x80, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x91, 0xff, 0xf6, 0xdb, 0x64, 0x92, 0x49, 0x20, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x90, 0x7d, 0xb6, 0xc9, 0x24, 0x92, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x80, 0x0d, 0xb2, 0x49, 0x24, 0x92, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x92, 0x49, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x92, 0x49, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x92, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
// TURBO ON ICON - Swap this array with the icon to show when the turbo button is enabled (default Cyrix 486 icon)
const unsigned char turbo_on [] PROGMEM = {
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0xc0, 0x60, 0x00, 0x03, 0x80, 0x01, 0xff, 0x00, 0x00, 0x03, 0xf0, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0xe0, 0xe0, 0x00, 0x07, 0x80, 0x07, 0x83, 0xc0, 0x00, 0x0f, 0x80, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0xf1, 0xe0, 0x00, 0x07, 0x80, 0x0e, 0x01, 0xe0, 0x00, 0x1e, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0xfb, 0xe0, 0x00, 0x0f, 0x80, 0x0c, 0x00, 0xe0, 0x00, 0x3c, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0xff, 0xe0, 0x00, 0x1f, 0x80, 0x1c, 0x00, 0xf0, 0x00, 0x78, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x7f, 0xc0, 0x00, 0x1f, 0x80, 0x1c, 0x00, 0x70, 0x00, 0xf0, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x3f, 0x80, 0x00, 0x3f, 0x80, 0x1c, 0x00, 0x70, 0x01, 0xe0, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x7f, 0xc0, 0x00, 0x3f, 0x80, 0x1c, 0x00, 0x70, 0x03, 0xc0, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0xff, 0xe0, 0x00, 0x77, 0x80, 0x1c, 0x00, 0x70, 0x07, 0x80, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0xfb, 0xe0, 0x00, 0xe7, 0x80, 0x1c, 0x00, 0xe0, 0x07, 0x80, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0xf1, 0xe0, 0x00, 0xe7, 0x80, 0x1e, 0x00, 0xe0, 0x0f, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0xe0, 0xe0, 0x01, 0xc7, 0x80, 0x1f, 0x01, 0xc0, 0x0f, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0xc0, 0x60, 0x03, 0x87, 0x80, 0x0f, 0x83, 0xc0, 0x1f, 0x1f, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x87, 0x80, 0x07, 0xc7, 0x80, 0x1e, 0x7f, 0xe0, 0x00, 0x00, 
	0x00, 0x00, 0x0c, 0x00, 0x00, 0x07, 0x07, 0x80, 0x07, 0xfe, 0x00, 0x1f, 0xf7, 0xf0, 0x00, 0x00, 
	0x00, 0x00, 0x1e, 0x7f, 0xe0, 0x07, 0x07, 0x80, 0x03, 0xfc, 0x00, 0x1f, 0x81, 0xf8, 0x00, 0x00, 
	0x00, 0x00, 0x3f, 0x3f, 0xe0, 0x0e, 0x07, 0x80, 0x01, 0xfe, 0x00, 0x3e, 0x00, 0x78, 0x00, 0x00, 
	0x00, 0x00, 0x3f, 0x3f, 0xe0, 0x1c, 0x07, 0x80, 0x00, 0xff, 0x00, 0x3e, 0x00, 0x7c, 0x00, 0x00, 
	0x00, 0x00, 0x1e, 0x7f, 0xe0, 0x1c, 0x07, 0x80, 0x01, 0xff, 0x80, 0x3c, 0x00, 0x3c, 0x00, 0x00, 
	0x00, 0x00, 0x0c, 0x00, 0x00, 0x38, 0x07, 0x80, 0x03, 0x8f, 0xc0, 0x3c, 0x00, 0x3c, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x07, 0x80, 0x07, 0x07, 0xe0, 0x3c, 0x00, 0x3e, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0xf0, 0x00, 0x70, 0x07, 0x80, 0x0e, 0x03, 0xf0, 0x3c, 0x00, 0x1e, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0xf0, 0x00, 0xe0, 0x07, 0x80, 0x1c, 0x00, 0xf8, 0x3c, 0x00, 0x1e, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x78, 0x00, 0xe0, 0x07, 0x80, 0x3c, 0x00, 0xf8, 0x3c, 0x00, 0x1e, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0xff, 0xe0, 0xff, 0xff, 0xf8, 0x3c, 0x00, 0x7c, 0x1c, 0x00, 0x1e, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0xff, 0xe0, 0xff, 0xff, 0xf8, 0x3c, 0x00, 0x7c, 0x1c, 0x00, 0x1e, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0xff, 0xe0, 0x00, 0x07, 0x80, 0x78, 0x00, 0x3c, 0x1e, 0x00, 0x1e, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0xff, 0xe0, 0x00, 0x07, 0x80, 0x78, 0x00, 0x3c, 0x1e, 0x00, 0x1c, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x80, 0x3c, 0x00, 0x3c, 0x0e, 0x00, 0x1c, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x07, 0x80, 0x3c, 0x00, 0x3c, 0x0f, 0x00, 0x3c, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x07, 0x80, 0x3c, 0x00, 0x38, 0x0f, 0x00, 0x38, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x07, 0x80, 0x1e, 0x00, 0x70, 0x07, 0x80, 0x78, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x07, 0x80, 0x0f, 0x00, 0xe0, 0x03, 0xc0, 0x70, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0xff, 0xc0, 0x00, 0x07, 0x80, 0x07, 0x81, 0xc0, 0x01, 0xf1, 0xe0, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x3f, 0xf0, 0x00, 0x07, 0x80, 0x03, 0xff, 0x80, 0x00, 0xff, 0x80, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x0f, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x0f, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x7f, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0xff, 0x1e, 0x00, 0x07, 0xff, 0x80, 0x00, 0x00, 0x00, 0x78, 0x3c, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0xfc, 0x0e, 0x00, 0x04, 0x00, 0x7f, 0x1f, 0xc0, 0x01, 0xcc, 0x66, 0x00, 0x00, 
	0x00, 0x00, 0x18, 0xf0, 0x02, 0x00, 0x04, 0x00, 0x3f, 0x3f, 0x80, 0x01, 0x8c, 0xc6, 0x00, 0x00, 
	0x00, 0x00, 0x3c, 0xc3, 0xc0, 0x00, 0x01, 0xf8, 0x3f, 0xbf, 0x00, 0x01, 0x99, 0x86, 0x00, 0x00, 
	0x00, 0x00, 0x7c, 0x03, 0xe0, 0x00, 0x1f, 0xfe, 0xff, 0xfe, 0xf8, 0x01, 0xb1, 0x8e, 0x00, 0x00, 
	0x00, 0x00, 0x78, 0x01, 0xe0, 0x00, 0x11, 0xff, 0x1f, 0xfd, 0xfe, 0x01, 0xe1, 0x8c, 0x00, 0x00, 
	0x00, 0x00, 0x78, 0x01, 0xe0, 0x00, 0x01, 0xff, 0x8f, 0xf9, 0xfe, 0x01, 0xe3, 0x0c, 0x00, 0x00, 
	0x00, 0x00, 0x70, 0x01, 0xe0, 0x01, 0xfd, 0xe7, 0xbf, 0xfb, 0xce, 0x03, 0x73, 0x0c, 0x00, 0x00, 
	0x00, 0x00, 0x70, 0x01, 0xe0, 0x01, 0x01, 0xe3, 0x87, 0xf0, 0x0e, 0x06, 0x33, 0x18, 0x00, 0x00, 
	0x00, 0x00, 0x78, 0x01, 0xe0, 0x01, 0x01, 0xe3, 0x87, 0xf0, 0x1e, 0x0c, 0x33, 0x30, 0x00, 0x00, 
	0x00, 0x00, 0x78, 0x01, 0xe0, 0x00, 0x3f, 0xe3, 0x8f, 0xf0, 0x3c, 0x0c, 0x63, 0x30, 0x00, 0x00, 
	0x00, 0x00, 0x3c, 0x03, 0xc0, 0x00, 0x21, 0xe7, 0x9f, 0xf8, 0x78, 0x07, 0xc3, 0xe0, 0x00, 0x00, 
	0x00, 0x00, 0x3e, 0x07, 0xc0, 0x00, 0x21, 0xff, 0xbf, 0xfc, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x1f, 0xff, 0x80, 0x00, 0x01, 0xff, 0x3f, 0xfd, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x0f, 0xff, 0x00, 0x00, 0x01, 0xfe, 0x7f, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x07, 0xfe, 0x00, 0x0f, 0xff, 0xf8, 0xfe, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x01, 0xf8, 0x00, 0x08, 0x00, 0x01, 0xfc, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xf8, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

// Array of all bitmaps for convenience. (Total bytes used to store images in PROGMEM = 2080)
// If you swap bitmaps above, be sure to swap their names to your new ones in this array
const int epd_bitmap_allArray_LEN = 2;
const unsigned char* epd_bitmap_allArray[2] = {
	turbo_off,
	turbo_on
};


////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////// STAR FIELD SCREENSAVER
//////////////////////////////////////////////////////////////// adapted from https://github.com/sinoia/oled-starfield/
////////////////////////////////////////////////////////////////

const int starCount = 512; // number of stars in the star field
const int maxDepth = 32;   // maximum distance away for a star

// the star field - starCount stars represented as x, y and z co-ordinates
double stars[starCount][3];

int getRandom(int lower, int upper) {
    /* Generate and return a  random number between lower and upper bound */
    return lower + static_cast<int>(rand() % (upper - lower + 1));
}

void drawStars() {
    int origin_x = 128 / 2;
    int origin_y = 64 / 2;

    // Iterate through the stars reducing the z co-ordinate in order to move the
    // star closer.
    for (int i = 0; i < starCount; ++i) {
       stars[i][2] -= 0.76;
       // if the star has moved past the screen (z < 0) reposition it far away
       // with random x and y positions.
       if (stars[i][2] <= 0) {
           stars[i][0] = getRandom(-25, 25);
           stars[i][1] = getRandom(-25, 25);
           stars[i][2] = maxDepth;
       }

       // Convert the 3D coordinates to 2D using perspective projection.
       double k = 128 / stars[i][2];
       int x = static_cast<int>(stars[i][0] * k + origin_x);
       int y = static_cast<int>(stars[i][1] * k + origin_y);

       //  Draw the star (if it is visible in the screen).
       // Distant stars are smaller than closer stars.
       if ((0 <= x and x < 128) 
           and (0 <= y and y < 64)) {
           int size = (1 - stars[i][2] / maxDepth) * 4;
           display.fillRect(x, y, size, size, 1);
       }
    }
}


////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////// MAIN
////////////////////////////////////////////////////////////////

// Begin with an initially impossible Turbo status. This will ensure the correct icon appears when the machine is started as the mode will be forced to switch.
int turboLastStatus = 99;
// Status 0 = Turbo Off, Status 1 = Turbo On
int turboStatus = 0;
// Accumulator of wait cycles to still allow polling for button changes while holding the processor icon on the screen (in cases you toggle in rapid succession)
int waitCycles = 0;

void setup() {
  Serial.begin(9600);

  // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
  if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
    Serial.println(F("SSD1306 allocation failed"));
    for(;;); // Don't proceed, loop forever
  }

  // initialize the turbo pin
  pinMode(turboPin, INPUT);

  // initialize random stars
  for (int i = 0; i < starCount; i++) {
    stars[i][0] = getRandom(-25, 25);
    stars[i][1] = getRandom(-25, 25);
    stars[i][2] = getRandom(0, maxDepth);
  }

  // Clear the buffer
  display.clearDisplay();
}

void loop() {
  turboStatus = digitalRead(turboPin);

  // if the button status has changed, show the icon for the new status
  if (turboStatus != turboLastStatus) {
    if (turboStatus == 0) {
      turboOff();
    } else if (turboStatus == 1) {
      turboOn();
    }
  }
  turboLastStatus = turboStatus;

  // run up the wait cycles, or swap to screensaver if enough have passed
  if (waitCycles < 1000) {
    delay(showIconFor);
    waitCycles = waitCycles + 1;
  } else {
    display.clearDisplay();
    drawStars();
    display.display();
  }
}

void turboOff() {
  display.clearDisplay();
  display.drawBitmap(0, 0, turbo_off, 128, 64, 1);
  display.display();
  waitCycles = 0;
}

void turboOn() {
  display.clearDisplay();
  display.drawBitmap(0, 0, turbo_on, 128, 64, 1);
  display.display();
  waitCycles = 0;
}

Turbo button bliss

With it all installed, now I can show off that there’s a cool Cyrix Cx486 DX2 80MHz chip in there even if it’s buried in the case where you’ll never see it. In this computer, Turbo is wired backwards so that having it engaged runs at 80MHz and disengaged drops to 4.77MHz. Disengaging the Turbo swaps to a little “i8088” icon depicting the classic chip.

Installed display showing an i8088 icon

The cool icons and animations you could use this for are endless. I was very seriously considering having the screensaver be the little Chrome pixel dinosaur jumping over cacti. Or maybe the Netscape Navigator loading animation.

Finished computer on desk

Naturally I had to go with wood grain on the case as an homage to the LGR wood grain PC, which initially got me into this. I highly recommend watching the original build and upgrade series.

Fork me on GitHub