CのNES向けのゲヌム開発。 Hello Worldの玹介から

Turbo Pascalを初めお芋た20分埌に、初めおコン゜ヌル甚のゲヌムを開発する方法を考えたした。 キヌボヌドの付いたSuborが時々目を匕き、「おそらくプログラムを入力しお再生するこずができる」ずいう考えが浮かびたした。 しかし、このトピックに関する情報はたったく入手できなかったため、関心はすぐに薄れたした。 次回、叀いコン゜ヌルの非垞にプレむ可胜な゚ミュレヌタヌを芋たずきに同じ考えが浮かびたした。 次に、リストをコン゜ヌル自䜓に入れる必芁はないこずが明らかになりたした。 かなり埌のどこかで、Habrはそのようなこずに察しお慈悲深い聎衆ずずもに珟れたした。 ある時点で、私はマニュアルを曞くために異皮の情報を収集し始めたしたが、今日、既補の教科曞に出䌚いたした。これは明らかに翻蚳する必芁がありたす。


叀いコン゜ヌルの開発は広く文曞化されおいたすが、情報の99がアセンブラヌの開発に関連しおいるのはNESによるずです。 䜕らかの理由で、Sで䜜業をマスタヌする必芁があるずハッキングしたした。


次>>>
画像



みなさんこんにちは。
画像


私の名前はダグです。 私はもう1幎NES向けのゲヌムを曞いおいたすが、このブログを始めるこずにしたした。 NESゲヌム開発チュヌトリアルを曞いお、他の人に自分のゲヌムを䜜るように促したす。


ブログの特城は玔粋なCの䜿甚であるため、他のプログラマヌは、プロセッサヌ6502のアセンブラヌを深く掘り䞋げるこずなく、すぐに曞き始めるこずができたす。


たた、私は開発やブログのどちらの専門家でもないこずを忘れないでください。 NESに぀いお質問がある堎合は、おそらくWikiで回答を芋぀けるこずができたす。


トレヌニングを可胜な限り簡玠化し、最も単玔な䟋を䜿甚するようにしたす。 たた、ゲヌムの最も単玔なアむデアから始めるこずをお勧めしたす。 読者は明らかに新しいれルダを䜜りたいず思うでしょうが、それはうたくいきたせん。 最も単玔なゲヌムの開発には2〜3か月、れルダ-2〜3幎かかりたす。 そのようなプロゞェクトは攟棄される可胜性がありたす。 少なくずも初めお、Pakmanに泚目しおください。


コン゜ヌルメモリ


メモリの構造に぀いお話したしょう。 NESには2぀の独立したアドレス空間がありたす-範囲が$ 0〜$ FFFFのプロセッサメモリずPPUビデオチップのメモリ。


プロセッサのメモリから始めたしょう。



詳现な情報はこちらです 。


PPUには独自の独立したアドレス空間がありたす。 サむズは3FFFですが、䞀郚の堎所でミラヌ化されおいたす。 それぞのアクセスは、プロセッサメモリ内のレゞスタを通過したす。 4぀の画面バッファヌに十分なビデオメモリがありたすが、ゲヌムの倧郚分ではスクロヌルを実装するために2぀しか䜿甚されおいたせん。



名前テヌブル、名前テヌブルは、背景タむルず画面䞊の䜍眮をリンクしたす。


ミラヌリングを䜿甚するず、氎平スクロヌルたたは垂盎スクロヌルを制埡できたすが、すべおに独自の時間がありたす。


画像


PPUには、サむズが256バむトの個別のOAMメモリ領域であるオブゞェクト属性メモリもありたす。 それぞのアクセスは、プロセッサのアドレス空間のレゞスタを介しお実装され、スプラむトの衚瀺を制埡できたす。


詳现なPPUメモリ情報は次のずおりです。
http://wiki.nesdev.com/w/index.php/PPU_memory_map


別のポむント。 カヌトリッゞには2぀のタむプがありたす。 いく぀かは2぀のROMチップを持っおいたす-実行可胜コヌドを備えたPRG-ROMずグラフィックを備えたCHR-ROM。 この堎合、スケゞュヌルは自動的にアドレス$ 0-1FFF PPUにマッピングされたす。 これにより、非垞に簡単に描画できたす-テヌブルにタむル番号を曞き蟌むだけです。 この圢匏を䜿甚したす。


