「HDMA入門」の編集履歴(バックアップ)一覧に戻る
HDMA入門 - (2011/06/30 (木) 18:44:30) のソース
*ここは? [#h9022c01] 資料が英語ばかりの鬼門「HDMA」の説明らしき物です。~ しかしHDMAはおろかWikiの編集も付け焼刃なのでグダグダです。~ 墓場のうんちくが来るまでの間に合わせってことでよろしく。~ *目次 [#m117020a] #contents *?描画あれこれ [#qe5799a9] ~ **走査線/Scanline、H-blank、V-blank [#s7ad179f] SFCでは画面が1秒間に60フレームほど描かれている気がします。~ そのうちの1回を取り上げて考えてみましょう。~ ~ &ref(a.png);~ ~ まずは左上の1ドットから描画がスタートし、右上に向かいます。~ こうして一番上の1行が描かれます。~ こういった横方向の行を「走査線 Scanline」と言います。~ 一番上の行がScanline 0です。~ 二番目の行がScanline 1です。~ ~ 一行目が右まで描かれると、次は二行目の左端を描くことになりますが、~ それまでに僅かな休み時間があります。これがH-blankです。~ 同様に、各Scanlineの描画が終わるたびにH-blankが来ます。~ ~ ゲーム画面の一番下の行はScanline 225 です。~ ここまで描ききった後のScanline 226?261にあたる期間は~ もうずっと休み時間です。これをV-blankと言います。~ H-blankが土日だとしたら、V-blankは長期休暇みたいなものなので長いです。~ その後はScanline 0に戻り、次のフレームの描画が始まります。~ ~ **実際に描画するには [#zee7eb24] さて、なぜこんなblankの話をしてるかというと、~ 描画情報の変更はこのblank中でやらなければならないからです。~ だからLevelASMとかで直接描画情報を変えようとしても、~ その瞬間が奇跡的にblank中だった場合しかうまく行かないことになります。~ ~ ただしいくつかのエミュではこういうのを考慮してないので~ blank無視しても正常に動作してしまう罠。~ 実機で動かなくてもエミュで動けばいいじゃんと見るかどうかは人によるけど…~ 最新のエミュでは、ちゃんと(?)blank無視した変更は反映されないようになってます。~ ~ じゃあblankのタイミングを狙うにはどうすればいいのか?~ 1つは、NMIという割り込みを利用する方法です。~ 画面下、Scanline 225を描いてV-blankに突入した瞬間、~ 流れているプログラムは一旦ストップ。~ Snes:$00816Aから始まるNMIルーチンが割り込んできます。~ この中でblank中でなければならない処理を~ まとめてやってしまうというのです。~ ~ しかしblankはV-blankだけではありません。~ H-blank中に描画設定を変えるとどうなるでしょう。~ たとえば、Scanline0の前で明るい画面に設定し、~ Scanline112の後のH-blankで暗い画面に設定すれば、~ 画面上半分(0?112)は明るく、画面下半分(113?225)は暗くなります。~ このようにScanlineとScanlineの間で描画設定を変えることができます。~ ~ こういった、指定したH-blankで描画設定を変えるという処理を~ 自動的にやってくれる装置があります。~ これがHDMAです。~ ~ ~ ~ *?まず何をすればいい? [#o1443331] ~ まずはHDMAを実装しましょう。いくつか方法があります。~ ・CからBMF98567氏のHDMAを持ってきてインストール~ ・自作物展示場や、あっぷろだXのASM_Supporter~ ~ 余計な機能がつきすぎるのが嫌でなければ、~ ASM_Supporterをオススメします。~ 後先考えるとインスタントNMIがあった方が便利っちゃあ便利。~ xkasの使い方は、あっぷろだXを参照。~ いや、CMのつもりでは…~ ~ 要望があれば、HDMAだけを入れるバージョンも作りますが。~ ~ ~ *?PPU[#jff40a94] ~ さんざん「描画設定を変える」とかいう表現を使ってきたわけですが、~ とりあえずそれがどういうことかを知らなければなりません。~ ~ SFCにはPPUというユニットがあり、~ こちらのプログラムとは独立して、描画処理を行ってます。~ このPPUにこちらから働きかけます。~ 難しそうですが、結局は$21xxへのストアです。~ $21xxに値をストアするというのがPPUとの手動通信です。~ ~ とりあえず[[すずめ愛好会のこのページ>http://web.archive.org/web/20070223001621/vsync.org/sn/index.html]]を見てみましょう。~ ~ 対してHDMAは自動通信です。~ HDMAは''「Scanline毎に$21xxの値を自動で変えてくれる物」''~ だということになります。~ ~ ~ *?チャンネルとテーブル [#r8718413] ~ ついに実際に簡単なHDMA効果を作ってみます。~ しかし、いきなりLevelASMから作るのは大変なので、~ チートで作ることにしましょう。~ テストもしやすいですし。~ ~ というわけで、代入可能なメモリビューアがついているエミュを用意しましょう。~ ちなみに私が使っているのは音楽再現度的な意味でSNESGTです。~ 最新のβ版ではblankも考慮されている上、~ 指定アドレスにジャンプができるので旧版より使いやすいです。~ ~ **テーブル [#j3bdbd92] まずHDMAをインストールしたROMを起動して好きなLevelに行き、ポーズ。~ メモリビューアを開いて、HDMAテーブルのある位置を見ましょう。~ 初期設定ではテーブルの位置は、BMF98567氏のHDMAでは$7FFF00~ ASM_Supporterでは$7FFE00と決められています。(現在の設定では$7F8190ですが変更可能)~ この先は後者として話を進めていきますので、~ 前者の場合は、各アドレスの3桁目をE⇒Fと脳内変換してください。~ ~ これからこのテーブルという領域に、~ HDMAに必要な手続きを行っていきます。~ そしたら先ほどインストールしたHDMA内部機構が~ 手続きに従い、自動的にHDMAを実行してくれるのです。~ ~ ~ **チャンネル [#c977e4fd] HDMAには0?7の8つのチャンネルがあります。~ 各チャンネルに「Scanline毎に$21xxの値を変える」という~ 一まとまりの仕事を割り振ることができます。~ つまり最大同時に8種類の仕事をさせることができるのです。~ ~ ただ本家でもHDMAが使われている箇所があるので、~ 本家HDMAの使用チャンネルとかぶるとキャンセルされてしまうかも。~ チャンネル3辺りを使えば心配無いでしょう。~ 本家HDMAの使用状況を知りたければ、~ [[ここ>http://geigercount.net/crypt/]]にある''デバッグ用Snes9x''でHDMAをトレースしてみましょう。~ ~ ~ **補足:チャンネルとテーブルの関係 [#b19b7240] 自分が理解するのに詰まったところなので勝手に補足。~ さて、上記のようにHDMAには8つのチャンネルがあり、同時に最大8種類の効果を出すことができます。~ 色々弄れば色々効果が出せるわけですが難しい。そこで「必要事項を指定の場所に入れてくれれば後はやってやんよ」~ というのがBMF98567氏の作ったものです。~ まず「チャンネルの設定パラメータを入れるRAMアドレス」(=チャンネルごとのテーブル)がチャンネルごとに6つずつあります。~ ここに転送方法とか弄る対象とか、設定を適宜入れていきます。~ ここで設定した「転送したいデータを入れるRAMアドレス」(=転送内容のテーブル)に、弄る走査線の数などを入れていきます。~ 前者はxkasなどでプログラムを挿入するときに決めるもので、後から変更はできず、~ 後者は毎回自分の好きなところに指定する必要があります。~ 次項からその実践です。~ ~ *?実践1:サイズの異なるモザイク [#bc6548fb] ~ **チャンネルの設定 [#se5733dc] まずはシンプルな1バイト入力系をやりましょう。~ モザイク設定は$2106ですね。~ テーブル周辺ははじめは全部00になっています。~ ここにメモリビューアから入力していきます。~ 結論を言うと、下のスクショですね。~ ~ &ref(b.png);~ ~ ここまで行く過程を説明しましょう。~ まず「チャンネル3」を使うことに決めました。~ 図のチャンネル3テーブルに基本設定パラメータを書いていきます。~ ~ まずはチャンネルON宣言です。~ テーブルの1バイト目には、下の表に従って値を入れましょう。~ 今回はチャンネル3なので、08ですね。~ |チャンネル|0|1|2|3|4|5|6|7| |1バイト目の値|01|02|04|08|10|20|40|80| ただし、今すぐ入力するのはやめてください。~ まだ設定が終わってないのにスイッチONにするのは自殺行為ですね。~ 軽くバグります。~ ~ 2バイト目は、転送方式の設定です。~ 一番難しいところです。下の表を見ましょう。~ |2バイト目の値 |1回分の処理の挙動 |使用例|使用例で何が起こるか| |00|1アドレスへ1byte書き|$2106にXXを代入|モザイク設定がXXに| |01|2アドレスへ1byte書き|$2126にXX⇒$2127にYY|ウィンドウ1左端XX、右端YY| |02|1アドレスへ2byte書き|$210FにXX、ついでYY|レイヤー2 x座標がYYXXに| |03|2アドレスへ2byte書き|拡大縮小回転マトリクス |2chに分けて画面に濃淡の演出&br;使用してるゲーム例 アダムスファミリー| |04|4アドレスへ1byte書き|思いつかん|| $2106は単純に1バイトを入力するものなので~ 00を入力します。~ ~ ~ 3バイト目は、弄る対象です。$21xxのxxを入力します。~ 今回は$2106モザイクなので、06です。~ ~ 4?6バイト目に、転送内容テーブルのありかを入力します。~ 空き場所ならどこでもいいですが、~ 今回は近くの$7FFE40に「転送内容」を書いていくことにします。~ よって40 FE 7F~ ~ ~ **転送内容と効果 [#n2307603] さて、ついに基礎設定が完了しました。~ 次はいよいよ「転送内容」を書いていきます。~ さっき$7FFE40に指定したので、そこに書いていきましょう。~ ~ 基本は「上から数えるScanline数⇒代入値」の繰り返しです。~ こっからは好きなようにしていいですが…~ ~ とりあえず上から40行分にサイズAの大モザイクをかけてみましょう。~ $2106の設定はAFです。~ よってまず「40 AF」と書いていきます。~ ~ 画面全体にモザイクがかかるでしょうが、無視して次行きましょう。~ 次の40行分はモザイクをサイズ6と、少し小さくしてみます。~ 続きに「40 6F」と書きます。~ ~ 次第に小さくして、最終的に「40 AF 40 6F 40 2F 40 00 (00)」としました。~ その結果がスクショです。そのとおりになってますね。~ ちなみに(00)は終了宣言です。~ ~ ~ こうして、チートでHDMAを作ることができました。~ あとは今回のチート入力を再現するLevelASMを組むだけです。~ 組み方は[[65C816プログラミング>http://vip.rgr.jp/sm4wiki/index.php?65C816%A5%D7%A5%ED%A5%B0%A5%E9%A5%DF%A5%F3%A5%B0#md66e84d]]の方を見ましょう。~ ~ ~ *?スクリーンあれこれ [#z4e04003] ~ **メインスクリーン、サブスクリーン [#g3bb16ac] このへんからだいぶ怪しい説明になります。~ 変なこと言ってたら直しちゃってください。~ ~ 次のステップに進む前に~ メインスクリーン、サブスクリーンとかを知っておくといいです。~ ~ もう一度[[すずめ愛好会>http://vip.rgr.jp/sm4wiki/index.php?plugin=attach&pcmd=open&file=sn031.htm&refer=HDMA%C6%FE%CC%E7]]を見ましょう。~ 今回見るべきは、~ ''$212C(メインスクリーン構成)''~ ''$212D(サブスクリーン構成)''~ ''$2131(カラー演算対象設定 $40から転記)''~ ''$2132(固定色層の色)''~ ~ ただし、$2131に関しては、~ 毎フレームのNMIで$40からコピーされているで、~ $2131を弄ってもすぐ潰されてしまいます。~ かわりに$40を弄ればOKです。~ ~ 描画される画面は、~ スプライト・固定色・BG1・BG2・BG3・BG4といった層の~ 重ね合わせで構成されているのですが、~ 細かく言うともう少し複雑です。~ ~ これらの層をメインスクリーン・サブスクリーンにグループ分け。~ 私たちにはメインスクリーンだけが見えます。~ ~ 内部でサブスクリーンというのを別に構成しておきます。~ この結果を、「メインスクリーンのうちの背景層」に足し加える。(カラー演算)~ そういう足し算の結果、メインスクリーンの後ろにサブスクリーンがあるように見えます。~ 足し算というとわかりにくいですね。~ サブスクリーンの内容を、プロジェクターで映し出すような感じです。~ ~ ~ $40に関してはここでも説明しておきましょう。~ $40の8つのbitを~ ~ +全BS4321 ~ と表すとすれば、それぞれ~ ~ +/? …カラー演算 加or減~ 全/半 …カラー演算 1倍or半分(プロジェクターの威力半減)~ BS4321 …それぞれ背景層/スプライト/BG4321。~ 1としたbitに対応する層へ演算する。(プロジェクターを照射する)~ ~ 口頭ではわかりにくいので、実例を見ましょう。~ ~ **通常のLevelでの描画構成 [#mfc136d2] ~ スクリーン構成は~ メイン…BG1,SP~ サブ …BG2,BG3~ $40 = 20 (+OB....)なので、加算対象は背景層のみ。~ ~ 図示すると下のよう。~ &ref(c.png);~ ~ さて、ここで一つ考えてみましょう。~ サブスクリーンが背景層に加算されていますが、~ これをやめたらどうなるでしょう?~ $40の値を20⇒00にしてみましょう。~ 「BG2、BG3、固定色」が見えなくなります。~ ~ では、背景層だけでなく、BG1にも加算するとどうなるでしょう。~ $40の値を、20⇒21にしてみましょう。~ プロジェクターが背景層だけでなく、レイヤー1にも照射をするという感じで、~ レイヤー1にサブスクリーンの内容が映ってしまいます。~ つまり、レイヤー1が''透けている''ように見えます。~ ~ 同様に、スプライトにも照射して透けさせることができますが、~ スプライトのうち「前面に表示」設定のタイルは~ どういうわけか特別扱いを受けているようです。~ こういった加算の影響を受けません。~ お化け屋敷ではこれを利用して、~ 透けたテレサ・透けないテレサを区別しているようです。~ ~ ~ さて、これを見ていると、~ 別にBG1,2,3,スプライト全てをメインスクリーンに設定してもいい気がします。~ 何故こんな回りくどいことをしているのか。~ それは、スプライトのうち「背面に表示」設定のタイルのためです。~ こう設定すると、タイルはメインスクリーンの一番後ろに行きます。~ BG2をメインスクリーンに設定すると、タイルがBG2背景の後ろに表示されます。~ 要するに、ピーパックンとかが背景の後ろに消えてしまうのです。~ ~ 一方、レイヤー2を使ったマップでは、~ 別にスプライトがレイヤー2に隠れても問題ありません。~ なのでここではBG2はメインスクリーンに設定されています。~