投稿日 : 2017/01/30 3:56:40
SHARP製ポケコン「PC-G850」のゲームをWindows上で開発
ポケコンとはポケットコンピュータの略で、つまりは小さいパソコンのことです。

そしてPC-G850とは主に工業高校に入ると必ず買わされる学校教育用のポケコンで、実は1980年代に一世を風靡した低価格の家庭用PC(当時はマイコンと呼んだ)として売り出された、MSXと同等のスペックを持ったれっきとしたパソコンです。

発売当初のPC-G800シリーズはZ80の3.58MHz、現在のPC-G850は8MHzにクロックアップ済み、またメモリ(RAM)は32KBと当時のスペックそのままの性能ですが、MSXのようにカセットを入れ替えることでゲームを変えるといったことは出来ません。
※実際には拡張バス側に独自のROMをセットしてバンクを切り替えるようなことを行えば出来なくは無い

また、このポケコンにはIOCSというファームウェアが実装されており、電源を投入した直後からそのコードが実行されますが、この上でプログラムを作ったり実行したり、またシリアル通信でPCとデータをやり取りしたりなどの機能があらかじめ備わっており、これが実質このポケコンのOSとして動作しています。

さらにこのファームウェアにはBASICの実行環境やC言語(風)のコンパイラ、またZ80のアセンブラが内蔵されており、MSXのようにフロッピーなどで別途ソフトウェアを用意する必要はありません。

これ1台で実質MSX以上の性能を持っているわけですが、一番のネックとなるのがやはり画面で、このポケコンに内蔵の液晶は白黒で144x48ドットの解像度しかありません。

MSXでは文字色として原色の8色(赤緑青とそれらの組み合わせ)が使えたり、画面モードを切り替えると画像のスプライト表示なども可能ですが、ポケコンにはこれらのグラフィックチップは積んでいないため、単純に液晶に対してピクセル値を出力することぐらいしか出来ません。また、この液晶の仕様では常に縦8ピクセルを1回の出力で書き換えることになっているため、普通は文字列のように縦8ピクセル単位で表示出来るものでゲームを作るしかないという制約があります。
※縦8ピクセルの6行分で縦48ドットとなる
※PC-G801などグラフィック表示に対応していない液晶も存在します

なお、現在はポケコンの生産が終了してしまったため、代わりに関数電卓を使うようになっているようで、昔は授業中にゲームを作ったりなどしてプログラムの腕を磨いていましたが、それがもう出来なくなってしまったのがとても残念でなりません。

BASICとアセンブラとC言語

ポケコンで使える言語として標準でBASICが使えます。
またPC-G803以降(?)からZ80アセンブラが、PC-G813からC言語が使用可能です。

言語的に、BASICはとても簡単ですが全ての動作が重いという問題があり、逆にアセンブラは超高速ですがプログラミングがとても難しいという問題があります。
そしてこれらのいいとこ取りをしたのがC言語ですが、実はC言語とはCのプログラムからアセンブラコードを作るためのコンバーターであり、最終的にはこのアセンブラコードをマシン語にアセンブルすることでCPUのネイティブコードを生成します。

なお、C言語はアセンブラコードをある決まった規則で生成するという仕様であるため、直接アセンブラを書くのと比べるとそのオーバーヘッドのせいで若干遅くなってしまったり、またコードがその分増えてしまうためメモリを余分に喰ってしまいます。しかしC言語はBASICのように変数や関数など人間が分かりやすい言語で書けるのと、アセンブラではとてつもなく労力がかかるような複雑な計算式もC言語なら普通に数式で書くことが出来るため、圧倒的に開発効率がよいという利点があります。

さらにもう1つ重要な利点を挙げると、C言語は言語として定義されているため、コンパイラさえあれば同じプログラムを他のCPUでも使えるコードを生成することが出来ます。つまりC言語とは、対象のCPUのアセンブラコードを一切知らずとも、そのCPUに対応したネイティブなマシン語コードを、自動的に生成してくれるという画期的な言語なわけです。

と、ここまでC言語の利点を説明しましたが実はここからが問題で、ポケコンに搭載されているC言語は実はなんちゃってC言語であり、上記のようにプログラムをコンパイルしてアセンブラコードを生成するのではなく、BASICと同じくインタプリタ型として1行ずつ解析して実行していくものです。

