View Full Version : Project pictures: Tetris from scratch

04-15-2018, 02:57 AM
After playing around a bit with my Analog Blackfin JAMMA board, I decided I wanted to try a more classic hardware design. I set out to make a working JAMMA PCB using nothing but standard 5v through-hole parts, using a well-known CPU that already had free tools.

Quickly, I realized that it would require a ton of parts to build anything approximating a sprite engine. With an 8-bit CPU, I didn't want to deal with a framebuffer, either. So I decided my project would be limited in scope, to a machine with just a tilemap for video.

This generally meant that I'd be building a puzzle game, as anything else would likely require some form of pixel-writing. So I decided to make a Tetris clone.

I like the Z80, and it's still made in speed grades up to 20MHz, so I started with that. I decided to use 32KBytes of RAM and 32KBytes of ROM, because those parts (62256 and 27256) are commonly available as well. That made the memory address space easy.

The video system is nearly as simple as possible. A set of dual-port RAMs (so the timing is easier) contains a tilemap, 2 bytes per 8x8 tile. 13 bits from each tilemap word address a pattern, and 3 bits address a palette. The patterns are stored in 4 64KByte ROMs, which are attached to 4 74LS166 shift registers to read out 8x 4bpp pixels. The 3 bits from the tilemap, and 4 bits from the pattern pixel, are used to address a palette ROM, containing a color palette. The color palette output goes to a set of 3x 4-bit DACs, made from resistor arrays. So it's basically one big chain of look-up tables in ROMs.

I initially thought that I'd use a bunch of individual 8-bit output ports to control the video system. With a single interrupt at 15KHz, I should be able to re-load a starting tile address at each line, straight from the CPU. It would work something like the Atari 2600 in that respect. I decided that was too much overhead, though. It was simpler to get a microcontroller for that stuff. So the video system is controlled by an 8051-compatible microcontroller, running at 500KHz machine cycles. It's essentially a big busy-loop that toggles vsync/vblank pulses at the right time. It controls a port to set which video line is scanned out, and can reset a pair of 74LS193 counters to count tiles within the line.

For other peripherals, I eventually settled on an 8255. It takes up the whole IO address space. I use port A to output commands to a microcontroller that makes sounds, and port B to output some extra address bits to the ROM containing the video color palettes. Port C, being bit-addressable, controls a set of shift registers an an I2C bus.

I spent a while working out each part of this design, drawing up a schematic in Eagle. I laid out a circuit board and ordered it from DirtyPCBs.


I put the board together, enough to get it booting and theoretically put something on screen. I had to wire up an adapter for the program ROM, because I wanted to use a battery-backed SRAM package instead of a UV EPROM.


Having waited 10 days for the order, I had whipped up an emulator on my computer. I could run my Z80 code on the emulator to see what it would look like. So when I put it together - and sorted out a devious issue with a transposed address line on the ROM - I got it up and running with the same test code.


Unfortunately, the 8255 didn't seem to work. I could read the ports as inputs just fine, but nothing I wrote to it seemed to do anything. Much debugging followed. I have an HP 1662CS logic analyzer which helped with logging tons of signals.


Eventually I re-read the datasheet and found out that the 8255 treats the ~CS pin like an address pin, not a strobe. Deasserting ~CS won't properly terminate a write, and I had just wired the ~CS, ~RD, and ~WR straight to the Z80's ~IORQ, ~RD, and ~RW. So I effectively wasn't holding the ~CS pin long enough. I re-did the decoding to just tie that pin low, and decode ~IORQ and ~RD/~WR into strobes specifically for the 8255, using a 74LS38.

For the sake of testing, I attached a Super Nintendo controller cable instead of the shift-registers on board. Normally, there would be a set of 3x 8-bit shift registers to latch 24 switch inputs from the JAMMA edge. A Super Nintendo controller is basically the same thing in a nice small package, though. Aside from inverting some of the signals in software, and getting them in a different order, it hooks right up.


I've gotten the software to the point where I can run through a bunch of test-mode screens, and at least start to display the game graphics. There are some utilities on my computer for converting PNG files into tile-mapped backgrounds for the hardware.


I'm writing up the basics of the actual game loop now. I hope to post some more progress when it's ready.

04-15-2018, 07:55 AM
This is really quite impressive. Looking forward to reading future updates. Awesome work :buttrock:

