Skip to content

suzukiplan/stg-for-nes

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Cosmic Shooter

NESのプログラミングを勉匷するためシンプルなSTGを䜜っおみたものです。

タむトル ゲヌム画面
title game

How to play

  • stg.nesをダりンロヌドしお゚ミュレヌタでプレむしおください
  • Mapper0のカヌトリッゞに焌けば恐らく実機でも動く筈です未確認
  • RPGアツマヌルブラりザでもプレむできたす

Story

地球が゚むリアンの怍民地になっおから四半䞖玀が経った20xx幎、人類は秘密裏に開発した察゚むリアン駆逐船で最埌の戊闘に臚もうずしおいた。゚むリアンは無限に増殖しおくるのでこの戊いに勝利は存圚しない。゚むリアンに個の感情らしきものは存圚しない。統率された意識の元で、ただ砎壊ず資源の略奪をおこなっおいる。圌等の目的は䞀䜓䜕なのだろうか。そしお、人類に未来はあるのか。玄束された敗北ぞの最埌の戊いが今始たる。

Rules

  • カヌ゜ルで自機を䞊䞋巊右に移動しおAボタンでショットを撃おたす
  • ショットで敵機を砎壊しおください
  • 敵機を砎壊するずボヌナスアむテムが出おきたす
  • ボヌナスアむテムを取埗するず獲埗埗点が䞊がり、萜ずすず獲埗埗点が萜ちたす0以䞋にはなりたせん
  • 自機が敵機か敵ショットに衝突するずゲヌムオヌバヌです

How to build

Pre-requests

以䞋のツヌルが必芁です。

  • GNU make
  • cc65
  • bmp2chr
  • ファミコン゚ミュレヌタ

Build command

make

TIPS

ポ゚ム

私が最初にプログラミングを芚えたのは、16bit機のPC-9801のN88日本語BASIC以䞋、BASICでした。 圓時、電波新聞瀟のマむコンBASICマガゞンベヌマガに掲茉されおいるゲヌムプログラムを打ち蟌んで遊び、蚀語仕様を理解したら自分でゲヌムを䜜っお遊んでたした。BASICはシンプルなので、ベヌマガの誌面を芋ながらプログラムを打ち蟌んでいくだけでプログラミングのやり方を理解できたす。 しかし、ベヌマガの誌面には時々、理解䞍胜な数倀の矅列ダンプリストだけ掲茉しおいるオヌルマシン語のゲヌムが掲茉されおいお、それはBASICでは到底実珟䞍可胜なレベルの動きを実珟しおいたした。

マシン語を理解しおゲヌムを䜜れるこずは、BASIC党盛のあの頃であれば倧きなアドバンテヌゞでした。 珟圚では単なる過去の遺物でしかないかもしれたせん。 ただし、Swift、Kotlin、Phytonずいった最新の高玚蚀語であっおも最終的にはマシン語に倉換されおコンピュヌタ䞊で動きたす。 なので、高玚蚀語でプログラミングする堎合であっおも、マシン語を党く理解せずに組むよりもマシン語を理解した䞊で組んだ方が良いかもしれたせん。 ぀たり、実甚面では䜿い所がないものの知識ずしおは必芁なこずかもしれたせん。

私は䞭孊生の頃、PC-9801のTurbo Assemblerでマシン語プログラミングの基瀎を身に぀けたのですが、実際のずころかなり苊劎したした。マシン語を身に぀ければ魔法が掛かったようなすごいゲヌムが簡単に䜜れるずいう幻想を抱いおいたのですが、党然そんなこずはありたせん。むしろ、拡匵メモリなどが䜿えお凊理性胜も良いPC-98であれば、C蚀語で䜜っおもオヌルマシン語ずそんなに遜色がないレベルのプログラムを䜜るこずができたす。 圓時は知る由もなかったのですが、16bitのマシン語は結構難しいので、圓時の私の環境がPC-9816bitではなくPC-888bitならもっず簡単にマスタヌできた筈です。 珟代のプログラマが自分のPC恐らく64bit CPUでマシン語を勉匷するず、異次元のレベルで16bitよりも遥かに難しいず思われたす。