簡単な処理はアセンブラ、複雑な計算などはC言語を使ってアセンブラコードを生成し、最終的に全てマシン語コードでゲームを高速化と思っていましたが、そういったことはポケコンでは出来ないということが分かったため、この時点でポケコンのC言語を使うという手段は無くなりました。

Window上での開発環境について

以下の内容は古いため、こちらの最新情報を確認してください。

ここではWindows上でC言語を使ってゲームを開発することを目的としていますが、そのために開発に必要な各種ソフトや開発用のワークフォルダについて説明しています。

以下は開発に必要なソフトの一覧です。
ソフト名 内容
SDCC
(Small Device C Compiler)
C言語コンパイラ & Z80アセンブラ。
g800 ポケコン用エミュレータ。
ここではWindows用を使用。
ポケコンビルドツール v1.00 開発に必要なソフト(ポケコン用ライブラリ、ビルド用バッチなど)と、
プロジェクトに必要なワークデータが入っています。
※ダウンロードなどの詳しい説明は後述

ビルドツールには以下のソフトが同梱されています。
ソフト名 内容
PCGTool ポケコン転送ツール。
転送の前に必ず一度起動し、転送用の設定(COMポートやボーレート)などを設定する必要があります。
※最新版は本家サイトのSOFTWARE→ポケコンにありますが、一応このビルドツールにも入れています 
AsciiConv 自作の文字表示ライブラリが使用するASCII画像「ascii.bmp」からCのヘッダファイルを生成します。
※現状は幅5px専用
IHXConv IHXファイルとバイナリファイルを相互に変換します。
ImgConv BMPやTGAを変換して自作の描画ライブラリで使用可能な形式のCのヘッダファイルを生成します。
※オプションでマスクの有無が設定可能
 マスクとは描画した画像に白い部分がある場合に先にマスク画像でANDし、
 そのあとで本来の画像をORすることで白抜き画像を表示する方法です
背景 画像 合成後
MargeRam ポケコンから吸い出したRAM領域(0000h-7FFFh)のバイナリイメージに、
SDCCから出力されたコードを結合して最終的なRAMイメージを生成します。

SDCC(Small Device C Compiler)

ポケコンのC言語とは、あくまでも教育用として基本的な構文の練習には良いのかもしれませんが、これを使って本格的なゲームを作ることはまず不可能です。
そこでここではポケコンのC言語を使うのではなく、Z80用のマシン語を書き出すことが出来る、本物のC言語コンパイラを使うことにします。

このC言語コンパイラはSDCCという名前のコンパイラで、これは誰でも無料で使うことが出来ます。
ただし一部のライブラリはライセンスの問題があるようで、そのライブラリをリンクしたプログラムの配布には注意が必要なようです。

ちなみに、SDCCには各種CPUに対応したコンパイラとアセンブラが入っているため、特にZ80専用というわけではありません。
またSDCCという名前から、基本的には組み込みCPU向けのコンパイラであり、組み込み向けCPUはたいてい8bitのため、int型などは2つのレジスタを使って16bitとして定義されたりします。このため、プログラミング時には型の範囲に気をつける必要があります。

SDCCにはWindows版の他にLinux版もありますが、ここではWindows用の開発環境を作ることを目的としています。
注意点としてはSDCCはバージョンが上がると言語自体やビルドオプションが変更されることがあるため、場合によっては読み替える必要があることを覚えておいてください。
※SDCCは特に古いバージョンでも問題無く使用出来るので、ここで説明しているバージョンと同じものを使った方が間違い無いと思います

SDCCのインストール

まずは以下からSDCCをダウンロードして下さい。
 https://sourceforge.net/projects/sdcc/files/
 → sdcc-win32 → 3.6.0 → sdcc-3.6.0-setup.exe
 ※2017/01/30時点で最新のリリース版は3.6.0ですが、開発中のベータ版として3.6.1も存在します

ダウンロードしたインストーラーを実行するとSDCCのインストール画面が表示されます。
ここでは特に何も考えず「Next」を押してデフォルトの設定でインストールしましょう。


途中、以下のようにPATHを追加するかどうか聞かれますが、これもチェックを入れたまま「Next」を押してください。

※これによりコマンドプロンプト上でSDCCの各コマンドが実行出来るようになります

