UNIX Development Environment for PlayStation.

contents

はじめに

「ネットやろうぜ」において提供されている開発環境は、DOS上の go32 環境で動作する GNU のツール群です。 この環境のツール自体は良くできているのですが、 DOS の制約のために、使い勝手が悪いものとなっています。

これらの開発ツール群はもともと UNIX 上で開発、発展してきたもので、 UNIX 上で動作させると非常に効率の良い開発環境として利用することが できます。

本橋さんが、PlayStation の実行形式のクロスコンパイル環境を 作成するためのパッチを、ソニーが公開したコードを元に作成して くださいました。これを用いることで、UNIX 上でも PlayStation の開発環境が構築できます。 この文書ではその開発環境を利用した実際の開発の手順について解説してあります。

注意事項

ツールの導入

PlayStation の開発においては、PlayStation で動作する実行形式のファイルを 別のマシン上で作成し、その実行ファイルを PlayStation に転送して実行させます。 このようなプログラム作成と実行を行うマシンが違う開発環境のことを クロス環境と言い、そのために利用するコンパイラのことを クロスコンパイラと呼びます。

UNIX 上で PlayStation の実行ファイルを作成するにはそのUNIX で 動作する PlayStation 向けのクロスコンパイラを作成する必要があります。 GNU のコンパイル環境である、binutils と gcc は、はじめからクロスコンパイル 環境を構築するための仕組みを持っており、ターゲットのアーキテクチャの CPU や 実行ファイルの形式などがわかっていればクロスコンパイラとして 動作させることができます。

PlayStatin 向けのクロスコンパイラを構築するためのパッチを 本橋さんが作成してくださっています。これらのパッチををあてることで、 PlayStation の実行ファイルを生成する gcc を作成できます。

このページにパッチと各ツールのインストール方法が紹介されています。 また、実行ファイルを PlayStation に転送して実行するためのプログラム も必要ですが、これも本橋さんが hsssutils として作成して同じページに おいてあります。ここを参考にして、ツール群をインストールして下さい。

以下のようなコマンドが作成されます。 通常 /usr/local/bin にインストールされるはずです。

psx-ar		アーカイバ
psx-as		アセンブラ
psx-ld		リンクエディタ
psx-nm		シンボル名表示
psx-ranlib	ライブラリの参照テーブル作成
psx-strings	ファイル中の文字列をさがす
psx-size	オブジェクトファイルのサイズ表示
psx-strip	実行ファイル中の不要な文字列の削除
psx-gcc		GNU C コンパイラ
psx-gdb		GNU デバッガ

psxfer		PlayStation との通信プログラム

ツールの基本的な使用方法

コンパイルなど

基本的には UNIX の通常の cc や gcc の代わりに psx-gcc をつかうだけです。make などは UNIX 標準のものをそのまま使用します。 Makefile の中に、CC = psx-gcc と記述しておけば良いでしょう。 UNIX のバージョン管理や差分とりなどの豊富なコマンド群はそのまま利用できます。

PlayStation への転送と実行

PlayStation へのデータの転送や、プログラムの転送、実行などは psxfer を用いて行います。

使用方法は次のとおりです。

psxfer -nfile -a80140000 		データの転送 UNIX -> PS
psxfer -nfile -a80140000 -s1000		データの転送 PS -> UNIX
psxfer file				プログラムの転送
psxfer -g file				プログラムの転送/実行
psxfer -g				プログラムの実行

GDB によるリモートデバッギング

GDB について

GDB は GNUのデバッガです。C言語などで書かれたプログラムを ソースレベルでデバッグすることが可能です。 GDB は非常に多機能で、その全ての操作をここで解説するのは 不可能です。ここでは、基本的な操作に限って解説します。

gdb 実行中に help コマンドを入力することで 簡単なヘルプを見ることが可能です。 正式なドキュメントは、配布に含まれている info ファイルです。

PlayStation 実行ファイルのデバッグ

ここでは PlayStation の実行ファイルと、HSSプロトコルによる 通信をサポートした psx-gdb と、HSS プロトコルによる転送プログラム psxfer を用いたデバッグの手順を示します。

ソースレベルでのデバッグを行うためには、コンパイル時に -g オプションを つけとコンパイルして、実行ファイルの中にデバッグ情報を埋め込む 必要があります。コンパイルオプションとして -g をつけると、 最適化が無効になって、変数や行番号などの情報などがオブジェクトに うめこまれます。Makefile などで、CFLAGS に -g を入れておきます。

# CFLAGS = -O		# OPTIMIZE
CFLAGS = -g		# DEBUGGING

