アセンブラー

UNIX第6版のカーネルのうちアセンブラーで記述されている部分はごくわずかだが、 カーネルのもっとも精妙な点を理解するのに避けて通ることはできない。 ここではPDP-11用アセンブラーの文法の特異な点をさぐる。

奇妙な演算子

このアセンブラーはずいぶん風変わりである。 それはlow.sの先頭、0507行目にはすでに発揮される。
. = 0^.
ここで「.」とは現在のアドレスのことである。 「^」は左辺の値、右辺の型をもつ結果を返す。 したがって「0^.」は「アドレス0」なので、 これは「現在のアドレスをアドレス0に設定する」ということになる。 「=」はC言語と同じく、破壊的代入をあらわす。 なお、0は0といっても、数字の列は.で終わらないかぎり8進数なので、 これは8進数の0である。

m40.sの冒頭、0604行目には別の使い方がある。

mfpi       = 6500^tst
これはアセンブラーに組み込まれていないmfpiという命令を定義している。 オプションのメモリー管理機構に関する操作であるがここで詳細には触れない。 機械語のオペコードは006500である。

一方、tstは

tst	+(r2)
のように演算の対象をひとつ引数にとる。 オペコードは005700で、 このうち下6bitには演算の対象(オペランド)で指定したアドレッシングモードとレジスターが入る。

ここでtstの「型」とは、「演算の対象をひとつ引数にとる。 オペコードの下6bitには演算の対象で指定したアドレッシングモードとレジスターが入る」 という部分になる。 リファレンスではこの型の命令を単対象命令(single operand instruction)と呼んでいる。 命令の「型」もアセンブラーにおける「型」である。

したがって、mfpiは

mfpi	+(r2)
のように演算の対象をひとつ引数にとる。 オペコードは006500で、 このうち下6bitには演算の対象で指定したアドレッシングモードとレジスターが入る、 ということになる。 mfpiが実際にどういう働きをする命令か、 ということまではアセンブラーは関知しない。

このように、他の命令を雛形にあらたな命令を定義することができる。 他にどんな型があるのかを正確に知るには、 アセンブラのソースコードを参照する必要がある。

じゃあこれは何だ?

mfpiの定義の直下、0606行目では
wait       = 1
という定義がある。 これも命令の定義なのだが、型の指定がない。

waitは演算の対象をもたないので、 オペコードの000001が命令として付け加わるだけである。 このように定数であるデータは絶対型(absolute)という。

なんでこうなるの?

0636行目はきわめつけである。
mov     $USIZE-1\<8|6,(r1)+
この行を読み解くには、 といった知識が必要である。 これをもとにC風に表記すると
*r1++ = (USIZE-1) << 8 | 6;
となる。

ところが、これで納得するわけにはいかない。 第一、「\<」がシフト演算子であるとはどこにも説明がないからである。 マニュアルには「<<」はあるのだが、そういう記法は使われていない。 ソースコード(asそのものがアセンブラーで書かれている)を見ても、 「<<」は存在しないようである。

後の第7版のマニュアルでは「\<」がシフト演算子であるとの記述があるので、 やはりマニュアルの間違いだったようである。

そういうことするかね

さらに、アセンブラー中で数字の列は「.」で終わらないかぎり8進数なのだが、 この行には「8」が出てくる。 こちらはマニュアル中に記述があって、 「8」や「9」は8進数の「10」「11」として扱うことになっている。 たしかに便利といえば便利かもしれないが、あんまりだ。

気をとりなおして

数字の列が「.」で終われば10進数なので、0646行目の
mov     $_u+[USIZE*64.],sp
はspに_u+USIZE*64を代入することになる。 カギカッコで括ると演算が優先される。

また、!377といった記法もよくみる。 実は!は前後に引数をとる二項演算子である。 このアセンブラーには

引数が省略されている場合は0があるとみなす
という面妖な規則がある。 よって、!377は0!377ということになる。 a!bをC流に書くとa | ~bである。0 | ~bは~bに等しいので、 !bはbの論理否定ということになる。 a!bは数理論理学でいうa→bに相当するのだが、 とりあえず、そこまで深読みが必要な使われかたはしていないようだ。

なお、Lions本のm40.sにはプロシージャーごとの区切りとして

/* ---------------------------       */
というCのコメントのような行が挿入されている。 アセンブラーでは「/」以降の行末まではコメントとみなす。 よって、これもまた単なるコメントである。

アセンブラーを極める

カーネルの一部のほか、アセンブラーで書かれたものはいろいろある。 特に、アセンブラー自身がアセンブラーで書かれている。

そしてお楽しみは続く

このほか、PDP-11のアセンブラーには、項のあいだの空白を「+」とみなすなど面妖な規則がある。

蛇足

DEC謹製の純正マクロアセンブラーMACRO-11というのもあるが、 UNIXのアセンブラとはだいぶ雰囲気が異なる。 PDP-11のアセンブラに関する本はいろいろ出版されているが、 ほとんどはこのMACRO-11の解説である。

これとは別にPAL-11Rという純正アセンブラーがあって、 UNIXのアセンブラーはこちらを踏襲している。 形式は似せてあるが、プログラムとして直接の関係はないらしい。

なお、純正アセンブラーでは間接モードの指定は「@」、即値モードの指定は「#」を使う。 UNIXではそれぞれ「*」「$」である。 当時のUNIXでは行削除として「@」を、一文字削除として「#」を編集用の記号に使っていた。 こういった文字をエディタでうっかりそのまま入力しようとしても、 入力した行や前の文字が抹消されてしまう。 そういうわけで「@」や「#」を回避したのであろう。

この編集方式はMulticsにまでさかのぼる。 C言語の間接演算子が「*」であることや、 「@」や「#」が言語内に登場しないのもこのあたりに由来がある。 のかもしれない(無保証)。

UNIXには関係ない蛇足の蛇足

『\』が『/』の鏡像ではなくて『¥』と表示されている場合は、 おそらく参照している環境に問題がある (要するに、筆者の責任ではない)。 『/』の鏡像に適当におきかえて理解されたし。

UNIXには関係ない蛇足の蛇足の蛇足

『/』の鏡像といっても、鏡をどう立てるかは問題かもしれない。 水平もしくは垂直に立てれば筆者の意図する像が得られるが、 斜めに立てるへそ曲がりもいるかもしれない。

画面と平行に鏡を立てるとどうなるか、という疑問もあるが、

視点から見て画面の手前に立てても後ろに立てても像を結ばない。 よって場合分けから除外される。
というのが一応の答えであろう。
戻る