インストールが完了したら試しにコマンドプロンプト(Win7の場合はスタート→アクセサリ→コマンドプロンプト、Win10の場合はスタートを右クリック→コマンドプロンプト)を開き、コマンドプロンプト上で「sdcc」と入力してENTERを押してみてください。


コマンドが正しく実行出来た場合は以下のようにsdcc.exeの実行結果(ヘルプ)が表示されます。


正しく実行出来たらこのウィンドウは閉じてしまってかまいません。
これでSDCCのインストールは終わりです。

めちゃめちゃ簡単ですね。

ビルドツールの準備

上記でダウンロードしたビルドツールには、開発に必要なツールやサンプルコードがまとまっており、まずはこれを任意の場所に解凍してください。
解凍すると「開発用テンプレート」というフォルダがあり、その中に必要な全てのツールなどがまとまっています。

ちなみに、この「開発用テンプレート」フォルダが実質1アプリ分のプロジェクトフォルダということになるので、あとあと作成するゲームの名前などに自由に変更してください。
※ここではテンプレートの中に各ツールが入っているため、プロジェクトが増えると同じツールが複数作成されることになりますが、ツール自体の容量はさほど大きくは無いので問題無いと思います

以下は「開発用テンプレート」内に入っているファイルの詳細です。
g800win32/ g800エミュレータを入れるフォルダです。
作成したゲームを即座に直接エミュレータから実行することが出来ます。
※詳細は後述
picture/ サンプルプログラム用の画像と、この画像をImgConvツールで変換したヘッダファイルが入っています。
特にこのフォルダ名である必要はありませんが、画像類はここにまとめることで管理しやすくなると思います。
tools/ PCGToolやAsciiConvなどの各種ツールが入っています。
※解凍後に一度PCGToolを直接起動して通信設定を行ってください
work/ ビルド時にこの中にワーク用のファイルが生成されます。
この中に出来るファイルは必要なければ削除しても問題ありません。
※ビルド時に生成される「_app.ihx」がゲーム本体となるため、これを公開することで他の人にも遊んでもらうことが出来ます
!!!build.bat 作成したプログラムのビルド(コンパイル&リンク)を行います。
エラーがあるとプロンプト上に内容が表示されるので、これを確認してコードの修正を行います。
ビルド後に何かキーを押すことでポケコンエミュレータが起動するので、直接作成したプログラムを実行することが出来ます。
!!!send.bat 作成したゲームを実機(ポケコン本体)に転送します。
単にビルド後に生成された「_app.ihx」を転送するだけのバッチなので、必ずビルドが成功したあとに実行してください。
※一度PCGToolを起動して通信設定を行っておく必要があります
ascii.h 自作のg800ライブラリの文字表示関数が使用している文字フォントバイナリヘッダです。
自分でフォントを作成・更新したい場合は、「tools/ascii.bmp」を編集してから「tools/AsciiConv.exe」を実行することで、
toolsフォルダに新しいascii.hが生成されるので、これを既存のものと置き換えてください
crt0.s ゲームを実行するための基本処理が書かれたアセンブラファイルです。
グローバル変数の初期化やCでは実行が遅くなってしまう処理などがライブラリ関数として定義されています。
ちなみにこのアセンブラのコードから最終的にCのmain()関数を呼び出しています。
※このコードはアセンブルするとアドレス0100hに配置されるため、ゲームの起動は必ずこのアドレスをコールする形となります
g800.c 文字列や画像表示を行うための自作ライブラリのCのソースコードです。
一部の関数はさらに内部でcrt0.sの関数を呼び出すことで、Cだと処理がかかる部分をラッパーしています。
g800.h 文字や画像表示を行う関数のプロトタイプなどが宣言されています。
このヘッダをインクルードしたCファイル中からこれらの関数を使うことが出来ます。
game.c ゲームのメイン処理を記述するためのソースコードファイルです。
基本的には全てのゲーム処理はこのファイルの中で定義してください。
game.h ゲーム関数のプロトタイプです。
このヘッダファイルは基本的にmain.cがインクルードするために用意されたもので、
game.cのみが使う関数は通常はgame.cの中でプロトタイプ宣言をしますが、
プロトタイプ宣言は必ずヘッダファイルに入れるというようなポリシーがあるなら、
game.hに定義してもかまいません。
main.c crt0.sから最初に呼び出されるmain()関数が実装されています。
本来は自分でmain()関数から書くのが基本ですが、ここではテンプレートということで
既にgame.hの関数を呼び出すコードが記述されているため、基本的にこのファイルを変更する必要はありません。
ゲームの作成は基本的に「game.c」のみを修正する形となるので、基本的にそれ以外のファイルはあまり気にする必要はありません。
なお、g800.h(.c)やgame.h(.c)を使用せず自分でmain()から実装することも出来ますが、この場合はこれらを削除したりビルドバッチから外すなどが必要です。

