NES on FPGA 18:25 2005/09/26

APU (Audio Processing Unit)

 NESはオーディオチャンネルとして、 矩形波×2、三角波、ノイズ、DMC(Delta modulation channel)を持っています。 それぞれのチャンネルはCPUから書き込まれるデータによって音の周波数や音量、 音色などが設定されます。

[レジスタ] [フレームシーケンサ] [エンベロープジェネレータ] [タイマ] [長さカウンタ] [スイープユニット] [矩形波] [線形カウンタ] [三角波] [ノイズチャンネル] [DMC] [出力]

各チャンネルでは、以下のサブユニットを使用します。

ユニット                 矩形波 三角波 ノイズ  DMC
----------------------------------------------------
ボリューム/エンベロープ    ○            ○
タイマ                     ○     ○     ○     ○
長さカウンタ               ○     ○     ○
スイープユニット           ○
デューティ                 ○
線形カウンタ                      ○
三角ステップ生成器                ○
波長コンバータ                           ○     ○
擬似ランダム生成器                       ○
! APUはチップ単体として存在してるわけではないので、pAPU(pseud:擬似)とも呼ばれる。

apu_ref.txt、nessound.txtを参考にしました。

NESの型番によってチャンネルの挙動や出力のミキシングが微妙に異なるらしい。

なんかズレてる?

_
コントロールレジスタ

 各チャンネルには4つのI/Oレジスタを割り当て、 ここに書き込まれる値によってサウンドコントロールを行います。 三角波とノイズチャンネルでは2番目のポートは使用しません。

$4000 矩形波1コントロール1
$4001 矩形波1コントロール2
$4002 矩形波1周波数1
$4003 矩形波1周波数2
$4004 矩形波2コントロール1
$4005 矩形波2コントロール2
$4006 矩形波2周波数1
$4007 矩形波2周波数2
$4008 三角波コントロール
$4009 未使用
$400A 三角波周波数1
$400B 三角波周波数2
$400C ノイズコントロール
$400D 未使用
$400E ノイズ周波数1
$400F ノイズ周波数2
$4010 DMCコントロール1
$4011 DMCコントロール2
$4012 DMCアドレス
$4013 DMCデータ長

$4015 サウンドコントロール

$4017 サウンドフレーム

_
フレームシーケンサ

 フレームシーケンサは各チャンネルのモジュールを励起したり、 フレームIRQタイミングを生成したりします。これは分周器とシーケンサで構成します。 分周器は、入力される約1.789MHzを7457分周することで240Hzのクロックレートを生成し、 これによってシーケンサを励起します。

 $4017への書き込みによって分周器とシーケンサをリセットします。 このレジスタの値によってシーケンサの2種類のモードとフレームIRQ生成の設定を行います。

$4017    mi-- ----
   7 m  シーケンサのモード
   6 i  IRQ無効フラグ

 シーケンサモードがクリアされているなら4ステップ、 セットされているなら5ステップのシーケンスを選択します。

    f = 割り込みフラグセット
    l = 長さカウンタとスイープユニットのクロック生成
    e = エンベロープと三角波の線形カウンタのクロック生成

モード0: 4ステップ 有効レート(おおよそ)
---------------------------------------
    - - - f      60 Hz
    - l - l     120 Hz
    e e e e     240 Hz

モード1: 5ステップ 有効レート(おおよそ)
---------------------------------------
    - - - - -   (割り込みフラグはセットしない)
    l - l - -    96 Hz
    e e e e -   192 Hz

_
エンベロープジェネレータ

 エンベロープジェネレータは矩形波とノイズチャンネルで愛用し、一定のボリュームや、 ループするのこぎりエンベロープを生成します。これは分周器とカウンタから構成します。 エンベロープは各チャンネルの最初のレジスタで制御します。 ループフラグのビットは、長さカウンタのフラグビットとしても使用していることに注意してください。

1番目のレジスタ  --ld nnnn
   5   l  ループフラグ
   4   d  エンベロープ無効フラグ
   3-0 n  エンベロープ周期
分周器の周期は n+1 となります。

 フレームシーケンサによって励起されるとき、 最後のクロック以降チャンネルの4番目のレジスタへの書き込みがあった場合、 カウンタへ$Fをセットし、分周器へエンベロープ周期をセットします。 そうでなければ、分周器を励起します。

 分周器が励起されるとき、カウンタがゼロでなければデクリメントします。 カウンタがゼロで、ループフラグがセットされているならカウンタへ$Fをセットします。

 チャンネルのボリューム出力として、 エンベロープ無効フラグがセットされているなら、 エンベロープ周期のnをそのまま出力します。 クリアされているならカウンタの値を出力します。

! 分周器の+1は、セットされる周期に1を加算するわけではなく、 カウンタのリロードが含まれるため、セットされた値+1の周期となる。

