Pinball 7-segment LED reverse engineering

Found in a box in the basement

This is a quick overview of an LED 7-segment driver board found in a box of arcade PCBs in Frank‘s basement.  We’ve been working on some of the low hanging fruit of his collection lately.  Osborne executive monitor repair (broken solder joint), although I still think we should burn new roms that have his name in them since that’s a service offered back in the day.  Commodore 1902 monitor repair (broken solder joint).  Verified the working status of a metamorphic force board I got out of a freecycle nintendo red tent (someone wanted to buy it from me).  Built a video cable for his commodore 64 and diagnosed it with a failing PLA.  There’s plenty more planned, but that’s the highlights for now.  On one expedition into his many boxes of random PCBs we came across this board.

back side

To me this looks like it could pretty easily be reverse engineered to drive with simple GPIO from, say, an esp8266.  The layout is as follows:

8-bit data bus for all the segments

4-bit address bus for the digits, last two select which board (player 1 or player 2)

7442N to decode the address bus for each clock input of the 74LS273

74LS273 for each digit to hold the display

ULN2003A to drive the displays (at 12v)

2n2222 to drive the decimal point on top (only 7 drivers in the darlington)

Resistor network for the LED segments

Interface board

There are also a few interesting notes I have on this board design:

The 2n2222 drives the decimal point through a 470R resistor, not using the resistor pack, but the resistor pack has a spare resistor because it has 8 and the darlington only has 7.  The values are the same so the board designer wasted two resistors on those decimal points as well as the labor to install them.

The bottom decimal points are not controllable, but could be by bodging a copy of the top circuit on the bottom.

Most strangely of all, the 7442N outputs are inverted from what the clock on the 74LS273 wants.  Here’s what I mean: the 7442N is a BCD to decimal decoder, meaning that on the 4 bit input you can count up from 0 to 9 and for each combination a different output pin will be enables, but note I didn’t say ‘will be high’.  The output of this chip is to have all the outputs high except the one being addressed, possibly for use as a low side driver for nixie tubes or something, but either way it means that when you switch to a digit you trigger a negative going pulse.  The 74LS273 latches in on a positive going pulse to the clock input, meaning for this relationship of chips you have to pick the segments you want to display and put it on the data bus while the location is selected on the address bus, then it is latched in when you switch away from that address.  This either means that you have to know which location you want to display next before writing the current one or, as I do, switch to an unused address (in retrospect this should be something out of range of the chip, like F, but I just picked one not used by my display).  I do this because processor cycles aren’t super important to my application and the ability to arbitrarily write to a certain location is.

If I were so inclined I might chop the traces between the clock and decoder chip and put in an inverter, but this project is an exercise of interfacing to the circuit without modifying it.  That being said I did modify it a bit by adding a connector for power that I had the mate to.

Interface board back side

On to the Howie did it section of the post.  The obvious solution to me was an esp8266, but they have nowhere near enough pins.  We’ve been using the MCP23017 as the go-to solution to this problem, especially as you can stack 8 on the same i2c bus without any collisions.  This also solves our level shifting problem going from 3.3v to 5v (although not really a problem because 3.3v is withing the ‘high’ range of just about all 5v chips).  I power the whole thing with a 12v barrel jack which runs unregulated into the 12v input on the power header (for the LED common lines only) and also run it into a 5v regulator board.  Now, these are rated at 2A.  I don’t know who rated them but I bet they’ll also tell you they caught a fish thiiiiiis big.  My design sags the voltage pretty bad under load, but not enough to cut out so whatever.  I chose that particular converter because it was thin and would fit between the PCB and the… other PCB (by this point I had avoided any components on the back and I wanted to keep that up).  For the esp8266 I used the d1mini despite having plenty of real estate and a bunch of boards I want to get rid of because I forgot I still had those kicking around.

Pin layout vs pin numbering in arduino library

Unusually for me I wired this up without testing anything (aside from testing if the i2c expander showed up on the bus before wiring the data and address lines).  This was a bit of a mistake as I guessed wrong about the direction the port goes on the expander and now my address bus is backward.  I also wired up jumpers for the address in case I wanted to change it… for some reason.  my main concern was getting the soldering wrapped up all at once so it would just be software after that.

Backwards bits for addressing

I spent a fair bit of time with the multimeter determining how the code mapped to the hardware and very little time actually coding the logic to drive the display.  The address bus being backward was confusing at first, but not more than the logic on the decimal decoder being inverted.  Throwing 0xFF on the data bus an latching it into each possible location made it easy to find where on the bus each display was located.  After that it wasn’t hard to count up on the data bus and continually latch it into one display to watch the segments count up like a binary counter.  That made decoding the segment locations easy.