また注意点ですが、デバッガがデバッグのための情報をやりとりするために シリアル通信を使用しています。このため、プログラム側で シリアル通信を行うと、誤動作します。 つまり printf などは使用してはいけません。 しかも、たとえプログラムでシリアル線を使用していなくても、 PlayStation のライブラリがかってにメッセージを出力します。 これを防ぐために、プログラムが開始してすぐの時点に、

close(0);
close(1);
をいれるようにして下さい。 コンパイルが完了した状態から解説していきます。

psxfer によるプログラムの転送

まず、psxfer を用いてプログラムを PlayStation に転送します。
% psxfer program
このとき -g オプションをつけると実際に実行することになります。 データなどについてはあらかじめ psxfer -nfile -aaddr で転送しておきます。

これにより、プログラムファイルが PlayStation に転送され、 実行可能な状態に展開されます。実際には、未定義変数領域の確保、 スタック、プログラムカウンタの初期化などが行われます。

続いて gdb を実行します。

% psx-gdb program

これにより、program のなかに埋め込まれた情報を gdb が読みだして、 ソースレベルでのデバッグが可能な状態になります。

このあと gdb のプロンプトが表示されるので、リモートデバッグの モードに移行します。

(gdb) set remotebaud 115200
(gdb) target hss /dev/cua00

左にでている (gdb) というのは、コマンド入力をうながすプロンプトです。 1 つめのコマンドは通信速度の指定です。 2 つめは、hss を用いたリモートデバッグで、接続先デバイスが /dev/cua00 であるというように指定しています。デバイス名は OS や環境によって 変わって来ます。

これを毎回行うのが面倒な場合は、カレントディレクトリなどの .gdbinit に 記述しておけば良いです。これについては後述します。

この状態で、実行可能な状態になってます。 ここからデバッグのためのコマンドを入力していくことになります。

ブレークポイントの設定

gdb では所定の位置にブレークポイントを設定することが可能です。 実行がブレークポイントにさしかかると、実行を一時中断して (gdb)のコンソールに制御がもどってきます。 指定の方法は次のようになります。
break				次の実行命令に設定
break function		指定した関数の先頭に設定
break -offset
break +offset		現在の中断位置の offset 行前後	
break linenum		現在参照中のファイルの行番号
break filename:function
break filename:linenum	ファイルを指定する場合
tbreak ...			1回のみ動作するポイント
現在設定されているブレークポイントは、info break で参照できます。 ブレークポイントの削除には次のようなコマンドを使用します。
clear 		現在の場所のブレークポイントを削除
clear function
clear linenum	関数、行に設定されたポイントを削除
delete nums ...	番号で示されるポイントを削除

他にも、無効化/有効化、条件づけなどが可能です。 詳しくは help や info を参照して下さい。

プログラムの実行

プロンプトに対して run コマンドを入力すると、実際の 実行が開始されます。 実行中に何らかのシグナルが通知されると、 プログラムが中断して gdb のコマンドプロンプトに戻って来ます。

シグナルというのは、OS からプログラムに対して送られて来る 割り込みの要求です。なんらかの要因でプログラムが実行できなく なったときなどに送られて来ます。 PlayStation の場合は次のようなシグナルが送られて来て動作が中断します。

番号
4	SIGILL		オーバーフローや演算のエラーなどが発生した
5	SIGTRAP		プログラムが中断した(ブレークポインタ、終了)
10	SIGBUS		バスエラーが発生した
11	SIGSEGV		不正なセグメントへのアクセス

SIGBUS というのは、メモリの境界に対して不正なアクセスを行った時に 発生します。PlayStation の CPU として使用されている R3000 は、 基本的に 4 byte 単位のメモリアクセスを行います。 4byte のデータをアクセスする時には、かならずそのアドレスも4byte 単位に なる必要があります。これを破ると SIGBUS が発生します。 char のポインタに対して long でアクセスしたりすると発生します。 (たまたま境界と一致した場合は発生しません)

SIGSEG というのは、メモリの領域への不正なアクセスが行われた場合に 発生します。PlayStation においても一応簡単なメモリの領域管理 がおこなわれており、データ領域でないアドレスに書き込んだりしようと するとこのシグナルが送られて来ます。

これら2つのシグナルがでた時は、ポインタ関連でなにか変なことをしてます。 その当たりを確認して見て下さい。ポインタの内容を表示してみると 原因がわかる場合が多いです。

以下実行に関連したコマンド一覧です。

run		プログラムの実行を開始します。
cont		ブレークポイントなどで中断したプログラムを継続させます
until ...	指定した部分(行、アドレス、関数)が終るまで実行します
step [n]	1行実行します。回数が指定できます。
next [n]	step と同様ですが、関数のなかには入っていきません
finish		現在の関数呼び出し終了まで実行します
stepi
nexti		マシンインストラクションレベルで動作します。