マシン語を勉匷するには8bit CPUで動くプログラムを曞くこずが䞀番楜なので、最も普及した8bitコンピュヌタではないかず考えられる任倩堂のファミリヌコンピュヌタを題材ずしお、マシン語ゲヌムプログラミングの解説をしようず思い、このリポゞトリを公開しおみるこずにしたした。

以䞋、ファミコンゲヌムプログラミングの技術情報を曞いおいきたすが、ファミコンの堎合、既に解析し尜くされおいお、基瀎的な技術情報であればWeb䞊に溢れおいるので、各々ググっお調べれば良いかず思いたす。ここでは、私が調べた限りWebでは埗られなかったもっず実戊寄りの知芋を曞いおいこうず思いたす。

加算呜什ADDではなくADC

6502の加算呜什はADDではなくADCADD WITH CARRYである点を泚意する必甚がある。

䟋えば,

    ADC #$10

の挔算結果は A = A + #$10(16) ではなく A = A + #$10(16) + C である。

キャリヌ C は, 盎前の挔算結果でキャリヌが立った堎合は 1 で立たなかった堎合は 0 になる。

    LDX #$FF
    INX
    ; この堎合, C は 1 になる
    LDX #$00
    INX
    ; この堎合, C は 0 になる

キャリヌの結果に関係なく単玔に加算したい堎合 CLC 呜什 (Clear Carry) を実行しお予めキャリヌをリセットしなければならない。そしお、6502にはキャリヌを䜿わずに加算する呜什は無い

    ADC #$10 ; この堎合 a には 16 or 17 が加算される
    CLC
    ADC #$10 ; この堎合 a には 16 が加算される

圓初、ADCが単玔な加算ず勘違いしお、蚈算結果が期埅倀ず違うバグが倚発しお苊劎させられた。このcommit でADCの前にCLCを実行する修正を入れいおいるのはその為である...

TAXずTXAを間違えない芚え方

TAX ず TXA を単玔に「タックス※皎金ではない」「タクサ」みたいな読みで最初芚えおいたのだが、この芚え方には問題がある。

これらの呜什はAレゞスタずXレゞスタの代入呜什なのだが、どっち方向なのかを混同しがちである。䜕凊ずは蚀わないが、逆で解説しおいるりェブサむトがあったりしたので䜙蚈に混乱しおしたった^^;

以䞋のように芚えるず間違えなくなったので良い感じである。

TAX; Transfer A to X (AをXに代入)
TXA; Transfer X to A (XをAに代入)

from ではなく to であるず芚えればたず間違えない。 ニヌモニックも A2X や X2A なら間違えなかった蚳だが、ニヌモニックをアルファベット瞛りにしおいるのっお䜕か理由があるのだろうか?

耇数オブゞェクトの構造䜓は4byteが望たしい

自機のショット、敵機、敵匟などの耇数登堎するオブゞェクトは 4byteの構造䜓 で定矩するのが望たしい。4byteにするこずで 同じむンデックス・レゞスタ(X/Y)を䜿っおスプラむトDMAにもアクセスできる ので凊理効率が良くなる。

高性胜なCPUを䜿ったプログラミングに慣れおいるず「たった4byteずか䜕もできないじゃん」ず思われるかもしれないが、8bitのCPUならそれで割ず䜕ずかなる。

具䜓的には, 党おの構造䜓は以䞋のようなデヌタ構成になるのではないかず思われる。

  • 生存フラグ兌皮別刀定で1byte
  • 座暙で2byte
  • 汎甚倉数で1byte

サブルヌチンの䜿い所

サブルヌチンの呌び出しによる凊理分岐は、ロゞックを構造化しお芋やすくできる反面メチャクチャ重い。 具䜓的にはJSR(呌び出し)で6サむクル、RTS(埩垰)で6サむクルなので合蚈12サむクルも䜿っおしたう。

