私は、新しいGBDK配布をリリースする時間がほとんどない。 私は今ちょうど博士論文を書くことで非常に忙しい。 コンパイラについてのよい知識を持った誰かがGBDKの一層の開発を助けてくれたら、私はうれしい。
浮動小数点とlong longサポートにいくつかバグがあります。 修正の前にこれらの型を使用しないでください。
このページは完全には完成していない。 ここで 古い(時代遅れの)ドキュメントにまだアクセスできる。
GameBoy開発キット(GBDK)は、任天堂GameBoyシステムのための、Cとアセンブラの両方でプログラムを開発することができる一組のツールである。 GBDKは、最も一般的な必要条件のための一組のライブラリを含んでおり、実際のGameBoyや VGB のようなエミュレータで使用するためのイメージファイルを生成する。
GBDKはUNIXとDOSで利用できる。 GBDKのUNIXバージョンはSolaris 2.5.1上でテストされた。 DOSバージョンはDOSにプロテクトモードインタフェース(DPMI)と共に、386DXプロセッサ(以上)を要求する。
GBDKは、現在いくつかのプロジェクトで使用されている。 GBDKを使用した最初の商用プロジェクトは Pro Action Replay II である。
GBDKの特徴:
GBsed (Javaで書かれた相棒スプライトエディタ)は、GameBoyプログラムに含まれるイメージとスプライトを作成することができる。 代わりに、 Ian James によるBMPファイルをCコードに変換するWin 95/NT BMP2GB プログラムか、GBDKのためのスプライトを作成するWin 95/NT GameBoyタイルデザイナー プログラムを使用することができる。
GBDKは商用でない開発のためのフリーウェアである。 使用する場合、あなたの活動を私に通知しておくために、私に電子メールを送るだけでよい。 GBDK本当に偉大だと感じれば、私にあなたの国の代表的なものを送ってください...
GBDKを商用プログラムを開発するために使用する場合、プログラムがGBDKを使用して作られたと(クレジット中で)言及し、私に完成品のコピーを送ることを求める。 寛大に感じれば、会社がGameBoyのために制作した他の製品のコピーも送ってください...
最新のGBDKバージョンは、lcc 4.1のプレリリースバージョンを使用するので、DOSバイナリのみで利用できる。 lcc 4.1が公式にリリースされればすぐにUnixバージョンを利用できるだろう。 最新のUnixバージョンを得たければ、私と直接連絡をとること。
LD (HLI),A LD (HLD),A LD A,(HLI) LD A,(HLD) ADD SP,offset LDHL SP,offset
GBDKのインストールは次のステップを要求する:
GBDKを構築するために、gcc、gnu makeと/bin/cshがなければならない。
% gzip -cd GBDK-2.0.tar.gz | tar xf -
% cd GBDK-2.0
% ./make_unixこれは、必要なすべてのファイルを中にもつディレクトリ(デフォルトでは$HOME/GBDK-2.0)を作成する。
% cd $HOME/GBDK-2.0/lib % make % cd $HOME/GBDK-2.0/examples % makeこの時点で、もはやソースを必要としないので削除できる。
少なくとも386DXプロセッサとDOSプロテクトモードインターフェイス(DPMI)がなければならない。 例えば、Windows95はDPMIを提供する。 さらに ここから 1つを入手することができる。
C:> unzip -d GBDK-2.0.zipこれは、必要なすべてのファイルを中にもつディレクトリ(\GBDK-2.0)を作成する。 GBDKは、ドライブのうちの1つのルートにインストールする必要がある(例えばC:¥GBDK-2.0)。
C:> cd \GBDK-2.0\lib C:> make C:> cd \GBDK-2.0\examples C:> make
コンパイラは、ANSI/ISO Cのためのフリーなターゲット非依存コンパイラである lcc に基づく。GBDKは、GameBoyカスタムZ80のためのコードを生成するlccのためのコードジェネレータを含んでいる。 完全な説明については、lcc配布に含まれる マニュアルページ を読む。
コンパイラは、基本的な型のための次のサイズを定義する:
型 | サイズ | 最小値 | 最大値 |
char | 1 バイト | -128 | 127 |
unsigned char | 1 バイト | 0 | 255 |
int | 1 バイト | -128 | 127 |
unsigned int | 1 バイト | 0 | 255 |
long | 2 バイト | -32768 | 32767 |
unsigned long | 2 バイト | 0 | 65535 |
ポインタ | 2 バイト | 0 | 65535 |
CPUが8ビットのプロセッサであるので、int値で動作することはlong値で動作するより非常に効率的である。 しかし、オーバフローに注意する必要がある。
GameBoyイメージを生成する時、リンカは、lib/gb.libテキストファイル中にリストされたオブジェクトファイルの各々の中で不確定シンボルを捜す。 これらのオブジェクトファイルのうちの1つがシンボルを含んでいる場合、メインプログラムとリンクされる。 これらのオブジェクトファイルのどれもシンボルを含んでいない場合、リンカはエラーを生成する。 したがって、明示的に標準ライブラリとメインプログラムをリンクする必要はない。
ここに、lcc使用法のいくつかの例がある:
lcc -o image.gb source.c
lcc -o image.gb source.s
lcc -c -o object.o source.c
lcc -c -o object.o source.s
lcc -o image.gb object1.o object2.o
lcc -o image.gb source.s source.c object.o
次のフラグは、オプションをプリプロセッサ、コンパイラ、アセンブラ、リンカへ渡すことができる:
-Wp -Wf -Wa -Wl
これらのフラグの典型的な使用法は、リストとマップファイルを生成するためである:
リストとマップファイルを常に生成することは一般によい習慣である。
アセンブラは ASxxxxクロスアセンブラ に基づく。
命令のいくつかは無く、いくつかは追加されているが、GameBoyプロセッサはZ80に非常に似ている。 さらに、裏レジスタ(BC', DE', HL', AF')とインデックスレジスタ(IX, IY)は無く、したがってDDとFDのオペコードテーブルはない。 最後に、I/Oポートは無くなっており、したがってすべてのIN/OUTオペコードはない。 変更された命令の説明のために、 GameBoy FAQ を読む。
GB特有のオペコードのいくつかの名前を変更した:
LD (HLI),A -> LD (HL+),A LD (HLD),A -> LD (HL-),A LD A,(HLI) -> LD A,(HL+) LD A,(HLD) -> LD A,(HL-) ADD SP,offset -> LDA SP,offset(SP) LDHL SP,offset -> LDA HL,offset(SP)
LDAオペコードは68x00アセンブラのような「ロードアドレス」を意味する。 両方が直交なので(異なる2つのレジスタで同じことを行う)、これらの命令をこのように名づけた。
アセンブラは次のフラグを受け付ける:
ASxxxx Assembler V01.75 (GameBoy Z80-like CPU) Usage: [-dqxgalopsf] outfile file1 [file2 file3 ...] d decimal listing q octal listing x hex listing (default) g undefined symbols made global a all user symbols made global l create list output outfile[LST] o create object output outfile[o] s create symbol output outfile[SYM] p disable listing pagination f flag relocatable references by ` in listing file ff flag relocatable references by mode in listing file
完全な説明については、docディレクトリ中のasmlnk.docファイルまたは このhtmlドキュメント を読む。
リンカは ASxxxxクロスアセンブラ に基づく。 それはGameBoyイメージの生成をサポートするために特有の拡張をされた。
リンカは次のフラグを受け付ける:
ASxxxx Linker V01.75 Startup: -- [Commands] Non-interactive command line input -c Command line input -f file[LNK] File input -p Prompt and echo of file[LNK] to stdout (default) -n No echo of file[LNK] to stdout Usage: [-Options] outfile file [file ...] Librarys: -k Library path specification, one per -k -l Library file specification, one per -l Relocation: -b area base address = expression -g global symbol = expression -yo Number of rom banks (default: 2) -ya Number of ram banks (default: 0) -yt MBC type (default: no MBC) -yn Name of program (default: name of output file) Map format: -m Map output generated as file[MAP] -x Hexidecimal (default) -d Decimal -q Octal Output: -i Intel Hex as file[IHX] -s Motorola S19 as file[S19] -z Gameboy image as file[GB] List: -u Update listing file(s) with link data as file(s)[.RST] End: -e or null line terminates input
完全な説明については、docディレクトリ中のasmlnk.docファイルまたは このhtmlドキュメント を読む。
いくつかのインクルードファイルは、GBDKの一部である。 そのいくつかは有用なマクロを単に定義し(関連するコードはない)、一方他のものは個別のオブジェクトモジュールで実装された関数を定義する。 ライブラリは、最終のイメージファイルのサイズを縮小するために、いくつかの小さなオブジェクトファイルに分割される(必須のモジュールだけが、メインプログラムとリンクされる)。 インクルードファイルとライブラリは次のグループに分けられる:
crt0.oオブジェクトモジュールは、GameBoy初期化ルーチン、Cサポートと他の本質的なものと共に、基礎的なCランタイムライブラリを含んでいる。 このライブラリは必須で、すべてのプログラムと自動的にリンクされる。
gb.hインクルードファイルは、GameBoyに関連する基礎的なマクロと関数を定義する。 さらに、それは、GameBoyハードウェアレジスタを定義するhardware.hファイルをインクルードしている。
ctype.h、stdarg.h、stdlib.h、string.hとtypes.hインクルードファイルは、標準Cライブラリのいくつかの関数を定義する。
基礎的なコンソールI/Oはconsole.hとstdio.hインクルードファイルに定義された一組の関数によって提供される。 コンソールI/OがGameBoyのほとんどのタイルとスプライトを使用しているため、グラフィックスプログラムと容易に混合できないことに注意する。
スクリーン上に点とイメージを描画するためのシンプルなグラフィック関数は、drawing.hインクルードファイルで定義される。 グラフィックライブラリがGameBoyのほとんどのタイルとスプライトを使用することに注意する。
GBDKはCとアセンブラの両方にいくつかの例プログラムを含んでいる。 それらはexamplesディレクトリとそのサブディレクトリに位置する。 それらは対応するディレクトリでmakeをタイプすることにより構築できる。
space.s例はスプライト、ウィンドウ、背景、固定小数点表現の値、その他の使用を実証するアセンブラプログラムである。 次のキーが使用されている:
Arrow keys : Change the speed (and direction) of the sprite Arrow keys + A : Change the speed (and direction) of the window Arrow keys + B : Change the speed (and direction) of the background START : Open/close the door SELECT : Basic fading effect
galaxy.c例はspace.sアセンブラプログラムのC変換である。
sound.c例は、GameBoyのサウンドジェネレータの実験を意図する(実際のGameBoy上で使用すること)。 GameBoyの4つの異なるサウンドモードを利用できる。 さらに、それは、Cの中のビットフィールドの使用を実証する(それは迅速なハックであるので、コードにあまり期待しない)。 次のキーが使用されている:
UP/DOWN : Move the cursor RIGHT/LEFT : Increment/decrement the value RIGHT/LEFT+A : Increment/decrement the value by 10 RIGHT/LEFT+B : Set the value to maximum/minimum START : Play the current mode's sound (or all modes if in control screen) START+A : Play a little music with the current mode's sound SELECT : Change the sound mode (1, 2, 3, 4 and control) SELECT+A : Dump the sound registers to the screen
rpn.c例は基礎的なRPN電卓である。 12 134*のような式を入力すると、1789+になる。
gb-dtmf/gb-dtmf.cプログラムは、 大橋修 によって書かれており、Dual Tone Multi-Frequency(DTMF)ジェネレータである。
banks.c例は、複数バンクプログラムの方法を例証する。
ram_fn.c例は、RAMやHIRAMに関数をコピーする方法と、Cからそれらを呼ぶ方法を例証する。
irq.c例は、IRQハンドラを取り付ける方法を例証する。
comm.c例は、コミュニケーションルーチンを使用する方法を例証する。
Deep Scan(dscan/dscan.c)は大橋修の友人によって書かれたゲームである。 目的は、ボートから潜水艦を破壊し、潜水艦の出す弾を避けることである。 ゲームは自明に違いない。 次のキーが使用されている:
RIGHT/LEFT : Move your boat A/B : Send a bomb from one side of your boat START : Start game or pause game When game is paused: SELECT : Invert A and B buttons RIGHT/LEFT : Change speed UP/DOWN : Change level
foo(i + 2 - 1);lccは単に1を加える代わりに、2を加えて1を引く。 この状況では、定数式を括弧でくくるべきである:
foo(i + (2 - 1));
typedef int BYTE; typedef unsigned int UBYTE; typedef long WORD; typedef unsigned long UWORD;この方法で、変数のサイズを常に知る。
UBYTE i, j = 0; i = j+0x80;コンパイラは、0x80が符号つきの値であると思う。また、それが最大の符号つき8ビット値(0x79)より大きいので、コンパイラはlong定数としてそれを扱う。 Cの仕様に従って、jはlongに拡張され、long加算(それは8ビットのプロセッサで高価である)を使用して0x80を加算し、8ビットの値に切り詰められ、iに割り当てられる。 この例が書かれるべきである:
UBYTE i, j = 0; i = j+0x80U;
int i1; /* OK : will be located in RAM */ char *s1; /* OK : will be located in RAM */ int i2 = 0; /* Error : will be located in ROM */ char *s2 = "Hi"; /* Error : will be located in ROM */ void main() { ... }
for(i = 0; i < 10; i++)は、次ほど効率的でない。
for(i = 0; i != 10; i++)
Cとアセンブラを混合するために、1つの言語につき1つのファイルを使用し(アセンブラをCコードにを埋め込むことができない)、両方のファイルをともにリンクする必要がある。 ここに、知っておくべきものがある:
ここに、Cとアセンブラを混合する方法の例がある:
main() { int i; int add(int, int); i = add(1, 3); }
.globl _add _add: ; int add(int a, int b) ; There is no register to save: ; BC is not used ; DE is the return register ; HL needs never to be saved LDA HL,2(SP) LD E,(HL) ; Get a in DE INC HL LD D,(HL) INC HL LD A,(HL) ; Get b in HL INC HL LD H,(HL) LD L,A ADD HL,DE ; Add DE to HL LD D,H LD E,L ; There is no register to restore RET ; Return result in DE
GBDKはMBC1とMBC2のデータコントローラのために複数バンクイメージ(複数ROMとRAMバンクの両方で)を生成できる。 複数RAMバンクはMBC 1だけでサポートされる。
複数ROMバンクでは、アドレス0x0000から0x3FFFは固定ROMバンクのために予約され、一方アドレス04000から0x7FFは切り替えできる、つまり、任意のバンクのために使用できる。 切り替え可能なROMバンクは_CODE_1,_CODE_2,...と呼ばれ、 固定ROMバンクは_CODEと呼ばれる(_CODE_0がないことに注目する)。 ROMバンクの最大の数は32である。
アドレス0xC000から0xDFFFは、常に内部RAMのために予約される。 アドレス0xA000から0xBFFFは、(切り替え可能)外部RAMのために予約される。 外部RAMバンクは_BSS_0, _BSS_1, _BSS_2...と呼ばれ、 内部RAMは_BSSと呼ばれる。 外部RAMバンクの最大の数は4である。
RAMバンクの割り当て方法を決定する時、ローカル変数が常にスタックに割り付けられ、初期化されたグローバル変数がROMに位置するることを思い出す。 初期化されていないグローバル変数か静的変数だけがRAMへ割り付けられる。
複数バンクイメージを生成するために、次のようにする必要がある:
256Kbit = 32KByte = 2 banks 512Kbit = 64KByte = 4 banks 1Mbit = 128KByte = 8 banks 2Mbit = 256KByte = 16 banks 4Mbit = 512KByte = 32 banks標準でサポートされたRAMサイズは:
None 64kBit = 8kB = 1 bank 256kBit = 32kB = 4 banks標準でサポートされたカートリッジタイプは:
0 : ROM ONLY 1 : ROM+MBC1 2 : ROM+MBC1+RAM 3 : ROM+MBC1+RAM+BATTERY 5 : ROM+MBC2 6 : ROM+MBC2+BATTERY
バンク切り替えはプログラムにおいて自動的ではない。 switch_rom_bank()とswitch_ram_bank()関数を明示的に呼ばなければならない。 完全な例に関しては、banks.cを参照する。
(memcpy()とhiramcpy()関数を使用して)RAMとHIRAMに関数をコピーし、Cからそれらを実行することができる。 コンパイラは、start_Xとend_X(Xは関数名である)という名前の各関数の開始と終了の2つのシンボルを自動的に生成する。 これは、関数をRAMにコピーする時に関数の長さを計算することができる。 RAMやHIRAMに関数をコピーするための十分な空きスペースがあることを確認する。
RAM、HIRAMまたはROMに位置した関数を呼ぶために、基本的に2つの方法がある:
第2のアプローチはわずかにより効率的である。 両方のアプローチはram_fn.c例において例証される。
GameBoyハードウェアは5つのタイプの割り込みを生成できる:
VBL : V-blank LCD : LCDC status TIM : Timer overflow SIO : Serial I/O transfer end JOY : Transition from high to low of joypad
これらの割り込みのどれでも独自のIRQハンドラ(Cまたはアセンブラの)を取り付けることができる:
完全な例に関しては、irq.cを参照する。
main()関数が呼ばれる前でちょうど割り込みが有効になる前に実行されるルーチンを取り付けることができる。 例えば、割り込みフラグを変更し、main()が実行される前にVBL IRQが扱われるのを避けるために初期化ルーチンを使用できる。 初期化ルーチンを取り付けるために、次のようにする必要がある:
-WlgXXX=YYYフラグ(XXXはデータ名、YYYは新しいアドレス)を使用して、リンク時にある重要なデータのアドレスを変更することができる。 変更できるアドレスは:
.STACK : Initial stack address .OAM : Location of sprite ram (requires 0xA0 bytes) .init : Initialization routine .irq_VBL : VBL IRQ routine .irq_LCD : LCD IRQ routine .irq_TIM : TIM IRQ routine .irq_SIO : SIO IRQ routine .irq_JOY : JOY IRQ routine
メッセージのタイプ:
u 0226 a 0329 u 0333
は、アセンブラのエラーメッセージである。 これらのエラーがどこで起きたか確かめるために、lccの-Wa-lフラグを使用してアセンブリリストを生成し、このファイルを見るべきである。 そのようなエラーがコンパイラの生成したファイルで起きる場合、リストと一緒に私にCソースを送る。
メッセージのタイプ:
?ASlink-W-Undefined Global .count referenced by module Demo
は、リンカのエラーメッセージである。 イメージファイルは生成されるが、悪くなっているはずである。 エラーに関する詳細な情報はマップファイル(lccの-Wlmフラグを使用して生成される)で見つけることができる。
DOSシェルは、128文字へコマンドラインを切り詰める。 コンパイラへ渡すフラグの数により、より長いコマンドラインを使用する必要があれば、bash(Unix Bourne-Again SHellのDOS移植)を入手するように強く勧める。 それはDOSシェルより非常に強力であり、コマンドラインを切り詰めない。 よりよい代案は、make(Unix makeユーティリィティのDOS移植)を入手し、DOSバッチファイルの代わりにmakefileを使用することである。 もちろん、両方を入手することができる。 それらは DJGPP (GNU CのDOS移植)の一部として利用できる。
UBYTE tiles[] = { 0x00, 0x01, ..., 0xFF };しかし、次の配列宣言はコンパイラエラーを起こす:
UBYTE tiles[128];ここに、lccの著者の一人であるDave Hansonが私のもとへ送った答えがある:
これは実際に最も重大な問題である。 ANSI Cは、任意のオブジェクトの最大のサイズとして32kb要求する。 lccは、サイズを保持するためにintを使用するので、intが16ビットの値を保持できると確かに仮定する(例えば型構造で)。 longで配列サイズを受理すれば問題はなく、lccが動作するマシンが16ビットのintを持っている限り、それは問題なく作動する。 しかし、サイズを保持するすべての変数/フィールドのために「int」を「long」に変更することは大変で、すべてのモジュールに触れることを必要とする。
この問題のための代替手段は、多次元配列を宣言し、一次元配列として使用することである:UBYTE tiles[64][8]; /* This declares an array of 64*8 = 512 unsigned bytes */ void foo() { UWORD l; ((UBYTE *)tiles)[500] = 0; l = 500; ((UBYTE *)tiles)[l] = 0; }