ポケコンエミュレータ「g800」の準備

作成したプログラムを直接エミュレータで起動させるには、上記のビルドツール内の「g800win32」フォルダにエミュレータ本体をコピーしておく必要があります。

まずはエミュレータを以下のサイトからダウンロードして下さい。
- Version 0 -
 SHARP PC-G850/G815/E200エミュレータ g800
 http://ver0.sakura.ne.jp/pc/index.html#g800
 → g800 win32バイナリ[g800win32.0.10.14.zip] ※2017/01/30現在

次にダウンロードしたエミュレータ本体や同梱のDLLなどを「g800win32」内に全てコピーしてください。
※g800win32フォルダの直下に「g800.exe」などが入る感じです

最後にエミュレータのデフォルト設定がLinux用になっている(?)ようなので、これを修正する必要があります。
エミュレータの直下にある「g800config」ファイルをメモ帳で開き、「ram_path」を「~¥g800user.txt」から「g800user.txt」に変更(「~¥」を消す)してください。

ちなみに「g800user.txt」とは単なるRAM領域(0040h~7FFFh)のIHXファイルです。
また、これに含まれない0000h~003Fhは割り込み用で、実機の場合は電源を入れなおすと毎回初期値に戻されます。

実機のROMを使うには
このページで説明している開発環境では特に実機のROMイメージ(IOCS)は必要ありませんが、もしエミュレートに対応していないアドレスをコールしたい場合は実機のROMが必要となります。
その場合は自分でイメージを吸い出し、エミュレータ内にそのイメージを配置してから「g800config」を開いてROMの位置(rom_dir)を指定しなければなりません。

さらにこの場合、エミュレータが参照している各種変数などのワークエリアの情報が必要となるため、一緒にRAM領域の吸出しも必要となります。
ただし、吸い出す前にアセンブラが利用するエリアを正しく設定しておかないと、アセンブラコードがBASICなどで上書きされてしまい、エミュレータが暴走する可能性があるため、吸い出す前にマシン語モニタ上で「USER6000」と、あらかじめマシン語エリアを最大限確保しておく必要があります。

ちなみにマシン語モニタ上からRAM領域を吸い出すには、PC側の通信ソフトを受信状態にしてから「W0,7FFF」と入力してください。
※ここでは開始アドレスは40hでは無く0hからとし、割り込みエリアも吸い出します

なお、ここでは吸い出したRAM領域を直接「g800user.txt」としてエミュレータに入れることはしません。

ビルドツールはこのRAM領域とビルドしたゲームコードを自動的に合成し、最終的に「g800user.txt」を作成するようにシーケンスされています。
そのためにはまずは、吸い出したIHXファイルを「tools/IHXConv.exe」にドラッグしてbinファイルを作成し、これを「tools/templete.bin」として上書きする必要があります。

これでエミュレータ上ではBASICもマシン語コードも正しく認識出来るため、IOCSが誤作動を起こす心配もありません。

ビルド方法

ビルドツールにはサンプルプログラムが入っているので、上記の環境が正しく構築出来ていればすぐに試すことが出来ます。

※これはフルスクリーンサイズの画像を表示するだけのシンプルなプログラムで、BREAK(ON)キーを押すと終了します

ビルド方法は「!!!build.bat」を実行するだけです。

実行するとコマンドプロンプトが起動し、ビルド中のメッセージが表示されます。

※初回起動時に削除に失敗と表示されることがありますが、これは以前のワークファイルを削除する際に
 そのワークファイルが存在しないため表示されているだけです

最後に「Start emulator ?」と表示されたらエンターキーなどを押すとエミュレータが起動します。
ビルド後すぐに実機に転送したい場合など、エミュレータを起動したくない場合はここで「×」を押して閉じてしまってかまいません。

エミュレータが起動したら「G100」と入力してエンターを押すことでゲームが起動します。


