Update - Nov 2023

See video below and project log for more details

Motivation

A few weeks ago, I found an Intel SDK-85 Single Board Computer at a ham-fest.  I am a sucker for anything with a keypad and 7 segment displays.  Having had a Heathkit ET-1 disappear in front of me, I made sure to act on this one as quickly as possible.  I didn't even know what it was, but I knew I needed it.

It's an Intel 8085 hardware/software development kit from the late 1970s.  It's aimed at the hobbyist/education market.  It sports a fine manual, generous 36 GPIO ports and a meager 256 BYTES of RAM.  It can be upgraded to 72 ports, 512 bytes of RAM, 2K EPROM, plus a full bus
expansion.

Being such a simple and vintage board, I wanted to recreate the experience of building something in a 1970s basement.  I looked for a problem space that was well matched to the SDK-85 and its obvious emphasis on hardware integration.  The answer became obvious - recreate the legendary Simon electronic game from 1978.

This is where alternative history took over.  I imagine myself as a lowly hobbyist inventor in my basement.  I could afford an SDK-85, but no teletype, and certainly not a "real" computer.  I would do this with the books, graph paper, pencils, and my trusty HP calculator.

Research and Design

I started by studying the Simon itself.  I didn't have one so I watched every YouTube video I could find.  I noted the color pattern and matched the tones to notes on my piano - E, A, C#, and E.  The
buzzer is the lowest D.  I also noted the game play patterns, such as displaying the correct color and tone for a correct guess, but also showing the correct color and the buzz for an incorrect guess, which also ends the game.

I made some concessions and have a simplified version of the game.  I omitted the GAME and DIFFICULTY switches, and also the START button.  Instead the game is started by simply pressing any of the 4 colors. I also don't stop the game after 10 moves, but allow the player to keep going for up to 256 moves.  I don't believe these choices detract from the game play and arguably make it more intuitive.

Lastly, when reading player input, the light and tone are displayed for a half second rather than however long the player held the button down.  This allowed a single tone routine and table to be used.  It also eliminated the need for any complex debounce routine as the player is given sufficient time to release the button while the tone is playing.

Next came hardware decisions.  The SDK-85 has a large wire-wrap prototyping area that makes up the left half of the board.  The buttons, lights, and speaker could all be installed here.  However, I was hesitant to "hack up" a vintage piece of hardware with non-reversible modifications.  I was also worried about pressure from the buttons flexing the board.

So, I decided on a detached daughter board that would house the 4 push button switches, lights, and speaker.  The daughter board connects to the SDK-85 using a ribbon cable into an IDC header I installed on the port 0/1 connector.  The switches I had on-hand were panel-mount type, so I drilled through the board to mount them - another reason to keep these on a daughter board.

The original Simon used incandescent lamps and a traditional cone speaker.  I decided to use colored LEDs and a piezo buzzer.  These parts are more readily available, are more compact, and use less power.  I am straying away from period correctness here, as blue LEDs weren't invented until the 90s.  However, this has no impact on the gameplay nor on the software development methodology, which is my real focus here.

Building It

The board was wire wrapped on a hot Saturday afternoon and does not represent my best work.  I completed the board in its entirety before getting to work on the software.  First order of business
was writing three board checkout routines to test the LEDs, switches, and speaker respectively.  Surprisingly, everything worked the first time.

A word on tone generation.  Typically this is done using a timer chip to generate a square wave of a set frequency.  The 8155 contains a timer, but it is used by the keypad/display and is not available for user use on the SDK-85.  I considered using the timer on the second 8155 I planned on adding.  However, it is a 14-bit timer, so additional circuitry would need to be added to divide the 3MHz clock frequency down to audible tones.

So my only option was to "bit bang" a GPIO pin.  This isn't a big disruption, since the game has no other useful work to do while tones are playing.  My algorithm uses two nested loops.  The inner loop is a delay loop that pauses for one half the period of the wave.  Once this delay finishes, the GPIO pin is toggled and the outer loop decremented.  Simply put, the inner loop sets the frequency and the outer loop sets the duration of the note.  To perform these design calculations, I enlisted the help of another 1970s icon - the HP calculator.

Design Calculations

The color tones are musical notes of the A major triad.  For each note, I looked up the corresponding frequency on a table.  From there, I used my HP calculator to compute the number of iterations for the inner loop.  I examined the loop and non-loop portions of the TONE routine to come up with a fixed cost of 98 T-states and a variable cost of 24 T-states per iteration of the loop.

Duration calculations were easy.  Since each color tone plays for half a second and each iteration of the inner loop is a half-cycle, we can simply use the frequency as the count (440Hz = 440 iterations).  The buzzer uses double duration, so it uses double its frequency.  See the file hpcalc.txt for my HP-15 design programs (LBL A in this case)

There are other places in the code that use fixed delays (such as between notes).  I computed these using a simpler method - divide the duration (in seconds) by STO 1 and then STO 3.  The fixed cost is
insignificant at these delay lengths.  This is available at LBL B.

