2008-08-01から1ヶ月間の記事一覧

最適化4

統計情報を利用した最適化を実装しました。エミュレータでゲームを普通に遊んでる間に、2種類の情報を取得します。 具体的には、$0000〜$FFFFのアドレスそれぞれについて、 ・そのアドレスの命令を実行した回数 ・そのアドレスに分岐命令で飛び込んだ回数 …

最適化5

LDA $2005 とかとか、Absoluteなアドレッシングだった場合、何にアクセスしてるかが事前に判別できます。上の場合はPPUですね。トランスレート時に、上のようなコードだったら、メモリマップではなくて、PPUに直接アクセスするコードに変換するようにしてみ…

デバッグ

エミュレータって、けっこう適当に作ってもそれなりに動いてしまうんですねえ。 だからこそ逆に、難しい気がしますね。なんとなくゲームが動く程度ならすぐなんですけど、完璧に動作するものを作るのはかなり大変だと思われます。CPUのエミュレーションで何…

最適化3

不要なフラグレジスタ操作のコードを除去する最適化を実装しました。結構大変だったわりには、地味な変化しかなかったですねえー。以下が、最適化前と最適化後の、1フレームあたりの6502CPUの処理時間です。 最適化前:1.434634[ms] 最適化後:1.273096[ms]処…

最適化2

メモリアクセスは、memoryクラスにインデクサを作って、それでやってます。 インデクサの中では、アドレスの範囲をみて、WRAM、PPU I/O、APU I/O、PRG-ROM等、どれにアクセスしているかで分岐するようになってます。そのため、メモリにアクセスするたびに、i…

最適化

C#のコードがどういう風にMSILに落ちるのかを調べつつ、なるべく高速で小さいバイナリになるように調整してます。 sealed partial class CPU { private int a; private int x; private int y; private int p; private byte sp; private int pc; //以下略 今…

不要なフラグレジスタ操作の除去

例えばですが、6502のAND命令をトランスレートすると、以下のようなC#のコード片を出力するようになってます。a&=mem[adr];p=(p&0x7d)|(a&0x80)|((a-1)>>7)&2;事前にアドレッシングモードに応じたアドレス計算を行って、adrに有効アドレスが入っていると思っ…

今後について

最近のPCだと、トランスレーションどころか、まったく最適化してないエミュレータでも、60fpsで動いてしまいますね。 トランスレーションしてもしなくても、CPUの負荷率は数パーセントなので、トランスレーションによってどれぐらい高速化できているかが見え…

不要なレジスタ操作の除去

フラグレジスタのエミュレートが意外と計算量が多いことは前に書きました。 例えばADC命令を実行した場合、影響があるフラグレジスタは以下の通りです。 参照 C 変更 NVZCもし、ADCが2つ連続に並んでいるコードがあった場合、1つ目のADCによるNVZフラグの…

コードトランスレーション4

NESファイル→C#ソースコード→実行形式ファイルという経路において、がっちり時間をかけて最適化を行うことが可能なはずです。 考えられる最適化の手法をメモしておきます。 不要なレジスタ操作の除去 ある命令を実行したときのレジスタ値の変化を、あとで実…

コードトランスレーション2

コードトランスレータで生成したコード中のswitch-caseが複雑すぎて、コンパイルが異常終了してしまう件は、ひとつのswitch分の中のcase句を4096個以内にすることで回避できました。 4096個っていう数は、割と適当に決めたので、最適化の観点からすると数を…

コードトランスレーション

コードトランスレーションは本当に可能なのか検証する目的で、NESファイルからC#のソースコードを生成するプログラムをでっち上げました。 検証目的なので、最適化とかは考えずにコーディング速度最優先で作ったところ、三時間ぐらいで出来上がりました。エ…

バグ

結局、今作ってるエミュレータのような低解像度のグラフィックを1ドットずつCPUが書き込むような処理方式では、MDXの利点が生かせないことが分かったので、配列に描画してそこからビットマップを生成してフォームに転送するやり方でグラフィックを表示する事…

コードトランスレーション3

NOPを実行したときに、なぜかPCを2進めてました… それを修正したら、ちゃんとマリオブラザーズが動作するようになりました。トランスレータによって、NES→C#→exeという変換を行って、最終的にできあがったexeのサイズが、1.07MBでした。 元のNESファイルが25…

Managed DirectDraw

要は画面に点を打てればそれでいいのですが、いろいろめんどくさいんですよね。最初は2DだけだしManaged DirectDrawを使うつもりで、DirectDrawの初期化処理を書きました。 後はサーフェスをLockして書き込めばいいはずですが、Lockメソッドの仕様がなんか謎…

