This is a hardware design to capture the LCD output of a Gameboy by connecting an FPGA to some of the pins on the ribbon cable between the main and LCD boards of a Gameboy.
Make sure git submodules are initialised (this repo includes migen as a submodule as the latest released version doesn't include Spartan 6 DDR Input).
Make sure you've got the
papilio-prog bitfile loader from here.
If using a Papilio Pro board
./run gbcap build ./papilio-prog/papilio-prog -f build/top.bit ./recv.py -b 2000000 /dev/ttyUSB1 -o frames.cap
Captured raw data can be decompressed into images using
./decomp_stream.py --output-gif frames.gif frames.cap
The GIFs output from this will be pretty big and unoptimised - try
optimise the size, or use
gif2webp to convert to
webp. More annoyingly they
will run too slow (at 50fps rather than 60fps) due to the low precision of the
GIF frame duration.
There is also an option to output a set of pgm images, one per frame.
./decomp_stream.py --output-pgm frames/ frames.cap
This code (in
gbcap.py) sets up which pins on the FPGA are expected to be
connected to which pins on the Gameboy:
plat.add_extension([ ('gb_lcd', 0, Subsignal('vsync', Pins('B:5')), Subsignal('hsync', Pins('B:4')), Subsignal('cpl', Pins('B:6')), Subsignal('clk', Pins('B:2')), Subsignal('pixel', Pins('B:0', 'B:1')))])
Compression is line based. Each line starts with a config byte. If the byte is
0xFF then the line is not compressed and the next 40 bytes in the stream are
the raw line pixel data. Otherwise the byte describes two things: the
most-significant bit is set if the line is an exact match, not otherwise. The
lower 7 bits encode a pixel offset in x & y in the range -4..4, in the form
((x + 4) * 9) + (y + 4) (i.e. x/y offsets are biased by 4 to make them in the range
0..9). The offset selects the line in the previous frame (relative to the
current line's y coordinate) and an x shift (in pixels, i.e. multiples of 2
bits). 0s are always shifted in to fill the whole line when shifting in x. This
shifted line is the reference line. For an exact match there is no more data in
the compressed stream for the current line - the output line is just the
reference line verbatim. For a non-exact match the next 5 bytes in the
compressed stream are a bitmask of which reference line bytes to output. For any
0 in the bitmask a byte is read from the compressed stream and output instead of
using the byte from the reference line. For any 1 there is no data in the
compressed stream for that byte, the byte from the reference line is output.
compress_ref.py for a reference implementation of the
./run uart_echo build ./papilio-prog/papilio-prog -f build/top.bit ./com.py -b 2000000 /dev/ttyUSB1 abcde
Should output the second argument 'abcde' in the example back. If it doesn't then something is wrong!
Build the verilator testbench:
./run gbcap verilator
Binary will be at
obj_dir/gbcap_tb. Pipe raw binary into the testbench, it
will output the bytes from the compressor, e.g.
./dump_pgm.py *.pgm | ./obj_dir/gbcap_tb > tb.cap