The Intel 8051 has a special spot in microcontroller history — not because it was the first (that honor goes to the TMS1802NC calculator-on-a-chip from Texas Instruments), but because the 8051 was the first microcontroller that saw historic, widespread use, and became a de-facto architecture that is still widely used today.

In fact, unlike the weirdo architectures used in early MCUs, a 1980s-era 8051 is plenty modern to be programmed by current tools. In this post, we’re going to use completely modern tools (an Eclipse-based IDE running in Windows 10, a recent compiler, a USB programmer) to program a vintage-1980 microcontroller. All we’ll do is make it blink an LED inside a timer interrupt, but there’s enough going on that you should be able to develop some intuition around what has and hasn’t changed in the last 40 years.

First, a bit of a disclaimer: we’ll actually be programming the EPROM version of the 8051, called the 8751. Why? An actual 8051 has no on-board programmable flash memory; we would have to wire in a separate EPROM chip, or order mask-programmed versions from Intel. Unfortunately, the 800-number I found in the datasheet for Intel’s microcontroller division seems to have been disconnected.

Erasing with Light

Note that the 8751 is an EPROM — not an EEPROM — device. This means that we program it electrically, but we erase it using UV light. There’s a small, sapphire window that exposes the die to light. If you recover these devices in the field, they’ll typically have a piece of tape over the window.

These ceramic / sapphire packages were very expensive, so you’ll only see these chips in boutique applications, or for engineering development and testing.

I found a UV light box specifically designed for erasing EPROM on Amazon. Dial it in for “2” (does that mean “20 minutes”?) — either way, it seems to work fine.

Development Environment

I’ll be using Simplicity Studio from Silicon Labs as an IDE. It’s a modern, Eclipse-based tool that runs well in Windows, Linux, and macOS. Simplicity Studio is used to develop firmware for Silicon Labs’ modern EFM8 and EFM32 MCUs, along with their wireless portfolio.

Since the EFM8 is an 8051-compatible MCU, Simplicity Studio will work perfectly. Simplicity Studio uses Keil C51, the industry-standard 8051 compiler. C51 is a bit archaic in terms of language support — Keil chose to implement C90, and has never gone back to update the compiler to the C99 or C11 standards. If you’re coming from a more modern compiler, such as gcc, you might be in for a bit of a shock the first time you try to declare a variable in a for() loop — and don’t forget that Keil C51 has a ton of reserved words not part of the C spec; if you use them for variable names, you’ll get strange and unhelpful error messages. No biggie — it’s not like anyone has ever declared a variable named “data” or anything like that.

My Keil grievances will have to wait for another day. We’ve got an LED to blink!

Developing the Code

We’ll fire up Simplicity Studio, create a new MCU project, and choose the EFM8BB10F8G. We’ll use this part because it’s the closest thing to a “stock” 8051 that Silicon Labs makes. Because this is a newer part, Silicon Labs offers a Simplicity Configurator file for it, which we’ll go with. This will help save a lot of time during development, since we can let the computer do the thinking for us. I selected an SOIC-16 package, but it’s largely irrelevant; we’ll be blinking an LED hooked up to P1.0, so I couldn’t really care less about which physical pinout Simplicity Studio thinks we’ll be using, as every package should have this pin bonded-out. Be a bit careful with some pins, though, since Simplicity Studio will quietly generate code that disables pins not present on the physical package (to save power).

Simplicity Studio is going to give us some grief for not disabling the watchdog timer, or enabling the crossbar. Little does it know that we’re just leaching off of it to build us a hex file for a 40-year-old MCU we bought off eBay.

Let’s configure Timer 0 to be in Mode 1 (16-bit counter), and start it:

Now let’s head over to the Interrupts peripheral, enable Global Interrupts, Timer 0 Interrupt, and make sure “Generate Interrupt Functions” is enabled:

Once you’re done save the hwconf file by hitting Ctrl-S. This will automatically generate all the initialization code, as well as the interrupt stubs.

Now we actually have to write a bit of code in our Interrupts.c file:

[cpp]

SI_INTERRUPT (TIMER0_ISR, TIMER0_IRQn)

{

P1_B0 = !P1_B0;

TCON_TF0 = false;

}