ちなみに実機のROMを使用している場合は実機と同じ画面で起動するので、
実機と同じくRUNモードから「CALL256」か「CA.256」と入力して起動してください。

実機で実行

エミュレータで動作が確認出来たなら実機に転送して実際に実行させてみましょう。

なお転送を行うにはポケコン側と通信ソフト側の通信設定を合わせておく必要があります。

ポケコン側の設定方法は、まず「TEXT」を押してテキストエディタに入り「S」を押してSioのメニューを表示させます。
次にSioのメニューから「F」を押して通信設定画面を表示させます。
ここでは主にボーレート(baud rate)とフロー制御(flow)の設定を確認します。
※通常はボーレート9600bps、フローをRS/CSにします

PC側の設定ですがこれは自作のPCGToolを利用するため、まずは「tools/PCGTool.exe」を単品で起動します。
 
この中で「設定」内のCOMポートを通信ケーブルが使用しているポートに、またボーレートとフロー制御はポケコンと同じ設定にしてください。
設定が終わったら「×」ボタンを押して閉じてください。
※この設定は1回だけ行えば保存されるので、転送を行う前に必ず1回だけ行っておいてください

次に実機の方でマシン語エリアが正しく確保されているか確認します。
RUN MODEで「MON」と入力してマシン語モニタに入り、マシン語エリアとして使用する領域を確保するために「USER6000」と入力します。
※ちなみに「USER」とだけ入力すると現在確保されているエリアの範囲が表示されます


上記の設定が全て終わっているなら、あとはポケコンを受信状態にし転送用のバッチを実行するだけです。
まずポケコンをマシン語モニタ画面にし「R」と入力して受信待機状態とします。

次に「!!!send.bat」を実行します。

通信が完了すればポケコンにデータが転送されたので、あとはポケコン上で実行するだけです。
マシン語モニタ上であれば「G100」、RUN MODEであれば「CALL256」または「CA.256」と入力すれば実行出来ます。

なお、通信で送信されるプログラム本体は「work/_app.ihx」となるので、ファイル名を変えて公開することで他の人にもプレイしてもらうことが出来ます。
※_app.ihxはRAMのイメージではなくコードのみが含まれたファイルなので、全RAM容量分のサイズではなくコード分だけのサイズとなります

これで開発から実機での検証、そしてリリースまでの流れの説明は終わりです。

g800ライブラリ

g800.hとg800.cにはゲームで利用可能な文字や画像表示などの関数が定義されています。
これを使うことでBASICと比べて高速に動作するゲームを簡単に作ることが出来ます。

注意点としてこのゲームライブラリは仮想VRAMという手法を行っており、これは画面に表示されるドットを一度メモリ上で作ってからこれを一気に画面に転送する方式です。
この方式はドット単位で画像を高速に描画出来るのが利点ですが、メモリ上に画面のイメージを生成する関係上その分のプログラムメモリを最初から消費してしまいます。
そのためゲームによってはメモリ不足(ユーザーエリアの6000hを超えてしまう)に陥る可能性があるため、そのあたりに気をつけて開発する必要があります。

また書式付き文字列出力関数を使うことで文字列内に数値を表示させることが出来ますが、ライブラリの仕様として常にshort型(Signed 16bit)が使われるため、-32768~32767より大きな範囲の値を表示したり、またBASICのような小数値を表示させることは出来ません。
この場合は自分で数値演算ライブラリを作る必要があります。

以下はg800ライブラリの関数の詳細です。
関数名 内容
BYTE __inp( BYTE port ) IOから指定のポートを直接読み取ります。
void __outp( BYTE port,BYTE data ) IOの指定のポートに直接値を出力します。
void __shutdown( void ) ポケコンの電源を切ります。
※まともに動作するかは未検証
BYTE peek( WORD addr ) 指定のアドレスのメモリを直接読み取ります。
※セーブデータのロードなどに利用可能
void poke( WORD addr,BYTE data ) 指定のアドレスに直接値を書き込みます。
※セーブデータのセーブなどに利用可能
void SetHookInterrupt( void *addr ) 割り込みハンドラを設定します。
ハンドラの型は「BOOL Handler( void );」です。
戻り値がTRUEなら元の割り込みハンドラが呼び出されません。
※常に0038hのコードを書き換えます
※割り込みについてよく分からない場合は使用しないでください
void di( void ) 割り込みを不可に設定します。
アセンブラのDIのラッパーです。
void ei( void ) 割り込みを許可に設定します。
アセンブラのEIのラッパーです。
void beep( WORD tone,WORD wait ) ポケコンの11pin I/O端子の3と7pinに繋げたスピーカーからビープ音を出します。
toneの値(音階)はこちらにまとめています。
※waitは音階によって調整が必要です(BASICのBEEP関数の引数値とは異なります)
void Print( CHAR x,CHAR y,const CHAR *str ) 終端がNULLで終わっている文字列をBIOS(IOCS)を使って描画します。
注意点としてx,yは文字単位の座標です。
※仮想VRAM上には書かれず直接画面に反映されるため、
 以下のG系関数と一緒に使うことは出来ません
 (GSwapBuffer後に重ねて表示は可能)