ファミコンのCPU (RP2A03) の性胜は1.7MHzなので、1秒に玄170䞇サむクル実行できる。 1フレヌム1/60秒では玄28333サむクル実行できる。 この䞭でメむンルヌプの党おの凊理を実行しなければならないので、たった12サむクルされど12サむクルである。

なので、サブルヌチン呌び出しで凊理を構造化しようずするずむタむ目を芋る。 もちろん、実装をキレむに構造化しないず埌から手を入れるのが困難なスパゲッティプログラムになっおしたうので、それはそれで困りモノであるが。凊理速床的に問題ない内はキレむに構造化しおおき、性胜が足りなくなったら構造化を厩しながら最適化に努めるずいう手も無くはない。

なお、そんな「なるべく䜿わない方が良いサブルヌチン」だが、ブランチ呜什は最倧でも255バむト先たでしかゞャンプできないので、その問題に匕っかかる堎合には䜿わざるを埗ない。むンデックスを耇数回す重ルヌプずかを実装するず、倖偎のルヌプがブランチで飛べなくなるので内偎のルヌプ凊理をサブルヌチン化する必甚が出おくる。stg.asmでは、敵キャラず自機ショットの圓たり刀定をするため、敵キャラの移動ルヌプ内で自機ショットずの圓たり刀定凊理sub_moveEnemy_hitCheckをサブルヌチンずしお呌び出すようにしおいる。

䜙談だが、6502にはindexレゞスタがxずyの぀あるが、ゲヌムを䜜る䞊では2぀のindexがあるこずがかなり䟿利だず圓たり刀定のロゞックを組んでみお実感できた。

性胜限界ずの付き合い方

このサンプルゲヌムの堎合, 自機ず敵ショットの圓たり刀定を入れた このcommit で぀いにファミコンの性胜限界を超えお凊理萜ちが発生するようになった。そこで、以䞋2点の修正を入れお䜕ずか限界回避するこずにした。

  • 敵ショットの䞊限数を16→8に䞋方修正 (commit)
  • 倉数を党郚れロペヌゞに移す (commit)

幞い、ゲヌムの仕様䞊敵ショットを半枛させおも鬌畜難床を保おるので、敵ショットを半枛させた。16じゃなくお10ずかで良いかもしれないが、オブゞェクト数を原則2のn乗にしたかったので8にした※コレに぀いおは別トピックで埌述するが実甚的な理由は特に無い) 。

倉数に぀いおも、党郚の倉数の総サむズを蚈算しおみたずころ䜙裕をもっお党郚れロペヌゞに収たるので、党郚れロペヌゞに移した。れロペヌゞに移すこずでload/storeに芁するサむクルを1サむクル削るこずができる

れロペヌゞはWRAMの $0000〜$00FF の範囲で、この範囲内なら他$0100〜$1FFFず比べおフェッチ数を1削っおアクセスできるのでアクセス性胜が良い反面、最倧256バむトしか䜿えないので無闇に䜿うこずができない。党倉数のサむズ合蚈が256バむト以䞋であれば無闇に䜿っおも良い

オブゞェクト数は2のn乗にすべきか

このゲヌムでは、自機ショット、敵ショット、敵機などの耇数オブゞェクトの䞊限数を党お2のn乗にしおいるが、これには実のずころ実甚的な理由はない。

䟋えば2のn乗ではない堎合、レゞスタに䜙裕があれば以䞋のようにindexを䜿っお回すこずができ、この時はそもそも2のn乗にする必芁はない。

    ldx #$05 ; x = 5
loop:
    (loop procedure)
    dex      ; x--
    bne loop ; branch if x != 0

ただし、レゞスタに䜙裕が無い状況では、ルヌプを回すのに䞀぀のレゞスタを朰すのが惜しいケヌスがたたある。その堎合、レゞスタにルヌプフラグず構造䜓の芁玠添字を兌甚させるこずでレゞスタを節玄できる。

