NESエミュ用の Hello World を作る
Ubuntu 15 上で NES エミュレータで動く Hello World を作ってみます。
コンパイラ
NES の CPU は 6502 ですので、コンパイラには cc65
を使います。Ubuntu
には cc65
のパッケージが用意されていて、apt-get でインストールできる
のですが、このバージョン(V2.13.9)には問題があってプログラムのリンクで
こけます。
cc65 の github リポジトリから 最新のソースツリーをクローンして、自前でビルドしてインストールしましょ う。
$ make
$ sudo prefix=/usr/local make install
エミュレータ
FCEUX という NES エミュレータを使います。謎めいた名前ですが、Wikipedia
によると FCE Ultra というエミュレータの各種派生版をマージしたものだそ
うです。apt-get
からインストールできます。
$ sudo apt-get install fceux
ランタイム環境
メモリ
- NESには、メインメモリ は 2KB しか載っていませんが、cc65 では 静的変数をセーブRAMに割り当てることで、8KB のメモリを静的変数として 扱うことができます。1
- また、6502 は、CPUスタックが 1 ページ(= 256 バイト)しかありませんが、 C関数の引数や自動変数は別の領域に割り当てられますのでもう少し使えま す。
malloc(3)
によるメモリの動的確保はできません。2
標準関数
- ファイル操作関数は使えません。
ファイルシステムが無いので当然ですが、printf(3)
による画面への出力や
abort(3)
によるプログラムの停止もそれぞれ、標準出力や標準エラー出力と
いう「ファイル」の操作になりますので、使えません。これらの関数を使った
プログラムはリンク時にエラーになります。
画面表示
画面への出力には conio.h
に定義される関数を使います。例えば、printf
の
替わりに cprintf
という関数があります。
NES では画面の操作は基本的に VBlank 3中にする必要があるのですが、 conio を使えば出力はいったんバッファにたくわえられて、VBlank 時に割り 込みルーチンが実際にビデオメモリに書き込んでくれます。メインプログラム の都合の良いタイミングで文字の出力ができるわけですね。
conio は画面のスクロールをサポートしていません。画面外の座標に書き込む とビデオメモリの内容が破壊されますので注意する必要があります。
Hello World
ソースコード
#include <conio.h>
int main()
{
cprintf("Hello World\r\n");
while (1)
;
}
main
を抜けてしまうと画面が消えてまたプログラムが最初から実行されてし
まうので、最後に無限ループを置いています。
また、cprintf
では改行するときに \n
の替わりに \r\n
を使います。
コンパイル
cc65
コマンドはアセンブリを出力するだけなので、リンクまでしてくれる
cl65
コマンドを使います。
$ cl65 -o main.nes -t nes main.c
実行
$ fceux main.nes
やったね。
画面をスクロールさせたりしようとすると、conio が邪魔になっていろいろ 大変なのですが、それはまた別のお話。