DMAとHDMA

DMA とは direct memory access の略で、さまざまなコンピュータに搭載されており、 周辺機器とメモリの通信を、CPU を介すことなく行うことができる。 SNES では、DMA の通信中は CPU が一時的にストップする。

HDMA は DMA と似ていますが、こちらは少しのデータを、 スキャンライン毎の H-Blank 期間中に転送する目的で使われる。 HDMA は、表示を乱れさせることなく 1 フレーム中に PPU のレジスタを 操作する時に非常に役立つ。

DMA, HDMA 共にそれぞれ 8 つのチャネル (0 ~ 7) がある。 HDMA は DMA より優先順位が高く、データの転送が重なった場合は、 全ての DMA チャネルが一時停止され、重なった DMA チャネルはその場で停止される。 また、番号が小さいチャネルが優先されて動作する。

DMA

DMA には 3 つの動作モードと、いくつかの設定用ビットがある。

  • 方向 (0x43x0 のビット 7) : PPUの読み込み/書き込み
  • アドレス固定 (0x43x0 のビット 3) : アドレスを調整するかどうか
  • インクリメント (0x43x0 のビット 4) : アドレス調整の方向
  • モード (0x43x0 のビット 0 ~ 2) : 下記参照
  • ポート (0x43x1) : ここに 'xx' が指定された時、0x21xx にアクセスする
  • Aアドレス (0x43x20x43x4) : CPUアドレス。絶対ロングアドレッシングモード で値を入れる
  • カウント (0x43x50x43x6) : 転送バイト数

これらの値は、DMA転送前に設定する必要がある。

0x43x0 のモードビットと転送モードは互いに関係している。 注:1つのレジスタに1度書き込むのと、1つのレジスタに2度書き込むのは同じことが起きる。  2つのレジスタに1度書き込むのと、2つのレジスタにそれぞれ交互に1度ずつ書き込むのも  同じことが起こる。  しかし、2つのレジスタに1度書き込むのと、2つのレジスタにそれぞれ交互に1度ずつ書き込むのは違う。 (訳者注:よく分かりません。)

DMAが1バイト転送する間に、マスターサイクルで8クロックの遅延がある。 (FastROM の設定に関係なく) チャネルごとに8クロックの遅延があり、全て転送するまでに12~24の遅延が起こる。

転送プロセス

  1. 1バイトを取得して転送先に書き込む
    • DMAは2つのアドレスバスと1つの共有データバスへの優先順位を得ているように見える。 AアドレスはバスAに、ポートはバスBに、それぞれ指定された方向に読み込み/書き込み信号を送る。 1つのバスは「読み込み」に設定され、データがバスに流れる。 もう1つは「書き込み」に設定され、バスのデータが書き込まれる。
    • PPU/APU/WRAMレジスタはバスBからのみアクセスできる。 これらに対してAアドレスでアクセスしようとすると、オープンバスアクセスの値を返す。
    • WRAMに対してバスA、バスB (0x21800x2183) の両方を使って アクセスしようとすると失敗する。 0x2180~0x2183 はオープンバス化されている。
    • また、DMAは 0x4300 ~0x437f、0x420b0x420cに対してアクセスすることはできない。 書き込みは変化なし、読み込みはオープンバスが返る。
  2. Aアドレスの調整
    • 「アドレス固定」ビットがセットされている時は何もしない。 「インクリメント」ビットがセットされている時は1減算、 セットされていない時は1加算する。
    • 注:バンクバイトは変更されない。
  3. カウント値のデクリメント。0でない場合、1から繰り返す
    • カウント値が最初から0の場合、値がテストされる前に65535にラップされ、 これにより、65536バイトが転送される。

注:カウント値 (0x43x50x43x6) は通常0になるまで繰り返されるが、 HDMAと衝突した場合、転送は途中で中止 (terminate) される。

HDMA

HDMAには4つのフラグと、5つの設定項目がある。 (*) の付いている項目はHDMAの転送前にセットする必要があり、 (+) の付いている項目はHDMAの転送が始まったフレーム中に変更する必要がある。

  • (*) アドレッシングモード (0x43x0 のビット 6) : 0 = 直接, 1 = 間接
  • (*) 転送モード (0x43x0 のビット 0~2) : 下記参照
  • (*) ポート (0x43x1) : DMAと同じ
  • (*) Aアドレス (0x43x20x43x4) : HDMAテーブルへのポインタ。 必ずしも転送中のフレームに変更する必要はないが、次の転送前に停止するためには変更する必要がある。
  • 間接アドレス (0x43x50x43x6) : 間接バンクと一緒に使用。下記参照。
  • (*) 間接バンク (0x43x7) : 間接アドレスと一緒に使用。下記参照。
  • (+) アドレス (0x43x80x43x9) : 下記参照。
  • (+) 繰り返し (0x43xA のビット 7) : 全てのスキャンラインに書き込むかどうか
  • (+) 行カウンタ (0x43xA のビット 0~6) : 下記参照。
  • 転送実行フラグ(DoTransfer) : 内部的に使用。

モード値はDMAと同じだが、スキャンライン毎に1度だけ実行される。 One Register Write Once モードはスキャンライン毎に1バイトだけ、 One Register Write Twice モードではスキャンライン毎に2バイト書き込まれる。