[/cpp]

A lot of the 8051’s SFRs are bit-addressable, which makes this a breeze. No, there’s no compiler trickery going on — that code is going to get compiled into CPL (complement bit) and CLR (clear bit) instructions.

You may be wondering how we’re using a GPIO pin without making it an output first. Unlike other architectures, the 8051 defaults to a quasi-bidirectional I/O scheme; if we write a 0, the pin will sink as much current as it can; if we write a 1, the pin will float up to VDD, via internal pull-up resistors. And if you want to make a pin an input, write a “1” to it to pull it up, and then read from it as usual. It’s an elegant, ingenious default that I’ve grown to love over the years.

We built the program. It’s 49 bytes long (this includes all the C startup code, timer init code, etc). Gotta love CISC architectures!

Programming the 8051

I bought one of these TL866A MiniPro universal programmers on Amazon. It seems to do the business, and supports a wide range of devices. The official software is Windows-only, but there’s an open-source *nix-friendly command-line utility. I selected the Intel 87C51, and did a Device > Read to ensure the MCU was erased:

Looks good. Let’s program it:

Good to go!

Hooking It Up

Alright, now it’s time to put the MCU on a breadboard and see if it works. Here’s a pinout of the chip:

Few funny things to note:

Reset (RST) is active high , not active low as it is on modern MCUs. Use a pull-down resistor to ground on the pin.

, not active low as it is on modern MCUs. Use a pull-down resistor to ground on the pin. No internal power-on-reset circuitry; you’ll need to use a big capacitor — like a 10 uF — hooked between the reset pin and 5V. This will briefly assert the reset signal when the chip is powered on.

External access enable (EA) must be tied to 5V (i.e., de-asserted) for the MCU to execute code from internal EPROM memory; otherwise, it will attempt to execute code from the external memory bus.

There’s obviously no sort of internal oscillator on this chip; build a crystal oscillator or ceramic resonator circuit, and attach it to XTAL1 and XTAL2. I used a 6 MHz crystal; much faster, and your blinking LED will give you a seizure.

Recall that the LED is attached to P1.0 (pin 1) — make sure to wire it in an open-drain (sinking) configuration; i.e., the anode should be wired to 5V, and the cathode should be wired to the MCU pin (through a resistor).

Here’s a video of the results of my breadboarding:

Wrap-Up / Why does this work?

It may seem perplexing that I can take a hex file meant for a modern, 3.3V MCU from 2017 and dump it on a 40-year-old MCU made by a different company — all without altering a single bit or doing any sort of trickery. Did I leave out some steps? Not at all; that’s just the nature of the 8051. We can get away with this because an “8051-compatible” MCU is simply an extension of the original architecture; not a reworking of it. In other words, the EFM8 has the same Timer0 as the original 8051 does; its Timer0 is at the same base address, and has the same configuration registers to configure it to do the same things. The EFM8 has the same GPIO port structure, with the same GPIO registers at the same GPIO addresses.

So if you program the EFM8, are you working with an obsolete dinosaur? Not at all! The EFM8 extends the original 8051 by adding a ton more timers and PWM capabilities, plus more than a dozen channels of 12-bit ADC conversion. Some EFM8s have 12-bit DACs, USB functionality, and can run at up to 72 MHz. Silicon Labs has completely pipelined and optimized the original 8051 core, too, so an EFM8 runs dozens of times faster than an 8051 running off the same clock speed. Oh, and it’s really, really, really low power.

The 8051 is probably the only true cross-manufacturer MCU architecture. Anyone who’s done ARM programming is grimacing right now. You probably thought that ARM Cortex-M programming would be great to learn, since so many manufacturers build ARM Cortex-M microcontrollers. What everyone forgot to tell you is that ARM Cortex-M is just a core; the moment you want to actually do something with that microcontroller, you’ve got to access GPIO, UART, SPI, I2C, Timers, and all sorts of other peripherals — and all of these (alright, almost all of these) are manufacturer-specific. So every time you switch to a different ARM microcontroller, you get to learn an entirely new set of peripherals. This is simply not true with the 8051.

It’s what makes it a great architecture to learn — in 1980, or in 2017.