_
タイマ

 タイマは全てのチャンネルで出力周波数を決定するために使用します。 タイマは分周器を持っており、約1.789MHzで励起されます。

矩形波と三角波では3番目と4番目のレジスタ、

$4002,$4006,$400A  llll llll
   7-0 l   タイマ周期の下位8ビット

$4003,$4007,$400B  ---- -hhh
   2-0 h   タイマ周期の上位3ビット
の11ビット値+1の値を分周器の周期とします。

ノイズとDMCはタイマの周期として波長コンバータからの値を使用します。

$400E,$4010  ---- iiii
   3-0 i   タイマ周期インデクス
タイマ周期インデクスが波長コンバータに入力され、 その出力を分周器の周期とします。

_
長さカウンタ

 長さカウンタによって音が鳴る時間を設定することができます。 これはカウンタと停止フラグを含みます。

 カウント停止フラグビットはチャンネルの一番目のレジスタの、 矩形波とノイズについてはビット5、三角波についてはビット7が割り当てられています。 停止フラグビットはまた、長さカウンタ(ノイズと矩形波)もしくは線形カウンタ(三角波)における他のフラグにも割り当てられていることに注意してください。

$4000,$4004,$400C  --h- ----
   5  halt (ノイズと矩形波チャンネル)

$4008              h--- ----
   7  halt (三角波チャンネル)

 チャンネルの4番目のレジスタへの書き込みによって、 上位5ビットによるインデックスに基づいて、 ルックアップテーブルからの値をカウンタにロードします。 フレームシーケンサによって励起されるとき、 停止フラグがクリアかつカウンタがゼロでないなら、カウンタをデクリメントします。

bit
3 7-4 76543 カウント値
------------------
0  0  00000   $0A
   1  00010   $14
   2  00100   $28
   3  00110   $50
   4  01000   $A0
   5  01010   $3C
   6  01100   $0E
   7  01110   $1A
   8  10000   $0C
   9  10010   $18
   A  10100   $30
   B  10110   $60
   C  11000   $C0
   D  11010   $48
   E  11100   $10
   F  11110   $20
1  0  00001   $FE
   1  00011   $02
   2  00101   $04
   3  00111   $06
   4  01001   $08
   5  01011   $0A
   6  01101   $0C
   7  01111   $0E
   8  10001   $10
   9  10011   $12
   A  10101   $14
   B  10111   $16
   C  11001   $18
   D  11011   $1A
   E  11101   $1C
   F  11111   $1E
カウンタはリセットによって1をロードします。

 ステータスレジスタ$4015の対応するビットがクリアされることで、 すぐにカウンタへ0をセットしチャンネルを無音化します。 またCPUがステータスレジスタを読むことで、カウンタの状態が分かります。 カウンタがゼロならチャンネルの対応するビットをクリアし、 そうでなければセットします。 チャンネルに対応するビットは次のようになっています。

$4015   ---d ntss
   4 d   DMC
   3 n   ノイズチャンネル
   2 t   三角波
   1 s   矩形波2
   0 s   矩形波1
! このユニットが60Hzで励起されるのは、 画面の描画タイミングに合わせるため?

_
スイープユニット

 スイープユニットは矩形波チャンネルの周波数を変化させ、 しり上がり、もしくはしり下がりといった音の効果を提供します。 これはスイープユニットが停止させられるまで継続します。

 スイープユニットは分周器とシフタから構成し、 チャンネルの2番目のレジスタによって設定されます。

$4001,$4005    eppp nsss
   7   e   スイープ有効フラグ
   6-4 p   スイープ周期
   3   n   スイープ方向
   2-0 s   スイープ量
分周器の周期は p+1 となります。

 チャンネルの4番目のレジスタへの書き込みによって、スイープユニットをリセットします。 次のクロックから分周器が励起され、分周器の出力クロックによってスイープを行います。

 それぞれのスイープ更新クロックにおいて、

- スイープ有効フラグがセットされている
- スイープ量が0ではない
- チャンネルの長さカウンタがゼロではない
以上の3つの条件がすべてそろえばチャンネルの周期を新しい値で更新します。
ビット3(スイープ方向)
----------------------
 0  しり下がりモード    新しい周期 = 周期 + (周期 >> N)
 1  しり上がりモード    新しい周期 = 周期 - (周期 >> N)
このNは0から7までのスイープ量です。

しり上がりモード(スイープ方向が1)ではチャンネルによって次の違いがあります。
- 矩形波チャンネル1では1の補数(NOT)を使用する。
- 矩形波チャンネル2では2の補数(NEG)を使用する。
これは、2つの矩形波チャンネルにおいて現在知られているだたひとつの違いらしいです。

 もしチャンネルの周期が8未満か、$7FFより大きくなったなら、スイープを停止し、 チャンネルを無音化します。これはスイープユニットが無効であっても働きます。

