どうもGAIさんです。
なぜかファミコンカセットを新しくつくることになったので混乱しないようにまとめながら進めます。
ファミコンを作るためのメモ。
環境構築&基礎学習
まずはこれをみて実行環境を整える。
NESアセンブラのDLはここから
https://www.2a03.jp/~minachun/nesasm/nesasm_x86.html
バイナリエディタ「Stirling」のDLはここから
https://www.vector.co.jp/soft/win95/util/se079072.html
動画の中で、でてくるコードはこちら
.inesprg 1
.ineschr 0
.inesmir 0
.inesmap 0
.bank 0
.org $8000;
Start:
lda #1
sta $00
.bank 1
.org $FFFA
.dw 0
.dw Start
.dw 0
僕が使っているエミュレータはVirtualNES
http://virtuanes.s1.xrea.com/
メモリ番地更新ってなんやねん
Start:
lda #1
sta $00
ここのこと
lda : レジスターAに値を読み込む。
レジスターは一時記憶領域。いったん保存しとくとこ。AとかBとかは、なんか3つくらいある。
ここだと、1を読み込む。load aの略(多分)
sta : メモリAに値を書き込む。
レジスタAに読み込んだのは1なので、1を00番地に書き込む感じ。
store aの略(多分)ストア→書き込み
ldaとstaは基本セット。読み込んだら書き込む。当然だね。
#とか$とか
# : 10進数の数字
$ : 16進数
#% : 2進数
ヘッダー情報
.inesprg 1 ;プログラムROM数
.ineschr 0 ;画像ROM数
.inesmir 0 ;ビデオメモリの構造
.inesmap 0 ;カートリッジの種類
これのこと。
ヘッダーはエミュレータに読み込むための前情報的なやつ。
ファミコンのROMには入ってない。
カートリッジにはROMが2つのってて、プログラムROMと画像ROM。
このヘッダーだとプログラムROMが1つで画像ROMが無いって感じ。
プログラムROMは1つにつき16384byte($4000byte)
ビデオメモリとかカートリッジの種類(マッパー)は・・・まあいったん後で(オイ
コードの中身
これ何?
.bank 0
.org $8000
.(ドット)から始まるやつはCPUへの命令にあらず。
NESファイルの何バイト目に書き込むとか、ただの数列とか。
NESファイルをコントロールするための構文らしい。特別な指示を与える、的な。
調べたやつをいくつか書く。ちなみに、nesasmを使った場合なので、cc65使ったときとかは変わるっぽい。
なんて面倒な。
.bank <bank number>
現在のバンク番号を設定。(バンクってなんやねん)
バンク番号は0から255の範囲で指定。
nesファイルで指定されたbankはエミュレータ―の特定のメモリから実行し始めるそうだが・・・わからん。
bankの設定はマッパーごとに異なるらしい。なんだそれ。
というわけでAIさんに訊いてみた。
AIさんのbank説明
ファミコンのゲームでは、たくさんのデータを扱う必要があります。
例えば、ゲームのプログラムや、キャラクターのグラフィック、音楽などです。
しかし、ファミコンの本体には、これらのデータを一度に扱えるだけのメモリ(記憶装置)が十分にありません。
そこで、bankという仕組みを使います。bankは、データを小分けにしてグループ分けするようなものです。例えば、ゲームのプログラムをbank 0に、キャラクターのグラフィックをbank 1に、音楽をbank 2に、といった具合に分けます。
ファミコンは、一度に1つのbankしかアクセスできません。つまり、bank 0のデータを使っているときは、bank 1やbank 2のデータには直接アクセスできないのです。でも、必要に応じてbankを切り替えることができます。
プログラムの中で.bankというアセンブラディレクティブを使うと、アクセスするbankを指定できます。例えば、.bank 1と書くと、bank 1に切り替わります。そうすると、bank 1のデータにアクセスできるようになるんです。
bankを使うことで、ファミコンの限られたメモリを有効に使い、大きなゲームを作ることができます。プログラムは、必要なデータがあるbankに切り替えながら、ゲームを進行させていくんですね。
このように、bankはファミコンのメモリを拡張するための仕組みで、ゲームのプログラムやデータを整理するのに役立っています。うまくbankを使い分けることが、ファミコンのゲーム開発には欠かせないんです。
めちゃめちゃわかった
.org <address>
現在のアセンブリ位置を指定されたアドレスに設定。
コードやデータをメモリ上の特定の位置に配置するために使用。
.db <byte1>[, <byte2>, ...]
1バイトのデータを定義。複数のバイト値をカンマで区切って指定できる。
.dw <word1>[, <word2>, ...]
2バイトのデータ(ワード)を定義。複数のワード値をカンマで区切って指定できる。
.incbin <filename>
バイナリファイルを現在の位置に含める。
主にグラフィックデータやサウンドデータを埋め込むために使用。
.include <filename>
指定されたファイルをソースコードに含める。
コードの共有やモジュール化に役立つ。
.def <label> = <value>
シンボルを定義し、値を割り当てる。
定数の定義などに使用される。
.if <condition>、.else、.endif
条件付きアセンブリを行うための制御構文。
.ifディレクティブは、条件が真の場合にのみコードを実行する。
.macro [<param1>[, <param2>, ...]]、.endm
マクロを定義するために使用。
.macroディレクティブは、マクロ名とオプションのパラメータを指定。
.endmディレクティブは、マクロ定義の終了。
_.proc <name>、.endproc
プロシージャ(サブルーチン)を定義するために使用。
.procディレクティブは、プロシージャ名を指定。
.endprocディレクティブは、プロシージャ定義の終了。
他にも、.rsset、.rs、.byte、.word、.inesprg、.ineschr、.inesmap、.inesmirとかあるらしい。
なんだろ、C#でいうところの・・・変数宣言とかそういうところ?
中身のないメソッド的な?そういう意味ではインターフェースが近いのか?
まだわからんので今後理解できると信じる。
さて、これを踏まえた上でコードに戻る
.bank 0
bank 0の下に命令を書くと$10バイトに命令が出力される。bank〇〇は0~255で指定するとのことだが
0が\$10番地に該当するのか
この$10のずれはなんなん?→ヘッダー分のずれ
.bank 0直下の命令は
.bank 0
.org $8000;
Start:
lda #1
だからlda #3
この命令がバイナリ上で$0010に書き出されるということ。
ldaに該当する16進数はA9らしいのでA9が書き出されている。
bankが1増えるごとに$2000増える。
bank 1なら$2010に書き込まれる。
詳しくはAIさんの説明を読もう。
さて次
.org $8000
読み方はオリジン。
役目
- bankで指定できない細かい書き込み位置の指定
- 下三桁が出力位置の指定($8005なら5バイト目)
- 上一桁は
NESファイルのメモリ位置とエミュレータ―にロードしたときのメモリ位置が違うので
orgでメモリの何番地に読み込まれるかを指定してる。
bank0は$8000から実行されるのでorg $8000を指定することで正常に動く、らしい。
bank1は$A000から、bank2は$C000、bank3は$E000かららしい。
じゃあこれが何に影響するの?ってのは、ラベルというものに関わる。
.org $8000
Start: ;これがラベル
これを書くと、NESASMに
「Startは$8000な」
って覚えさせられる。
Start:
lda #1
sta $00
.bank 1
.org $FFFA
.dw 0
.dw Start ;ここでStartを呼んでる
.dw 0
.dwは、2バイト長で書くという指示。
つまり、
lda #1 →1を読み込み
sta \$00 →00番地に1を書き込み
.bank 1 →bank1に切り替え
.org \$FFFA →\$FFFA番地に移動(つまり\$4ffa)
.dw 0 →指定の番地に0を2バイトで書き込み
.dw Start →Startを2バイトで書き込み(\$8000を書き込み。順番に書き込むので表示は0080)
.dw 0 →0を2バイトで書き込み
ということか。
なるほど。
bank1のFFFAって何?
$FFFA~FFFFは特別な場所。
\$FFFA〜\$FFFB: Non-Maskable Interrupt(NMI)ベクタ
\$FFFC〜\$FFFD: リセットベクタ
\$FFFE〜\$FFFF: IRQ/BRKベクタ
つまり・・・どういうことだってばよ?
NMIベクタ:VBlank時に実行する
リセットベクタ:リセットボタン押した時に実行
IRQ/BRKベクタ:入力による割込み時に(コントローラー入力など)
VBlankってなんだよ
ファミコンは、1秒間に60回(NTSC方式)または50回(PAL方式)の速度で画面を描画しています。
画面の描画が終わると、次の描画までの短い期間があります。
この期間をVblank(垂直帰線期間)と呼びます。
そういうことね。うんうん、2000%理解した。
VBlankの時に画面の更新やスコアの更新を行うことで、なめらかに動いているように表現する。
つまり、画面更新中は黙ってみてろってことだね。
とりあえず仕組みの一端はなんとなく把握したので、次は音出し。
to be continued