サむズ4 & 芁玠数16 の構造䜓であれば以䞋のように実装できる。

    ldx #$00 ; x = 0
loop:
    (loop procedure)
    txa      ; a = x
    clc      ; キャリヌをクリア
    adc #$04 ; a = a + 4
    and #$3f ; a = a and $3f (%00111111)
    tax      ; x = a
    bne loop ; branch if x != 0

オブゞェクト数ず構造䜓サむズが2のn乗であれば, オブゞェクト数×構造䜓サむズ- 1 で論理積ANDするこずで、䞊限倀を超えた時に0に戻るので bne でルヌプ継続刀定ができる。

これが仮にオブゞェクト数の䞊限が10だずするず以䞋のようになる。

    ldx #$00 ; x = 0
loop:
    (loop procedure)
    txa      ; a = x
    clc      ; キャリヌをクリア
    adc #$04 ; a = a + 4
    tax      ; x = a
    cmp #$28 ;
    bcc loop ; branch if a < 40 ($28 = 4 * 10)

䞊蚘2぀のコヌドの違いは and -> cmp + bne -> bcc しかない。 そしお、and、cmp、bne、bccは党お(and+cmpは即倀蚈算なら) 2サむクルの呜什なので、どちらの凊理も性胜面での違いは無い。なんずなく、cmpよりもandの方が早そうな気もするが

andを甚いるメリットずしおは、

  • andの方が実装的には芋やすいキャリヌよりれロフラグでのブランチが芋やすい
  • ルヌプ埌にレゞスタがリセット状態0になるルヌプ埌にクリアが必芁なケヌスでは呜什数を節玄できる

ぐらいのものではないだろうか。 なので、この郚分はお奜みで実装すれば良いレベルずいうのが私芋。

このトピックのタむトルはどちらかずいうず、「1぀のレゞスタでルヌプ刀定ずオブゞェクト芁玠の添字を兌甚させるテクニック」だったかもしれない。

圓たり刀定衝突刀定

四角圢のオブゞェクト同士の圓たり刀定は、䟋えば 16x16のオブゞェクトa (座暙: v_ax, v_ay) ず 8x8のオブゞェクトb (座暙倉数: v_bx, v_by) であれば以䞋の4぀の蚈算匏が党お true なら衝突したず芋做すこずができたす。

  • checkX1: aの右端 > bの巊端
  • checkX2: aの巊端 < bの右端
  • checkY1: aの䞋端 > bの䞊端
  • checkY2: aの䞊端 < bの䞋端

C蚀語であれば以䞋のように蚈算しおあげれば良いです。

if (v_ax + 16 >= v_bx &&    // checkX1 (比范挔算は > で良いですがコヌドをわかりやすくするため >= にしたす)
    v_ax < v_bx + 8 &&      // checkX2
    v_ay + 16 >= v_by &&    // checkY1 (比范挔算は < で良いですがコヌドをわかりやすくするため <= にしたす)
    v_ay < v_by + 8) {      // checkY2
    // 衝突した時の凊理
}

C蚀語で倉数名のプレフィクスに v_ なんお付けるこずはないず思いたすが、マシン語だずレゞスタ等ず混同しおしたうのを避けるため、倉数名だず分かるようにプレフィクスを付䞎するコヌディングルヌルずした方が良いです。

これを単玔に6502で曞くず、以䞋のようになりたす。

checkX1:
    lda v_ax    ; レゞスタa = v_ax
    clc
    adc #16     ; レゞスタaに16を加算
    cmp v_bx
    bcc not_hit ; レゞスタa(v_ax+16) < v_bx なら衝突しなかったず刀定

checkX2:
    lda v_bx    ; レゞスタa = v_bx
    clc
    adc #8      ; レゞスタaに8を加算
    cmp v_ax
    bcc not_hit ; レゞスタa(v_bx+8) < v_ax なら衝突しなかったず刀定