_
矩形波

 矩形波チャンネルはエンベロープジェネレータ、スイープユニット、タイマ、 シーケンサ、長さカウンタで構成します。

$4000/$4004   ddld nnnn
   7-6 d   デューティ
   5   l   エンベロープループ
   4   d   エンベロープ無効
   3-0 n   ボリューム/エンベロープ周期

$4001/$4005   eppp nsss
   7   e   スイープ有効
   6-4 p   スイープ周期
   3   n   スイープ方向
   2-0 s   スイープ量

$4002/$4006   llll llll
   7-0 l   チャンネル周期下位

$4003/$4007   cccc chhh
   7-3 c   長さカウンタインデクス
   2-0 h   チャンネル周期上位

 1番目のレジスタによってデューディサイクルが設定されます。 シーケンサはタイマから励起され、次のような波形を出力します。

    dd   波形
    ---------------------
          _       1
    00   _ ______ 0 (12.5%)

          __      1
    01   _  _____ 0 (25%)

          ____    1
    10   _    ___ 0 (50%)

          ______  1
    11   _      _ 0 (75%)

 4番目のレジスタへ書き込まれたとき、シーケンサをリセットします。 シーケンサの出力が0のとき、チャンネルの出力は0となります。

_
線形カウンタ

 線形カウンタは、三角波チャンネルのための二つ目のより正確な音長コントロールとして動作します。 これはカウンタを含みます。

レジスタ$4008にコントロールフラグとリロード値が含まれます。

$4008    crrr rrrr
   7   c   コントロールフラグ
   6-0 r   音の長さ

 コントロールフラグのビット位置はまた、長さカウンタ停止フラグに反映されることに注意してください。

 レジスタ$400Bへの書き込みによって、線形カウンタを停止し、カウンタへ音の長さをロードします。 フレームシーケンサによって励起されるとき、 コントロールフラグがクリアかつカウンタがゼロでなければカウンタをデクリメントします。

! 線形カウンタのみで使用することはできない。

_
三角波

 三角波チャンネルは、タイマ、長さカウンタ、線形カウンタ、32ステップのシーケンサで構成します。

$4008  clll llll
   7   c   長さカウンタ無効フラグ
   6-0 l   線形カウンタ

$400A  llll llll
   7-0 l   チャンネル周期下位

$400B  llll lhhh
   7-3 l   長さカウンタインデクス
   2-0 h   チャンネル周期上位

 タイマがクロックを生成し、長さカウンタ及び線形カウンタが両方ともゼロカウントでなければ、 シーケンサを励起します。  シーケンサは、下記の32ステップシーケンスを繰り返し出力します。


F E D C B A 9 8 7 6 5 4 3 2 1 0 0 1 2 3 4 5 6 7 8 9 A B C D E F

 シーケンサが励起されなければチャンネルの出力は一定値となるため、 音は聞こえません。 また周期が短い($400B=0と$400A=0か1)場合、 生じる周波数は非常に高いものとなりますが、 可聴領域を越えているため人間の耳には聞こえません。

! 三角波はよくベースラインとして使用されます。 もちろんメロディーラインも。

音量の調節はできません。

高音域になるほど音痴になるらしい。

_
ノイズチャンネル

 ノイズチャンネルは長さカウンタ、 エンベロープジェネレータ、タイマ、15ビットのリニアフィードバックシフトレジスタで構成します。 この15ビットシフトレジスタによって擬似乱数を生成、ノイズとなります。

$400C   --le nnnn
   5   l   エンベロープループ、長さカウンタ無効
   4   e   エンベロープ無効フラグ
   3-0 n   ボリューム/エンベロープ周期

$400E   s--- pppp
   7   s   ランダム生成モード
   3-0 p   タイマ周期インデクス

$400F   llll l---
   7-3 l   長さインデクス

 $400Eの周期インデックスによって、表の波長コンバータからタイマの周期を選択しセットします。

  値 タイマ周期 オクターブ スケール
-----------------------------------
  $0   $004       15  A
  $1   $008       14  A
  $2   $010       13  A
  $3   $020       12  A
  $4   $040       11  A
  $5   $060       11  D
  $6   $080       10  A
  $7   $0A0       10  F
  $8   $0CA       10  C
  $9   $0FE        9  A
  $A   $17C        9  D
  $B   $1FC        8  A
  $C   $2FA        8  D
  $D   $3F8        7  A
  $E   $7F2        6  A
  $F   $FE4        5  A

 ランダム生成モードフラグがセットされていればショートモード、 クリアされていればロングモードとなります。 ショートモードの時のビットシーケンスは93ビット、 ロングモードの時は32767ビットです。

 15ビットシフトレジスタにはリセット時に1をセットしておく必要があります。 タイマによってシフトレジスタが励起されるたびに1ビット右シフトし、 ビット14には、ショートモード時にはビット0とビット6のEORを、 ロングモード時にはビット0とビット1のEORを入れます。

 シフトレジスタのビット0が1なら、チャンネルの出力は0となります。