スタックフレームの操作

C のプログラムは、main() からはじまって、順次関数が呼び出されて行く ことで実行されます。この関数の呼び出しは、一般的には 引数やリターンアドレスなどがスタックにつまれてからおこなわれるので、 この各関数の呼び出しの情報をスタックフレームと呼びます。

gdb では、このスタックフレームを解析して、 現在実行中の部分がどういう引数でどう呼び出されて来たのかを 追跡する機能があります。 プログラムの流れを確認するときに便利です。

変数を参照するときなどは、たとえばローカル変数は、現在 注目しているフレームのもののみが参照できます。 プログラムが中断した時点では、その中断した場所のフレームが 注目の対象になっています。

次のようなコマンドが使用できます。

backtrace	呼び出されて来たフレームの一覧を表示します
where		backtrace のエイリアスです
frame n		指定したフレームを選択します
up [n]		n個前のフレームを選択します。
down [n]	n個次のフレームを選択します。
これらのコマンドを実行すると、そのフレームに関連する情報を 表示します。

情報の表示

変数の内容の表示
ソースレベルデバッガなので、実際に用いられている C での変数名で、メモリの領域を参照できます。 表示は変数の型に応じて自動的に切り替わります。 構造体などの表示もサポートされています。

変数の内容の表示には、print コマンドを用います。 しよう方法は単純で、print 変数名 とします。書式指定付きの printf も あります。書式は C の printf と同様です。

例:
print 

情報の自動的な表示
ステップ実行などのときにいつも ある変数の内容が表示できると非常に便利です。 gdb が停止した時に毎回表示させる変数を指定できます。 以下のようなコマンドを使用します。


設定ファイル

最初に設定した通信の設定などを毎回行うのは面倒です。 gdb は、カレントディレクトリやホームディレクトリに .gdbinit という ファイルがあると、それを読み込んで実行してくれます。 この中にコマンドを記述しておくと良いでしょう ホームディレクトリにおくと、本来の gdb のほうにまで読まれてしまうので、 作業ディレクトリ毎においておくのが良いでしょう。

DOS 版の開発ツールについているものをちょっといじったものです。 これの中で次のようなコマンドが拡張されています。

start	HSS通信モードの起動
dr	レジスタ内容の一覧
si	stepi して レジスタ一覧をだす
ni	nexti して レジスタ一覧をだす

この例では、通信の設定を "start" と入力することで行うようになっています。 これを psx-gdb が起動した時に常に行うようにする場合は、

define start
set remotebaud 115200
target hss /dev/cua00
end
document start
start remote PSX debugging
end
の部分を
set remotebaud 115200
target hss /dev/cua00
というように変更して、start の定義のわくをはずせば良いです。

MULE上での統合的な開発環境

多機能エディタ Mule を用いることにより、統合的な開発環境を 構築することが可能です。

Muleの基本的な操作

Mule の基本的な操作や用語については次の文書を参照して下さい。

hilit19

hilit19 は、mule のバッファに色をつけてわかりやすくするための 仕組みです。これを有効にすると、例えば c-mode での予約語やコメント などに色がつき、視認性が向上します。

hilit19を使用するには、$HOME/.emacs などに次のような行を加えます。

