Post

Troubleshooting FDS Audio Channels in Animal Crossing's NES Emulator

The Problem

Famicom Disk System ROMs can now be injected into Animal Crossing to play on its innate emulator thanks to research done by myself and foundation work done by James Chambers and Cuyler. Some Famicom Disk System games utilized an extra sound channel which gave certain games enhanced audio, such as Metroid and The Legend of Zelda. However, GameFAQs user Petuona pointed out that the games sound different when loaded in Animal Crossing versus a standard, external emulator. This is because standard NES emulators do not contain that extra sound channel. Since Animal Crossing’s emulator is based around the cartridge-based NES and Famicom rather than the Disk System, it would typically make sense for this sound channel to not be properly emulated. However, Animal Crossing’s emulator contains a Famicom Disk System BIOS and is able to handle this extra sound channel, but it is currently unknown how to force games to load with it.

Details

Animal Crossing’s Japanese version, Doubutsu no Mori+ (どうぶつの森+), actually contains two Famicom Disk System games in the form of Clu Clu Land D (クルクルランドD) and The Legend of Zelda (ゼルダのでんせつ). When any game is loaded in Doubutsu no Mori+, the emulator properly emulates the extra sound channel and enhances the audio for compatible games like Zelda. It would make sense for Animal Crossing to carry over this trait since their codebase is very similar, yet Animal Crossing does not properly emulate the extra sound channel no matter which game is loaded or through which method. However, I know it is possible for Animal Crossing to handle this extra Famicom Disk System sound channel since I replaced Doubutsu no Mori+’s emulator/BIOS with Animal Crossing’s and the extra audio channel still functioned. Thus, Animal Crossing’s emulator and BIOS is able to emulate this properly, but somewhere in the code this audio emulation gets blocked or is skipped entirely.

Potential Solutions

Solving this would likely require looking into the two games and determining how they load the proper emulator. The most likely method for this to be achieved involves the NSA’s Ghidra program and Cuyler’s GameCube extension for it, which can decompile a fair amount of Animal Crossing’s code to a more readable state.

Clues

Console Debugging Logs for Doubutsu no Mori+ (どうぶつの森+)
These are the developer debugging logs when attempting to load the furniture item Clu Clu Land D that has had its ROM replaced with with 19a_zelda_3.qd.szs in Doubutsu no Mori+.