There is one more step needed for hand assembly - convert these decimal values to hex.  While the HP-15C does not directly support hexadecimal, it was straightforward to write another program (LBL C) to do these conversions.  The only manual step is to convert each digit from the displayed 0-15 to the correct hex digit 0-9 or A-F.  Press R/S to move onto the next digit.  

These HP calculator programs were almost as rewarding as the game code!  But, such a calculator is certainly not a requirement.  The frequency calculations can be done on any scientific calculator.  The Intel 8080/8085 Assembly Language Programming Manual has tables in the back that make quick work of hex conversions.

Bottoms Up!

As far as coding style, I attempted to break the problem down into a number of reasonably sized subroutines.  These are:

I developed and tested each subroutine fully before moving on to the next.  I started with INIT to get the stack and hardware setup correctly.  Then I started with the "bottom layer" routines (NEXT, SHOW, RDKEY, TONE).  Then came PLAY and REC, and lastly GAME, which is the overall game flow.

The SDK85 firmware made it relatively easy to test each routine as I could pre-load the registers with input values and examine them after the fact.  Most routines could also be single-stepped and examined in flight.  It is necessary to write a small stub to do subroutine calls as the GO command is a JMP, not a CALL.  These should end with an RST 1 to return control to the debugger.

I was quite surprised how large this program wound up being, and it clearly would not fit in the 192 bytes left to me by the SDK85 firmware.  Thankfully, Intel had an option to easily add another 256
bytes by adding a single 8155 chip, which I did after procuring it from an internet source.  This memory is not contiguous, so I made an intentional decision to move the utility routines (TONE, SHOW, etc) into the expansion RAM at 2800H, leaving the overall game play in the base RAM at 2000H.

Another option would have been to add an 8755 EPROM, but that would have greatly complicated (and slowed down) the edit/assemble/debug cycle as EPROMs would need to be programmed with external hardware and UV erased.  This is how the final product might be delivered to a customer.

Development Methodology

I wrote the code out on graph paper using a mechanical pencil.  The mnemonics started near the middle of the page.  This left room to put the addresses and hex bytes on the left hand side of the page, just like an assembler program's listing file would use.  There was scant room for comments, but I scribbled a few in.  I also left extra space between modules so that I didn't have to re-key 300 bytes of code just to add a single instruction!

After completing the code, I would read through it a few times to make sure it made sense.  Then came the assembly process itself.  For each line I would write the address, look up the mnemonic in the handy card provided and fill in the appropriate bytes.  This goes pretty quickly as you tend to use the same opcodes frequently and you can refer to previous lines.  You also start to memorize them.  Care must be taken to make sure that you use the right number of data bytes (one for MVI,
two for JMP, etc) and that two-byte values are placed in little endian order.

The address for the next line is then computed and I found it easiest to just count it off using the bytes I wrote.  Since the 8080 doesn't use relative branching, there was no need to use a calculator during the assembly phase.

Lastly, was typing in the instructions.  This is pretty straightforward, but I did find it useful to verify the address every instruction or two.  There were cases where I either skipped a byte, or had the
addresses wrong on paper.  Lastly, I would go back to the top and read it all back.  It was common to find errors due to contact bouncing or other fat finger mistakes.

Other Details

One of the changes this code underwent was to keep almost all data in registers.  The 8080 instruction set is rather limited when it comes to loading and storing data from known addresses.  You can only use the accumulator and each LDA/STA is 3 bytes long.  Instead, I would establish a convention for each routine (such as B=random seed, C=step count) and have called routines preserve the BC registers for the caller.  SHOW and TONE also preserve the A register which made
PLAY and REC much cleaner.  I only have one global variable - the state of the random number generator (LFSR).

The random number generator is the classic Galois LFSR design using an 8-bit register.  I consulted internet tables to find taps (1,2,7) that produce a maximal length 255 bit sequence.Each call to NEXT generates two random bits to get a value between 0 and 3 representing one of the
four colors.

A time-based random seed is generated at the beginning of the game.  On each call to PLAY or REC, this value is restored to the LFSR state.  This causes the "random" sequence to be consistent from turn-to-turn and between the machine and player's turns.  This avoids the need to store
the sequence.

The Real Simon

Simon was actually created by legendary video game pioneer Ralph Baer with Howard J. Morrison and programmed by Lenny Cope.  The 8085 would be a horribly expensive choice for a consumer product with millions of units.  Instead, it was developed on the Texas Instruments TMS-1000 series of embedded 4-bit micro controllers.  Cope had access to a mainframe assembler, albeit through a "very slow teletype".  It is reasonable to say that this team worked in conditions only marginally better than our fictitious basement hacker.

The game itself is a brilliant piece of engineering.  There are only two chips on the board - the micro controller (a custom job with an MB part number) and a current driver IC to power the lights and speaker.  The case is a stunning piece of industrial design rivaling that of Steve Jobs and Jony Ive.  It looks like a spaceship.  "People of earth - we come from the future to entertain you!"  The cultural impact of the game is huge and versions of it are still produced to this day.