Massive Urethra Chode
04-15-2018, 01:12 PM
Amazing, man. Will you put it in a kind of case to make of consolized? Maybe even make the controls built in so itís like a plug n play super gun thing

04-15-2018, 10:35 PM
Good luck completing your project.
Looks like a bit of work, but fun.
What's the grey stuff under the chip with the yellow wires
After this, will you focus on other games?

04-16-2018, 01:02 AM
Any plans to make and sell as a kit?

Jedah Doma
04-16-2018, 01:09 AM
Nice idea. Looking forward to seeing where this goes.

04-16-2018, 03:04 AM
Amazing, man. Will you put it in a kind of case to make of consolized? Maybe even make the controls built in so it’s like a plug n play super gun thing
I have trouble finishing projects, to put it nicely. I'm trying to keep the scope of the project down - just a plain JAMMA board that plays a game.

Good luck completing your project.
Looks like a bit of work, but fun.
What's the grey stuff under the chip with the yellow wires
After this, will you focus on other games?
It's duct tape. :cool: When I build the other copies (I've got 10 bare PCBs) the extra chip will probably sit on top of the 8255. And yeah, I have a couple other designs I want to try. One thing at a time, though.

Any plans to make and sell as a kit?
I'm probably going to publish the source code and gerbers on my web site, so it shouldn't be hard to duplicate. I might sell my extra copies at-cost if I get them to a point where it's actually a usable game.

I worked on the structure of the game code a bit today. I've got it cycling through some full-screen graphics as an attract mode, and accounting for coins/credits. There's an I2C EEPROM on board, so the plan is to score bookkeeping, high-scores, and coinage settings in the EEPROM. Right now I've got it stubbed-out so it's just 1 coin per credit.

04-19-2018, 12:46 PM
It would definitely be cool if you shared the nitty gritty, even if you didn't sell any sort of kit.

I can't wait to read more about this. Hobbyists can do some really amazing things, so I look forward to reading about your progress!

04-19-2018, 01:16 PM

PLEASE keep this name.

Real talk: Good luck, I'm looking forward seeing where this goes.

04-20-2018, 12:52 PM
Only just saw this thread... NICE ONE!!!!

04-21-2018, 11:57 PM
I've been working on the gameplay code - systems programming is fine in assembly, but doing gameplay just sucks. Definitely going with a machine that can handle GCC for my next project...


I've written the collision routine - the code that checks if a tetromino could drop to a given space without hitting anything - but don't have it hooked up yet. Drawing, un-drawing, and re-drawing the tetrominos works though.

PLEASE keep this name.

Real talk: Good luck, I'm looking forward seeing where this goes.

"Tetris" is trademarked, and the owners are quite litigious. So "Falling Block Game" it is.

04-22-2018, 02:32 AM
Got a few more gameplay bits hooked up - the collision detection seems to be working.


Some more technical details:

The audio system is driven by another 8051-compatible microcontroller, this one with 4KB of program ROM (AT89C4051). It's running at the full 12MHz, executing 1MIPS peak. The part is in a 20-pin package, with only two ports (1 and 3) available instead of all four. Port 3 on the audio MCU is connected to port A on the 8255. So, the Z80 CPU can write a byte to IO address 00, and it will show up on the Port A pins of the 8255 PPI, and the audio 8051 MCU can then read those bits on its port 3 SFR. That's the whole entire interface between the audio MCU and the rest of the system. I don't have this totally hooked up yet, but the basic idea is that the possible music tracks and SFX are all assigned a 6-bit number, and are just played back when the MCU sees that at its input. It seems to be how many arcade systems implement this. On the other side of the audio MCU, on its port 1, is a simple 8-bit DAC - an AD7524. This is hooked up as shown in its datasheet to a bit of analog circuitry, including a 20W audio amplifier running off the 12v JAMMA rail. The audio MCU is running an interrupt-driven audio soft-synth; at half of the video line rate (7812.5Hz) it will generate a new 8-bit sample and push it out to the DAC. About 60-70% of the MCU time is spent in these interrupt handlers. 4 channels are supported - one 256-sample wavetable, one LFSR-driven noise channel, and two 50% pulse channels. All have an 8-bit instantaneous multiplicative volume setting. Instantaneous amplitudes are accumulated with 16-bit precision, a dither is added (from that same LFSR, but different bits), and the 8-bit sample is ready. In the "outer loop", outside of the interrupt handler, a simple playback engine runs. A music track for each channel can be defined as a sequence of envelopes - every 262 samples (i.e. once per video frame), the volume of the synth channels is adjusted. In-between envelopes, the frequency of the channel can be adjusted. The system isn't great, but manages to make around 80 of the standard MIDI pitches with acceptable quality. Chords can be generated by altering the wave table in the first channel. Percussion effects are recognizable with the noise channel.