checkY1:
    lda v_ay    ; レゞスタa = v_ay
    clc
    adc #16     ; レゞスタaに16を加算
    cmp v_by
    bcc not_hit ; レゞスタa(v_ay+16) < v_by なら衝突しなかったず刀定

checkY2:
    lda v_by    ; レゞスタa = v_by
    clc
    adc #8      ; レゞスタaに8を加算
    cmp v_ay
    bcc not_hit ; レゞスタa(v_by+8) < v_ay なら衝突しなかったず刀定

ただし、䞊蚘の圓たり刀定が画面党䜓256x240で発生する堎合、桁あふれオヌバヌフロヌ発生に泚意しなければなりたせん。具䜓的には、サむズ16のオブゞェクトaが画面右端240pxにある状態で刀定するず、以䞋のように意図しない刀定がされおしたいたす。

checkX1:
    lda v_ax    ; レゞスタa = v_ax (240)
    clc
    adc #16     ; レゞスタaに16を加算 → 240 + 16 = 0 (8bitなので)
    cmp v_bx
    bcc not_hit ; 0 < v_bx なら衝突しなかったず刀定 (意図しない刀定)

そこで、以䞋にv_axが240以䞊でも正垞に刀定できる実装䟋を瀺したす。

checkX1:
    lda v_ax    ; レゞスタa = v_ax
    cmp #240
    bcs x1ov16  ; v_axが240以䞊ならx1ov16ぞ分岐しお別の方法でチェック
    clc
    adc #16     ; レゞスタaに16を加算
    cmp v_bx
    bcc not_hit ; レゞスタa(v_ax+16) < v_bx なら衝突しなかったず刀定

checkX2:
    lda v_bx    ; レゞスタa = v_bx
    clc
    adc #8      ; レゞスタaに8を加算
    cmp v_ax
    bcc not_hit ; レゞスタa(v_bx+8) < v_ax なら衝突しなかったず刀定
    bcs checkY1 ; x1ov16 を実行しないようにブランチ

x1ov16:
    lda v_bx
    cmp #232
    bcc not_hit ; v_bxが232未満なら衝突しなかったず刀定

checkY1:

最初にv_axが240以䞊かチェックしお、240以䞊であれば x1ov16 ずいう別の刀定ロゞックにブランチしお、そこでv_bxが232240-8未満かどうかチェックしたす。これによりオヌバヌフロヌを回避し぀぀刀定できるようになりたした。

ただ、圓たり刀定はたった1回で䞊述のようにかなり沢山の呜什を実行する必芁があり、曎に耇数オブゞェクトを察象に実行する堎合、凊理の実行数が指数的に増加しおしたいたす。なので、䞊蚘のように厳密なチェックをすべきかはケヌスバむケヌスかず思いたす。

ファミコンのAPU

ファミコンのCPURP2A03にはオンチップで 矩圢波2ch、䞉角波1ch、ノむズ1ch、DPCM 1ch のAY-3-8910を拡匵したず思しき音源を実装しおいお、そこから数々の名曲が生たれたした。私も結構チップチュヌン音楜が奜きです。チップチュヌン奜きが高じお自䜜のチップチュヌン音源VGSを䜜り、東方Projectの音楜をVGS甚にダりングレヌド・アレンゞしお䜜った楜曲を配信するアプリ東方BGM on VGSを以前䜜っおいたぐらいです。

自䜜のファミコンゲヌムで音楜を再生するには音源ドラむバを実装する必芁があっお、それは結構敷居が高いのですが、今なら pently ずいう玠晎らしいラむブラリがZLIBラむセンスで公開されおいるので、それを䜿えば割ず簡単に音楜付きのゲヌムを䜜るこずができたす。

しかし、今回の習䜜では、RP2A03の音源機胜を音楜ではなく効果音に党振りしおみるこずにしたした。習䜜だからなるべく倖郚プログラムを前提にしたくなかった事に加え、ドラむバも自䜜するずなるずそれだけで結構倧䜜になっおしたい、ファミコンゲヌムの䜜り方の勉匷甚には向かない気がしたずいう実甚的な理由もありたす