スキャンライン毎にHDMAがアクティブになる度に (つまり、そのフレームで少なくとも1つは停止も終了もされていない時)、 マスターサイクルで~18のオーバーヘッドがかかる。 それぞれのチャネルは転送が実際に起こった場合、 スキャンラインごとに追加で8のオーバーヘッドがかかる。 (たぶんテーブルから0x42xAに値がロードされているだろう) 間接アドレスが変更される場合、ロード時に 16のマスターサイクルが必要になる。 そして、1バイトの転送ごとに8サイクルかかる。 結果として、HDMAはスキャンラインごとにマスターサイクルで 合計466サイクルの時間を必要とする。 (8チャネル全てがアクティブ、間接アドレスの変更、4バイトの転送が全て同時に起こった場合)

HDMAの動作には2つの段階があり、 最初の段階はフレーム開始時に (V(垂直)=0, H(水平)=約6)、 全てのアクティブなHDMAチャネルに対して発生する(0x420c 参照)。

  1. 「アドレス」値に、Aアドレスがコピーされる
  2. テーブルから 0x43xA に対して値をロードする (0x00 をロードすると、その場でチャネルを停止する…だろう)
  3. 必要なら、間接アドレスをロードする
  4. 転送実行フラグ(DoTransfer) をTrueにする

CPUはこの動作の間停止される。 マスターサイクルで~18のオーバーヘッドがあり、 それぞれのチャネルが、直接モードのHDMAチャネルにセットされている時は8サイクル、 間接モードのHDMAチャネルにセットされている時は24サイクルが追加で必要となる。

フレーム開始後にHDMAを開始させた場合は基本的に、 間接モードチャネル用に 0x43x80x43xA0x43x50x43x6 を使って 手動で初期化プロセスを実行する必要がある。 次のステップの4段階目の最初の1回は転送が行われないので注意すること。 それから、フレーム中にチャネルがすでに停止されていた場合、再スタートすることはできない。 (もしくはチャネルを有効にした時に4段階目が自動的に実行されてしまう??)

V=0 から V=0xe0 (オーバースキャンが有効の場合、V=0xef) の H=0x116 くらいの時に次の動作が始まる。

  1. 転送実行フラグ(DoTransfer) が False の場合、3段階目へ
  2. この転送モードの時、1か2か4バイトが必要
    1. アドレス/間接アドレスのいずれかから1バイト読み込み、インクリメントする。
    2. ポートに1バイト書き込む。Port+1, Port+2, Port+3 も、転送モードによっては書き込む。
    • DMAの時と同じように、0x2180 を通じてPPU から PPU、RAM から RAMに転送する時は注意が必要。
  3. 0x43xA をデクリメント
  4. 転送実行フラグ(DoTransfer) に「繰り返し」ビットと同じ値をセット
  5. 行カウンタが0の場合
    1. 「アドレス」から次の1バイトを読み込み、0x43xA に入れる(行番号と繰り返しも同じようにする)。
    2. 間接アドレスモードの時、「アドレス」から2バイト読み込み、「間接アドレス」に入れ、 「アドレス」の値を2バイト分インクリメントする。
      • 注(奇妙な動作):0x43xA が 0 で、処理中のチャネルが現在の行で最後のHDMAチャネルだった場合、 「アドレス」から1バイトのみ読み込まれ、下位バイトには0x00が適用される。 「アドレス」は1つだけインクリメントされ、1少ないCPUサイクルが消費される。
    3. 0x43xA が0の時、処理中のHDMAチャネルのこのフレームでの転送は終了する。 0x420c のビットはクリアされないが、次のフレームには自動的に開始される。
    4. 転送実行フラグ(DoTransfer) をTrueにする
  6. 次のスキャンラインで1段階目から繰り返す

HDMAはV-Blank期間中は起こらない。 画面エフェクトは何も起こらないだろう。 フレーム開始時、(V-Blankの終了時)にアクティブなチャネルはリセットされる。 V-Blank中にHDMAレジスタを更新することで、 書き込み直後に転送されたり、PPUの状態を意図せず変更してしまったり というような、厄介な挙動を避けることができる。

今までHDMAテーブルというものが暗黙的に使われてきたが、 これは項目の配列を指し、行番号と繰り返しフラグから始まる。 繰り返しフラグがFalseの時、1スキャンライン分のデータと、 次の項目が始まる前までの行番号の入ったカウント値が続く。 繰り返しフラグがTrueの時、複数のスキャンラインのデータが続く。 このデータは、ポインタ値(間接HDMA)か即値(直接HDMA)を指す。

以上のことから、フレーム処理中にHDMAを開始する時に なぜ手動で「アドレス」、「繰り返し」、「行番号」を初期化しないと いけないのかが見えてくると思う。 これらはフレーム開始時に初期化されている。 「アドレス」、「繰り返し」、「行番号」はHDMAによって変更されるが、 「Aアドレス」は変更されないことに注意して欲しい。

タグ:

+ タグ編集
  • タグ:

このサイトはreCAPTCHAによって保護されており、Googleの プライバシーポリシー利用規約 が適用されます。

最終更新:2017年02月20日 16:53