A walk-through of hacking Flappy Bird. Sos created a port of the iOS game Flappy Bird for the C64. The difficulty of the game is crazy, so I hacked it and implemented a computer player for it (my highscore without the hack was 9).
Unpack
First I needed to unpack the game. I loaded it in the VICE emulator, set a breakpoint in the monitor at the program start and after some single stepping and more breakpoints finally the game started with a jump to $3000. I saved the game with "bsave" (from $0801 to $7fff) in the monitor. There was another basic header after unpacking, which jumps to $3000.
Analyze
Then my idea was to emulate the joystick fire presses. I set a breakpoint on joystick input read ("break load dc00") and there are multiple positions in the program where it is read: waiting for fire when the game starts, acknowledge of the help graphics and then in the game. The position in the game is at $5da8. I replaced it with a "jsr $c000", filled the PRG file until $bfff and wrote some assembler to concat at the end of the PRG. First test was a "lda $dc00; rts" and it worked.
Then I analyzed how the game detects a hit with the tubes. This was easy, because when I disabled "sprite-background collisions" in the VIC settings in VICE, it didn't detect hits with the tubes anymore. So the tubes are characters with a modified character set and of course the bird is a sprite. A quick fill test for $0400 showed that the standard character screen position was still used. I set a breakpoint on store on $0427 (last character in the first row, "break store $0427") and found the scroll function and function for creating new tubes. It uses the voice 3 oscillator register for the y position, with the noise generator as a random number generator. There is a counter at $1e1e for the distance between the tubes and when it is 0, I can read the y character position in $1e20.
Hack
The rest was easy: Calculate the y raster position from the character position, delay it with a ringbuffer and compare it with the bird y sprite position. If too low, flap. As a bonus, the hack can be enabled with joystick left, and disabled with right. The source code, save it as flappy.asm:
.pc = $c000
txa
pha
// default result is original joystick value
lda $dc00
sta result
// joystick left = enable hack
and #4
bne !+
lda #1
sta hackEnabled
// joystick right = disable hack
!: lda result
and #8
bne !+
lda #0
sta hackEnabled
// filter joystick left/right
!: lda result
ora #12
sta result
// increment ringbuffer pointer
inc ringRead
inc ringWrite
// save last character position as y raster position when valid
lda $1e1e
cmp #0
bne !+
lda #20
sec
sbc $1e20
asl
asl
asl
clc
adc #14
sta lastPos
// write last position to ringbuffer
!: ldx ringWrite
lda lastPos
sta ringbuffer,x
// test if hack is enabled
lda hackEnabled
beq end
// default value for joystick, if hack is enabled
lda #$ff
sta result
// compare delayed last position with bird sprite y position
ldx ringRead
lda ringbuffer,x
cmp $d003
bcs end
// flap, with a delay between the flaps
inc flapDelay
lda flapDelay
cmp #2
bne end
lda #0
sta flapDelay
lda #$00
sta result
end: pla
tax
lda result
rts
lastPos: .byte 0
result: .byte 0
flapDelay: .byte 0
hackEnabled: .byte 0
ringWrite: .byte 70
ringRead: .byte 0
.align $100
ringbuffer: .fill 256, 150
For faster turnaround time while adjusting the offsets and ringbuffer delay, I used a script to compile my assembler program, concat it to the original program and start it in VICE:
java -jar "C:\Program Files (x86)\kickassembler\KickAss.jar" -binfile flappy.asm
copy /b flappy-unpacked.prg + flappy.bin flappy.prg
"C:\Program Files\WinVICE-2.4-x64\x64.exe" flappy.prg
Pack
Finally I packed the result with exomizer: flappy-bird.prg