(cond (window-system
       (setq hilit-mode-enable-list  '(not text-mode)
             hilit-background-mode   'light)
       (require 'hilit19)))
この設定は Mule が Window System で動作しているときのみ有効になります。 hilit-mode-enable-list で、text-mode 以外で hilit19 を有効にしています。 hilit-background-mode では、デフォルトの色を設定しています。 背景を暗い色にしている場合は、これを 'dark にしたほうが読みやすく なります。

hilit19に関する詳しい情報は、インストールされている mule の lisp ディレクトリ (/usr/local/lib/mule/19.28/lisp など。バージョンなどにより 少し異なる) の、hilit19.el という hilit19 のソースを直接参照して下さい。

c-mode

c-mode は、C 言語でのプログラミング作成を支援するためのモードです。
自動的なインデントや、括弧の対応表示などを行ってくれます。

基本的な操作

TAB		インデントを行う

C-c C-u		#ifdef などの階層内を1つあがってその先頭に移動
C-c C-p		#ifdef などの階層の同じ階層の次の部分に移動
C-c C-n		#ifdef などの階層の同じ階層の前の部分に移動

ESC e		{}やifなどの対応の末尾に移動
ESC a		{}やifなどの対応の先頭に移動

ESC C-h		関数全体をマーク

最後の関数全体のマークは、C-h と DEL を入れ替えたりしていると 誤動作します。こういう場合は、C-M-h (Ctrl と Meta(Alt) と hを同時に押す) で大丈夫なはずです。

関数全体マークはかなり便利です。 たとえばソースの中で関数を移動させたい場合などは、目的の関数内で ESC C-h して、C-w (kill-region) して目的の場所で C-y (yank) します。

c-mode の詳しい操作方法は、c のファイル (hogehoge.c) を開いて c-mode にした状態で、M-x help-for-help m とすることで参照できます。 これは、現在のモードに関するヘルプを表示させるコマンドです。

設定の例です。

(setq c-mode-hook
      (function
       (lambda ()
	 (setq c-tab-always-indent nil
	       c-auto-newline nil
	       c-continued-statement-offset 4
	       c-indent-level 4
	       c-label-offset -4
	       tab-width 4 ))))
setq でパラメータを調整しています。次のようなパラメータが 調整できます。
c-tab-always-indent
通常 c-mode では TABを押すと自動的にインデントされます。 これを行頭のみにして、それ以外の場所での TAB は TAB として動作させます。
c-auto-newline
t にすることで },:,; のあとなどで自動的に改行するようになります。
c-indent-level
インデントの量です
c-continued-statement-offset
if や while などで、{}なしで改行した場合のオフセット量です。
c-continued-brace-offset
if や while などで、{ を改行したときのそのオフセットです。
c-brace-offset
いきなり { 始まったときのオフセットです
c-argdecl-indent
関数の引数にたいするインデントです。
c-label-offset
ラベルや、case: などに体するオフセットです。

compile-mode

compile mode は、各種コンパイルの支援を行うモードです。 実際のコンパイルの様子がバッファに表示されます。 エラーなどが出た場合にそのファイルのその場所にジャンプさせること などができます。

M-x compile としてコンパイルモードを起動します。 すると、ミニバッファでコンパイルのためのコマンド入力待ちになります。 デフォルトは make -k になっているので、Makefile がある場合はそのまま リターンで良いです。いちど入力したコマンドは次回も有効です。

編集中のファイルで更新されてないものがある場合、ディスクに 書き込むか聞いて来るので、適宜答えてください。

新しいバッファが開いてそこでコンパイルが開始されます。 エラーがでた場合は、C-o でバッファを移動し、目的の行に カーソルをあわせて C-c C-c と入力することで、そのファイルのその行 を開くことができます。 X Window System などでマウスを使用している場合は、その行にマウスカーソルを あわせて中ボタンでもジャンプします。

Debugger mode

MULE 上の Debugger mode から gdb を起動させることで、ソースコードと密着 した形でのデバッグが可能になります。

Debugger mode の起動

Mule から M-x gdb で デバッグモードになります。 まず起動するコマンドを聞いて来ます(デフォルト "gdb") ここで "psx-gdb 実行ファイル名" と入力します。 すると、新しい mule のバッファが開いて gdb のプロンプトが表示 されます。

この状態で通常通りに GDB を利用することが可能です。 Debugger mode では、バッファのうえでカーソルを自由に移動することが 可能で、移動先でリターンをおすことで、 その場いん表示された内容をコマンドとして実行できます。 さらにバッファの内容は通常の mule の操作で編集可能です。

Debugger mode の操作

gdb へのコマンドラインになっているバッファを GUD バッファと呼びます。 また、ブレークポイントなどで中断した場合、その場所が含まれる ソースが自動的に開かれて、中断場所が→で示されます。 GUD バッファと、その新しく開かれたバッファで次のようなコマンド を使用することが可能です。
C-c C-b		その場所にブレークポイントを指定する
C-c C-d		その場所のブレークポイントを解除する
C-c C-l		最後に GUD バッファから参照された場所のソースを表示します。

C-c C-s		1行実行する
C-c C-n		1行実行する(関数の内部には入らない)
C-c TAB		1命令づつ実行する

C-c C-r		実行を継続する

C-c C-p		カーソルのある位置の変数の値を表示させる

C-c C-t		1回限りのブレークポイントの設定
C-c <		スタックフレームを1つあがる
C-c >		スタックフレームを1つさがる

なお、他の全てのバッファ(例えばソースコードを開いたバッファ) においても、 通常の prefix key である C-c の変わりに C-x C-a を使用することで、上記のコマンドが使用できます。 つまり、あるファイルで C-x C-a C-b とするとそこにブレークポイントが 設定されますし、C-x C-a C-p なら、その変数の内容が GUD バッファに 表示されます。