AssemblerKernelのソースを読んでいると、インラインアセンブラの記述が良く出てくる。あの記述は、まるで呪文のようだ!素のアセンブラならある程度は読めるが、インラインアセンブラが出てくると思考が止まってしまう。言う状態なので調べてみた。基本的なインラインアセンブリの構文は次のようになります。code");また次のようにも書くこともできます。code");"asm"と"__asm__"は、基本的には同じであるが、"__asm__"は使用している言語の予約語/関数等とぶつかる場合使用します。C互換のコードをつくっている場合、"asm"キーワードが使用できなくなるので"__asm__"を使います。code");}インライン構文は複数に渡って書くことができます。複数の命令を書く場合は、一行毎にダブルクオーテーションで括り、命令の後に"\n\t"を書いておけば良いです。#NO_APP*/"\n\t"の代りに";"を使うことも出来ます。%al;#NO_APP*/また、一行にまとめて書いても問題ありません。%al;");レジスタの破壊インライン構文の中ではレジスタを自由に書き換える事が出来ます。変更したレジスタの値をを復元しないで戻ってくると、まずい事が起る可能性があります。レジスタ変数である変数をレジスタに割り当てていた場合、そのレジスタをインライン側で破壊してしまうと、変数の値が変ってしまいます。次のコードを書いたとしましょう。printf("%d\n",a);}これは次のようなアセンブラコードになります(前後のコードは省略しています)。*/#NO_APPこの場合、変数aが破壊されるので、a+=bの結果が515となってしまいます。こんな時、自動的に破壊を避けるようなコードをコンパイラが出力してくれれば便利です。次に説明する拡張アセンブリ構文にその機能があるのです。基本的なインライン構文では命令列を書くことしか出来なかったが、拡張アセンブリ構文ではオペランドの指定をする事が出来ます。具体的には、入力レジスタ、出力レジスタ、破壊されるレジスタのリストを指定することが出来ます。アセンブリテンプレートは、アセンブラ命令が書かれますが、拡張アセンブリ構文の場合レジスタなどの書き方が、基本的なアセンブラ命令の書き方とは微妙に異なります。各オペランドは、オペランド制約文字と括弧で括られた、C言語の式を記述できます。言語プログラムに挿入されるアセンブリ命令が書かれます。各命令は、ダブルクォートで囲まれているか、命令全体が一対のダブルクォートで囲まれている必要があります。各命令の最後は":"または"\n\t"で終わっている必要があります。テンプレートでのレジスタの表記アセンブリテンプレートの中で、eaxやebxと言ったレジスタ名を使用したい場合は、%%eaxや%%ebxというように、%%を付けて使用します。%2,...と表記されます。);この番号は、左から指定した順に採番されるので、出力オペランドで2つ変数を指定すると、以降の番号は変る事になります。);オペランドC言語の変数は、拡張アセンブリ構文中だとオペランドとして機能します。各オペランドの記述は、ダブルクォートで囲んだオペランド制約とオペランドそのものを表すC言語の式が書かれます。(C言語の変数)出力オペランドの場合、修飾子がさらに付加されます。(C言語の変数)破壊レジスタの指定インラインアセンブリ命令を実行したときに、レジスタの値を破壊してしまうものがある場合、破壊されるレジスタの一覧を、3つめの":"の後に列挙する必要があります。このリストはgccに、これらのレジスタを使用し変更を行うことを知らせます。gccは、これらのレジスタの破壊を回避するようなコードを生成します。破壊されるレジスタを"%eax"などの様に指定します。cc命令によって条件レジスタ(x86の場合ステータスレジスタか?)が変更されるならば、変更されるレジスタのリストに"cc"を追加する必要があります。bitが破壊されるので、"cc"を指定しています。"memory"を記述します。volatile"を追加する必要があります。1つにまとめられたりすることを防ぐことができます。その命令を除去したりループの外へ移動したりしません。volatileキーワードを指定するべきでしょう。大きく移動されることもありません。入力オペランドと出力オペランドに記述するオペランド制約。これが理解できないと、インラインアセンブルは謎の文字列としか見えないと思います。理解できれば、「なるほど」と思えるでしょう(たぶん...)。詳しくはgccマニュアルにあるオペランド制約の項に書いてありますが、良く使われるものを簡単にまとめておきます。単純な種類の制約は全部が英文字からなる文字列です。その一つ一つの文字が、許されるオペランドの一つの種類を記述します。レジスタの制約レジスタの制約は下記の例のように、入力および出力にどのレジスタを使用するか指定します。);レジスタの制約は、機種依存CPUが変ると制約文字も変ります。ここではx86系を対象に記述します。"a"eaxレジスタ"b"ebxレジスタ"c"ecxレジスタ"d"edxレジスタ"S"esiレジスタ"D"ediレジスタ"r"eax,ebx,ecx,edx,esi,ediの中から、使用レジスタが自動割り当てされる。"q"eax,ebx,ecx,edx,の中から、使用レジスタが自動割り当てされる。ビットを使うよう指定するのに使われた制約である。"g"メモリ・即値オペランドを許容する、任意の汎用レジスタ。汎用レジスタ以外は許さない。"f"浮動小数点レジスタの中から、自動割り当てされる。"t"第一(スタックの一番上の)浮動小数点レジスタ"u"第二浮動小数点レジスタマッチング制約あるC言語の変数が、入力と出力として使われる事もある場合は、マッチング制約を指定する事が出来ます。);"0"は0番目の出力オペランド制約であることを示しています。"=b"(in2)が"1"となります。:"=a"(in_out),"=b"(in2)この方法以外にも、制約修飾子(+)を使えば、変数を入出力に指定することが出来るようです。);メモリ制約sgdt命令やsidt命令などのように、レジスタでなくメモリ上に値をストアする命令の場合、メモリ制約を使用します。レジスタ制約が、レジスタにまず値を入れてから、メモリ上にストアするのに対して、メモリ制約は、直接メモリ上にストアされます。*/"m"以外にも、下記のメモリ制約も存在します。"o"メモリオペランドのうち、アドレスがオフセット可能なもの、すなわち小さなオフセットを足しても有効なアドレスが得られるようなアドレス。アドレスが定数であれば、それはオフセット指定可能である。一個のレジスタと一個の定数の和もオフセット指定可能である。"V"オフセット指定可能でないメモリオペランドを指定する。"<"自動デクリメントのアドレスのメモリオペランドを指定する。プリデクリメントでもポストデクリメントのどちらでも良い。">"自動インクリメントのアドレスのメモリオペランドを指定する。プリインクリメントでもポストインクリメントのどちらでも良い。間接/インデックス修飾アドレスがオフセット指定可能かどうかは、その機種がサポートする他のアドレッシングモードに依存します。"i"整数の即値オペランド(定数値のもの)が許される。これには、値がアセンブル時にならないとわからないシンボリックな定数も含まれる。"n"既知の数値を持つ整数即値のオペランドが許される。多くのシステムでは、語長よりも小さいオペランドにはアセンブル時の定数は使えない。