Zombie Extraction
Yesterday, Travis Goodspeed wrote a good post about bitmapped sprites on the IM-me.
As a teaser of his upcoming ZombieGotcha game, he released a binary for a demo and challenged people to extract the images and recreate the startup animation.
And I can’t resist a challenge…
According to Dave the IM-me LCD is connected over SPI, with an extra line (A0) to signify whether a command or data byte is being transferred.
Handily, all of the LCD lines are brought out to pads on the PCB. They’re arranged like this (this will make sense if you’re looking at it):
(A) (B) (D) (C) (E)
- (A) P0.3 - MOSI
- (B) P0.5 - SCLK
- (D) P0.2 - A0
- (E) P0.4 - CS
I wired my Bus Pirate up and started a SPI capture. I got garbage.
Next, I had a look at the clock line on an oscilloscope. The clock was going at over 2.5Mbps - far faster than the Bus Pirate can deal with. I had to slow it down…
In Dave’s original code the SPI bit rate is set like this:
void configureSPI() { U0CSR = 0; //Set SPI Master operation U0BAUD = SPI_BAUD_M; // set Mantissa U0GCR = U0GCR_ORDER | SPI_BAUD_E; // set clock on 1st edge, -ve clock polarity , MSB first, and exponent }
By disassembling the ZombieGotcha code I found this:
L0009: 061E 758600 MOV 86h, #0h 0621 75C2AA MOV 0C2h, #0AAh 0624 75C530 MOV 0C5h, #30h 0627 22 RET
From the SDCC header files, you can see that 0×86 is U0CSR, 0xC2 is U0BAUD and 0xC5 is U0GCR. U0GCR_ORDER is 0×20. So, we can deduce that SPI_BAUD_M = 0xAA and SPI_BAUD_E = 0×10
From the datasheet, this makes the bit rate:
(((256 + 170) * 2^16) / 2^28) * 26000000 = ~2.7Mbit/s
I picked the lowest data rate listed (M=131, E=6):
(((256 + 131) * 2^6) / 2^28) * 26000000 = ~2400bps
I couldn’t get the output from dis51 to re-assemble, so tweaked the binary with a hex editor (after running through hex2bin.py). I now had:
0621 75C283 MOV 0C2h, #083h 0624 75C526 MOV 0C5h, #26h
I converted my nobbled binary back with bin2hex.py and flashed this onto the IM-me with my GoodFET31. Now that the SPI traffic was slow enough for Bus Pirate, I could capture it. Here’s the raw data. I then filtered out the MISO data (it wasn’t connected) and turned the data into a C header file (vim regexps, ugly but effective).
Finally, I wrote a little C program to parse the data and output a series of image files. Because Travis always updates the entire screen, we don’t need to distinguish between data and command bytes, we know there will always be a 3 byte setCursor() command followed by 132 data bytes.
To convert the images to GIF I used ImageMagick:
convert -delay 30 -loop 0 -scale 400% zombie*.pnm zombie.gif