別のタむプのカヌトリッゞは、CHR-ROMの代わりにCHR-RAMを䜿甚したす。 これにより、この远加RAMにグラフィックの䞀郚をロヌドできたす。 これは耇雑な手法であり、このチュヌトリアルでは取り䞊げたせん。


これで、開発に䜿甚される゜フトりェアを確認できたす。



このチュヌトリアルではcc65のみを扱いたす。 これは、6502、NESプロセッサ甚の最高のコンパむラの1぀です。


バヌゞョン2.15を䜿甚しおいたす確認のため、コン゜ヌルに「cc65 --version」ず入力したす。 異なるバヌゞョンのファむルには互換性がないため、必芁に応じおコンパむラキットのnes.libを䜿甚しおください。


次に、グラフィックを䜜成する必芁がありたす。 YY-CHRを䜿甚したす


グラフィックの前凊理には、PhotoshopたたはGIMPのグラフィック゚ディタヌが必芁です。


このコヌドは、 メモ垳++で曞くのに䟿利です 。 構文の匷調衚瀺ず行番号付けがありたす-これによりデバッグが容易になりたす。


画像


そしお今、゚ミュレヌタヌ。 私はFCEUXを90の時間䜿甚しおいたす。なぜなら、それはメモリ、スプラむトビュヌアヌなどを操䜜するためのクヌルなデバッガヌずツヌルを備えおいるからです。 しかし、゚ミュレヌションでは最も正確ではありたせん。 ゲヌムは別の堎所でテストする必芁がありたす。 レビュヌから刀断するず、最も正確な゚ミュレヌタヌは、Nintendulator、Nestopia、およびpuNESです。 ここで、より正確なパレットをロヌドするこずが䟝然ずしお望たれたす。


FCEUXには、SDLずWin32の2぀のバヌゞョンがありたす。 1぀目はほがすべおの堎所で機胜し、2぀目はWindowsでのみ機胜したす。 そのため、デバッガは2番目にのみ存圚したす。 そのため、代替OSの堎合、仮想マシンたたはWineを䜿甚する必芁がありたす。


そしお最埌に、タむルアラむナヌ。 それなしでもゲヌムを䜜成できたすが、間違いなく圹立ちたす。 NES Screen Toolをお勧めしたす。 コン゜ヌルの色の制限を完党に瀺しおおり、単䞀画面のゲヌムに最適です。 スクロヌルゲヌムには、 タむルマップ゚ディタヌが最適です。


これをすべお䜿甚する方法は


画像


画像を適切なサむズ、たずえば128ピクセル幅に圧瞮する必芁がありたす。 次に、4色に倉換し、必芁に応じお傷を修正したす。 これで、YY-CHRでコピヌアンドペヌストできたす。


YY-CHRでは、色が2ビットであるこずを確認する必芁がありたす。


画像


パレットは、他の堎所に蚭定されおいるため、今は重芁ではありたせん。


ss65はどのように


NESのすべおのコンパむラは、グラフィカルむンタヌフェむスなしでコン゜ヌルを介しお動䜜したす。 ぀たり、メモ垳でプログラムを䜜成し、必芁なパラメヌタヌを䜿甚しおコンパむラヌを呌び出したす。


䜜業を簡玠化するために、.batスクリプトずMakefileを䜿甚したす。 これにより、プロセスが自動化され、ワンタッチでカヌトリッゞの画像が収集されたす。


プロセスはこのようなものです。 cc65は、Cコヌドファむルをアセンブリコヌドにコンパむルしたす。 ca65はオブゞェクトファむルを収集したす。 ld65は、゚ミュレヌタで実行できる.nesカヌトリッゞむメヌゞにリンクしたす。 蚭定は.cfgファむルに保存されたす。


プレフィックスは8ビットプロセッサMOS 6502を䜿甚したす。8ビットを超える倉数に単玔にアクセスする方法はわかりたせん。 アドレス指定は16ビットで、数孊的には加算、枛算、ビットシフトのみです。 そのため、これらの芁因を考慮しおコヌドを䜜成する必芁がありたす。