! ノイズは主にパーカッションとして使用されます。

LFSR(Linear Feedback Shift Register):M系列のビットパターンを容易に生成する。 安価な擬似ランダム発生器としてよく使用される。

_
DMC (Delta Modulation Channel)

 DMCは1ビットのDPCM(Differential Pulse Code Modulation)サンプルを出力します。 これはDMAリーダ、割り込みフラグ、サンプルバッファ、タイマ、 出力ユニット、7ビットのデルタカウンタによって構成します。 デルタカウンタの出力はDACへ接続します。

$4010    il-- ffff
   7   i    割り込み有効フラグ
   6   l    ループフラグ
   3-0 f    周期インデクス

$4011    -ddd dddd
   6-0 d    デルタカウンタ初期値

$4012    aaaa aaaa
   7-0 a    サンプル開始アドレス

$4013    llll llll
   7-0 l    サンプルバイト数

 レジスタ$4010への書き込みによって、割り込み有効、ループ、タイマ周期をセットします。 もし新しい割り込み有効フラグがクリアなら、割り込みフラグをクリアします。 タイマ周期インデックスにより波長コンバータからタイマ周期をタイマへ入力します。

  値 タイマ周期 オクターブ スケール
-----------------------------------
  $0    $1AC      5   C
  $1    $17C      5   D
  $2    $154      5   E
  $3    $140      5   F
  $4    $11E      5   G
  $5    $0FE      5   A
  $6    $0E2      5   B
  $7    $0D6      6   C
  $8    $0BE      6   D
  $9    $0A0      6   F
  $A    $08E      6   G
  $B    $080      6   A
  $C    $06A      7   C
  $D    $054      7   E
  $E    $048      7   G
  $F    $036      8   C

 デルタカウンタはパワーオン時に0をセットします。 $4011への書き込みによってデルタカウンタに新しい値をセットします。

$4011   -ddd dddd     新しいデルタカウンタ値

 サンプルバッファは、 出力ユニットへ連続してサンプルを渡せるように1サンプルバイトを保持します。 DMAリーダによってフェッチされたサンプルが一時的にストアされ、 出力ユニットによって空にされます。 つまり一旦配置されたサンプルバイトはいずれ出力されることになります。

 DMAリーダはサンプルバッファが空になったとき、次のサンプルバイトをストアします。 これは、アドレスカウンタと残りバイトカウンタを持っています。 DMCサンプリングを開始するとき、 アドレスカウンタにはレジスタ$4012 * $40 + $C000をセットし、 残りバイトカウンタにはレジスタ$4013 * $10 + 1をセットします。
・残りバイトカウンタがゼロでないとき、サンプルバッファが空になったら、 DMAリーダはアドレスカウンタが示すアドレスからサンプルバイトをフェッチします。 このアドレスはCPUのメモリマップを示します。 フェッチしたサンプルバイトはサンプルバッファへストアします。 アドレスカウンタをインクリメントし、$FFFFをこえた場合は$8000へ丸め込みます。 また残りバイトカウンタをデクリメントします。
・残りバイトカウンタがゼロになった場合、 ループフラグがセットされているなら残りバイトカウンタを$4013の値によってリセットします。 ループフラグがクリアされており、 割り込み有効フラグがセットされているなら割り込みフラグをセットします。
DMAリーダがメモリへアクセスする間、CPUは4クロックサイクル停止します。

 出力ユニットは、連続するサンプルによるサウンドを出力します。 これは8ビット右シフトレジスタ、カウンタ、サイレンスフラグを含みます。
サンプルバッファが空である間サイレンスフラグをセットします。 そうでなければサイレンスフラグをクリアし、 サンプルをシフトレジスタにストアすることによって出力サンプリングを開始します。 カウンタには$7をロードします。
出力ユニットがタイマから励起されるとき、 サイレンスフラグがクリアされているなら、次の動作を行います。

シフトレジスタのビット0
-----------------------
        0        デルタカウンタが   1 より大きいなら -2
        1        デルタカウンタが 126 より小さいなら +2

 またカウンタがゼロでなければシフトレジスタを1ビット右シフトし、カウンタをデクリメントします。

! 適当なプログラム領域をサンプルとして指定して、 なんとなくバスドラっぽい音を出してるゲームもあるらしい。

_
出力

 出力としてDMCは7ビット、それ以外は4ビットとなります。 全てのチャンネルとマイクからの入力が加算され、本体から一旦カートリッジに出力されます。 カートリッジに拡張音源が存在するならその出力と加算され、また本体に入力されます。 この信号をオーディオとして出力します。


Copyright(C) pgate1 All Rights Reserved.