NintendOscope

A couple of months ago I explained how to access and interpret the video signal from the original grayscale Gameboy. My image reconstruction at the time was done in a graphing program with some saved data. Good for a proof of principle, but a little bit lame as far as applications go.

That’s right my friends. Gameboy on an oscilloscope. I’ll walk you through the details of what’s going on here, but if you’re feeling impatient there’s a YouTube clip at the end of the post.

This project can be pretty cleanly broken up into two parts: Getting the data from the Gameboy and then displaying it on the oscilloscope.

1) Getting data from the Gameboy

When I discussed the video signals earlier, I implied that they were slow enough to be trivial for your typical 20MHz 8-bit microcontroller. In actual fact a 4MHz datastream is fast! You get 250ns between rising clock edges, and in that time you need to:

Wait for the clock edge (it’s not perfectly synchronous, so you can’t just use a 4MHz timer)

Read the digital value on two pins

Store that value in memory somewhere

At 20MHz, you’ve got 5 clock cycles to do it. Good luck with that.

I thought about this for a while, and decided what I needed was some kind of serial→ parallel conversion. That way I could read in 8 bits at a time on each channel, and only have to do it every (250ns*8)=2μs. Since I’m not yet one of the cool kids who plays with FPGAs, I started doing this with 74 logic – counting clock pulses, latching data from a serial-parallel shift register every 8 counts, so forth and so such. It took a while.

And then a month into setting up this complicated circuit, it struck me that I am a moron; that most microcontrollers already have exactly this feature and it’s called SPI. Also since SPI modules are done in hardware they run much faster than you could do in software, definitely fast enough to keep up with this video signal in any case.

With that little design hiccup aside, the rest went pretty quickly. I’m using an XMega128A1 controller on one of Justin Mattair’s XMEGA development boards. This thing runs at 32MHz, and is a great option for this project because the XMega series have peripherals coming out of their asses. Seriously, there’s 4 SPI modules, 2 DACs, something like 800 timers and probably a couple thousand UART modules, I lost count.

In this case having two SPI modules makes life very easy – one for each data channel. I gave each module one of the data channels and the clock signal from the Gameboy. The HSYNC and VSYNC signals go to IO pins to keep everything synchronized. I’ll try to keep this description fairly high level; check out the source code if you want to get into the details.

The only other thing to attend to is level conversion – the Gameboy is giving 5V signals but the XMega wants 3.3V. Since this is a read-only arrangement, it’s sufficient to just use a voltage divider on every signal line.

The end result of this is that I have a couple of arrays of pixel data that are being updated in real time. All I have to do is find something suitably cool to do with that information.

2) Putting it on a frickin oscilloscope

Here’s a shot of the hardware, which I’m now halfway through describing. The other half consists of the digital-to-analog converters responsible for driving the oscilloscope.

One of the neat things about analog scopes is how easy it is to repurpose the display. You are typically given the option to switch from the time-sweep mode of operation to instead control the brightness and x-y deflection with analog voltages. Which means you just have to stick some DACs on your micro to produce those voltage signals and you have a video display! There a whole bunch of great projects along these lines; two very noteworthy examples are Jan’s clock and Matthew’s incredible terminal console.

The scope I have is a PM3065, a 100MHz dual channel cathode ray scope and apparently a collaboration between Fluke and Phillips. I paid AU$130 second hand last year. Importantly it has a BNC input at the rear for controlling the beam intensity (“Z input”), in this case going smoothly from full-on at about 0.8V to completely blanked at about 1.06V. The exact numbers depend on what you have the intensity knob set to, but 26mV is more or less the range.

Let’s discuss the X-Y control first. Switched to XY mode, the voltage on the first input controls the X-deflection of the spot and the second controls the Y-deflection. By applying sawtooth ramp signals to both inputs you can raster out a square.

Tracing out a square in XY-mode

To match the Gameboy resolution we need a resolution of 160 x 144 pixels (DAC steps), and to get a halfway decent framerate we’re going to need to sweep them pretty fast. The analog voltage range is not particularly important because we can easily adjust the gain settings on the scope.

This is where the boatload of inbuilt peripherals on the XMega comes in handy again – it has 2 inbuilt 12-bit DACs that can run at 1MHz. They have a little bit of non-linearity at low and high values, but with 4096 (12bit) resolution there’s plenty of room to just add an offset to the ramps.

We’re nearly there – all that’s left is to set the intensity at each point of the raster according to the pixel data read from the Gameboy. Sadly we’ve used up the 2 inbuilt DACs to do the rastering, but since we only need 2-bit control a simple R2R ladder will suffice. There’s a little bit of fine tuning required though – for the intensity modulation input we need something like 9mV steps with about 800mV offset. Here’s what I’m using:

Schematic for the R2R DAC controlling spot intensity. Values in red are what I actually used.

Pretty straightforward I hope. I built it all on breadboard with multi-turn potentiometers so I could easily tune resistor values/swap things out. By putting a 2-bit value on DB1 and DB2 with a couple of IO pin on the XMega, we get an analog voltage out with an adjustable step size and offset. A good check that this works is to put the scope on regular (time sweep) mode and watch the R2R analog output. You’ll see the discrete values, and if at the same time you connect that output to the intensity modulation input you get something like this:

Demonstrating intensity modulation

From there it’s easy to play with the various potentiometers until you get 4 different spot intensities (as I have in the picture above).

