前回まで論理演算命令の「代表」AND命令をやりました。今回からは他の論理演算ORRやXORは飛ばし、転送命令MOVへ入りたいと思います。A64の命令多すぎ、さっさとやらないと終わりませぬ。しかしMOV、されどMOVであります。例によってMOVという実体なくaliasで紡がれているのですが事情いささか込み入っております。
※「ぐだぐだ低レベルプログラミング」投稿順indexはこちら
※通例、Armの64ビット命令を実行できる実機上で小さなアセンブラ関数を書いて動作確認を行っておりますが、今回、「込み入った」MOVの説明を図にして書いている間に力尽きました。実習はまた今度っと。
MOVの実体
Arm社およびインテル社はアセンブラ・ニーモニックとして MOV を使い、モトローラ社(現NXP)は MOVE とフルスペルで書き、RISC-Vインタナショナルは素っ気なく mv と書きます。転送命令。データをあっちゃからこっちゃへ動かすというだけの命令デス。転送先に「制御レジスタ」などという衣の下の鎧が見えてしまうと結構ヤバイ命令になったりもしますが、今回扱うのは「汎用レジスタとそのお友達」の間だけの転送です。お友達=SP(スタックポインタ)です。ArmはRISCとしてはチト特殊で、デディケイテッドなSPもってるのですな。
さて、転送命令 mov、思ったとおりでほとんどが alias、つまり他の命令の特定のオペランドの組み合わせを mov として読み替えています。既にやったことになっております算術演算命令、論理演算命令の一部も mov として使われとります。
-
- 転送元とゼロレジスタとのORをとって転送先に書き込めば mov
- 転送元とゼロレジスタを加算して転送先に書き込めば mov
以下の表にまとめましたが、上記の1は汎用レジスタ間の転送に、上記の2は汎用レジスタとSPの間の転送に活躍しておるそうな。
全部 OR でも良さそうなのに、なんでSPとの間だけは Add なのか、大人の事情ってやつですかね。
即値使ったMOVは少しヤバイ
しかし、即値、immediate が含まれるとかなり複雑になります。ORR命令の第2オペランド(第1ソース)をゼロレジスタにして、第3オペランド(第2ソース)を即値にする、という形で即値を転送先に送り込んでもよいのですが(上の表の黄緑部分)、よりビット幅の広い専用命令群が用意されているためです。
RISCあるあるで、レジスタの全幅(Wレジスタであれば32ビット、Xレジスタであれば64ビット)に一気に即値を代入するような命令をエンコードしずらいためだと思います。
16ビット即値を取り扱える命令は以下の3種類あります。
-
- MOVZ
- MOVN
- MOVK
どの命令も16ビット幅の即値を命令中にコードでき、そして転送先にシフトして格納できます。シフト表記としては LSL #16 みたいな形で他の任意ビットシフト付きの命令群と同じです。しかし実際に指定できる値については制限がキツイです。
-
- シフトはLSL(左論理シフト)のみ指定可能
- シフト量は、転送先がWの場合、0か16ビット、転送先がXの場合、0、16、32、48ビット。
つまり32ビットもしくは64ビットのレジスタ幅を16ビット毎に分割してそのどこかに値を「突っ込める」というスタイルです。
MOVZは、転送先のレジスタをゼロクリアした上で即値16ビットを16ビット幅のどこかにはめ込みます。MOVNの動作はMOVZした後の転送先の値を全ビット反転させるのと等価です。そしてMOVZとMOVNについてはシフト量0ビットの時だけ
MOV
というエイリアスで表記可能だと(つまり、MOV X0, #1234 という形で16ビット即値のMOVが書ける。)便利なようなかえって混乱するような。。。
MOVZとMOVNの説明図が以下に。
ただし、上記のMOVZとMOVNでは、16ビットの即値をちまちま組み合わせて32ビット幅にするとか、64ビット幅にするとかはできませぬ。そのためにかMOVKという命令もありました。
MOVK。転送先のレジスタの指定位置の16ビットのみ上書き。他のビットは保持するのであります。
MOVKの説明図が以下に。
MOVKの場合、やっている操作が特殊なので、MOVエイリアスはありませぬ。
即値用の専用命令とエイリアス表記のMOV、レジスタ対象の論理演算、算術演算のエイリアス表記のMOV、ということでよろしゅうございますかね。年寄りは直ぐに忘れてしまうけれど、若者ならこのくらいなんでもないか?