void PrintString( CHAR x, CHAR y,const CHAR *str,WORD len ) 表示したい文字数を指定して文字列をBIOS(IOCS)を使って描画します。
注意点としてx,yは文字単位の座標です。
※仮想VRAM上には書かれず直接画面に反映されるため、
 以下のG系関数と一緒に使うことは出来ません
 (GSwapBuffer後に重ねて表示は可能)
void DrawVram( void *vram ) crt0.sに定義されたVRAMをBIOS(IOCS)を使って実際に画面に転送します。
GSwapBufferが内部で使っているのでプログラム側が呼び出す必要はありません。
void GetKey( LPKEYDATA data ) 用意したKEYDATA構造体の実体を渡すことでこの中に現在のキー情報を返します。
この関数は一度で全てのキーの状態を取得するため、キーの同時判定も可能です。
プログラム中でキーが押されているか判定するにはIsKey()マクロを使用してください。
例)
 KEYDATA kd;
 GetKey( &kd );
 if( IsKey(&kd,VK_A) ) {
   // Aキーが押されている場合の処理
 }
 ※この関数は処理が重いため通常は1フレームに1回呼び出すようにし、
  取得したKEYDATAを使いまわすような作り方にして下さい
 
void GInitialize( void ) グラフィックライブラリの初期化を行います。
通常は最初に1度だけ呼び出すだけです。
void GClear( BOOL black ) 仮想VRAM全体を白または黒で塗りつぶします。
黒にする場合はblackの引数をTRUEにして下さい。
※ビューポートの影響は受けません
void GClearArray( int x,int arr_y,int w,int arr_h ) 仮想VRAMの一部の領域を高速に白で塗りつぶします。
縦の座標と高さを8ドット単位でクリアする必要がある場合は、この関数を使うことで高速にクリア出来ます。
注意点としてxとwはピクセル単位、arr_yとarr_hは行単位となります。
※ビューポートの影響は受けません
void GSetViewport( int x,int y,int w,int h ) 以下の各描画関数を使用した際に実際に表示が行われるビューポート領域を設定します。
画面外を指定した場合は画面内に納まるように補正されます。
void GClearViewport( void ) ビューポートをクリアして全画面を描画の対象とします。
※GSetViewport( 0,0,144,48 )を実行するのと同じです
void GSetPixel( int x,int y ) 仮想VRAM上の指定の座標に1ドットを表示します。
void GClearPixel( int x,int y ) 仮想VRAM上の指定の座標の1ドットを消します。
void GDrawImage( int x,int y,const LPBYTE buf ) 仮想VRAM上の指定の座標にImgConvでコンバートしたバイナリ画像を表示します。
画面外を指定した場合は補正されます。
void GDrawLine( int x1,int y1,int x2,int y2 ) 仮想VRAM上の始点座標から終点座標まで線を引きます。
内部では整数演算による座標計算を行っているため、斜めの線はあまりきれいには表示されません。
void GDrawRect( int x,int y,int w,int h ) 仮想VRAM上の指定した座標とサイズで四角形を描画します。
この関数は周りに線を引くだけで塗りつぶしは行われません。
void GFillRect( int x,int y,int w,int h ) 仮想VRAM上の指定した座標とサイズで四角形で塗りつぶします。
void GDrawAscii( int x,int y,BYTE c ) 仮想VRAM上の指定の座標に文字を表示します。
表示されるフォントはascii.hに定義したフォントです。
※現在は1文字あたり5×6ドットのフォントとなっています(tools/ascii.bmp参照)
void GDrawString( int x,int y,const CHAR *str,... ) 仮想VRAM上の指定の座標に文字列を表示します。
また文字列には一部の書式付フォーマットが使用可能です。
※文字列(%s)、数値(%d)の他、%04dなど桁付きにも対応(詳しくはg800.c参照)
void GSwapBuffer( void ) 現在の仮想VRAMを実際に画面に反映します。
内部ではDrawVramが呼び出されているだけです。