スプライトだしますた

スプライトも表示されるようになりました。 マリオとノコノコが表示されて楽しげな雰囲気に。スクロールレジスタも実装したはずですが、このゲームは画面スクロールしないですね…スプライトを8x16モードにすると、どう表示されるかがよく分からなかったので…

絵が出た2

インダイレクトのJMPのアドレス計算間違ってた… とかとか、バグをGNESのログと比較しながらデバッグしたところ、スーパーマリオもちょっとだけ動きました。でも見てのとおり、なんか変です。スクロールの処理が未実装だからでしょうか?

絵が出た

スーパーマリオブラザースの方は、うまく動かないんですが、スーパーじゃない方はとりあえずそれっぽい絵が出ました。スプライトを実装してないので、マリオと亀が出てないです。 地面が凸ってるのとかを見ると、ああ、動いてる…っていう実感が。でも、見て…

トランスレータ2

ファミコンをトランスレータでiアプリに変換する上で、携帯Javaゆえの問題点があります。 クラスファイルのサイズ 大きくなったとしても全体で1メガバイト以外にしなければならないと思います。 AND命令の場合ですが、6502では2〜3バイトです。これをJavaで…

トランスレータ

エミュレーションがちょっと動き出した程度しかまだ出来上がってない状況ですが、将来的な野望を書いておこうと思います。 携帯アプリとしてファミコンのソフトを60fpsで動かしたい 携帯でエミュレータを動かしたことがそもそも無いのにこんなことを書いてる…

マリオが動いてる?

VBlankを一定周期で発生させるようしたところ、マリオ1のVBlank待ちで無限ループになってた所から先へ、処理が進むようになりました。GNESと私が作成したエミュレータで、マリオ1を動かしたときのトレースログを100万クロック分それぞれ出力したものを、DIFF…

デバッガにはめられた

PPUのステータスレジスタのVBlankのフラグは、読み込みでクリアされる仕様になっています。私はPPUのレジスタを、C#のプロパティとして実装しました。 仕様どおりにするため、ステータスレジスタのgetのプロパティにアクセスすると、フラグをクリアするコー…

VBlankの周期

PPUのレジスタをとりあえず適当に実装しました。マリオ1を実行すると、8命令ぐらいを実行したところで 800A : lda $2002 800D : bpl $800A というコードで無限ループになってしまいました。 これは、VBlank(垂直帰線期間)待ちのコードの様です。 $2002とい…

Flashのファミコンエミュレータ

EMU

こちらの方は、Flashでファミコンエミュレータを作成されています。 http://d.hatena.ne.jp/h_sakurai/20080523 AS3の他に、D言語でも実装されています。 ファミコンの画面が3Dになるのが面白いですねー。 ちゃんとスプライトに影がついてる。 その他にも、…

PPUの実装

6502コアはだいたい良さそうな感じになってきたので、次にPPUの実装に取り掛かろうと思います。 6502コアを動かして、レジスタの値が変化するのを眺めてても、あんまり面白くないわけでして、やっぱ、はやいとこ絵を出したいわけですよ。ファミコンのPPUにつ…

メモリマップの実装方法2

メモリにアクセスするコードは、とりあえず以下のような感じにしました。 public byte this[uint address] { get { if (address < 0x17FF) { // WRAM return wram[address & 0x7FF]; } else if (0x2000 <= address && address <= 0x3FFF) { // PPU I/O // ad…

メモリマップの実装方法

ファミコンのメモリマップを実装するにはどうすればいいか考えています。Memoryクラスにインデクサを定義して、indexの範囲によってifで分岐するのが素直な実装方法だと思います。 でも、そういう実装にすると、メモリアクセスのたびに条件分岐するので、遅…

NESフォーマットのファイル読み込み3

結局、難しいことはやめて、素朴な感じに組みました。 以下が、NESファイルを読むコードです。 using System; using System.Collections.Generic; using System.Text; using System.IO; namespace FCC { class NESFile { private byte prgPageSize; private …

NESフォーマットのファイル読み込み2

こんな構造体を定義しておいて、 [StructLayout(LayoutKind.Explicit)] unsafe struct NESHeader { [FieldOffset(0)] public fixed byte bytes[16]; [FieldOffset(0)] public fixed char ID[3]; [FieldOffset(3)] public byte PrgSize; [FieldOffset(4)] pub…

NESフォーマットのファイル読み込み

6502のエミュレータのテストはまだ半分ぐらいしか終わっていませんが、実際にファミコンのコードを走らせてみたくなりました。そのためには、まずNESフォーマットのファイルを読み込めるようにする必要があります。iNES形式のファイルフォーマットについては…