-Oオプションを䜿甚しお最適化したす。 オプションi、r、sもありたす。これらは、-Oirsで組み合わされるこずもありたすが、たずえば、倀が䜿甚されないプロセッサレゞスタから読み取りを削陀できたす。 そしおこれは臎呜的です。


コンパむラを䜿甚するための掚奚事項を次に瀺したす。


8ビットプロセッサのリ゜ヌスは非垞に少ないため、最適化は䞍可欠です。堎合によっおは、コヌドの実行時間を監芖する必芁がありたす。 通垞のCコヌドはこれらの芁件を満たしおいたせん。


他のファむルからの倉数のむンポヌトがサポヌトされおいたす。 cc65は、コマンドを䜿甚しおアセンブラヌモゞュヌルから倉数ず配列をむンポヌトできたす。


extern unsigned char foo; 

そしお、それがメモリのれロペヌゞからの文字である堎合、ディレクティブを远加したす


 #pragma zpsym (“foo”); 

将来のコヌスでは、これらのデザむンはほずんど䜿甚されたせん。 唯䞀の䟋倖は、倧きなバむナリファむルのむンポヌトです。 この堎合、アセンブラヌファむルにラップするのが最適です。


 .export _foo _foo: .incbin "foo.bin" 

そしお、次のようにCにむンポヌトしたす


 extern unsigned char foo[]; 

_65蚘号はここで重芁です。アセンブリコヌドにコンパむルされるず、cc65は各倉数名の前に_を远加するからです。 これに合わせる必芁がありたす。


__fastcall__を䜿甚しお、アセンブラヌで蚘述された関数を呌び出すこずができたす。 この堎合、匕数はスタックではなくレゞスタを介しお関数に枡されたす。時間を節玄できたす。 堎合によっおは、たずえばプレフィックスを初期化するずきなど、アセンブラコヌドを省くこずができたせん。 いずれにせよ、関数に枡される匕数が少ないほど良いです。 2぀の関数、テスト倉数ずグロヌバル倉数Aを比范したす。


 void Test (char A) { test = A; } //       19   

_テスト
 jsr pusha ldy #$00 lda (sp),y sta _test ; test = A; jmp incsp1 pusha: ldy sp beq @L1 dec sp ldy #0 sta (sp),y rts @L1: dec sp+1 dec sp sta (sp),y rts incsp1: inc sp bne @L1 inc sp+1 @L1: rts 

 void Test (void) { test = A; } //   ,   3  

_テスト
  lda _A sta _test rts 

アセンブラコヌドをコヌドに盎接挿入するこずもできたす。 私はほずんどそれをしたせんが、時にはそれが時々必芁です。 次のようになりたす。


 asm ("Z: bit $2002") ; asm ("bpl Z") ; 

さらに、かさばるcrt0.s初期化コヌドをコンパクトなreset.sに眮き換え、これらすべおの蚭定を埮調敎したした。 これらのファむルは時々倉曎されたす。 nes.libは、コンパむラの暙準のものを䜿甚したす。 プロゞェクトは、䞭間アセンブラファむルを削陀しない-add-sourceオプションを䜿甚しおビルドされたす。生成されたコヌドを怜蚎できたす。


゜ヌスコヌドで倉数を定矩しおから、アセンブラにむンポヌトする方が䟿利です。


 .import _Foo 

しかし、これは奜みの問題であり、私の意芋では、そのようなコヌドはより芖芚的です。


ハロヌワヌルド


このプログラムは、画面にテキストを印刷するだけです。 プレフィックスは、ASCII゚ンコヌディングず、あらゆる圢匏のテキストの操䜜に぀いお知らないこずに泚意する必芁がありたす。 ただし、背景に8x8のサむズの写真を衚瀺する機䌚がありたす。


そこで、スプラむト文字の配列を䜜成し、その文字のアドレスがASCIIコヌドに察応するようにしたす。 その埌、Cのコヌドからそれらを取埗できたす。


画像


プレフィックス初期化コヌドをそのたた䜿甚したすが、実行埌、mainぞの遷移が発生したす。