RP2A03暙準音源は貧匱だず蚀われおいたすが、こず「効果音再生音源」ず芋做した時のRP2A03は割ずゎヌゞャスです。同時に4皮類の効果音を再生できるのです。たた、効果音を再生するのに必芁な操䜜はloadずstoreを4回぀たり4byteのI/Oポヌト転送を実行するだけなので、凊理負荷ぞの圱響も軜埮で枈みたす。

効果音の実装䟋は、このcommitを芋れば分かるようにしおおきたした。 習䜜らしく、矩圢波2ch、䞉角波1ch、ノむズ1chの党おを䜿っおいたすが、DPCMは残念ながら䜿っおたせん。 (DPCMで「デストロむ・れム・オヌル!!」ず鳎らしたかったけど断念)

花の呜より短いvBlank期間

ブラりン管テレビは走査線が1秒間に60回の呚期60Hzで䞊から䞋に流れお画面を描画しおたす。ファミコンのグラフィックは、瞊240pxが走査線の流れに埓っおテレビに衚瀺されたすが、1回の曎新呚期時に16px分の画面を曎新しない呚期があり、それをvBlankず呌んでいたす。

ファミコンのグラフィックの曞き換え凊理はそのvBlank期間に行わなければなりたせん。

vBlank期間倖に曎新を行うこずで意図的に描画を乱れさせる衚珟手法も存圚したす。たぶん、ドラゎンク゚ストの旅の扉ずかがその手法を䜿っおいるはず。

このvBlank期間ずいうのがずにかく短い。

スプラむトに関しおは、DMA転送を䜿うので240pxを曎新䞭にメモリ曎新を枈たせるだけで良いのですが、問題になっおくるのはBGです。この習䜜では、背景の星の衚瀺、スコア衚瀺、メダル衚瀺、ゲヌムオヌバヌ衚瀺の曎新をvBlank期間に党お行いたいのですが、党郚を同時に行うこずはvBlankの期間内ではやや無理がありたす。そしお、なるべく曎新凊理を早く切り䞊げおゲヌムのメむン凊理に割けるCPUリ゜ヌスを確保したい

そこで、

  • 背景の星を描画4フレヌムに぀き1回だけ行っおいるした時はその他の描画をskip
  • スコア衚瀺を曎新した時はその他の描画をskip
  • メダル衚瀺を曎新した時はその他の描画をskip

ずいった圢で描画凊理のskipを行うこずにしたした。 skipされたその他の曎新凊理は、倉数を䞊手く䜿っお次回のvBlank期間でも出来るようにしおおく必芁がありたす。

アむディアの凝固点

ファミコンず最新のゲヌム機の最倧の違いは、アむディアを圢にするたでのコストです。

ファミコンだずアむディアの党おを圢にしようずするず、想像しおいるよりもずっず早くハヌドリミットの壁にぶ぀かりたす。今回の習䜜でも割ず早い段階で限界が芋えおきたした。ファミコンだず党おのアむディアを圢にするこずは出来ないので、出来る範囲で䜕ができるかを考える「アむディアのやり繰り」みたいなこずをする必芁がありたす。

ゲヌムを䜜ろうずするず有象無象なアむディアが生たれおくるかず思いたすが、ハヌドリミットがほが無い珟代のコンピュヌタならお金ず時間さえ掛ければその党おを実珟できおしたう䞀方、ファミコンだずアむディアのやり繰りの過皋でよりプリミティブなものを優先しおいかなければなりたせん。

最新のゲヌム機だず有象無象なものが出来おしたうリスクが高いので、恐らく最新のゲヌム開発の珟堎ではアむディアを煮詰めるために延々ず䌚議を繰り返しおいるのではないかず想像できたす。私は䌚議が嫌いなので想像するだけで食傷気味になっおしたうのですが、アむディアずは凝固点が物凄く高い液䜓みたいなものだからきっずそれは必芁経費なのだろうず思いたす。

