ポケモン赤でFizzBuzz

ゲームボーイポケモン赤」の任意コード実行バグを使ってFizzBuzzを実装しました。 FizzBuzzとは数字を順に言っていくゲームで、ただし3の倍数ならFizz、5の倍数ならBuzz、3の倍数かつ5の倍数ならFizzBuzzを言うというものです (参考文献 [1])。

f:id:fujidig:20220104015520p:plain

↓これは最初に作ったバージョン (低機能)

機能

下にスクロールすることができて、一番上の項目に応じてポケモンの鳴き声 or 効果音が流れます。 数字ならポケモンの鳴き声、FizzとBuzzなら効果音、FizzBuzzならファンファーレが鳴ります。 なおメニューに戻る方法は実装していません。

実行方法

バイナリエディタ (参考文献 [2])を導入します

・なかよしバッヂからバイナリエディタを起動し次のように書き換えます。

アドレスD985にC3 B3 D9 (jp D9B3)を書き込む
アドレスD9B3 (ボックスのポケモンのステータスが格納される場所)に以下の305バイトの内容を書き込む
18 10 00 00 a0 a0 a0 a0 a0 a0 00 00 00 00 00 00
00 00 cd 67 01 06 00 0e 10 26 d9 2e b5 16 8e 1e
50 cd bb 01 cd 81 01 06 01 c5 cd 67 38 c1 21 a0
c3 f0 b3 cb 7f 28 38 c5 c5 cd 3a da c1 fe 00 28
0e fe 01 28 14 fe 02 28 1a 78 cd b5 2d 18 1e 3e
89 cd 33 0e cd 7e 37 18 14 3e 86 cd 33 0e cd 7e
37 18 0a 3e 8e cd 33 0e cd 7e 37 18 00 c1 04 0e
00 e5 c5 cd 77 da c1 e1 3e 14 85 6f 30 01 24 0c
79 fe 12 20 ec 18 a2 af e0 95 e0 96 e0 97 78 e0
98 3e 0f e0 99 06 04 cd f0 38 f0 99 fe 00 28 1b
fe 03 28 1a fe 06 28 16 fe 09 28 12 fe 0c 28 0e
fe 05 28 0d fe 0a 28 09 3e 03 c9 3e 00 c9 3e 01
c9 3e 02 c9 e5 e5 f8 02 36 00 23 78 81 47 70 cd
3a da e1 fe 00 28 19 fe 01 28 2e fe 02 28 3e e5
f8 02 54 5d e1 36 f6 23 01 07 c2 cd 7d 3c 18 3f
36 cc 23 36 6e 23 36 bd 23 36 e5 23 36 ca 23 36
e5 23 36 bd 23 36 e5 18 26 36 cc 23 36 6e 23 36
bd 23 36 e5 3e 7f 23 22 22 22 77 18 12 36 ca 23
36 e5 23 36 bd 23 36 e5 3e 7f 23 22 22 22 77 e1
c9

バイナリエディタを閉じてもう一度なかよしバッヂを使う

FizzBuzzが実行されます

参考文献

[1] Fizz Buzz - Wikipedia

[2] 【初代ポケモン】バイナリエディタで Do-Dai 【任意コード実行】 - Wonderland Seeker

[3] GitHub - ulrikdamm/Assembler: Assembler for Gameboy games

文献[2]のバイナリエディタのコードはFizzBuzzのコードを作成する上でも非常に参考にした。

付録

書いたアセンブリです。 文献[3]のアセンブラアセンブルできます。 もっと短いコードに出来ると思います。

[org(0xd9b3)]
first:
    jr start
tile:
    db 0x00
    db 0x00
    db 0xa0
    db 0xa0
    db 0xa0
    db 0xa0
    db 0xa0
    db 0xa0
    db 0x00
    db 0x00
    db 0x00
    db 0x00
    db 0x00
    db 0x00 
    db 0x00
    db 0x00
start:
    call 0x0167 ; DisableLCD
    ld b, 0
    ld c, 0x10aa
    ld h, (tile >> 8)
    ld l, (tile & 0xff)
    ld d, 0x8e
    ld e, 0x50
    call 0x01bb ; CopyData
    call 0x0181 ; EnableLCD
    ld b, 1
mainloop:
    push bc
        call 0x3867 ; JoypadLowSensitivity
    pop bc
    ld   hl, 0xC3A0 ; wTileMap
    ld   a, [0xffb3] ; hJoyPressed
    bit 7, a
    jr z, skipincrcounter
    push bc
        push bc
           call judge_fizzbuzz
        pop bc
        cp 0
        jr z, fizzbuzz
        cp 1
        jr z, fizz
        cp 2
        jr z, buzz