Applies to Doubutsu no Mori+ and properly handles the Famicom Disk System’s extra audio channel!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
N[OSREPORT]: アリーナの内容 (0x80200800)
N[OSREPORT]: メモリブロック範囲 status サイズ  [時刻  s ms us ns: TID:src:行]
N[OSREPORT]: 81474320-81475ff0  確保 00001ca0 [6614004384124879: 0:**NULL**:0]
N[OSREPORT]: 81475ff0-81476100  確保 000000e0 [6614079643101817: 0:**NULL**:0]
N[OSREPORT]: 81476100-81576150  確保 00100020 [6614079643103200: 0:**NULL**:0]
N[OSREPORT]: 81576150-817efec0$ 空き 00279d40
N[OSREPORT]: 確保ブロックサイズの合計 0x00101da0 バイト
N[OSREPORT]: 空きブロックサイズの合計 0x00279d40 バイト
N[OSREPORT]: 最大空きブロックサイズ   0x00279d40 バイト
N[OSREPORT]: アリーナの内容 (0x80200800)
N[OSREPORT]: メモリブロック範囲 status サイズ  [時刻  s ms us ns: TID:src:行]
N[OSREPORT]: 81474320-81475ff0  確保 00001ca0 [6614004384124879: 0:**NULL**:0]
N[OSREPORT]: 81475ff0-81476100  確保 000000e0 [6614079643101817: 0:**NULL**:0]
N[OSREPORT]: 81476100-817efec0$ 確保 00379d90 [6614079644854188: 0:**NULL**:0]
N[OSREPORT]: 確保ブロックサイズの合計 0x0037bb10 バイト
N[OSREPORT]: 空きブロックサイズの合計 0x00000000 バイト
N[OSREPORT]: 最大空きブロックサイズ   0x00000000 バイト
N[OSREPORT]: MALLOC_MALLOC: u8 CHR_TO_I8_BUF_SIZE 1048576Byte 確保成功
N[OSREPORT]: MALLOC_MALLOC: ksNesCommonWorkObj sizeof(ksNesCommonWorkObj) 36728Byte 確保成功
N[OSREPORT]: MALLOC_MALLOC: ksNesStateObj sizeof(ksNesStateObj) 6776Byte 確保成功
N[OSREPORT]: MALLOC_MALLOC: u8 KS_NES_CHRRAM_SIZE 8192Byte 確保成功
N[OSREPORT]: MALLOC_MALLOC: u8 KS_NES_BBRAM_SIZE 32768Byte 確保成功
N[OSREPORT]: MALLOC_MALLOC: u8 KS_NES_NOISE_DATA_SIZE 520192Byte 確保成功
N[OSREPORT]: MALLOC_MALLOC: u8 KS_NES_DRAW_RESULT_BUF_SIZE 116736Byte 確保成功
N[OSREPORT]: buffer used:
N[OSREPORT]: wp            =  35.891KB
N[OSREPORT]: sp            =   6.641KB
N[OSREPORT]: nesromp       = 768.047KB
N[OSREPORT]: chrramp       =   8.016KB
N[OSREPORT]: bbramp        =  32.016KB
N[OSREPORT]: noise_bufp    = 508.016KB
N[OSREPORT]: chr_to_i8_bufp=1024.016KB
N[OSREPORT]: result_bufp   = 114.000KB
N[OSREPORT]: total          2496.641KB.
N[OSREPORT]: if chrrom-type, and no bbram, minimum total size:  664.547KB + nesrom size max + (nesrom chr size max * 4) bytes.
N[OSREPORT]: aram data . comptype. 2
N[OSREPORT]: dest, destLast = 8164ad20 816c9d20
N[OSREPORT]: MALLOC_MALLOC: u8 * nesrom_count * sizeof(u8*) 96Byte 確保成功
N[OSREPORT]: MALLOC_MALLOC: char length 671Byte 確保成功
N[OSREPORT]: nesrom_buffer_size=786480
N[OSREPORT]: 11:<GAME/01/10_cluclu_1.qd.szs>
N[OSREPORT]: aram data . comptype. 2
N[OSREPORT]: dest, destLast = 81580c40 815a0c40
N[OSREPORT]: aram data . comptype. 2
N[OSREPORT]: dest, destLast = 8037cae0 8037d020
N[OSREPORT]: nesinfo_tag_process1 開始
N[OSREPORT]: タグ=GNM 長さ=14
N[OSREPORT]: CLU CLU LAND D
N[OSREPORT]: タグ=GNO 長さ=1
N[OSREPORT]:  09
N[OSREPORT]: タグ=OFS 長さ=2
N[OSREPORT]: タグ=QDS 長さ=5
N[OSREPORT]:  00 a1 70 00 50
N[OSREPORT]: タグ=END 長さ=0
N[OSREPORT]: nesinfo_tag_process1 終了
N[OSREPORT]: nesinfo_tag_process1 開始
N[OSREPORT]: タグ=GNM 長さ=14
N[OSREPORT]: CLU CLU LAND D
N[OSREPORT]: タグ=GNO 長さ=1
N[OSREPORT]:  09
N[OSREPORT]: タグ=OFS 長さ=2
N[OSREPORT]: タグ=QDS 長さ=5
N[OSREPORT]: ディスクセーブエリアをロード
N[OSREPORT]:  00 a1 70 00 50
N[OSREPORT]: タグ=END 長さ=0
N[OSREPORT]: nesinfo_tag_process1 終了
N[OSREPORT]: nesinfo_tag_process2 開始
N[OSREPORT]: タグ=GNM 長さ=14
N[OSREPORT]: タグ=GNO 長さ=1
N[OSREPORT]: タグ=OFS 長さ=2
N[OSREPORT]: タグ=QDS 長さ=5
N[OSREPORT]: タグ=END 長さ=0
N[OSREPORT]: nesinfo_tag_process2 終了
N[OSREPORT]: ksNesReset() OK PC=ee24, prg_size = 0x0, chr_size = 0x2000
Console Debugging Logs for Animal Crossing
These are the developer debugging logs when attempting to load the furniture item Clu Clu Land D that has had its ROM replaced with with 19a_zelda_3.qd.szs in Animal Crossing.