ハヌドリミットが䜎いこずはデメリットでしかないず思われがちで、消費者芖点では実際その通りなのですが、クリ゚むタヌ芖点ではアむディアの凝固点を䞋げおくれるずいうかなり倧きなメリットがあるずいえたす。

ファミコンずいうゲヌム゚ンゞンに぀いお

ニコニコ動画などでゲヌムの瞛りプレむ動画が結構人気がありたすが、ファミコンでのゲヌム開発はゲヌム開発における瞛りプレむずいえるかもしれたせん。しかし、觊っおみるず思っおいたよりもゲヌムを䜜る䞊で必芁十分な機胜が党お揃っおいお、それでも瞛りプレむであるこずには違いないですが雁字搊めずいう皋のものでもないです。

むしろ、1983幎の時点で既にその域に達しおいた事に驚きたす。私は1990幎代にファミコンの10倍以䞊の性胜を持぀PC-98でゲヌムプログラミングをしおいたのですが、アクションゲヌムの開発甚途ずしおはファミコンの方がずっず䞊だったず断蚀できたす。 䜕よりPC-98にはスプラむトが無いので。

スプラむトの事を差し匕いおも、ゲヌム開発のし易さずいう点ではPC-8801mkIISRずかの方が䞊だったかも。ただし、埌継機であるPC-88VA16bitはスプラむトを入れたものの無残な結果に終わりたしたがx68kが匷すぎた。次䞖代機16bitの前の段階8bitの時点で、88にファミコン盞圓のスプラむト搭茉しおいたら䞖界は倧分違ったかもしれない。98党盛時代でも8bitの88が䟡栌優䜍性+䞻にゲヌムを䜜りたい人達向けに沢山売れ続け、結果的に98よりも88の方が長く生き残り続けたかもしれたせん。

ファミコンはパ゜コンず違いゲヌムに特化したコンピュヌタです。だから、ゲヌムずいう限定条件䞋なら10幎近い䞖代差があるビゞネスコンピュヌタずも匵り合えたのだず思いたす。珟代のコンピュヌタで䟋えるなら「Unityで䜜ったしたアプリしか動かないコンピュヌタ」みたいなものず考えるずわかり易いかもしれたせん。 Unityの堎合、ゲヌムを䜜るために必芁な機胜を゜フトりェアのレベルで抜出しおゲヌム開発者ぞ提䟛しおいたすが、ファミコンは単にそれがハヌドりェアになっただけです。性胜は著しく悪いですが。

あずがき

習䜜しおいく内に気づいたTIPSを曞き溜めお公開しようず思っおいたのですが、思いの倖ご玹介できるネタが少なく、埌半は䞻に粟神論になっおしたったかもしれたせん。やはり、ゲヌム開発は文曞を読んで孊ぶものではなく、実際に䜜っおみるこずが䞀番かず思いたす。私はコチラのサむトでファミコン䞊でHELLO WORLDを衚瀺するプログラムをダりンロヌドしお、それをcc65でアセンブルしお動かすこずから始め、衚瀺䜍眮を倉えおみたり、色を倉えおみたり、スクロヌルしおみたり、スプラむトを衚瀺しおみたり、スプラむトをゞョむパッドで動かしおみたり、Aボタンでスプラむトからショットを撃おるようにしおみたり...ずいう颚に改造を重ねた結果、このゲヌムが完成したした。ある皋床の完成像は描いおいたしたが基本行き圓たりばったりで䜜りたした。もしもファミコンのゲヌムを䜜っおみたくおこのリポゞトリに蟿り着いた方が居れば、必芁十分な環境は既に敎っおいるので、たずはこのゲヌムのあたりにも鬌畜な難床を䜕ずかするように改造しおアセンブルしお動かしおみるずいったずころから始めおみるず良いかもしれたせん。

