M1 Macで作る、ファミコンソフトプログラミング。 アセンブラでハローワールド編
2021.11.08
この記事は最終更新日から1年以上が経過しています。
どもです。
前回のこちらの記事に引き続きファミコンプログラミングでもと。
前回は、NES研究所さんのサンプルを動かして「HELLO WORLD」を表示させたのですが、今回は実際にアセンブラでプログラムを書いて作って行こうかと思います。
やりたいこととしては、前回同様に「HELLO WORLD」の文字を画面に出したい。
そんなの、「HELLO WORLD」の文字列を出すだけなら簡単でしょ。
と思いそうですが、昔々のがファミコン時代の頃には文字列というのもなく、端的に言うと文字を出すのも画像(スプライト)を作る必要がある訳なのです。
では、M1 Macの環境でファミコンゲームの作成を行って行きましょう。
環境
- Mac mini (M1, 2020)
- Mac OS 11.6
- Apple clang version 13.0.0
スプライト画像作成
という訳で、スプライト画像の作成から行って行くのですが、それには「YYCHR」というツールを用いて作成していきます。
YYCHR – ダウンロード
こちら、Windows用のアプリとなりますので、前回の「M1 (Appleシリコン)Macで Widowsアプリを起動」の記事で紹介したように、CrossOver Wineをインストールしそちらで起動します。
$ wine64 YYCHR.exe
起動すると、この様な表示になるかと思います。
今回は、「YYCHR」の使い方詳細は割愛させていただきます。
いつか機会があれば書いていきたいとは思いますが、こちらのYYCHRのWikiに使用方法など書かれているので、そちらを使用方法は確認していただけると理解できるかと思います。
ABC…とアルファベットのスプライトを作って行くのですが、こちらの書籍など参考にすると作成がスムーズに進みますので、レトロゲーム作成される方は、1冊持っておくのをオススメします!
という事で、上記の書籍を参考に以下の様にスプライトの作成。
作成したスプライトは、character.chrファイルとして書き出しを行います。
アセンブラでプログラミング
先程、「YYCHR」で作成したスプライトのcharacter.chrファイルを読み込み、「HELLO WORLD」と表示される様に、hello.asmファイルのアセンブラプログラムを作成します。
作成するアセンブラプログラムは以下の様な感じです。
hello.asm
;---- iNES Header .inesprg 1 ; PRG 16KB .ineschr 1 ; CHR 8KB .inesmap 0 ; Mapper 0 .inesmir 0 ; horizontal mirroring ;---- bank0 .bank 0 .org $C000 RESET: sei cld .vblankWait1 bit $2002 bpl .vblankWait1 .vblankWait2 bit $2002 bpl .vblankWait2 ; reset PPU status lda $2002 lda #$3F sta $2006 lda #$00 sta $2006 ; X register initialization ldx #0 .loadBgPalette lda bgPalette, x sta $2007 inx cpx #16 bne .loadBgPalette ; reset PPU status lda $2002 lda #$3F sta $2006 lda #$10 sta $2006 ; X register initialization ldx #0 .loadSpritePalette lda spritePalette, x sta $2007 inx cpx #16 bne .loadSpritePalette ; H lda #$50 sta $0200 lda #$07 sta $0201 lda #%00000001 sta $0202 lda #$10 sta $0203 ; E lda #$50 sta $0204 lda #$04 sta $0205 lda #%00000011 sta $0206 lda #$18 sta $0207 ; L lda #$50 sta $0208 lda #$0B sta $0209 lda #%00000011 sta $020A lda #$1F sta $020B ; L lda #$50 sta $020C lda #$0B sta $020D lda #%00000011 sta $020E lda #$26 sta $020F ; O lda #$50 sta $0210 lda #$0E sta $0211 lda #%00000011 sta $0212 lda #$2D sta $0213 ; W lda #$50 sta $0214 lda #$16 sta $0215 lda #%00000011 sta $0216 lda #$37 sta $0217 ; O lda #$50 sta $0218 lda #$0E sta $0219 lda #%00000011 sta $021A lda #$3F sta $021B ; R lda #$50 sta $021C lda #$11 sta $021D lda #%00000011 sta $021E lda #$46 sta $021F ; L lda #$50 sta $0220 lda #$0B sta $0221 lda #%00000011 sta $0222 lda #$4D sta $0223 ; D lda #$50 sta $0224 lda #$03 sta $0225 lda #%00000011 sta $0226 lda #$54 sta $0227 ; NMI lda #%10000000 sta $2000 lda #%00010000 sta $2001 .forever jmp .forever NMI: lda #$00 sta $2003 lda #$02 sta $4014 rti IRQ: rti spritePalette: .byte $0D, $01, $05, $20 .byte $0D, $01, $05, $20 .byte $0D, $01, $05, $20 .byte $0D, $01, $05, $20 bgPalette: .byte $01, $32, $36, $3A .byte $01, $22, $26, $2A .byte $01, $12, $16, $1A .byte $01, $02, $06, $0A ;---- bank1 .bank 1 .org $FFFA .word NMI .word RESET .word IRQ ;---- bank2 (CHR bank0) .bank 2 .org $0000 .incbin "character.chr"
色々書かれていますが、全部説明となると途方もないので、とりあえず、bank2に先程作成したスプライトのcharacter.chrを読み込んで、.loadSpritePaletteでスプライトの種類、位置を設定して画面に配置しております。
それでは、上記のアセンブラプログラムを元に、ファミコン実行ファイル「.nes」形式にアセンブルするために「nesasm」をインストールしていきます。
nesasm インストール
NES 6502 assemblyをアセンブルするため「nesasm」をインストールしていきます。「nesasm」のソースはgithubに上がっております。
nesasm
https://github.com/camsaul/nesasm
zipファイルをダウンロードを行うか、git clone行うかしローカルにファイルをダウンロードします。
以下のコマンドを実行nesasmをインストールします。
$ cd source && make && sudo make install
インストール完了後、nesasmコマンド実行し、以下の様に表示すれば問題なくインストール完了です。
nesasm NES Assembler (v3.1) nesasm [-options] [-? (for help)] infile -s/S : show segment usage -l # : listing file output level (0-3) -m : force macro expansion in listing -raw : prevent adding a ROM header -srec : create a Motorola S-record file infile : file to be assembled
先程の作成したスプライトのcharacter.chrファイルと、hello.asmが置かれたディレクトリにて以下のコマンドを実行します。
$ nesasm hello.asm
すると、新しく「hello.fns」と「hello.nes」ファイルが生成されます。
「hello.nes」ファイルが実行ファイルとなりますので、エミュレーターを使ってデバッグしてみましょう。
シミュレータでデバッグ
ファミコンシミュレータは沢山存在します。
とりあえず、開発用として重宝されている有名な「fceux」でデバッグを行うと。
fceux
おお。問題なく表示されていますね。
続いて、「Open Emu」でデバッグしてみると。
Open Emu
おや、「LD」の2文字が表示されません。。
バグか。。と思われがちですが、ファミコンの仕様としてはこちらが正解。
スプライトは、同時に8個以上並べられない仕様なのです。8個以上並べると表示されなくなります。
Open Emuのコアは、FCE Ultraぽいので、こちらのほうが仕様に沿っていて、FCE Ultraからフォークされて拡張された「fceux」では仕様通りではないって感じでしょうか。(検証してみた感想)
という訳で、仕様に沿って文字表示されたいとなると、Wからの文字をずらしたりすると良いでしょうか。
Wから Y座標を10ずらしてみます。
; W lda #$60 ....
すると、以下の様に全てのスプライトが表示され「HELLO WORLD」と認識できました。
この手法はなにか見覚えありますね。
そう、チャレンジャーのゲーム中によく出てくる文字表示の表現だ!
仕様に沿って、8個以上文字が並ばないように、少しずつずらして、文字を表示されているのがわかりますね。
当時はおしゃれでやっているのかと思っておりましたが、そういった理由があったのですね。
終わり
と、ざっくり書いてきましたが、アセンブラでプログラムを作成すると辛いことが確認できましたので(そりゃそうだ 笑)、現在では C言語などでも作成できるようになっておりますので、今後は開発する際はそちらでやって行こうかなとか思っております。
ただ、やることいっぱいなので、後になっていくのだろうなぁと思っております。。。
ではでは。またまた。