Applies to Animal Crossing and DOES NOT handle the Famicom Disk System’s extra audio channel

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
N[OSREPORT]: ファミコンセーブデータを読み込みます
N[OSREPORT]: メモリーカードが、スロットに挿入されているかどうかを調べます。
N[OSREPORT]: メモリーカードをマウントする
N[OSREPORT]: メモリーカードはマウント状態になった
N[OSREPORT]: ファイルのオープン [DobutsunomoriP_F_SAVE]
N[OSREPORT]: ファイル読む
N[OSREPORT]: ファイル閉じる
N[OSREPORT]: 読み込み成功!!
N[OSREPORT]: ヘッダIDは正しいです
N[OSREPORT]: ヘッダチェックサムは正しいです
N[OSREPORT]: バージョンとヘッダサイズが合致しました
N[OSREPORT]: 個人ID&データチェックサム0は正しいです
N[OSREPORT]: 個人ID&データチェックサム1は正しいです
N[OSREPORT]: 個人ID&データチェックサム2は正しいです
N[OSREPORT]: 個人ID&データチェックサム3は正しいです
N[OSREPORT]: データは正常!!
N[OSREPORT]: ヘッダIDは正しいです
N[OSREPORT]: ヘッダチェックサムは正しいです
N[OSREPORT]: バージョンとヘッダサイズが合致しました
N[OSREPORT]: 個人ID&データチェックサム0は正しいです
N[OSREPORT]: 個人ID&データチェックサム1は正しいです
N[OSREPORT]: 個人ID&データチェックサム2は正しいです
N[OSREPORT]: 個人ID&データチェックサム3は正しいです
N[OSREPORT]: MALLOC_MALLOC: u8 CHR_TO_I8_BUF_SIZE 1048576Byte 確保成功
N[OSREPORT]: MALLOC_MALLOC: u8 KS_NES_NESFILE_HEADER_SIZE + KS_NES_PRGROM_SIZE + KS_NES_CHRROM_SIZE 786448Byte 確保成功
N[OSREPORT]: MALLOC_MALLOC: ksNesCommonWorkObj sizeof(ksNesCommonWorkObj) 36728Byte 確保成功
N[OSREPORT]: MALLOC_MALLOC: ksNesStateObj sizeof(ksNesStateObj) 6776Byte 確保成功
N[OSREPORT]: MALLOC_MALLOC: u8 KS_NES_CHRRAM_SIZE 8192Byte 確保成功
N[OSREPORT]: MALLOC_MALLOC: u8 KS_NES_BBRAM_SIZE 32768Byte 確保成功
N[OSREPORT]: MALLOC_MALLOC: u8 KS_NES_NOISE_DATA_SIZE 520192Byte 確保成功
N[OSREPORT]: MALLOC_MALLOC: u8 KS_NES_DRAW_RESULT_BUF_SIZE 116736Byte 確保成功
N[OSREPORT]: buffer used:
N[OSREPORT]: wp            =  35.891KB
N[OSREPORT]: sp            =   6.641KB
N[OSREPORT]: nesromp       = 768.047KB
N[OSREPORT]: chrramp       =   8.016KB
N[OSREPORT]: bbramp        =  32.016KB
N[OSREPORT]: noise_bufp    = 508.016KB
N[OSREPORT]: chr_to_i8_bufp=1024.016KB
N[OSREPORT]: result_bufp   = 114.000KB
N[OSREPORT]: total          2496.641KB.
N[OSREPORT]: if chrrom-type, and no bbram, minimum total size:  664.547KB + nesrom size max + (nesrom chr size max * 4) bytes.
N[OSREPORT]: MALLOC_MALLOC: u8 * nesrom_count * sizeof(u8*) 84Byte 確保成功
N[OSREPORT]: MALLOC_MALLOC: char length 581Byte 確保成功
N[OSREPORT]: nesrom_buffer_size=786480
N[OSREPORT]: 11:<GAME/01/10_cluclu_1.qd.szs>
N[OSREPORT]: nesinfo_tag_process1 開始 mode = 0
N[OSREPORT]: タグ=GID 長さ=3
N[OSREPORT]: CLU CLU LAND D
N[OSREPORT]: タグ=GNM 長さ=14
N[OSREPORT]:  09
N[OSREPORT]: タグ=GNO 長さ=1
N[OSREPORT]: タグ=OFS 長さ=2
N[OSREPORT]:  00 a1 70 00 50
N[OSREPORT]: タグ=QDS 長さ=5
N[OSREPORT]: nesinfo_tag_process1 終了
N[OSREPORT]: nesinfo_tag_process1 開始 mode = 1
N[OSREPORT]: タグ=GID 長さ=3
N[OSREPORT]: CLU CLU LAND D
N[OSREPORT]: タグ=GNM 長さ=14
N[OSREPORT]:  09
N[OSREPORT]: タグ=GNO 長さ=1
N[OSREPORT]: タグ=OFS 長さ=2
N[OSREPORT]: ディスクセーブエリアをロード
N[OSREPORT]:  00 a1 70 00 50
N[OSREPORT]: タグ=QDS 長さ=5
N[OSREPORT]: nesinfo_tag_process1 終了
N[OSREPORT]: nesinfo_tag_process2 開始
N[OSREPORT]: タグ=GNM 長さ=14
N[OSREPORT]: タグ=GNO 長さ=1
N[OSREPORT]: タグ=OFS 長さ=2
N[OSREPORT]: タグ=QDS 長さ=5
N[OSREPORT]: タグ=END 長さ=0
N[OSREPORT]: nesinfo_tag_process2 終了
N[OSREPORT]: ksNesReset() OK PC=ee24, prg_size = 0x0, chr_size = 0x2000
Noticeable Differences in Console Logs
These are the important logs present in Doubutsu no Mori+ that are NOT present in Animal Crossing, translated into English.