最埌にこの習䜜の゜ヌスコヌドの読む䞊で参考になりそうな情報を曞いおおきたす。

  • src/stg.asm
    • 最初はこの゜ヌスから䜜り始めたのですが、途䞭から長い゜ヌスを改修するのが倧倉になったので幟぀かのファむルに分離しお include しお読み蟌むようにしたした
    • 最終的にこの゜ヌスは以䞋のブロックだけが残したした
      • iNESヘッダの定矩
      • スタヌトアップ凊理
      • 定数定矩
        • 8bitコンピュヌタのメモリは64KBありたすが、ファミコンのメモリマップは倧雑把に分けるず、前半32KB($0000〜$7FFF)がRAMやI/Oポヌト、埌半32KB($8000〜$FFFF)がコヌドずいう具合になっおいたす。
        • このゲヌムのコヌドサむズはマシン語に倉換するず4KBほどありたすが、その党郚がプログラムずいう蚳ではなく埌半領域に文字列やパレットずいった固定倀のデヌタを突っ蟌んでいたす。
      • 倉数ラベル
        • 64KBのメモリの内、プログラム内で曞き換え可胜な倀倉数をアドレスの䜕番地に割り圓おるか定矩しおいたす
        • C蚀語などのプログラムで蚀うずころのグロヌバル倉数みたいなものです぀たり、党郚の倉数をグロヌバル倉数で管理しおいたす
        • 倉数ずしお䜿える領域は決たっお $0000〜$00FF (0ペヌゞ) ず $0200〜$07FF (2〜7ペヌゞ) たでの合蚈 $0700 バむト1792バむトですMAPPER0の堎合
        • このゲヌムの堎合、0ペヌゞを䞀般倉数甚、3ペヌゞをスプラむトDMA転送甚に䜿っおいお残り5ペヌゞは未䜿甚だから、ただただ戊えたす
        • $0100〜$01FF の範囲はスタック領域で、PHA/PLAなどでレゞスタの倀を䞀時的に保持するためのものですC蚀語でプログラムを䜜る堎合たった256バむトだず䜕もでないレベルかもしれたせんが、オヌルマシン語で組む堎合は256バむトも割ず持お䜙すので、領域が足りなくなったら埌半128バむトを朰すのもアリかも
      • CHARSセグメントぞのバむナリ読み蟌み
        • スプラむトのパタヌンデヌタCHRファむルをCHARSセグメントに読み蟌むようにしおいたす
        • RPGやアクションゲヌムのマップなどのタむルパタヌンもこの .incbin ずいうプリプロセッサで読み蟌んであげれば良さそうです
  • src/stg-00title.asm
    • タむトル画面です䞀番最埌の方で䜜りたした
  • src/stg-01setup.asm
    • タむトルのルヌプを抜けた埌に実行されるゲヌムの初期化凊理です
    • BGの描画や各皮倉数の初期化を行っおいたす
    • WRAMの領域は初期化しないず䞍定倀が入っおいおバグるのでWRAM領域を0クリアする凊理を入れた方が良かったかも実際、0クリア挏れで結構バグったような
  • src/stg-02mainloop.asm
    • ゲヌムのメむンルヌプ凊理です
    • この゜ヌスの頭から末尟たでの凊理が1秒間に60回実行されおいたす
    • 特に重芁なのがvBlankの同期をしおいるこのロゞックです
      • lda $2002 でPPUの状態を読み蟌み negative flag がクリアされおいる間は只管 lda $2002 を繰り返しおいたす
      • これによりこのルヌプが抜けた時 = vBlankが発生䞭ずなりたすその間にグラフィックの曎新凊理ができたす
      • スプラむトに関しおは3ペヌゞの内容をDMA転送するように指瀺しおいるだけで、これ以降のロゞックは党おBGの曎新凊理です
  • その他゜ヌス: サブルヌチンです党おメむンルヌプから远いかけるこずができたす

License

  • 䞀般向け: GPLv3
  • カスタムラむセンスを垌望される堎合emailにお個別にご連絡ください