And that’s pretty much it! The program logic is along the lines of:

Capture a frame:

Listen in on the video data until a new frame starts (VSYNC pulse)

Capture the pixel data for that frame with the SPI ports

Draw the frame:

For each pixel in Y (0…144)

Increment the Y-deflection DAC output

For each pixel in X (0…160)

Increment the X-deflection DAC output

Set the R2R DAC value for this pixel

Repeat

The XMega is capable of capturing data at the full 60Hz that the Gameboy is refreshing, but in the above scheme we stop capturing when we draw a frame to the screen. While that’s going on we miss some frames; at the moment it captures one then skips three. This gives a 15Hz refresh rate on the scope. Here’s a logic analyzer capture that shows this going on:

Logic analyzer of the NintendOscope; showing 6.5 screen refreshes.

The signals of interest here are #3 (Gameboy rereshing screen), #5 (XMega capturing data) and #6 (XMega drawing). #3 is just the VSYNC signal from the Gameboy, while I’m getting #5 and #6 from flashing status LEDs on the XMega board. #5 goes high while it’s reading in a frame, #6 while it’s drawing on the scope.

15Hz is a slow enough frame rate to be kind of annoying. The XMega has the speed to push this up to at least 20Hz, however at that speed the image starts to look terrible. I’m using a 1μs delay after each pixel to give the DACs time to settle. We can check this out with the scope by looking at DAC sweeps:

Settling times for the horizontal raster DAC (left) and spot intensity DAC (right). Timebase is 2μs/division

Unsurprisingly the culprit is the R2R DAC output, which is being passed back and forth all over a breadboard. It’s taking about 1μs to settle after small changes and nearly 2μs for large changes. Hence the 1μs delay in the raster loop – it sharpens up the picture at the expense of refresh rate.

It would be very easy to improve on this by simply relocating the R2R ladder and amplifier to some stripboard or a proper PCB. Alternatively you could think about scrapping the R2R DAC for a decent IC version, or shopping around for a micro with 3 DACs.

And now I’ll leave you with a video of it in action. Sound is coming from the Gameboy speaker, and the weird horizontal brightness bands are an artifact of trying to film it. Enjoy, and be sure to let me know if you try something similar!

P.S. It’s also worth pointing out the work going on at RivalCorp using an FPGA for the capture and outputting to VGA instead of an oscilloscope. I believe he’s looking at eventually selling a kit for it?

About CraigCraig is getting towards the end of a PhD in experimental nanotechnology. Arguably he might be finished by now if it weren't for all the crap described on this blog.
Queries/comments to flashingleds@gmail.com

I work for one of the larger test and measurement companies, helping to design the software that runs on our current oscilloscope products. I’ve seen a lot of scope-related hacks over the years, but this is the best by far. Would have been better if you’d used one of our scopes, though. :)

Fascinating. I have but one question. What’s the gamma? Yes, gamma. 42 bits (4 levels) are enough, but it’s no longer 1 bit, you have to start worrying about what levels those midtones are. Since this is an oscilloscope, it probably has the same gamma as a CRT, 2.5 (it comes entirely from electron gun physics), which may or may not be the same as the original GB screen. Though I admit it doesn’t matter all that much…

Personally, I’d have reserved the operational amplifier/manual signal generation method for either the horizontal or vertical sweeps (heck, no need for them, especially horizontal sweep, to be discrete digital steps, they can be continuous analog ramps) and used one of the DACs for the level generation, instead of using the operational amplifier, which (ordinary ones, at least) are not renowned for being high frequency components, for the fastest switching job in the system (level generation).

If done well (and with fast enough hardware, dunno whether the hardware you use here would be capable of it), it should be possible to fit signal generation in the 68us of dead time in each line, that would require the level to switch at 2MHz.

Hi Pierre. In this case the gamma is set by adjusting the 4 output voltage levels from the R2R dac “until it looks OK”.
You’re absolutely right that the sweeps don’t need to be discrete. I’m using vector graphics techniques to render raster graphics, which is not really optimal.
Thanks for your comments.

And yes, as much as this is cool, it would be even cooler to use the oscillo to display the output of a vector system such as the Vectrex. As while we can emulate a whole Gameboy system, including the display, on current hardware, for stuff like the Vectrex we have to settle on rasterized vector graphics.

Hey Craig,
thanks for your reply in German!! Did you learn it in school? If you have any questions about the German language feel free to ask me.

I’m very impressed that a microcontroller is fast enough to handle the clock signal of the Gameboy. I used a Nexys2 board, but for my project there was no need for the external flash or ram placed on the board. The video data is stored by flip-flops on the FPGA.

By the way, recently I got interested in the Super Gameboy, because it is much cheaper than the Gameboy Classic and does not have any screen. If someone was able to use it with a VGA screen or an oscilloscope and an external input it would be great, don’t you think so?

As a lover of both all things nintendo-ish and oscilloscope-ish, I think you deserve TWO internets! :-)

Very nice hack, although after I had been messing with the internals of a laptop recently, I wonder how easy to handle ribbon cables are for this kind of projects. They didn’t seem very ‘malleable’ if you know what I mean.

If there isn’t enough bandwidth to do more than 15 Hz progressive, I wonder if you could pull off 30 Hz interlace. Double the vertical scan rate but not the horizontal, and send two 160×72 pixel fields: even-numbered lines of pixels in one field and odd lines in the next.