Somewhat logical bit layout

I really like how the data bit layout ended up, kinda the opposite of the address bit layout.  You can see that the lower nibble controls the lower half of the digit and the upper nibble controls the mirrored upper half of the digit.   This means that it’s fairly easy to read the font, knowing what the bottom and top half each look like.

Now I’ll give a small explanation of my code (linked below).  The first section is the defines at the top:

//this is the lower 4 bits backward, to make more sense it would be wired the other way
uint8_t displays[] = {0x00, 0x08, 0x04, 0x0C};

//unused display output used as a state to send the latch to when not used
uint8_t neutral = 0x01;

//hand coded 7-segment font
uint8_t digits[] = {0xEE, 0x88, 0xD6, 0xDC, 0xB8, 0x7C, 0x7E, 0xC8, 0xFE, 0xFC};

//buffer that writeBuffer pulls from
uint8_t buffer[] = {1, 2, 3, 4};

//buffer that writeBufferRaw pulls from
uint8_t bufferRaw[] = {0xFA, 0xFE, 0x66, 0xEE};

The first section is the addresses of the displays, so I can refer to them as displays[2] instead of 0x08 for example.  The next one is just an unused display, for more correct code (and stuff compatible with a second board chained) it should be something like 0x0F.  I’ll also not here that it just needs to be in the lower 4 bits, the upper 4 aren’t used and could be inputs if you like.  The next section is the font table, like the first one it’s so I can say digits[3] instead of 0xDC and display a ‘3’.  If you wanted an alphanumeric font you could either do an ascii table with null or unique but wrong values for the unprintable stuff and bytes representing the font for all printable characters, or you could do key-value pairs and look them up while having a default value for anything not in that table.  The first one is easy, the second one probably takes less memory.  The last two sections are just buffers that you can update, then trigger functions to update all the displays at once.

uint16_t writeAddr(uint8_t high) {
uint16_t a = mcp.readGPIOAB();
a = high << 8 | (a & 0x00FF);
return a;
uint16_t writeDigit(uint8_t low) {
uint16_t a = mcp.readGPIOAB();
a = low | (a & 0xFF00);
return a;

Both of these are unashamedly stolen from some IOT code on the i3detroit github but I renamed them so you can tell what they actually control in this code.  All they do is take a byte and write it to half the expander, one for high and one for low.

void latchIn(uint8_t digit) {


Like I explained before, you latch in the data bus when you switch away from the address you currently have selected, this function just jumps from wherever it is (it’s at the neutral address most of the time) to the target address, waits a tiny bit, then goes and parks itself back at the neutral address.  This means if the neutral address were actually being displayed somewhere then it would always hold the last value written in because I set the data bus before this so both jumps latch in the data.

void writeBuffer()
for (int i = 0; i < 4; ++i) {

I broke this out into a function, but inside you can see how to write the digit.  This function iterates through the buffer, correlating it with the font table and writing those bits to the data bus.

void writeBufferRaw()
for (int i = 0; i < 4; ++i) {

Here is the last section of code but it’s important.  I have included a 0-9 font table, and right now that’s the only thing I intend to use this display for, but years from now I’d like to be able to do something else with it without having to rewrite the code, so I’m leaving raw access to the bits in (you can push animations or letters to the display via mqtt if you want).  This just writes the buffer of raw bits the same way the above one writes the buffer of numbers.  Raw also happens to be the only way to drive the decimal points (although I could have made 0-9 digits and 10-19 digits with decimal in the font table).

This is a pretty good example of how I tend to go about reverse engineering things so I’ll present it here.  First I name and number the connectors and chips if they aren’t already done.

Board layout has been numbered

Then I trace all the pins on a given connector, then the next, then the next.  Then I do the same for the chips, tracing how they interconnect until I can look at the whole picture and not have any further questions about how the board is wired up.

Address chip pinned out

Sometimes I deviate a bit from this for ease of readability (like in the case of the player select on this board or the bare transistors) but labeling things and having a convention to stick to while going back and forth is key.  I even wrote on the board to remind me which pin I declared pin 1 on the headers or where the clock input was on each latch.

Input pin header

That’s really all I needed, the latches and darlingtons are transparent, logically.  I have the whole album here as well as the code here.

printing on the board:



Ledia Grayhound Electronics Inc. © 1988

Now if anyone has one google should bring them here

Is it from a dart game?


Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: