前回までのビットフィールド転送命令群と比べて今回のレジスタ抽出命令EXTRの動作は分かり易いです。エイリアスもシンプル、たった1個。ROR(右ローテイト)命令だけです。ただ、左にシフトして取り出すような操作のEXTRに対して、詰まっているデータから見ると右ローテイトに見えると。左右が時々混乱するのは私だけ?
※「ぐだぐだ低レベルプログラミング」投稿順indexはこちら
※以下は下記にて動作確認しています。
-
- Raspberry Pi 4 model B、Cortex-A72コア ARMv8-A
- Raspberry Pi OS (64bit) bullseye
- gcc (Debian 10.2.1-6) 10.2.1 20210110
EXTR命令
EXTR命令は2つのレジスタを接続して、そこから1レジスタ分のデータを抽出する命令です。何に使うのと思われる方もいらっしゃるかもしれませんが、結構重宝する命令です。個人的にこの手の命令、私は好んで使っております。
接続する2つのレジスタをソース1(n)とソース2(m)として、デスティネーション(d)に取り出します。取り出し位置は取り出すデータのlsb位置を即値で指定します。
上記では、ソースに異なる2つのレジスタを取っていますが、同一のレジスタをソース1とソース2に指定すると、EXTRのエイリアスである ROR (右ローテイト)命令となります。先ほども書きましたがデータからみると右です。左ローテイト命令が無いですが、シフト量を調整すれば右ローテイトは左ローテイトにもなります。右に行き過ぎれば左になると。
実験用アセンブリ言語ソース
例によって1命令=1被テスト関数のスタイルですが、エイリアスであるROR命令については、ROR命令と同一の機械語に落ちるEXTR命令を先に置いて比べられるようにしてあります。
.globl extrW, extrX, rorW, rorX .text .balign 4 extrW: extr w0, w1, w2, #4 ret extrX: extr x0, x1, x2, #4 ret rorW: extr w0, w1, w1, #4 ror w0, w1, #4 ret rorX: extr x0, x1, x1, #4 ror x0, x1, #4 ret
上記でEXTR命令とROR命令が、同じ機械語に落ちている様子をディスアセンブルリストで見たものが以下に。
アセンブル関数を呼び出してテストするCのコード
「とりあえず各関数1例だけ」触ってみるコードが以下に。
#include <stdio.h> #include <stdint.h> extern uint32_t extrW(uint32_t, uint32_t, uint32_t); extern uint64_t extrX(uint64_t, uint64_t, uint64_t); extern uint32_t rorW(uint32_t, uint32_t); extern uint64_t rorX(uint64_t, uint64_t); int main(void) { uint32_t result; uint64_t resultX; result = extrW(0, 0x12345678, 0x9ABCDEF0); printf ("extrW: %08x\n", result); resultX = extrX(0, 0xA5A5A5A5A5A5A5A5, 0x0123456789ABCDEF); printf ("extrX %lx\n", resultX); result = rorW(0, 0x87654321); printf ("rorW: %08x\n", result); resultX = rorX(0, 0x87654321A5A5A5A5); printf ("rorX: %lx\n", resultX); return 0; }
ビルドして実行
ビルドは以下のコマンドラインで。
gcc -g -O0 -o extr extr.c extr.s
実行結果は以下のようです。
意図通りに抽出、あるいは右ローテイトされとります。左右こんがらがらなくてよかった。