LogTranslation
アリーナの内容 (0x80200800)“Arena value (0x80200800)”
メモリブロック範囲 status サイズ [時刻 s ms us ns: TID:src:行]“Memory block range / Status size [Time s ms us ns: TID:src: row]”
81474320-81475ff0 確保 00001ca0 [6614004384124879: 0:NULL:0]“81474320-81475ff0 / Allocating 00001ca0 [6614004384124879: 0:NULL:0]”
81475ff0-81476100 確保 000000e0 [6614079643101817: 0:NULL:0]“81475ff0-81476100 / Allocating 000000e0 [6614079643101817: 0:NULL:0]”
81476100-81576150 確保 00100020 [6614079643103200: 0:NULL:0]“81476100-81576150 / Allocating 00100020 [6614079643103200: 0:NULL:0]”
81576150-817efec0$ 空き 00279d40“81576150-817efec0$ / Freeing 00279d40”
確保ブロックサイズの合計 0x00101da0 バイト“Total size allocated: 0x00101da0 bytes”
空きブロックサイズの合計 0x00279d40 バイト“Total size free: 0x00279df0 bytes”
最大空きブロックサイズ 0x00279d40 バイト“Maximum size free: 0x00279df0 bytes”
aram data . comptype. 2
dest, destLast = 8164ad20 816c9d20
MALLOC_MALLOC: u8 * nesrom_count * sizeof(u8*) 96Byte 確保成功“MALLOC_MALLOC: u8 * nesrom_count * sizeof(u8) 96Byte / Allocation succeeded”*
MALLOC_MALLOC: char length 671Byte 確保成功“MALLOC_MALLOC: char length 671Byte / Allocation succeeded”
Internal Code Examined by Cuyler
These are snippets of internal code Cuyler found relating to Famicom Disk System emulation and noise.bin, which handles audio emulation.

Applies to Animal Crossing only. This is NOT an exhaustive list of how the game handles audio emulation, only a few places where it pops up.

1
2
3
4
5
6
7
if ((famicomCommon.noise_bufp == NULL) || (iVar2 = JKRFileLoader::readGlbResource(famicomCommon.noise_bufp,0x7f000,"noise.bin.szs",1), iVar2 != 0)) {
  if (famicomCommon.noise_bufp != NULL) {
    jaudio_NES::EmuSound_Start(famicomCommon.noise_bufp + 0x2000);
  }

  /* ... */
}

Defaults audio emulation to noise.bin.szs + offset 0x2000, which makes sense since that is where the FDS BIOS ends and the sound handling begins.

1
2
3
4
5
6
7
8
9
10
11
com_work_obj->noisebuf_p[0xebd] = 0x42;

/* ... */

iVar4 = MSL_C.PPCEABI.bare.H::memcmp((void *)(*(int *)&state_obj->field_0x1868 + 0x10),"koro",4);
if (iVar4 == 0) {
  com_work_obj->noisebuf_p[0x1a0] = 0xff;
}
else {
  com_work_obj->noisebuf_p[0x1a0] = 0x7f;
}

If the game loaded has ‘koro’ in its internal name, it stores 0x7F at BIOS+0x1A0, otherwise it stores 0xFF at BIOS+0x1A0 (clearing the top bit).

To my knowledge, no internal FDS game with the tag ‘koro’ exists at offset 0x10. Further, the standard format for internal names at that offset is three ASCII characters, not four. Unknown how this applies.

Closing Thoughts

It is clear that Doubutsu no Mori+ loads the emulator in a way to handle the Famicom Disk System’s extra sound channel while Animal Crossing does not. Even though Animal Crossing’s emulator is perfectly capable of handling this sound channel, it elects to ignore it altogether for some reason. If this is due to innate code that purposefully skips over handling it, then there’s little we can do to correct this without modifying the game’s code. However, if the game can load this proper sound channel by structuring certain flags or bytes in the ROM or save file containing the ROM, then it would be possible on vanilla hardware. This is looking unlikely however, since the exact same ROM and save structure works in Doubutsu no Mori+ without working in Animal Crossing. This leads me to believe that the way the two games load up the emulator is inherently different, and Animal Crossing’s code will always choose to ignore the extra sound channel.

Unfortunately, I will likely not be pursuing this further until Prakxo and Cuyler’s Animal Crossing decompilation project is complete, which should give us a definitive answer. This may seem like a very small detail since Disk System emulation still works in Animal Crossing, but it is unfortunate that we cannot have the complete experience with the extra sound channel.

This post is licensed under CC BY 4.0 by the author.