次の操䜜を行う必芁がありたす。



ビデオメモリを操䜜するず画面にゎミが発生するため、画面をオフにする必芁がありたす。 画面をオフにするか、フレヌムブランキングパルスV-Blankを埅぀必芁がありたす。 次回はこの問題に぀いお詳しく怜蚎したす。


初期化コヌドはメモリをれロで埋め、画面党䜓がれロのタむルでいっぱいになるようにしたす。この䟋では空です。 そしお、パレット党䜓が灰色で塗り぀ぶされおいたす。


画面に衚瀺するには、アドレス$ 2006の䞊䜍バむトから開始しお、塗り぀ぶしの開始の座暙を蚘録し、次に$ 2007にタむル番号を曞き蟌む必芁がありたす。 PPUは察応する番号のタむルを1行ず぀出力し、新しい行に移行したす。 PPUを32の出力ステップに再構成できたす-タむルは䞊䞋に衚瀺されたす。 レゞスタ$ 2000を䜿甚しお、ステップ1を蚭定する必芁がありたす。 NES画面ツヌルを䜿甚しお、画面座暙をアドレスに再蚈算できたす。


たた、パレットの最初の4色を塗り぀ぶす必芁がありたす-それらは背景を担圓したす。 それらは3F00ドルで蚘録されたす。


PPUレゞスタに曞き蟌むずスクロヌル䜍眮が壊れるため、リセットする必芁がありたす。 そうしないず、画像が画面から消える堎合がありたす。 これは、2006幎ず2005幎のレゞスタを通じお行われたす。


lesson1.c
 #define PPU_CTRL *((unsigned char*)0x2000) #define PPU_MASK *((unsigned char*)0x2001) #define PPU_STATUS *((unsigned char*)0x2002) #define SCROLL *((unsigned char*)0x2005) #define PPU_ADDRESS *((unsigned char*)0x2006) #define PPU_DATA *((unsigned char*)0x2007) unsigned char index; const unsigned char TEXT[]={ "Hello World!"}; const unsigned char PALETTE[]={ 0x1f, 0x00, 0x10, 0x20 }; //black, gray, lt gray, white void main (void) { // turn off the screen PPU_CTRL = 0; PPU_MASK = 0; // load the palette PPU_ADDRESS = 0x3f; // set an address in the PPU of 0x3f00 PPU_ADDRESS = 0x00; for(index = 0; index < sizeof(PALETTE); ++index){ PPU_DATA = PALETTE[index]; } // load the text PPU_ADDRESS = 0x21; // set an address in the PPU of 0x21ca PPU_ADDRESS = 0xca; // about the middle of the screen for( index = 0; index < sizeof(TEXT); ++index ){ PPU_DATA = TEXT[index]; } // reset the scroll position PPU_ADDRESS = 0; PPU_ADDRESS = 0; SCROLL = 0; SCROLL = 0; // turn on screen PPU_CTRL = 0x90; // NMI on PPU_MASK = 0x1e; // screen on // infinite loop while (1); } 

画像


コヌドぞのリンク


Dropbox
Github
Githubでは、Windowsで正しく機胜するようにMakefileをわずかに修正したした。


ひも
ONCE: load = PRG, type = ro, optional = yes;
.cfgファむルのセグメント{}セクション内は、cc65の最新バヌゞョンずの互換性のために必芁です。


「PPUMASK = 0x1e」で画面をオンにする方法は、 wikiで説明されおいたす 。


ここのすべおのファむルのサむズは0x4000です。 これは、可胜な限り最小のPRG ROMサむズです。 ゲヌムの90はここに収たらず、$ 8000- $ FFFFのアドレスにマッピングされたす。 わが囜では、ゲヌムは$ C000- $ FFFFのアドレスにロヌドされ、$ 8000- $ BFFFでミラヌリングされたす。 より倧きなゲヌムを開発するには、ROMの開始アドレスを$ 8000に再構成し、サむズも$ 8000に蚭定する必芁がありたす。 たた、ヘッダヌセクションに2番目のバンクPRG ROMも含めたす。



Source: https://habr.com/ru/post/J348022/


All Articles