更新日: 2023 年 10 月 27 日
GCC.DOC
説明
GCC(真里子版) の添付ドキュメントです。
GCC.DOC
X68000 Gcc Ver 1.39 Extended(Mariko Version) NIFTY-Serve FSHARP users 1991 Mar. 5 Abstract X68000 用のGnu C compiler についてのマニュアルです。オリジナルGcc にない多 くの機能を拡張してありますので、本マニュアルをよく読んで独自のX68000 ならではの 世界を広げましょう。本原稿はLaTEX システムを通すことによって読むことができます。 1 COPYING Using and Porting GCC Copyright (C) 1987, 1988 ,1989, 1990 Free Software Foundation, Inc. GNU CC Copyright (C) 1987, 1988 ,1989, 1990 Free Software Foundation, Inc. Porting X68000 Ver 1.36 Masaki KONDO, 1989 Porting X68000 Ver 1.37 C. Yoshino, 1990 Porting X68000 Ver 1.37.1 C. Yoshino, 1990 Porting X68000 Ver 1.38 C. Yoshino, 1991 Porting X68000 Ver 1.39 C. Yoshino, 1991 Copyright (C) C. Yoshino Nifty-Serve ID PED00647 Include. GNU's extension from A.Sing 90/02/25 13:59:16 いかなる媒体でも次の条件がすべて満たされている場合に限り、本小冊子をそのまま複写し 配布することを許可する。また、配布者は第三者に対して本許可告知と同一の許可を与える 場合に限り、再配布することを許可する。 ○ 受領、配布された写しに著作権表示および許諾告知が前もって載せられていること。 ○ 写しの受領者がさらに再配布する場合、その配布者が本告知と同じ許可を与えている こと。 GNU CC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any later version. GNU CC is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNU CC; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 2 ご注意 このマニュアルに記載されている機能はオリジナルのgcc に備えられている機能とそう でない機能があります。本マニュアルで説明されるのgcc 以外のgcc を使う場合は標準でな い機能と標準で備わる機能とを混同しないように十分注意してください。 3 バグレポート このgcc はオリジナルからかなりの変更を加えています。動作が変だなという場合はま ず、環境変数「真里子」の拡張機能を全て外してください。これで正しく動作した場合はエ ンバグですので、連絡をいただければ幸いです。 バスエラーでコンパイラが停止したり、暴走してリセットしなければ復帰しなかった場 合はまずコンパイラのスタックオーバフローを疑ってください。gcc はかなり安定したコン パイラです。スタック指定のやり方はこのマニュアルに記載されています。また、「**をコ ンパイルしたらバスエラーで止まりました。」「間違ったコードを出します。」「動作が不安定 です。」だけではなんの情報も得られません。バスエラー、アドレスエラーでコンパイラが 停止した場合は環境変数「真里子」に'F' だけを設定して-Q オプションを付けて再度コンパ イルしてください。エラーを起こす関数を特定できるので、その結果とソースをを連絡いた だければ対処できるかも知れません。また、私が使っていて気がついた範囲のバグは修正し てあります。 4 Gnu C compiler Gnu C compiler は多くのEWS で稼働している高性能なANSI 準拠のC コンパイラで す。X68000 に移植にあたっては近藤さんのgcc Ver 1.36 のソースを元に、以下の特徴を中 心に移植を行いました。 1. 幅広文字をも含めた完全な日本語対応 2. 親切(?) な日本語エラーメッセージ 3. X68000 の多くのハードウエアを利用するための割り込み処理のサポート 4. 再帰プログラムでのスタックオーバーを防ぐスタックチェックコードの生成 5. 定数掛け算での展開演算 割り込み処理の拡張によって、ゲームなどのハードウエアに密着した処理も記述可能になり ました。 5 Gnu CC の稼働環境について 5.1 ハードウエア環境 Gnu C compiler は以下の環境で稼働できます。 1. メインメモリ2M バイト以上のX68000 システム。 2. XC のライブラリ一式 6 Gnu C compiler のインストール 本Gnu C compirer インストールキットには以下のファイルが納められています。個々の プログラムについての詳しい解説はFSHARP にアップロードされている"Using and Porting GCC"を参照してください。 1. gcc_cc1.x コンパイラ本体です。 2. gcc_cpp.x プリプロセッサです。 3. gcc.x コンパイラドライバです。 4. jgcc.tex 本ドキュメントです。 各実行ファイルは環境変数"PATH"の示すディレクトリにコピーしてください。基本的にXC コンパイラと同じ環境で動作します。メインメモリ2M の方は環境変数"TEMP"はHDD か GRAM ディスクにしてください。gcc はランタイムライブラリとしてgnulib.a を必要とし ます。本パッケージには添付されておりませんが、Ver 1.36 以上のX68000 用gnulib.a なら 使用できる筈です。 7 インストール XC Ver 2 とそれ以前のバージョンではインストール方法が異なります。環境変数とリン カ、ライブラリに十分注意してください。 7.1 XC Ver 1.01 までの環境 gcc はANSI 準拠のコンパイラですからヘッダファイルのプロトタイプ宣言を利用しま す。一部、XC Ver 1 の宣言はANSI の文法に合わないので、変更をする必要があります。 1. #ifdef FORWORD を#ifdef __STDC__に置き換えます。 2. 関数foo(); はfoo(void); にします。 3. printf(char ,) のように","で終わっている関数はprintf(char ,...) にします。 以上でインストールは一応終了です。以下のプログラムをエディタで作成します。 #include < stdio.h > main() { fprintf(stdout,"Hello! "); } それではコンパイルしてみましょう。 gcc -O test.c X68k Assembler v1.01 Copyright 1987 SHARP/Hudson No Fatal error(s) X68k SILK Hi-Speed Linker v1.07 Copyright 1989,1990 SALT 正しくコンパイルできましたか?エラーがでた人はソースをじっくり眺めるか、環境変数な んかを十分チェックしてください。コンパイルできた方はインストールが終わりました。 7.2 XC Ver 2 以上の環境 XC Ver2 からはヘッダはANSI タイプに変更されています。基本的にヘッダを修正する 必要はありません。このバージョンに対応するために以下の変更を加えてあります。 1. ソースコードデバッガに対応 可能な限りXC が出力するソースコードデバッガ対応形式のアセンブラソースと同一 になるように対応してみました。ただし、ソースコードデバッガ自体がこのVer 2.00 の段階ではバグがかなりあって、希望する動作ができないことがかなりあります。 2. リンカーの選択 リンカーも少しは速くなりましたが基本的には遅いので、ライブラリを.a 形式に変更し てhlk.x を使いたい場合があるでしょう。その為に環境変数GCC_LINK を追加してあ ります。これはリンカとして使われるプログラムを文字列として与える環境変数です。 3. ライブラリサフィックスの指定環境変数GCC_LIB にライブラリのサフィックスを指定 できます。.a または.l をリンカーに応じて設定してください。 4. アセンブラの選択環境変数GCC_AS に設定されたファイルネームのプログラムをアセ ンブラとして起動します。 8 コンパイラコマンドオプション 最も重要で、複雑なのがこのオプションフラグです。大文字小文字を完全に区別します ので注意してください。 1. -o f ilename -o の後に指定されたファイルネームが最終的に出力されるファイルネームになります。 (例) gcc test.c -S -o mariko.s mariko.s がアセンブラソースとして出力されます。 gcc test.c -c -o marina.o marina.o がリロケータブルオブジェクトとして出力されます。 2. -llibname リンカーに渡すライブラリを指定します。ライブラリはいわゆるハナモゲラで指定し ます。例えばbaslib.a なら-lbas となります。 (例) gcc test.c -lbas basic のライブラリをリンクします。 gcc test.c -ldos -liocs doslib.a とiocslib.a をリンクします。 3. -S アセンブラソースを出力して終了します。-o が指定されていない場合は拡張子を.s に したファイルネームになります。 4. -c リンクを実行しません。-o が指定されていない場合は拡張子が.o になります。 5. -O 最適化を実行します。gcc とXC の決定的な差はこの最適化です。ただし多くのメモリ と少し多くのコンパイル時間を必要とします。(とマニュアルにはあるが、実際は時間 もかかります。) メモリが足らないと怒られた時にはメモリを買いましょう。 6. -M makefile の生成規則原形を作成して標準出力に出力します。#include < F ILE >の F ILEもふくまれます。通常は下の-MM を使うほうがいいでしょう。 7. -MM 上と同じですが#include < F ILE >のF ILEを含めません。これを使ってちょこっ といじればmakefile の出来上がりです。(あたりまえですがリダイレクトしないと意味 ないです) 8. -E プリプロセッサの出力を標準出力に書き出した後、終了します。マクロ展開のため文 法違反位置がはっきりしない場合に出力をリダイレクトして結果を検討する場合に有 効です。 9. -ansi ANSI 規格にフル準拠します。asm 文、typeof 等のgcc 拡張機能を認識しません。 10. -pedantic ANSI 規格に合致しない記述は全てワーニングかエラーになります。-ansi をより厳密にしたものです。 11. -traditinal 古い形式のC に準拠します。色々変わりますが主な変化は下のいくつかです。 ○ 関数内部で宣言されたextern はそのファイル全体に有効になります。XC と同じ になります。 ○ unsigned short, unsigned char はunsigned int に変換されます。 ○ マクロ__STDC__はdefine されなくなります。 ○ 文字列マクロが有効になります。これは後々考えて使わないのがベターです。 12. -W 拡張ワーニングを許可します。色々できますが-Wall ってやっておけばいいでしょう。 これでワーニング食らわないプログラムを書ける貴方はなかなかその筋です。-O を同 時に指定していないと無効です。 13. -Wid-clashnum 頭からnum-1 文字までの長さを識別子の認識の最大長にして、警告を出します。例え ば-Wid-clash-6 の場合は'marikochan' とmarikosama' は「類似した識別子」として警 告します。X68000 の場合は全く実用上問題ないのですが、一部の貧弱なアセンブラや リンカで動かす可能性のあるソースを書く場合は有効です。これは警告を出すだけで内 部的にはgcc では識別子の長さは無限大です。また、デバッキングオプション-g,-fscd が指定された場合は自動的に-Wid-clash-7 が指定されます。SCD.X がどうも長い名 前の識別子に弱いようなので追加してあります。 14. -w 小うるさいワーニングを禁止します。使わないほうがいいでしょう。ワーニング食ら うプログラムはよくバスエラーになりますから。 15. -mshort int 変数を16bit にします。マメにlong にキャストすればライブラリを使えますが、何 から何まで自分でやるゲームを作る場合には相当有効でしょう。ポインタは32bit なの で注意。 16. -mregparm 関数のパラメータをレジスタで渡すコードを生成します。デバッグされていませんが、 OS9/68000 のGCC でも動いていますから大丈夫だと思います。ただし、可変長引数 関数は正しく動作しません。勿論XC のライブラリを呼ぶと暴走します。 17. -Idir include file を検索するパスを指定します。 18. -Dmacro プリプロセッサにマクロを定義して渡します。これの逆が-U です。 19. -Umacro プリプロセッサにマクロを消去させます。これの逆が-D です。 20. -fsigned-char char を符号付で扱います。デフォルトはこちらです。 21. -funsigned-char char を符号なしにします。 22. -fvolatile 全てのポインタを通すメモリ参照はvolatile (揮発性)として取り扱います。I/O ステ イタスをポインタで直接読み取る場合に有効です。 23. -fomit-frame-pointer 必要のない場合はスタックフレームを生成しません。かなり速度、コードサイズに貢 献します。 24. -fstrength-reduce これはループのオプチマイズを行います。単純なループだとかなり賢いコードを生成 します。 25. -fwritable-strings 文字列をデータ領域に割り当て書き込み可能にします。使わない方がいいです。これ を指定しないと動かないプログラムは移植性は低いです。 26. -fforce-mem メモリ上での演算を行う場合一旦レジスタにコピーして演算します。共通部分式があ る場合により良いコードを生成するでしょう。(略訳です。) 27. -fforce-addr 定数アドレスをできる限りレジスタにコピーして演算します。効果は-fforce-mem と 同じです。(略訳です。) 28. -fno-defer-pop 関数コール後毎回スタックを補正します。通常gcc は何回かのコールの後まとめてス タックを補正しますが、これを禁止します。 29. -fcombine-regs 1つのレジスタを他にコピーする命令を結合させることを許可します。(直訳、意味 不明) 30. -fno-function-cse 関数アドレスのレジスタ間接呼び出しを禁止します。 31. -finline-functions inline 展開出来る関数をinline 展開します。もし関数が展開できた場合にはstatic に宣 言してある関数はアセンブラコードから消えてなくなりますので驚かないよーに。Ver 1.36 ではコケていたドライストーンベンチもちゃんと通るコードを生成します。 32. -fkeep-inline-functions 上記のオプションで驚きたくない人はこっちを使いましょう。関数の実体を出力します。 33. -fsyntax-only 文法だけチェックの便利なオプション。作ったばっかりのプログラムはこれを指定すれ ばXC なみとはいかないけれど素早くチェックできます。これはGCC の未公開オプショ ンですから、しばしばバスエラーを引き起こします。このフラグをたてるとgcc_cc1 は EXIT CODE を1 にして終了します。(gcc.x にas.x を起動させないようにするため) 34. -fcaller-saves 関数コールで破壊されるレジスタに変数を割り当てることを許可します。このような 割り当ては通常そのほうがより良い場合を除いて避けられます。 35. -ffixed-reg regで与えられるレジスタを使用しないコードを生成します。スタックポインタとフ レームポインタ、関数の戻り値に使用されるレジスタを指定すると収拾のつかない混 乱を招きます。X68000 で指定できるレジスタは下の16 個です。 'd0' 'd1' 'd2' 'd3' 'd4' 'd5' 'd6' 'd7' 'a0' 'a1' 'a2' 'a3' 'a4' 'a5' 'a6' 'sp' 通常'd0' 'a0' 'a1' 'a6' 'sp' は固定できません。 36. -call-used-reg 指定されたレジスタを関数で破壊できるレジスタとして扱います。これが指定された レジスタは関数の入り口で保存されません。 37. -call-saved-reg 指定されたレジスタを関数で保存すべきレジスタとして扱います。これが指定された レジスタは関数の入り口で保存され、出口で復帰されます。 以上が主だったオプションです。数多くのオプションが貴方のプログラムテクニックを支援 します。 9 X68000 専用拡張オプション 以下にX68000 専用に拡張されたオプションについて説明します。なお、先のセクション で示されたオプションは移植者の如何にかかわらず有効な筈ですが、こちらのオプションは この配布GCC のみのオプションです。(一部、近藤版gcc のオプションをも含みます) 9.1 追加されたスイッチ 1. -as-symbols=num アセンブラの最大シンボル数を指定します。num は10 進数値です。 2. -clk-symbols=num リンカの最大シンボル数を指定します。num は10 進数値です。linker がhlk.x(Ver 2.0?) では指定しても意味がありません。 3. -cc1-stack=num コンパイラgcc_cc1.x の割り当てスタックサイズをnum にします。 4. -cpp-stack=num プリプロセッサgcc_cpp.x の割り当てスタックサイズをnum にします。 5. -fstrings-align 文字列の先頭を強制的に偶数アドレスにするオプション。文字列を構造体にキャスト するなどの危ないプログラムに有効です。 6. -fstack_check 関数の入り口でスタックチェックを行うコードを生成します。XC のライブラリがばぐっ ているので、実際にオーバーフローするとリセット以外復帰できなくなります。 7. -fall-bsr 全ての関数をbsr コールします。小物ユーテリティの作成に使えば有効です。(サイズ が小さくなります)大きなプログラムだとジャンプオフセットが16bit で届かなくなっ てリンカーでエラーになります。 8. -fstrings-nopcr 関数で用いられた文字列をプログラムカウンタ相対アクセスしないようにします。大 きなプログラムでアセンブラがエラーになった時指定してください。 9. -fall-jsr static 関数はbsr で呼び出しますがこのオプションはそれを禁止してjsr で呼び出し ます。 10. -fscd XC Ver 2 のソースコードデバッガ対応フォーマットでコンパイルします。なお、-g オ プションでも同様になります。 11. -all-text 全ての変数をテキストセクションに出力します。これについては文法上に制限があり ます。ANSI-C では定義のない宣言は仮の宣言で、ファイルの最後まで定義があらわ れなかった場合は初期値0 の定義になります。つまり以下の場合はcom.b で変数は確 保されます。 file1.c int hen; file2.c int hen; 通常これはリンクエラーになりませんが、このオプションではこの変数はds.b でテキ ストセクションに出力されるので、リンクエラーになります。(2 重定義になります。) 必ず他のファイルで定義される変数はextern と宣言してください。 9.2 環境変数による指定 環境変数GCC_OPTION によっていくつかのスイッチを指定されたことにできます。何 時も同じオプションを設定しておきたい場合タイピング節約に有効です。 set GCC_OPTION= AEFGILMPST GCC_OPTION、LFIAMWSP は全て大文字でなければならず、'=' と'GCC_OPTION' の 間にスペースを置くと無効になります。 ○ 'L' -fstrength-reduce を指定されたことにします。 ○ 'F' -fomit-frame-pointer を指定されたことにします。 ○ 'I' -finline-functions を指定されたことにします。 ○ 'A' -fforce-addr を指定されたことにします。 ○ 'M' -fforce-mem を指定されたことにします。 ○ 'W' -Wall を指定されたことにします。 ○ 'S' -fstack_check を指定されたことにします。 ○ 'P' この場合だけ他のとは異質で、pea.w 0 とclr -(sp) のコードを選択します。前者が2 バイト多くて2 クロック高速なコードです。デフォールトはpea.w 0 です。 ○ 'G' この場合はコンパイラは作業用メモリをメインメモリ上で使い尽くすとスーパバイザ モードに移行してGRAM(512Kbytes) を作業メモリとして使います。GRAM の内容 は無条件に破壊しますので注意してください。 ○ 'T' さらに、GRAM を使い尽くした場合はマウスプレーンテキストRAM を作業メモリと して利用します。 ○ 'O' X68000 専用の最適化パスを許可します。最終的なコードのループ内部不変定数の移動 を行います。 ○ 'E' X68000 のNemacs がリリースされたので、エラーメッセージをgcc の元の形に戻しま した。この'E' を設定すれば従来の形式のエラーメッセージを生成します。 -f が付いたスイッチはそれぞれコマンドライン上で-fno-... を指定すればで環境変数を否定 できます。その他は否定できません。 10 初めてGCC を使う方々へ MS-DOS の殆どのコンパイラコンパイラもですが、エラーメッセージが英語で出力され るため、ハードルが高くなってしまいます。このgcc はほぼ全てのソースエラーを日本語で 出力します。 GCC はXC とは比較にならないくらい優れたコードを出す高性能なコンパイラです。が、 今までまとまった日本語のマニュアルがなかったのと、コンパイラドライバが一切ヘルプを 出さない仕様等、XC から見ると「とっつきにくい」コンパイラです。 そこで、今までのgcc 関係でよく質問にあがった点をここでまとめておきます。 ○ リンカにundefined symbol を指摘される BtoC されたソースでよく出ます。ソースのエラー以外では100%、-l スイッチの指定 忘れです。 ○ '/' をパスの区切りにして、「ファイルがみつからない」のエラーになる Human Ver 2.00 でこの現象が起きます。 ○ XC で動くプログラムが再度コンパイルしなおすと動かない 「プログラム言語C 第2 版」をじっくりみて、ソースがANSI に沿っているか検討し てみてください。-traditional オプションが有効かも知れません。 11 X68000 GCC 拡張機能 このGCC ではX68000 のハードウエアをC 言語から最大限に利用するための改造がさ れています。なお、これらの拡張機能を利用する為には環境変数「真里子」を設定する必要 があります。 Ver 1.39 X6_05 からは「MARIKO」も使うことが可能です。同時に指定されていた場合 には漢字の環境変数が優先されます。 以下のセクションで説明される機能の項目に付けられたABC の各該当機能がこの環境変数の 設定で有効になります。また、他のgcc ではこれらの機能は拡張されていないので、ソースの ポータビリティは制限を受けます。そのため、マクロmariko_cc,MRIKO_CC,__mariko_cc__, __MRIKO_CC__がプリディファインされます。ポータビリティを考慮する場合はこれらの機 能を使う時にこのマクロをテストするようにソースを記述してください。これらのマクロは このX68000 専用バージョンのみ定義されます。 11.1 2 進ビット表現の拡張(A) '0b' を数値表現の頭に付けることで2 進数を表すことができます。表現方法はXC と同 じです。なお、使用するごとにワーニングが出力されます。 11.2 日本語識別子拡張(A) ソーステキストの変数、関数名にシフトJISコードが使用できます。C 言語の仕様で [識別子]を記述可能な所には全てシフトJISコードを使うことが可能です。この機能は は-pedantic が指定された場合は無効になります。 11.3 割り込み関数の記述(A) X68000 GCC Ver 1.39 X6_XX は__builtin_saveregs 関数を利用することによって割り込 み処理関数を記述可能です。さらに、この拡張関数を利用すると全てのレジスタを破壊せず に処理を行い、任意のアドレスに分岐する関数をも記述可能になっています。強力である反 面、非常にデバッグは困難ですから、十分な知識と覚悟を持って利用してください。誤った 使い方でディスク関係をtrap すると貴重なファイルが全滅することもあります。 割り込み関数をinline にするとか、inline 関数でフレームポインタを指定するような意味 のない記述をしないでください。これも動作の保証はありません。これ以外では特に最適化 の制限はないはずです。 11.3.1 __builtin_saveregs 関数 この関数はGCC の内部処理に影響を及ぼす関数で、実体があるわけではありません。し たがって、この処理を行うライブラリがある訳ではないので注意してください。 __builtin_saveregs は0 個か1 個の引き数を持つ関数としてソースには記述します。この 引き数の数がその関数の振る舞いを決定します。同一の関数で、引き数の数が異なる使い方 をした場合の振る舞いは不定です。 1. 引き数が0 個の場合 純粋な割り込み処理関数として振る舞います。その処理関数が別の関数を呼び出さな い場合は関数が破壊するレジスタのみをスタックにセーブし、処理が終わったらrte で 復帰します。関数呼び出しがある場合は通常gcc がセーブしないレジスタも全てスタッ クにプッシュして処理を行います。外部にglobal register が宣言してあった場合はそ のレジスタは退避されません。ですが、gcc はこのレジスタはワークとして使うことは ありませんから、特に大きな問題は生じません。明示的にasm 文を用いてレジスタ変 数を宣言した場合は、ワーニングを出して注意を促します。 2. 引き数が1 個の場合 この引き数をリターンアドレスと見なして処理が終わったら全てのレジスタを復帰し てrts ジャンプします。(つまりスタックにそのアドレスを格納してrts することによっ て、そこにエントリします。) この処理は関数が記述されるごとに設定されますから、 複数のジャンプ先を記述できます。これによってDOS CALL 処理もフックして横取 りすることが可能になります。さらに、引き数が0(NULL) だった場合はその関数は全 てのレジスタを保存する関数として振る舞います。つまり、通常の関数に全てのレジ スタを退避するコードが付加されることになります。 3. 引き数に__builtin_saveregs 自身へのポインタを渡した場合 この場合はgcc は自動的にrte だけのダミー関数へジャンプするコードを生成します。 明示的なダミー関数は不用になりました。 11.3.2 記述に関する注意 実際に割り込み処理を記述する場合の注意がいくつかあります。それはHuman68k はリ エントラントに作られたOS ではないということです。また、ライブラリの幾つかは静的な 変数を参照します。これらの処理を割り込み関数で呼び出した場合は正しい動作を期待でき ません。(理解できない人は理解できるまでこの機能は利用しないでください。) また、浮動小数点ドライバも呼び出しできるのかは調査していませんので、関数が浮動 小数点関係のgnulib を呼び出す場合は十分注意してください。 くれぐれも注意していただきたいのは、C で記述することのメリットは保守性能のメリッ トだけで、アセンブラで記述するのと安全性に関しては全く同じなのです。 通常C のプログラムはユーザーモードであって、68000 の場合は86 系列に比べると安全 性は極めて高いのです。ヌルポインタのアクセスは100%バスエラーで停止しますし、それ によってOS 自体が破壊されることはまずありません。 ところが、割り込み処理関数はスーパーバイザモードであって、68000 の持つ保護機能 は殆ど無力になります。スタックもシステムスタックであることを忘れないでください。ま た、__builtin_saveregs を使ってジャンプする場合は割り込み処理を実際にフックする前に必 ずジャンプ先を設定してからフックしてください。大抵はこの設定変数は大域変数で、初期 値は0 でしょう。番地0 にジャンプすると悲惨な結果が待っています。(フックしてから設定 したのでは遅すぎる場合があるのです。) 割り込み関数をスタックチェックオプションでコ ンパイルするような馬鹿なことはしないでください。(スタックが違いますから。) この機能とgcc のasm との組み合わせは無敵の機種依存記述性(!?) をX68000 に提供 します。安全性と記述性は相反する要素ですが、この機能を使った面白いゲーム等を期待し ます。 11.4 register 指定変数の拡張 割り込み関数記述性を向上させる為にレジスタを指定した変数を大幅に強化しました。こ れによって、従来アセンブラでないと記述出来ない関数も完全にC のみで記述できるはず です。 11.4.1 asm("extern reg") の拡張(A) iocs のようなレジスタでパラメータを受け取る関数を記述するための拡張です。extern にされたレジスタは割り込み関数であってもスタックに退避されません。従ってこれらのレ ジスタは呼び出し元に変更して返すことが可能です。ただし、d0-d2,a0-a2 はコンパイラは 破壊可能レジスタとして扱いますので、これらのレジスタをパラメータとして使う場合、値 を保存しておかなければならない時には必ず明示的に変数にコピーし、関数終了時に復帰し てください。 11.4.2 asm("frame reg") の拡張(B) dos コールのサブルーチンを記述する場合a6 がパラメータポインタとなるため、a6 をフ レームポインタにはできません。そこで、asm 文でスタックフレームをa3-a6 の任意のレジ スタに設定できるように拡張しました。これはファイルの先頭か、関数の先頭でasm 文で適 当な変数をasm 文でレジスタ宣言することで有効になります。以下の例を参考にしてくだ さい。 /* 指定されない関数はフレームポインタa4 */ register int __frame1 asm("frame a4"); fa4() /* この関数はa4 を使う */ { .... } fa5() /* この関数のみa5 */ { register int __frame asm("frame a5"); .... } 11.4.3 Sample Program /* This File is IOCS hack sample */ /* We trap IOCS 0x46 & count it */ #include < interupt.h > extern int count; extern (iocs_old)(); static count_call() { count ++; } trap_46() { PRAMREG( iocs_no, d0); if ( iocs_no == 0x46 ) { count_call(); IJUMP_RTE(); } IJUMP(iocs_old); } #include < interupt.h > int (old_keyctrl)(); int dos_sample_keyctrl( dos_alist) { /* a5 をフレームポインタにしてa6 をextern reg にするマクロ */ /* arg はchar */ dos_list arg; /* return value はd0 */ RETREG( result, d0); short MD; short grop; dos_start( arg); MD = dos_arg( arg, short); switch (MD) { case 0: case 1: case 2: result = old_keyctrl(); break; case 3: grop = dos_arg( arg,short); result = do_something( grop); break; case 4: grop = dos_arg( arg,short); result = ins_do_something( grop); break; } dos_end( arg); IRTS(); return result; } このサンプルは本gcc に添付されているプログラムです。ヘッダも添付してあるので、この 機能を利用する場合にはできる限りこのヘッダを使ってください。 11.4.4 ソースコードデバッガ対応(C) -fscd をデェフォルト設定します。コンパイルされたオブジェクトは全てソースコードデ バッグ対応フォーマットに設定されます。 11.4.5 疑似統合環境の実現(D) icam 氏のご協力により、疑似的な統合環境をコンパイラドライバに持たせることができ ました。環境変数「真里子」と「満里奈」でこの機能を制御します。「満里奈」には貴方がお 使いのエディタを拡張子も含めて設定してください。デフォールトはEM.X です。エディタ にはEM.X が起動された場合を除いて、エラータグファイルmariko.err が渡されますので、 それを使ってタグジャンプしてください。タグファイルの形式はED.X で使用されるものと 同一です。環境変数TEMP は最後にn を付けないで使用してください。リリース後にバグ が確認されています。"E" を環境変数" 真里子" に設定しておけばワーニングでもエラー扱 いにして、エディタを起動するようになります。 11.4.6 コンパイル過程の表示(F) コンパイラがバスエラーを発生するソース等のデバッグ情報を得るために追加された機 能です。普通に使う場合は意味がありません。(コンパイル時間が異常にかかるソースをコ ンパイルする場合には暇潰しにはなるかもしれませんが・・) 12 X68000 のGCC の出力コードの違い このGCC はかなりオリジナルのGCC とは異なった68000 コードを生成します。以下に まとめておきますので、「どうも出力コードが誤っているのでは?」という時に参考にして ください。 12.1 覗き穴最適化 以下のコードは覗き穴最適化されています。 ○ bsr func 関数がstatic と宣言か定義された後の該当関数へはbsr でエントリします。 ○ dbra d?,label d?に格納されたループ変数が定数で$7FFF 以下だとdbra の後のコードを自動的にオ ミットして単純1 重ループにします。 ○ asl.? #1,d? これらはそれぞれのサイズのadd 命令に変更されます。 ○ peaW これはpea ??.w をas.x(Ver 1.0?) がアセンブルしないので、マクロでこれを強制的に 生成しています。この生成マクロコールがこのpeaW です。 ○ lea ??(pc),a? 文字列がリードオンリの通常コンパイルのときは文字列をPC 相対でアクセスします。 ○ pea ??(pc) 上記と同様です。 ○ clr.l ! move.l #0,d? move.l d?,target 整数0 クリアの方法を変更してあります。 ○ 文字列転送を最適化します。禁止するには-fno-peep-hole を指定してください。 文字列を変更するプログラムでは自動的にPC 相対アクセスをしませんが、前の章にもあっ たように移植性が下がるのでそういうコーディングは避けたほうがより良いプログラムだと 思います。 12.2 定数乗法のシフト加算展開 Oh!X で配布されたgcc と同様に定数の乗法をシフトと加算、減算に展開します。Oh!X 版 gcc と異なっているのは定数であれば必ず展開するのと、オプチマイズを阻害しない(Oh!X のgcc では-fstrength-reduce オプションの効力がかなり阻害されている) という2 点です。 13 ANSI から拡張されたgcc の機能 以下のドキュメントはOS9-UG が配布しているOS9/68000 GCC のディストリビュート に含まれるファイルをLaTEX フォーマットに直して挿入しました。 GNU's extension from A.Sing 90/02/25 13:59:16 C 言語のGNU による拡張 GNU C ではANSI 標準C には見られないいくつかの言語機能が提供されています。 (`-pedantic' オプションで、これらの機能が使われた場合に警告を出させることが出来ます。) 条件コンパイルでこれらの機能の有効性をテストするには既定義マクロ`__GNUC__' を調べ ます。このマクロはGNU CC では常に定義されています。 13.1 式の中の文と宣言 GNU C では式のなかで、括弧でくくられた複文を使うことが出来ます。従って式の中で 変数を宣言することが出来ます。例えば ({ int y = foo (); int z; if (y > 0) z = y; else z = - y; z; }) は(すこし冗長ですが)`foo ()' の絶対値を求める有効な式です。この機能は特にマクロの 定義を(各オペランドをちょうど一回だけ評価して)安全にするのに便利です。例えば”最 大値”関数は、標準的なC ではマクロとして、普通は次のように定義されます。 #define max(a,b) ((a) > (b) ? (a) : (b)) しかし、この定義ではA とB のどちらか一方を2 度計算しますから、もしオペランドに副 作用があるとよくありません。GNU C ではオペランドの型が判っていれば(ここではint としましょう)次のようにマクロを安全に定義出来ます。 #define maxint(a,b) (fint _a = (a), _b = (b); _a > _b ? _a : _b; g) enum 定数の値、ビット・フィールドの幅、静的な変数の初期値などの定数式に文を埋め込 むことは出来ません。オペランドの型が判らない場合でもこのようなことが可能です。ただ、 そのためには`typeof' (Typeof の節を参照)か型の名前付け(型の名前付けの節を参照)が 必要です。 13.2 式の型の名前付け 初期化子を伴う`typedef' 宣言を使って、式の型に名前を付けることが出来ます。EXP の型に型名としてNAME を定義するには次のようにします。 typedef NAME = EXP; これは”式の中の文”機能と組にして使うと便利です。一緒に使えば、次のようにして任意の 算術型のオペランドを持つことが出来る安全な”最大値”マクロを定義することが出来ます。 #define max(a,b) ({ typedef _ta = (a), _tb = (b);\ _ta _a = (a); _tb _b = (b); \ _a > _b ? _a : _b; }) アンダースコアではじまる局所変数名を使う理由は、`a' と`b' に置き換えられる式の中に現 れる変数名との名前の衝突を防ぐためです。最終的には新しい宣言の構文を考えだして、初 期化子の後からスコープが始まる変数を宣言出来るようにしたいと思っています。これは前 述の名前の衝突に対する、より信頼のおける方法になるでしょう。 13.3 `typeof ' による型の参照 式の型を参照するには`typeof' を使うもう一つの方法があります。このキーワードを使 うための構文は`sizeof' と同様ですが、意味論的には`typedef' で定義された型名と同様に 振舞います。 `typeof' の引数の書き方には2 通りあります。式または型です。式を使う例は typeof (x[0](1)) です。これは`x' が関数の配列であると仮定しています。宣言される型は関数の返す値の型 です。型を引数に使う例は typeof (int ) です。ここで宣言されている型は`int' へのポインタの型です。 ANSI C のプログラムにインクルードされても動作する必要のあるヘッダー・ファイル を書いているならば、`typeof' の代わりに`__typeof' と書いて下さい(代わりのキーワード を参照)。`typeof' 構文要素はtypedef 名が使えるところではどこでも使えます。例えば、宣 言で、キャストで、そして`sizeof' と`typeof' の中で使えます。 ○ 次は`x' が指すものの型で`y' を宣言します。 typeof ( x) y; ○ 次はその値の配列として`y' を宣言します。 typeof ( x) y[4]; ○ 次は文字へのポインタの配列として`y' を宣言します。 typeof (typeof (char )[4]) y; これは次の従来からのC の宣言と同値です。 char y[4]; `typeof' を使った宣言の意味を見るために、そして何故便利なのかを理解するために、上 の例を次のマクロを使って書き換えてみましょう。 #define pointer(T) typeof(T ) #define array(T, N) typeof(T [N]) さて、宣言はこんなふうに書き換えることが出来ます。 array (pointer (char), 4) y; ですから、`array (pointer (char), 4)' は`char' への4 つのポインタの配列なのです。 13.4 一般化された左辺値 複合式、条件式、そしてキャストは、そのオペランドが左辺値である限り、左辺値とし て認められます。従って、アドレスをとったり値を代入することが出来ます。例えば、複合 式では最後の式が左辺値である限り代入が出来ます。次の2 つの式は同値です。 (a, b) += 5 a, (b += 5) 同様に複合式のアドレスをとることが出来ます。次の2 つの式は同値です。 &(a, b) a, &b 条件式については、その型がvoid でなく真と偽の分岐先の式がともに有効な左辺値ならば、 有効な左辺値です。例えば、次の2 つの式は同値です。 (a ? b : c) = 5 (a ? b = 5 : (c = 5)) キャストは、オペランドがそうなら有効な左辺値です。キャストのアドレスをとることは結 果の型を除いてキャストなしにアドレスをとることと同じです。例えば、次の2 つの式は同 値です。(ただし、2 つめは`a' の型が`int ' へのキャストを許さない場合にも有効になり 得るという点を除いて。) &(int )a (int )&a 左辺がキャストであるような単純代入式は、右辺がまず指定された型に変換され次に左辺の 内側の式の型に変換されます。代入の後で、値は指定された型に戻されて、これが代入の値 となります。従って、もし`a' が型`char ' を持てば次の2 つの式は同値です。 (int)a = 5 (int)(a = (char )5) `+=' のような計算をともなう代入でキャストが使われた場合には、キャストされた型を用 いて計算が行われ、その後前述の動作が行われます。従って、次の2 つの式は同値です。 (int)a += 5 (int)(a = (char ) ((int)a + 5)) 13.5 中間項を省略した条件式 条件式では、2 つめのオペランドを省略することが出来ます。この場合1 つめのオペラン ドが0 でなければ、その値が条件式の値になります。従って、式 x ? : y の値は`x' の値が0 でなければその値で、さもなければ`y' の値です。この例は完全に x ? x : y と同値です。 この単純な場合では、2 つめのオペランドを省略出来ることが特に便利というわけではあ りません。それが便利なのは、1 つめのオペランドが副作用を含み得る(それがマクロの引 数などの)場合です。オペランドを中間項で繰り返すと副作用が2 回行われます。2 つめの オペランドを省略すれば再計算による不用な効果なしに、すでに計算された値が使われます。 13.6 長さ0 の配列 GNU C では長さ0 の配列が使えます。これは、実際には可変長のオブジェクトのヘッダ である構造体の最後の要素として非常に便利です。 struct line { int length; char contents[0]; }; { struct line thisline = (struct line ) malloc (sizeof (struct line) + this_length); thisline >length = this_length; } 標準C では`contents' に長さ1を与えなければなりません。これでは領域を無駄にしたり、 `malloc' に複雑な引数を与えなければならなかったりします。 13.7 可変長の配列 GNU C では、可変長の自動配列が許されます。これらの配列は、他の自動配列と同様に 宣言しますが、定数式でない長さを持ちます。領域はその時に割り当てられ、中括弧のレベ ルを出ると開放されます。例えば FILE concat_fopen (char s1, char s2, char mode) { char str[strlen (s1) + strlen (s2) + 1]; strcpy (str, s1); strcat (str, s2); return fopen (str, mode); } 可変長の配列を関数の引数に使うこともできます。 struct entry { char data[foo] }; struct entry tester (struct entry arg) { struct entry new; int i; for ( i = 0 ; i < foo ; i++ ) new.data[i] = arg.data[i] + 1; return new; } 配列の長さは、それが宣言されている中括弧レベルに入る時に計算され、`sizeof' で参照出 来るように、配列名のスコープ内で記憶されています。配列名のスコープの外にジャンプし たりブレークする際にも領域は開放されます。スコープ内にジャンプすることは許されませ ん。そうするとエラーメッセージが出るでしょう。関数`alloca' を使って可変長の配列と殆 ど同じ効果を得ることが出来ます。関数`alloca' は他の多くのC のインプリメンテーション で使うことが出来ます(全部ではありませんが)。他方、可変長の配列はもっとエレガントで す。これらの2 つの方法には他にも違いがあります。`alloca' で確保された領域はそれを含 む関数が復帰するまで存在します。可変長の配列で確保された領域は配列名のスコープが終 わると同時に開放されます。(もし、可変長の配列と`alloca' を同じ関数で使うと、可変長の 配列の開放によって`alloca' で最後に確保された領域も開放されます。) 13.8 左辺値でない配列の添字 単項の`&' 演算子が許されない場合でも、GNU C では左辺値でない配列に添字を付ける ことが許されています。例えば次は他のC の方言では無効ですが、GNU C では有効です。 struct foo { int a[4]; }; struct foo f(); bar (int index) { return f().a[index]; } 13.9 void ポインタと関数ポインタの算術 GNU C では`void' ポインタと関数ポインタの加法、減法がサポートされています。こ れは`void' と関数のサイズを1 として行われます。従って`void' 型と関数型の`sizeof' が可 能で、1 を返します。 13.10 非定数初期化子 合成体の初期化子の要素はGNU C では定数式でなくても構いません。次は実行時に変 化する要素を持つ初期化子の例です。 foo (float f, float g) { float beat_freqs[2] = { f-g, f+g }; ... } 13.11 構成子式 GNU C は構成子式をサポートします。構成子は初期化子を含むキャストという形をし ています。その値はキャストで指定された型を持つオブジェクトで、初期化子で指定され る要素を持ちます。型は構造体、共用体または配列でなければいけません。`struct foo' と `structure' が次のように宣言されていると仮定しましょう。 struct foo { int a; char b[2];} structure; 次は構成子を用いて`struct foo' を作る例です。 structure = ((struct foo) { x + y, 'a', 0 }); これは次のように書くのと同値です。 { struct foo temp = { x + y, 'a', 0 }; structure = temp; } 配列を作ることも出来ます。もし構成子の全ての要素が単純な定数式(から作られたもの) で初期化子として使えるならば、構成子は左辺値でありその最初の要素へのポインタに変換 することが出来ます。例えば char foo = (char []) { "x", "y", "z" }; 要素が単純な定数でない配列の構成子はそれほど便利ではありません。なぜならこの場合構 成子は左辺値ではないからです。有効な使い道は2 通りしかありません。添字を付けて使う か、配列変数を初期化するために使うかです。前者は多分`switch' 文より遅くなるでしょう。 一方後者は普通のC の初期化子と同じことです。 output = ((int[]) { 2, x, 28 }) [input]; 13.12 関数の属性の宣言 GNU C ではプログラムで呼び出される関数についてある種の宣言をして関数呼び出し の最適化を助けることが出来ます。`abort' や`exit' のように、幾つかの関数は復帰しませ ん。これらの関数は`volatile' と宣言すべきです。例えば extern volatile void abort (); は`abort' が復帰しないと仮定出来ることをコンパイラに知らせます。これによって少し良 いコードが生成されますが、より大切なことは、それが初期化されない変数に対する見掛け だけの警告を出さないことの助けになることです。 多くの関数はその引数以外を調べませんし、戻り値以外に効果がありません。そのよう な関数は算術演算子と全く同様に共通部分式の削除やループ最適化の対象になり得ます。こ のような関数は`const' と宣言すべきです。例えば extern const void square (); と宣言すれば仮の関数`square' はプログラムに書かれているよりも少ない回数呼び出しても だいじょうぶということになります。 ポインタを引数に持ち、ポインタの指すデータを調べる関数は`const' と宣言してはいけ ない事に注意して下さい。同様に、`const' でない関数を呼び出す関数も`const' であっては なりません。 この機構に反対して、ANSI C の#pragma を代わりに使うべきだと主張する人もいま す。私がそうしなかったのには2 つの理由があります。 1. マクロから#pragma コマンドを作り出すことが出来ないこと。 2. #pragma コマンドはこれらのキーワードと同様に他のコンパイラでは他の事を意味す るに違いないこと。 この2 つの理由は`#pragma' を”どのよう”に応用してみても有効でしょう。私が理解で きる限りにおいて、`#pragma' は決して便利なものではありません。 13.13 識別子の名前のドル記号 GNU C では識別子の名前にドル記号を使うことが出来ます。多くの伝統的なC コンパ イラがそのような識別子を許しているので、それに従ったわけです。 13.14 型と変数のアラインメントの問い合わせ キーワード`__alignof' によって、オブジェクトの整合のされ方や型によって通常要求さ れる最小のアラインメントを調べることが出来ます。構文は`sizeof' と同じです。 例えば対象のマシンで`double' の値が8 バイトの境界に整合されなければならないなら、 `__alignof (double)' は8 です。これは多くのRISC マシンで成り立つことです。より伝統的 なマシンのデザインでは`__alignof (double)' は4、またはより小さく2 です。 実際には全くメモリの整合を必要としないマシンもあります。それらは奇数アドレスに おいてさえ、任意のデータ型の参照を許します。このようなマシンでは`__alignof' は型のア ラインメントの”推薦値”を返します。 `__alignof' のオペランドが型ではなく左辺値の場合、その左辺値が持つと判っているアラ インメントの最大値が`__alignof' の値です。左辺値はデータ型に由来するアラインメントそ のものを持つこともありますし、構造体の一部であれば構造体から受け継がれたアラインメ ントを持つこともあります。例えば次の宣言のもとで struct foo f int x; char y; g foo1; `__alignof (foo1.y)' の値は多分2か4でしょう。`foo1.y' のデータ型はそれ自身ではどのよう なアラインメントも必要としないにもかかわらず、値は`__alignof (int)' と同じになります。 13.15 マクロと同じくらい速いインライン関数 関数をinline と宣言すれば、GNU CC はその関数のコードを呼び出し側に融合します。 これによって関数呼び出しのオーバー・ヘッドが無くなりますから実行が速くなります。さ らに、実引数に定数があればその既知の値を用いてコンパイル時の簡略化が可能になり、イ ンライン関数の全てのコードを使う必要が無くなります。関数をインラインと宣言するには、 キーワード`inline' を宣言で使います。例えば次のようにします。 inline int inc (int a) { (a)++; } ( ANSI C のプログラムにインクルードされるヘッダー・ファイルを書いているならば、`inline' の代わりに`__inline' と書いて下さい。代わりのキーワードの節を参照) また、オプション`-finline-functions' を用いて、全ての”十分に単純な”関数をインライ ンにすることが出来ます。ただし、関数定義におけるある種の使い方がインライン展開には 不適切になることに注意して下さい。関数がインラインでかつ`static' な場合、もしその関 数の全ての呼び出しが呼び出し側に融合されてしまったなら、その関数自身のアセンブラ・ コードは全く参照されなくなります。このような場合、オプション`-fkeep-inline-functions' を指定しない限り、GNU CC は実際にはその関数のアセンブラ・コードを出力しません。 いろいろな理由で融合出来ない呼び出しもあります(特に、関数定義の前にある呼び出 しは融合できません、また定義内の再帰呼び出しも融合出来ません)。もし融合出来ない呼 び出しがある場合には、その関数は普通通りアセンブラ・コードにコンパイルされます。 インライン関数が`static' でない場合は、他のソース・ファイルからの呼び出しがあると 仮定しなければなりません。どんなプログラムでも大域シンボルは一回しか定義出来ません から、その関数は他のソース・ファイルでは定義されてはいけないことになり、他のファイ ルにある呼び出しは融合できません。従って、`static' でないインライン関数は常にそれ自 身として普通の方法でコンパイルされます。 13.15.1 割り込み関数のinline 制限 X68000 GCC Ver 1.39 X6_XX では割り込み関数をサポートしています。これに関し ては別の章を参照していただくとして、割り込み関数はinline にできません。従来の制限、 「-finline-functions を指定しては絶対にいけません。」はX6_04 ではなくなりました。誤っ たinline 宣言は警告を受けるでしょう。 13.16 C の式をオペランドに持つアセンブラ命令 `asm' を使ったアセンブラ命令で、命令のオペランドをC の式で指定することが出来る ようになりました。これは、使いたいデータがどのレジスタにあるか、メモリのどこにある かを考える必要がないということを意味します。 この機能を使うには`asm' で、マシン記述にあるのと良く似たアセンブラ命令テンプレー トと、オペランド毎にオペランド制約文字列を指定する必要があります。例えば、68881 の `fsinx' 命令を使うには次のようにします。 asm ("fsinx %1,%0" : "=f" (result) : "f" (angle)); ここで、`angle' はC の式で入力オペランドです、一方`result' は出力オペランドです。それ ぞれが"f" をオペランド制約文字として持っています。これは浮動少数点レジスタを必要と することを表しています。`=f' の中の`=' はそのオペランドが出力であることを示します。 全ての出力オペランドの制約文字列で`=' を使わなければいけません。制約文字列はマシン 記述にあるのと同じ言語を使います。(制約文字列に関しては後述します) 各オペランドは、オペランド制約文字列、続いて括弧に囲まれたC の式で記述されます。 コロンによって、アセンブラ・テンプレートと最初の出力オペランドを区切り、もしあれば 最後の出力オペランドと最初の入力オペランドを区切ります。出力オペランド同士、入力オ ペランド同士はカンマで区切ります。オペランドの総数はマシン記述にある命令パターンに 含まれるオペラントの最大数によって制限されます。 もし、出力オペランドが無く入力オペランドがある場合には出力オペランドのあるべき 位置を囲んで2 つの連続したコロンが必要です。出力オペランドの式は左辺値でなければな りません。コンパイラはこれをチェック出来ます。入力オペランドは左辺値である必要はあ りません。コンパイラは、実行しようとする命令にとってオペランドが意味のあるデータ型 を持つかどうかはチェック出来ません。アセンブラ命令を解析することも出来ませんし、そ の命令の意味も知りません。それが意味のあるアセンブラへの入力であるかどうかも知りま せん。拡張された`asm' の機能はコンパイラがその存在を知らない命令のために最もよく使 われます。 出力オペランドは書き込み専用でなければなりません。GNU CC は、それらのオペラン ドの値がもはや使われなく生成する必要もないと仮定します。読み書き両方が必要なオペラ ンドや、全てのビットが書き込まれず残りのビットに何らかの有用な情報があるオペランド に対しては、その機能を論理的に2 つのオペランド、1 つは入力オペランド、もう1 つは書 き込み専用の出力オペランド、に分けなければいけません。これらの関連は、命令実行時に 同じ場所になければならないというオペランド制約文字列によって表現されます。両方のオ ペランドに同一のC の式でも、異なる式でも使うことが出来ます。例として、`bar' を読み 込み専用の入力オペランドとして持ち、`foo' を読み書き両用のオペランドに持つ(仮想的な) 命令`combine' を書いてみましょう。 asm ("combine %2,%0" : "=r" (foo) : "0" (foo), "g" (bar)); オペランド1 の制約文字"0" はオペランド0 と同じ場所を占めることを表します。制約文字 列の数字は入力オペランドにのみ許されます。それは出力オペランドを参照しなければなり ません。 制約文字列の数字のみが1 つのオペランドと他のオペランドが同じ場所にあることを保 証出来ます。`foo' が両方のオペランドの値であるというだけでは、生成されるアセンブラ・ コードで同じ場所にあることを保証出来ません。次は動作しないでしょう。 asm ("combine %2,%0" : "=r" (foo) : "r" (foo), "g" (bar)); いろいろな最適化や再ロードによって、オペランド0 と1 は異なるレジスタに置かれる可能 性があります。そうしてはならない理由をGNU CC は知らないのです。例えば、コンパイ ラは`foo' の値のコピーを見付けてそれをオペランド1 として使いながら、一方では他のレ ジスタにオペランド0 への出力を生成するかも知れません。(それを後で`foo' 固有のアドレ スにコピーする)。勿論、オペランド1 のレジスタはアセンブラ命令でも参照されていませ んから、結果は動作しないということになるでしょう。しかし、GNU CC はそれを知らせ ることが出来ないのです。 出力オペランドが制約修飾子`&' を持たない場合、出力が生成されるまでに入力が読み 取られるという仮定のもとに、GNU CC は出力オペランドを関係のない入力オペランドの レジスタに割り当てることがあります。この仮定は、アセンブラ・コードが実際には2 つ以 上の命令から成り立っていたりすると正しくない場合があります。そのような場合には、入 力と重なってはいけない出力オペランドのそれぞれに`&' を使って下さい。 特定のレジスタを破壊する命令があります。このことを表すには、3 つめのコロンを入力 オペランドの後に書いて、続けて(文字列の形で) 破壊されるレジスタ名を書きます。次は vax の現実的な例です。 asm volatile ("movc3 %0,%1,%2" : /* no outputs */ : "g" (from), "g" (to), "g" (count) : "r0", "r1", "r2", "r3", "r4", "r5"); 複数のアセンブラ命令を1 つの`asm' テンプレートに書くこともできます。それには1 つ1 つの命令を改行(`/n' と書かれる) で分離するか、もしアセンブラが許すならセミコロンで分 離します。GNU アセンブラではセミコロンが使えます。全てのUnix のアセンブラも同様 と思われます。破壊されるレジスタは入力オペランドとしても、出力オペランドのアドレス としても、使われないことが保証されます。従って、破壊されるレジスタは好きなだけ読み 書きが出来ます。次は1 つのテンプレート内の複数の命令の例です。サブルーティン`_foo' がレジスタ9 と10 で引数を受け取ることが仮定されています。 asm ("movl %0,r9;movl %1,r10;call _foo" : /* no outputs */ : "g" (from), "g" (to) : "r9", "r10"); もしアセンブラ命令で設定される条件コードをテストする必要がある場合には次のように、 分岐命令とラベルが`asm' で必要になります。 asm ("clr %0;frob %1;beq 0f;mov #1,%0;0:" : "g" (result) : "g" (input)); ここではGNU アセンブラや殆どのUnix のアセンブラと同様に、アセンブラが局所ラベル をサポートしていると仮定しています。 普通これらの`asm' 命令を使う最も便利な方法はそれらをマクロに閉じ込めて関数のよ うに使う方法です。例えば #define sin(x) \ ({ double __value, __arg = (x); \ asm ("fsinx %1,%0": "=f" (__value): "f" (__arg)); \ __value; }) ここで、変数`__arg' は命令が本来の`double' の値で動作することを保証し、`double' に自 動的に変換できる引数のみを受け付けるために使われています。 正しいデータ型で命令が動作することを保証するもうひとつの方法は`asm' でキャスト を使うことです。この方法はより広い型が変換される点で、変数`__arg' を使う方法と異な ります。例えば必要な型が`int' ならば、なんの警告もなしにポインタの引数が`int' にキャ ストされて受け付けられます。一方、`__arg' という名前の`int' の変数にポインタを代入す れば、呼び出し側ではっきりとキャストしない限り警告が出るでしょう。 `asm' が出力オペランドを持つ場合、GNU CC は最適化をするために出力オペランド以 外には副作用がないと仮定します。これは副作用を持つ命令が使えないということを意味す るのではありませんが、注意が必要です。それは、出力オペランドが使われていないならコ ンパイラはその命令を削除することがありますし、ループの外に移動するかも知れませんし、 共通部分式になる場合には2 つの命令を1 つにしてしまうかも知れないからです。さらには、 命令の副作用が他では変化しないとわかっている変数に対して行われる場合には、その変数 がレジスタにあったりすると変数の古い値がのちに再利用されたりします。 `asm' の後にキーワード`volatile' を書いて、`asm' 命令が削除されたり、移動されたり、 組み合わせられたりすることを防ぐことが出来ます。例えば次のようにします。 #define set_priority(x) \ asm volatile ("set_priority %0": /* no outputs */ : "g" (x)) (ただし、出力オペランドを持たない命令はそれが実行される限りにおいて、どんな場合に も削除されたり移動されたりはしません。) アセンブラ命令による条件コードの変化を調べる方法を見付けようとするのは自然な考 えです。しかし、これを実現しようとすると信頼出来る形での動作が保証出来なくなります。 問題は出力オペランドに再ロードの必要が生じる場合があるからで、それによって付加的に ストア命令が生成されることです。殆どのマシンで、これらの命令は条件コードを変化させ てしまいます。それは条件コードをテストしようとする前に起こるのです。この問題は普通 の"test" や"compare" 命令では起こりません。なぜならこれらは出力オペランドを全く持 たないからです。 ANSI C のプログラムにインクルードされるヘッダー・ファイルを書いているならば、 `asm' の代わりに`__asm' と書いて下さい。代わりのキーワードの節を参照。 13.17 アセンブラ・コードで使用される名前の制御 アセンブラ・コード内でC の関数や変数のために使用される名前を指定することが出来 ます。それには宣言の後に`asm' (または`__asm')キーワードを次のように書きます。 int foo asm ("myfoo") = 2; これによってアセンブラ・コードで変数`foo' の名前として、普通の`_foo' ではなく`myfoo' を使うことが指定されます。通常C の関数や変数の名前の前にアンダースコアを付けるシス テムでは、この機能によってリンカのためにアンダースコアで始まらない名前を定義するこ とが出来ます。 関数定義においてはこの方法で`asm' を使うことは出来ません。その代わりに関数定義よ り前で`asm' を付けた宣言を書くことで同じ効果が得られます。例えばこんなふうにします。 extern func () asm ("FUNC"); func (x, y) int x, y; ... 選んだ名前が他のいかなるアセンブラ・シンボルとも衝突を起こさないようにすることはユー ザーの責任です。また、レジスタの名前を使ってはいけません。そうすれば全く無効なアセ ンブラ・コードが生成されるでしょう。GNU CC はまだ静的変数をレジスタに持つことが 出来ません。将来、この機能が付け加えられることになるでしょう。 13.18 大域レジスタ変数 ブログラム言語のインタプリタのようなプログラムでは非常に繁雑にアクセスされる一 組の大域変数があって、これらのためにレジスタをとって置くことが十分意味のあることに なり得ます。GNU C では次のようにして大域レジスタ変数を定義することが出来ます。 register int foo asm ("a5"); ここで、`a5' は使用されるレジスタの名前です。ユーザーのマシンで関数呼び出しで通常保 存復帰されるレジスタを選んで下さい。そうすればライブラリの呼び出しでもそのレジスタ は破壊されません。 レジスタ名は自然にマシン依存になりますから、CPU の種類によって場合分けをする必 要があります。68000 では`a5' はポインタ型の変数に対する良い選択でしょう。レジスタ・ ウィンドウを持つマシンでは関数呼び出し機構によって影響されないように”大域”レジス タを選ばなければならないことに注意して下さい。 さらに、CPU の種類が1つであっても、オペレーティング・システムによってレジスタ の名前に差があり得ます。その場合にはさらに場合分けが必要です。例えば`%a5' をレジス タ名に使う68000 のオペレーティング・システムもあります。 最終的にはコンパイラに自動的にレジスタを選択する機能を要求する道があるのかも知 れません、しかし、そのためにはまず、選択の方法、そしてその選択をユーザーが制御する 方法を見付ける必要があります。解決方法は明らかではありません。あるレジスタを大域レ ジスタ変数として定義すると、そのレジスタは少なくとも現在のコンパイルでは、そのため だけにとっておかれます。現在のコンパイルでは他の目的のために用いられることはありま せん。関数はこのレジスタの保存復帰を行いません。このレジスタへの代入はそれが無効で あっても削除されませんが、参照は削除されたり移動されたり、簡略化さたりすることがあ ります。 シグナル・ハンドラ、より一般に複数の制御のスレッドから大域レジスタ変数にアクセ スすることは安全ではありません。なぜならシステムライブラリ・ルーチンは(それらを今 の目的のために再コンパイルしない限り)一時的にそのレジスタを他の用途のために使って いるかも知れないからです。 大域レジスタ変数を使っている1 つの関数がもうひとつのそのような関数`foo' をこの大 域レジスタ変数に関して何も知らない(つまり、その変数が宣言されてない別のソース・ファ イルにある)第三の関数`lose' を介して呼び出すことは安全ではありません。なぜなら、`lose' はそのレジスタを保存して他の値を入れることがあり得るからです。例えば`qsort' に渡さ れる比較関数内で大域レジスタ変数が使えると期待することは出来ません。それは`qsort' が他のものをそのレジスタに入れることがあり得るからです。(もし、同一の大域レジスタ 変数で`qsort' を再コンパイルすることが出来れば、この問題を解決することが出来ます。) もし、大域レジスタ変数を実際には使わないような`qsort' や他のソースファイルを、そ のレジスタを他の目的のために使わないようにするために再コンパイルしたいのなら、コン パイル・オプション`-ffixed-REG' を指定すれば十分です。実際にソース・コードで大域レ ジスタ宣言を付け加える必要はありません。 大域レジスタ変数の値を変化させる可能性のある関数は、この変数なしにコンパイルさ れた関数からは安全に呼び出すことは出来ません。なぜなら、復帰時に呼び出し側がそこに あると期待している値を変化させ得るからです。従って、大域レジスタ変数を使うプログラ ムの部分の入り口になる関数では、呼び出し側に属する値を明確に保存復帰しなければいけ ません。 殆どのマシンで、`longjmp' はそれぞれの大域レジスタ変数を`setjmp' の時点での値に 戻します。しかし、大域レジスタ変数の値を変えることのないマシンもあります。可搬性を 持たせるために、`setjmp' を呼ぶ関数には手を加えて大域レジスタ変数を保存し、それらを `longjmp' の時に復帰させる必要があります。こうすれば`longjmp' の行うことに関わらず 同じ動作が起こるでしょう。 全ての大域レジスタ変数の宣言は全ての関数定義の前になければなりません。もしその ような宣言が関数定義の後に現れると、その宣言は遅過ぎて、前に定義された関数でそのレ ジスタを他の目的のために使うことを止めさせることが出来ないのです。 大域レジスタ変数は初期値を持つことは出来ません。なぜなら、実行可能ファイルはレ ジスタに初期値を与える方法を持たないからです。 13.19 代わりのキーワード オプション`-traditional' は幾つかのキーワードを無効にし、また`-ansi' は他のキーワー ドを無効にします。過去からあるプログラムや、ANSI C プログラムを含めて全てのプログ ラムで利用可能でなければならない汎用のヘッダ・ファイルの中でANSI C の機能や、GNU C の拡張を使いたい場合には、これは問題になります。`-ansi' のもとでコンパイルされるプ ログラムでキーワード`asm', `typeof' と`inline' は使えません、一方、`-traditional' のもと でコンパイルされるプログラムでは`const', `volatile', `signed', `typeof' と`inline' は使えま せん。 この問題を解決するには問題になり得るキーワードの前に`__' を付けます。例えば`asm' の代わりに`__asm' を、`const' の代わりに`__const' を、`inline' の代わりに`__inline' を使 います。 他のC コンパイラはこれらの代わりのキーワードを受け付けないでしょう。もし他のコ ンパイラでコンパイルしたいならば、代わりのキーワードをマクロで定義してそれらを普通 に使われているキーワードに置き換えることが出来ます。たとえば次のようにします。 #ifndef __GNUC__ #define __asm asm #endif ここまでがOS9/UG が配布されたドキュメントの挿入部分です。以下からは筆者が追加 した部分となります。 13.20 関数内部での指定レジスタ変数 GNU CC では関数の自動変数に使用されるレジスタを指定することができます。また ブロック内部での一時変数もレジスタを指定することができます。次の卑近な例をみてくだ さい。 int iocs21(char parm) { register int res asm("d0"); register char p asm("a1"); res = 0x21; p = parm; asm ("trap\t#15":"=g"(res):"g"(res),"g"(p)); return res; } これは以下のようにコンパイルされます。 .text .even .globl _iocs21 _iocs21: link a6,#0 moveq.l #33,d0 move.l 8(a6),a1 APP ON (APP) trap #15 APP OFF (NO_APP) unlk a6 rts これで正しくiocs の$21 が発行されます。 13.21 オペランド制約文字について asm 文の説明で出てきた「オペランド制約文字」について説明しておきます。これはター ゲットCPU に依存するものと依存しないものがありますが、話を68000 に限定して説明し ます。 asm 文に記述された命令はgcc は一切関知しません。従ってその命令のオペランドがその 命令で許されないオペランドであっても平気で通してしまいます。以下の例をみてください。 int foo1(int parm,int ret) { asm("lsr.l %2,%0":"=g"(ret):"0"(ret),"g"(parm)); return ret; } int foo2(int parm,int ret) { asm("lsr.l %2,%0":"=d"(ret):"0"(ret),"d"(parm)); return ret; } NO_APP .text .even .globl _foo1 _foo1: link a6,#0 move.l 12(a6),a0 APP ON (APP) lsr.l (a0),(a0) APP OFF (NO_APP) move.l (a0),d0 unlk a6 rts .even .globl _foo2 _foo2: link a6,#0 move.l 12(a6),a0 move.l (a0),d1 move.l 8(a6),d2 APP ON (APP) lsr.l d2,d1 APP OFF (NO_APP) move.l d1,(a0) move.l d1,d0 unlk a6 rts foo1 では明らかに誤ったアドレッシングモードです。foo2 では正しくレジスタにコピーして 副作用までちゃんと評価しています。この差を作っているのが"g"と"d"です。以下に68000 でのオペランド制約文字について説明しておきます。(asm 文で使えそうな物のみにとどめ ます) 1. 'd' データレジスタd0-d7 です。変数は何等かの方法でデータレジスタに必ずコピーされ ます。 2. 'a' アドレスレジスタa0-a5 です。 3. 'r' 汎用レジスタ全てです(X68000 では関係ありませんがfpa のレジスタは含まれません。) 4. 'm' メモリ参照です。例えば10(a6),(a0) 等です。 5. 'i' アセンブラではイミーディエイトオペランドにあたります。 6. 'n' 数値です。0,10,25 等。 7. 's' 'i','n' を併せたオペランドです。 8. 'g' 'm','r' とここでは挙げませんでしたが'o' (オフセットオペランド)が含まれる汎用オ ペランドです 以下に具体的な例を示しておきますので参考にしてください。ただし最適化をかけると 簡単には例示できないので、最適化しないでおきます。 int val; foo() { register int reg1; register int reg2; int mem; asm("ope %1,%0": "=m"(mem) : "g"(val)); asm("ope %1,%0": "=r"(reg1) : "d"(val)); asm("ope %1,%0": "=d"(reg1) : "a"(val)); asm("ope %1,%0": "=d"(reg1) : "i"(&val)); asm("ope %a1,%0": "=a"(reg2) : "i"(&val)); asm("ope %d1,%0": "=a"(reg2) : "i"(&val)); } _foo: link a6,#-4 APP ON (APP) ope _val,-4(a6) APP OFF (NO_APP) move.l _val,d1 APP ON (APP) ope d1,d0 APP OFF (NO_APP) move.l d1,a1 APP ON (APP) ope a1,d0 ope #_val,d0 ope _val,a0 ope #_val,a0 APP OFF (NO_APP) ?1: unlk a6 rts .bss .xdef _val .comm _val,4 .data .text GCC ではasm 文でオペランドの種類までユーザーが自由に指定できてしまいます。ただ、 この「オペランド制約文字」を理解するのはこの説明だけでは不足なので、詳しく知りたい 方は英文のオリジナルマニュアルを参照してください。 さらに、注意しなければならないのはレジスタが暗黙に変化するタイプのアドレッシン グモードを使用する場合には十分注意してコーディングしてください。特に変数をスタック に積んで呼ぶDOS コールをasm 文で記述する場合はgcc はスタックの位置が変化している ことを知る手段がありません。 14 GNU CC の非互換性 GNU CC のオリジナルマニュアルではUNIX のPCC との比較で記述してありますが、 X68 000 のXC との比較に書き改めて大まかに意訳しておきます。 以下の非互換性はほとんど「伝統的」なC コンパイラとANSI 規格との相違からくるも ので、我々は-traditional オプションでGNU CC に他のC コンパイラと同様に振る舞うよ うに指示することで、非互換性の殆どをを回避できるように努力しました。 ○ 文字列定数はGNU CC では通常[read-only] です。いくつかの「同一文字列」はまと められて1つのコピーにされます。この結果、mktmp は文字列を引き数に使用するこ とができなくなります。なぜならmktmp はその引き数のポインタが指している文字列 をいつも変更するからです。別の結果としてあるシステムではsscanf やfscanf などの フォーマット制御文字列を使う関数が動かなくなるでしょう。それらの関数が文字列 を変更する場合があるからです。 この解決策は文字列の代わりにchar-array 変数を文字列で初期化して使うことです。 もしそれが不可能なら「殆どのC」が文字列を扱う扱い方をGNU CC に指定するフラ グ-fwritable-strings を使ってください。(XC のライブラリでは問題ないと思います) ○ GNU CC は文字列リテラルの中に現れるマクロの引き数を展開しません。 例えば #define foo(a) "a" はマクロの引き数'a' が何であっても"a"に展開します。-traditional フラグはそのよう なマクロを(non-ANSI) な形態のコンパイラと同様に扱うようにGNU CC に指示し ます。(XC のプリプロセッサは「伝統的な(non-ANSI)」プリプロセッサです) ○ setjmp とlongjump を使った場合、auto な変数で有効な値が保証されるのはvolatile 宣言された変数のみです。これはauto 変数がレジスタに割り当てられた時に起きます。 以下の例を考えてみましょう。 jmp_buf j; foo() { int a,b; a = fun1(); if (setjmp(j)) return a; a = fun2; /* longjump がfun3 で起きた */ return a + fun3; } この場合、たまたま変数'a' がレジスタであれば前の値(fun1) が、そうでなければ後の 値(fun2) が返されるでしょう。'-O' と'-W' が同時に指定されていればGNU CC は警 告をこういう場合は出すでしょう。 -traditional フラグはsetjmp を呼び出す関数はauto 変数を普通のC と同様にスタッ クフレームに割り付けるようにGNU CC に指示します。結果として「伝統的な」コン パイラと同じ結果を得るでしょう。(XC は「伝統的な」コンパイラです) ○ ブロック内部で宣言されたextern 変数、関数のスコープはそのブロック内部に制限さ れます。 伝統的なC ではブロック内部のextern 宣言は宣言以降のコンパイル単位内で有効にな ります。-traditional フラグはGNU CC に「伝統的な」コンパイラと同じようにブロッ ク内部のextern 宣言を扱うように指示します。(XC は「伝統的なコンパイラ」です) ○ typedef でlong とその他を以下のように結合できません。 typedef int foo; typedef long foo bar; ANSI-C はこれを許していません。なぜならlong は暗黙に型がint であるとされるか らです。これはC ソースのレベルでなくbison に与える文法規則の違いであるため、 対処はできません。(XC はnon-ANSI です。この宣言は有効になっています。) ○ PCC(UNIX) ではtypedef 名を関数引き数に使えますがGNU CC では使えません。こ れも上記と同様対処できません。(XC でもエラーになります) ○ '+=' はANSI-C では1 つのトークンです。'+ =' はエラーになりますが、PCC (UNIX) ではエラーになりません。これも上記と同様対処できません。(XC では' ペア' でエラー になりません) ○ GNU CC ではいつも' 偽' である#if ディレクティブに文字列が含まれるとエラーにな ります。 #if 0 I'm not strings #endif -traditional フラグはこのエラーを抑制します。 ○ float を返す関数はPCC(UNIX) ではdouble に変換されます。GNU CC ではfloat を そのまま返します。もし同じにしたいのなら意味道理にdouble を返すように宣言しな ければなりません。(XC もPCC と同じです) ○ 構造体を返す関数は多くのUNIX のコンパイラとは全く異なった方法で値を返します。 1,2,4,8byte の構造体はint と同じくレジスタで返され、その他の構造体は呼び出し側 が指定したアドレスをレジスタに渡し、関数ではそこに戻り値を格納して返します。 PCC やXC では静的な領域を確保してそこに値を返します。この方法はGNU CC で は使いません。なぜならその方法は遅いし、リエントラントでないからです。もし同 じ方法を望むならフラグ-fpcc-struct-return を指定してください。 今のところXC のライブラリには構造体を返す関数はありません。ですが、このよう な関数を含む場合、オブジェクトの混在リンクは悲惨な結果を招くでしょう。 15 アセンブラインターフェース 強力なasm 文と最適化機能をもつGNU CC ではあまりアセンブラとのリンクはやらな いでしょうが、カリカリに速度を目指す場合はやはりアセンブラに頼ることがあるでしょう。 この章ではアセンブラインターフェースについて少し記述しておきます。 ○ 引き数の渡し方 gcc では引き数は全てスタックで渡されます。一番左の引き数がスタックの先頭で(リ ターンアドレスは除きます)順次スタックに積まれてエントリします。68000 ではrtd 命令がないので、スタックは呼び出し側で捨てますが、68010 以上のCPU ではrtd 命 令で呼び出され側でスタックを捨てることがあります。(これは当面関係なさそうです) ○ 破壊可能レジスタ 'd0-d2' と'a0-a2' は破壊してかまいません。その他のレジスタを使う場合はスタック に退避するなりして保存してください。 ○ 戻り値がある場合 - int,float の場合d0 に格納して返します - long long int,double の場合d1(Hi),d0(Lo) で返します - 構造体でサイズが1,2,4,8 それぞれd0,d1 に返します - 上記以外の構造体の場合は、戻し値のリターンアドレスがa1 に格納されていま すからそこに格納します。 16 gcc での効率の良い記述方法 gcc は大変効率の良いコードを生成しますが、ソースの記述の仕方を工夫することで、更 に高速なコードを生成します。本来はこの手の姑息なテクニックは使わないのがいいのかも 知れませんが、見通しの悪い記述にならない限りは積極的に利用するのがいいと思います。 (X68000 はマシンパワーが不足です) ○ 一時変数は明確に記述しておく ブロック内部(f とg で囲まれた部分) で使用する一時変数は明確にブロック内部で初 期化して使用したほうが、効率の良いコードを生成します。特に関数へのパラメータ で初期化して使用する変数を冒頭で初期化して、関数の終わりの方のブロックで使用 するのは最悪です。その変数の寿命が必要以上に長く評価されるので、効率が悪化し ます。関数の頭で全ての変数を宣言するのはC では効率的ではありません。 ○ 変更しない文字列は配列にしない 特定の文字列をデータとして保持する場合それを書き換えないのならばchar の配列 ではなくポインタとして宣言したほうが圧倒的に効率がいいコードになります。ただ し、mktemp 等の文字列を書き換える関数に渡す文字列は必ず配列にしなければなり ません。 ○ アドレス演算子を適用する変数はループ変数として用いない scanf 等を呼び出す為にアドレス演算子を適用した変数はループ変数にすると大損を します。それらをループ回数として利用する場合は別の一時変数にコピーしてそれを ループ変数としたほうが圧倒的に速いコードを生成します。アドレスを取られた変数 はvolatile になったと考えてコーディングすることをお勧めします。 ○ 自動変数の文字配列を初期化するときは数を指定する 文字の数はコンパイラが計算しますが、実は予め大きさを明示したほうが速いコード を生成します。 ○ malloc された領域を関数内部だけしか使わないならalloca を使う 勿論、alloca は__builtin_alloca を使用します。スコープが関数内部だけのmalloc は損 です。64k バイトのスタックは殆どの場合半分は遊んでいます。 ○ 副作用のない関数はconst にする これはポータビリティがないので、マクロ__GNUC__をテストするコーディングをすべ きです。なお、この機能はオリジナルgcc ではバグっているので、マクロ__mariko_cc__ も同時にテストしてください。 ○ 戻ってこない関数はvolatile にする これもポータビリティがないので、マクロ__GNUC__をテストするコーディングをすべ きです。 17 本GCC のソース配布について ソースに関する問い合わせは以下でのみお願いします。 Nifty PED00647 吉野智興 18 謝辞 本コンパイラは以下のソフトウェアを利用して制作されました。作者及び移植者の方々 に感謝いたします。 ○ X68000 XC コンパイラ ライブラリVer 1.01 (SHARP/Hudson) ○ EMACS (icam Version) ○ GNU make (homy Version) ○ HLK.X Ver 2.01 (copyright SALT) ○ Gnu Gcc Ver 1.38 (Thanks for 健ちゃん) ○ 1.39 Patch data (Thanks for 健ちゃん) ○ Patch.x (icam Version) ○ TeX system X68000(TSG Version) ○ ASCII 日本語pTeX system ○ TeX Preveiwer,Printer Driver (copyright Ext) ○ Bison (Ver 1.11) ○ GNU grep (Mariko Version) ○ Cshwild lib (Written by Mad player) ○ Nemacs (Test Version) icam また、定数乗算展開ルーチンはExt さんの作成された関数を参考に作成しています。 最後に、本マニュアル、コンパイラ作成の退屈な時間と作業を可憐な歌声と容姿で励ま して(!?) くれたMiss MAGAZINE(Not C magazine!!) 吉田真里子ちゃん を応援することを誓って終わりにさせていただきます。 USE BGM List innocent (Mariko Yoshida) ○ 君の夢が聞こえる ○ 夏の恋人達 ○ 夢を追いかけて ○ 冬すみれ ○ 翼になりたい ○ 嘆きの天使 ○ 瞳をふせないで ○ 潮騒 ○ 未来(あした) I love Mariko... 19 参考文献 ○ Using and Porting GNU CC (Richard M. Stallman) ○ プログラム言語C(第2 版) ○ C magazine 1989 10 - 1991 1 ○ 68000 プログラマーズマニュアル Special Thanks Mr. Ohtuki Mr. Ext Mr. 187 Mr. Yaasan Mr. YAMA Mr. KURI Mr. NASH Mr. Syaruru (FC) Mr. Kikuti (C magazine)