プログラムのヒント

グローバル変数
グローバルに定義した変数に初期値を入れる場合、変数用のメモリと初期値用のメモリが必要となるため実質2倍のメモリを使うことになります。
例えば配列に最初から値を設定したい場合、「int a[4] = { 100,200,300,400 };」といったプログラムを組むと、intはポケコン上では実質short型(2byte)なので「2×4」で8byteのメモリに、初期値(100,200,300,400)用にも8byteのメモリが必要となり、結果的に16byteのメモリを使うことになります。

この場合は初期値を入れず変数のみ定義しておき、初期化関数で値をセットするようにすることでメモリを抑えることが出来るかもしれませんが、この初期化用のコードのサイズが初期値を入れたときより小さくなるかはプログラム次第なので、このあたりを考慮しつつ変数を定義することでなるべくメモリを節約することが出来ます。
※例えばメモリを0で始めたい場合、「int a[100] = { 0 };」とすれば未定義の部分も常に0が入りますが、
 これだと初期値用のメモリが生成されてしまうため、この場合は初期値は入れず初期化関数にて「memset( &a,0x00,sizeof(a) );」と、
 メモリを0クリアする関数を呼び出すようにします
静的配列
上でグローバル変数に値を入れると2倍のメモリを使用すると説明しましたが、例えば値が変化しない配列をグローバル変数として使用したいなら、「static const」を付けるようにします。

一般的にはstatic constは変化の無い変数を定義するのに使いますが、これを付けた時と付けない時の挙動を理解していない人が意外と多いのですが、まず重要な要素として、static constを付けた場合はRAMではなくROMの方にデータ列が生成されます。
もしstatic constを付けなかった場合は、一度そのデータ列をRAM上に生成する必要があるため、CPUがメモリの確保と値のセットを実行する必要がありますが、ROMの場合は最初から値が既に出来上がっているので、CPUで初期化というプロセスは必要ありません。
つまりROMにあるということは、ただそのアドレスにアクセスするだけで値が即座に取れるので、CPUによるオーバーヘッドがまったく無いわけです。

そしてここからが特に重要ですが、よく関数内にグローバル変数と同じように初期値を含めた配列をstatic constを使わないで定義するのを見かけますが、この時の関数内の挙動を詳しく書くと、実はこの配列は上の説明と同じく関数が呼ばれるごとに毎回CPUにより初期化されます。
もしこれがグローバル変数であれば、起動時の1回だけで済むので若干起動が遅くなるだけですが、関数の中にstatic constの無い配列を定義した場合は、その配列の初期化を関数が呼ばれる毎に毎回CPUが行うことになります。
※例えばCRC処理用の関数で「unsigned int table[256] = { 0x00000000,0x04C11DB7,0x09823B6E,… }」のようにstatic constの無いテーブルを作った場合、
 アセンブラ的には「table[0] = 0x00000000; table[1] = 0x04C11DB7; table[1] = 0x09823B6E; …」と256個分の値を代入するコードが生成され、
 関数が呼ばれる毎にこの代入が行われます(VisualC++などでintが32bitの場合)

ちなみにstaticを付けずconstだけ付けた場合について補足すると、constは変化しないという意味ですが、これはあくまでもコンパイル時に値を書き換えていないかを判断するものであり、動作的には実質constを付けないのと同じです。

またstaticだけを付けた場合については、この変数はグローバル変数として定義されたことになり、結果的にはRAM上の値ということなので最初に必ず初期化が行われます。
ただし、関数内でstaticをしたものについては、グローバル変数ではあるがその関数内だけでしか使えないものとしてコンパイラが認識します。

これらのことから基本的に参照のみで使用される変化の無い配列は、必ずstatic constを付けて明示的にROM領域に定義されるように心がけてください。
コメントはまだ登録されていません。
コメントする
名前
コメント
※タグは使用出来ません
この記事に関連するタグ
ポケコン(PC-G850)