Hacking Flappy Bird

in #hacking8 years ago (edited)

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