general0:
        ld a, b
        call 0x2DB5 ; PlayCry
        jr endofcry
fizzbuzz:
        ld a, 0x89
        call 0x0e33 ; PlaySound
        call 0x377e ; WaitForSoundToFinish
        jr endofcry
fizz:
        ld a, 0x86
        call 0x0e33 ; PlaySound
        call 0x377e ; WaitForSoundToFinish
        jr endofcry
buzz:
        ld a, 0x8e
        call 0x0e33 ; PlaySound
        call 0x377e ; WaitForSoundToFinish
        jr endofcry
endofcry:
    pop bc
    inc b
skipincrcounter:
    ld c, 0
loop:
    push hl
        push bc
            call print
        pop bc
    pop hl
    ld a, 0x14
    add a, l
    ld l,a
    jr nc, skipincrh
    inc h
skipincrh:
    inc c
    ld a, c
    cp 0x12
    jr nz, loop
    jr mainloop
judge_fizzbuzz:
    xor a
    ld [0xff95], a
    ld [0xff96], a
    ld [0xff97], a
    ld a, b
    ld [0xff98], a
    ld a, 0x0F
    ld [0xff99], a
    ld b, 04
    call 0x38F0 ; Divide
    ld a, [0xff99]
    cp 0
    jr z, zero
    cp 3
    jr z, mod3
    cp 6
    jr z, mod3
    cp 9
    jr z, mod3
    cp 0x0c
    jr z, mod3
    cp 5
    jr z, mod5
    cp 0x0A
    jr z, mod5
    ld a, 3
    ret
zero:
    ld a, 0
    ret
mod3:
    ld a, 1
    ret
mod5:
    ld a, 2
    ret
print:
    push hl
        push hl
            ld hl, sp+2
            ld [hl], 0
            inc hl
            ld a, b
            add a, c
            ld b, a
            ld [hl], b
            call judge_fizzbuzz
        pop hl
        cp 0
        jr z, ifzero
        cp 1
        jr z, ifmod3
        cp 2
        jr z, ifmod5
general1:
        push hl
            ld hl, sp+2
            ld d, h
            ld e, l
        pop hl
        ld [hl], 0xF6
        inc hl
        ld bc, 0xc207
        call 0x3C7D ; PrintNumber
        jr return
ifzero:
        ld [hl], 0xCC
        inc hl
        ld [hl], 0x6E
        inc hl
        ld [hl], 0xBD
        inc hl
        ld [hl], 0xE5
        inc hl
        ld [hl], 0xCA
        inc hl
        ld [hl], 0xE5
        inc hl
        ld [hl], 0xBD
        inc hl
        ld [hl], 0xE5
        jr return
ifmod3:
        ld [hl], 0xCC
        inc hl
        ld [hl], 0x6E
        inc hl
        ld [hl], 0xBD
        inc hl
        ld [hl], 0xE5
        ld a, 0x7F
        inc hl
        ld [hl+], a
        ld [hl+], a
        ld [hl+], a
        ld [hl], a
        jr return
ifmod5:
        ld [hl], 0xCA
        inc hl
        ld [hl], 0xE5
        inc hl
        ld [hl], 0xBD
        inc hl
        ld [hl], 0xE5
        ld a, 0x7F
        inc hl
        ld [hl+], a
        ld [hl+], a
        ld [hl+], a
        ld [hl], a
return:
    pop hl
    ret

付録2

機能の少ない最初のバージョンのコードも載せておきます。 DE64 (ボックスのニックネームのアドレス)に以下を書き込めばよいです。 これは159バイトなのでボックスのニックネームのメモリ領域に収まります。

06 01 c5 cd 67 38 c1 21 a0 c3 f0 b3 cb 7f 28 01
04 0e 00 e5 c5 e5 cd 8f de e1 c1 e1 3e 14 85 6f
30 01 24 0c 79 fe 12 20 ea 18 d7 e5 f8 04 36 00
23 78 81 47 70 e1 af e0 95 e0 96 e0 97 78 e0 98
3e 0f e0 99 06 04 cd f0 38 f0 99 fe 00 28 1a fe
03 28 25 fe 06 28 21 fe 09 28 1d fe 0c 28 19 fe
05 28 23 fe 0a 28 1f 18 29 36 cc 23 36 6e 23 36
bd 23 36 ca 23 36 bd c9 36 cc 23 36 6e 23 36 bd
3e 7f 23 22 77 c9 36 ca 23 36 bd 3e 7f 23 22 22
77 c9 e5 f8 04 54 5d e1 01 05 c2 cd 7d 3c c9