11.入門編番外(おわりに)
アセンブラー基礎講座・入門編のおわりに番外編として実用的なヒントなどを少し。ここまでわかれば「OS/390アセンブラーハンドブック」を読んでもきっとその内容が理解できることと思います。
ハンドブックで述べていますが、アセンブラーのプログラムは「大部分が定石ともいうべきコードの書き方で、大半が構成される」と言えます。ここまでに解説したことはその定石コードを理解するために必要な前提知識でした。OS/390アセンブラーハンドブックには実際の現場で役立ついろいろなヒントが載っていますから、是非活用して下さい。
また実用品のプログラムを書くためには、MVS(MSP、VOS3)のAPIを使うことを避けて通れません。CPU命令だけの実用品プログラムはOS自身の割り込み処理ルーチンでもない限り、我々のレベルで書くことはまずありません。APIについては別のカテゴリーで新たな講座を始めようと思います。
レジスターに1を足す、1を引く
レジスターの値に固定値を加算、減算するには加減算命令の使用が思い浮かびます。しかし内容によっては加減算命令でなくても行う方法があります。
1を足す LA R1,1(,R1) GR1 = GR1 + 1 nを足す LA R1,n(,R1) GR1 = GR1 + n (nは0から4095) 1を引く BCTR R1,0 GR1 = GR1 - 1
このテクニックはS/370アセンブラー言語では古くから使われています。いずれも命令の動作を応用した「へぇー、そうなんだ」と言うものの代表です。
覚えるとアセンブラーがちょっとわかってきた気がする、小さな幸せですが、実戦では多用されています。
(LA命令は24ビットモードで最大16MB-1、31ビットモードで最大2GB-1までしか扱えないのと減算は出来ないことに注意)
他にもいくつかの「へぇー、そうなんだ」が「OS/390アセンブラーハンドブック」にも載ってます。
nを足す AHI R1,100 GR1 = GR1 + 100 nを引く AHI R1,-10 GR1 = GR1 - 10
ESA/390で追加された新しい命令です。BCTR命令では1しか引けませんが、これなら-32768から+32767の加減算が命令とは別のハーフワード数の定義なしでできます。講座ではS/370アーキテクチャーでの基本的な(伝統的な)命令しか解説してませんが、慣れたら命令リファレンスを見て便利そうな命令は積極的にトライして見て下さい。ただし富士通、日立でも動かすプログラムを作る場合は注意して下さい。特にzアーキテクチャーで追加されたような最新の命令セットはどこまで互換かは動かしてみないとわかりません。
文字列転送
これもESA/390での新しい命令で、MVSTを紹介します。C言語で言うstrcpyです。MVCやMVCLと違い転送する長さを指定しません。文字列の終端文字(NULLターミネート)で長さが認識されます。終端文字も含めて文字列が転送されます。
----+----1----+----2----+----3----+----4----+----5----+----6----+----7-- LA R0,0 SET TERMINATE CHAR = NULL LA R14,DATA2 LOAD TARGET ADDRESS LA R15,DATA1 LOAD ORIGIN ADDRESS MVST R14,R15 MOVE STRING WITH NULL TERMINATE : DATA1 DC C'ABCDEFGHIJKLMNOPQRSTUVWXYZ',X'00' DATA2 DC 256X'FF'
比較ならCLST命令があります。
ESA/390以降ではUNIXシステムサービスやLINUX自身をサポートするようになったため、LINUXの移植を意識した?と思われる命令がいろいろと追加で実装されています。それらはすべてが公開されていないようですが、一部はアーキテクチャー解説書に載っています。MVSTもCLSTも公開命令です。
わかりやすいロジックを心がけましょう
アセンブラー自体がわかりにくい、と言われてしまえばそれまでですが、わかりやすいプログラムがいちばんです。せっかくアセンブラーを使うのですからクールなロジックを書くのもいいのですが、他人が見てわかるプログラムでないといけません。多少冗長なロジックでも、メモリーをそれなりに使っても、CPUを少し余計に使っても、普通の人が見てわかるロジックにすることが実用品のプログラムでは大切です。LA命令で加算したり、BCTR命令で1を引いたりするのは、命令の動きや機能がわかっていれば簡単に解読できるので、そういうものはいいと考えます。ただしロジックを解読するのに複雑な数学の知識を必要としたり、あまりにもトリッキーなコードは考え物です。書いた人自身が生涯を掛けてメンテナンスするならともかく、ほとんどのプログラムがそうではありません。特にアセンブラー言語でのコーディングが要求されるプログラムはOSの出口ルーチンなどシステムプログラムの部類に入るものがほとんどです。そうでなくてもOSのコントロールブロックを参照したりするため、CPU命令そのものよりはOSの内部動作や構造に関する知識が求められます。命令自身で処理する部分はなるべくシンプルなコードにして保守する人の負担を減らした方がいいと考えます。いくらコメントをきちんとつけても難解なものは難解です。
このあたりはプログラマーのプライドにも関わってくるので賛否両論ありましょうが、自分自身の経験から言えば、やはり「シンプル・イズ・ザ・ベスト」です。そしてこの種の文句は自分の作ったプログラムに対してではなく、他人が作ったプログラムに対して生まれます。そもそも自分が書いたコードですら何年か経って見返した時、「これって何をやってたんだ?、何のために必要だったんだっけ?」「ひどいなぁ」と思うのですから、人が書いたコードなら尚更です。
実用ではたぶん必要としませんが、アセンブラーっぽい例題でひとつ例を挙げます。「ビットの数を数え上げる」
BINARYと言うフルワードの領域に入っている内容のうち、1であるビットの数を 数える処理。(符号は考えない、32ビット中の0/1のみを判定する) ■たいていの人が考えつく方法(結果はGR15に入る) L R1,BINARY LOAD 32BIT VALUE SLR R15,R15 CLEAR NUM OF BITS LA R14,32 SET LOOP COUNTER LOOP1 DS 0H SLR R0,R0 CLEAR WORKREG SLDL R0,1 MOVE NEXT BIT TO WORKREG AR R15,R0 ADD 1 OR 0 BCT R14,LOOP1 LOOP UNTIL FINAL BIT ■数学がわかる人が考えた方法(結果はGR2に入る) L R2,BINARY LOAD 32BIT VALUE LR R3,R2 GR3 <-- gr2 srl r3,1 gr3 = GR3 >> 1 N R3,=A(X'77777777') GR3 = GR3 & 0x77777777 LR R4,R2 GR4 <-- gr2 srl r4,2 gr4 = GR4 >> 2 N R4,=A(X'33333333') GR4 = GR4 & 0x33333333 LR R5,R2 GR5 <-- gr2 srl r5,3 gr5 = GR5 >> 3 N R5,=A(X'11111111') GR5 = GR5 & 0x11111111 SR R2,R3 GR2 = GR3 SR R2,R4 GR2 = GR4 SR R2,R5 GR2 = GR5 LR R3,R2 GR3 <-- gr2 srl r3,4 gr3 = GR3 >> 4 AR R2,R3 GR2 = GR2 + GR3 N R2,=A(X'0F0F0F0F') GR2 = GR2 & 0x0F0F0F0F LR R3,R2 GR3 <-- gr2 srl r3,16 gr3 = GR3 >> 16 AR R2,R3 GR2 = GR2 + GR3 LR R0,R2 GR2+3 = (GR2+2) + (GR2+3) N R0,=A(X'0000FF00') I SRL R0,8 I AR R2,R0 V N R2,=A(X'000000FF') GR2 = GR2 & 0x000000FF
最初の方法はビットを左へ1つずつずらして、溢れたビットを足し算しています。溢れたビットが1なら+1になり、0なら0を足しても変わりません。命令の動きを知っていれば比較的容易に理解できます。
2番目の方法は雑誌に載っていたインテルのx86アセンブラーのサンプルをS/370用に直したものです。32ビットを4ビットずつ8つのブロックに分けて、4ビット単位に1の個数を求め、その考えを32ビットに拡張するものだそうです。4ビットをabcdと4つに並べた時、この4ビットの値は「8a+4b+2c+1d」となるそうです。これから「7a+3b+1c」を引くと、その4ビット中で1になっているビットの個数が求められる、と言うものです。
このページを書いた僕自身がよくわかっていないので、サンプルのコメントもただの命令動作の説明でしか書けませんでした。ちなみにその雑誌の記事では、上記2通りが紹介されていて、アセンブラーならではのコードとして紹介されていたのが「8a+4b+2c+1d」から答えを求めるものでした。その理由は実行する命令数が少ないからです。1番目の方法はソースコードの行数は少ないですがループするので、実際に実行される命令数は131命令になります。2番目の方法はソースコードの行数は多いですがループしないので、実際に実行される命令数は25命令で済みます。CPUの使用量、実行に掛かる時間で言えば、当然2番目の方法に軍配が上がります。
しかし実際の現場仕事としてのプログラムならどうでしょうか?少なくとも私にはよくわかりませんでした。今もわかってませんし突き詰めようとも思ってません。サンプル通りに書いたから動きますが、考え方だけ言われてゼロからコードを書けとなったら、きっとお手上げです。このトピックのネタにしたのも、アセンブラー講座の題材を探すためにめくってた昔の雑誌に載っていたのを見て、「これって(アセンブラーらしいプログラムだよと)言いたいことはわかるけど、こんなのみんなパッと見てわかんのかぁ」と思ったからです。
数学がわかっている人には常識かも知れないし、簡単じゃんこんなの、となるかも知れませんが、実際の現場には数学を知ってる人ばかりとは思えません。たった25命令で処理できるクールなロジックなのは認めますが、普通の人にこんなのわかるかぁ?って思います。CPU使用量は約5倍ですけど、たかが131命令。実際のところ大した違いではありません。数値計算や科学技術計算のプログラムなら別ですが、たいていのシステムプログラムではここまでこだわる必要性はないと考えます。それよりは後で別の人が、普通の人が見てもわかるプログラム、として書く方がいいと思います。アセンブラーらしいとか、CPUの機能を知り尽くしたようなコードもありですが、ソートやデータベースの検索エンジンなど、性能命のソフトはともかく、実用で作るプログラムはわかりやすくないと困ります。そうでなくてもアセンブラーなんてわかんないから困るって言うのが現実ですから。