04-29-2018, 11:32 PM
I worked on this a bit more - but it would look about the same in screenshots. The controls are now hooked up, and lines clear properly when you make them. The progression from stage to stage now works, and each stage requires more lines to clear, and drops the pieces faster. The game-over screen works, and you lose if a new tetromino gets stuck immediately on spawning. I hooked up a couple example "sound effects" - just different beeps triggered during gameplay.

I got the I2C EEPROM driver working, too. This is a screenshot of 256 random bytes that I wrote into the EEPROM from my ROM burner:

I hooked up a test-mode screen that can clear the EEPROM back to defaults, too. Currently, I track some basic statistics (coins, credits, game starts, power cycles), and will also put the high-scores there when I hook those up.

The I2C bus is made out of 2 outputs and 2 inputs on the 8255 C port. Two of the C-port outputs are attached to a 74LS01's inputs, and the 74LS01 can then drive two of the 8255's inputs. Those inputs are then also attached to a pair of pull-up resistors and the EEPROM's SDA/SCL pins.

Most of the game code just operates on a shadow of the EEPROM contents in RAM. It gets written back every time the attract mode cycles through the high-score screen.

05-07-2018, 02:57 PM
Looking really good!

05-07-2018, 07:23 PM
Very cool!

05-09-2018, 11:24 PM
Over the weekend, I built up a complete second copy of the board - this time, with the controls fully in place (shift registers getting switches from the JAMMA edge). It also doesn't have every single IC socketed like the first board - just the ROMs and 8051s.

I found a way to fix the 8255 decoding problem without any additional chips. I had a 74LS139 on the board, with only one of its two decoders in use. So, I use the second decoder to do the IO strobe decoding - the enable pin is tied to the CPU's ~IORQ, and the two select pins are tied to the CPU's ~RD and ~WR. Then the second and third decoded outputs become the read and write strobes for the 8255. This requires lifting the inputs on the '139 (previously tied to ground) as well as the inputs on the 8255. It's a fairly clean fix though, aside from my ugly soldering.

I tested the board out on my Sigma.

The video output looks... not great. I think the video amp doesn't like it when the level from my poor-man's DAC gets too low. It's probably not able to get down there near 0 volts... and ends up smearing the leading edges coming out of 0-value areas. It looks fine if I just crank up all the colors slightly in software, though, so that might be the easy fix.

05-11-2018, 11:48 AM
Damn, this is a cool project.

05-19-2018, 02:32 PM
I've been working on finishing up the remaining bits of work on this. The game loop is all done and basically works - though it isn't very polished, because I'm real frustrated by writing gameplay code in assembly. I've got audio commands going out to the 8051, triggered by gameplay events, so you get the usual beep-boop-boop noises as you drop pieces. I built out another board, so I've got three that work now. My workspace is getting a bit cluttered, and I'm paranoid about it because I don't have proper ESD-safe benches...

I wrote some more code that depends on the EEPROM working - which it does, but not on the emulator. I decided it wasn't worth it to simulate a serial EEPROM in software, so I just test this code on the real machine. I hooked up the statistics and made a test-mode screen to display them.

Not that it matters much for a pretend project like this, but I hooked up coinage/controls settings too. Like the stats and high-scores, they're saved in the EEPROM. The board doesn't have any DIP switches on it - just the one 24C04 for all settings.

The high-scores work as well. There's a screen to enter your initials, and the scoreboard shows during the attract mode. It writes to the EEPROM automatically, so scores persist across reboots. And, there's no battery required.

I didn't hook up a free-play mode, because it would have required re-writing the attract mode sequence - currently, the attract exits to the title card when there's a nonzero number of credits accumulated, and then the title card waits to start the game. Free-play would have required more code to handle pressing start during the attract sequence, and I'm sick of writing assembly at this point.