
前回までロードストア命令を実習してきました。まだまだロードストアあるのですが別件実習してからの方がよさそうな奴らばかりなので一たんお休み。今回から分岐命令に入りたいと思います。最初は条件分岐命令です。どのプロセッサも「似たようなもん」ですが、条件の組み合わせを全部通すのはメンドイです。
※「ぐだぐだ低レベルプログラミング」投稿順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
ARMv8もいろいろレベルがあり、Arm Cortex-A72はARMv8の中でもベーシックな(命令数の少ない)ARMv8p0です。
※A64の最新のマニュアルは以下でダウンロード可能です。毎月レビジョンヒストリが更新されているみたいっす。
Arm Architecture Reference Manual for A-profile architecture
条件付き分岐命令
条件付き分岐命令にはコードすべきことは2つしかありません。
B.cond label
condのところに分岐する条件2文字を記するのと、飛び先ラベルを記することです。条件2文字はこんな感じ。
| 条件シンボル | いい加減な説明 |
|---|---|
| EQ | 等しい |
| NE | 等しくない |
| CS | キャリーセット |
| CC | キャリークリア |
| MI | マイナス |
| PL | プラス |
| VS | オーバフローセット |
| VC | オーバーフロークリア |
| HI | ハイ(符合無) |
| LS | ハイでない(符合無) |
| GE | 大きいか等しい |
| LT | 小さい |
| GT | 大きい |
| LE | 小さいか等しい |
| AL | いつでも |
とび先ラベルはエンコード的には19ビットですが、ワード境界アライメントで、2ビット下駄を履かせるので21ビット相当の範囲(±1Mバイト)まで指定することができます。
実験に使ったアセンブリ言語ソース
いつものように手抜き(関数プロローグもエピローグもない)なものですが、条件フラグを操作しないことには何もできないので、
-
- msr NZCV, x0命令で第一引数の値をNZCVに突っ込む
- それを受けて条件分岐
- 条件成立すれば1を返す
- 不成立なら0を返す
というスタイルです。その上、例えば、EQとNEという「対」になる条件の場合、EQのみをあからさまに判定し、NEはEQでないとき、ということであからさまには書いてないっす。それでも数が多いです。ソースが以下に。
.globl bEQ_NE, bCS_CC, bMI_PL, bVS_VC, bHI_LS, bGE_LT, bGT_LE, bAL
.text
.balign 4
bEQ_NE:
msr NZCV, x0
b.EQ lblEQ
mov x0, 0
ret
lblEQ:
mov x0, 1
ret
bCS_CC:
msr NZCV, x0
b.CS lblCS
mov x0, 0
ret
lblCS:
mov x0, 1
ret
bMI_PL:
msr NZCV, x0
b.MI lblMI
mov x0, 0
ret
lblMI:
mov x0, 1
ret
bVS_VC:
msr NZCV, x0
b.VS lblVS
mov x0, 0
ret
lblVS:
mov x0, 1
ret
bHI_LS:
msr NZCV, x0
b.HI lblHI
mov x0, 0
ret
lblHI:
mov x0, 1
ret
bGE_LT:
msr NZCV, x0
b.GE lblGE
mov x0, 0
ret
lblGE:
mov x0, 1
ret
bGT_LE:
msr NZCV, x0
b.GT lblGT
mov x0, 0
ret
lblGT:
mov x0, 1
ret
bAL:
msr NZCV, x0
b.AL lblAL
# Never!
mov x0, 0
ret
lblAL:
mov x0, 1
ret
実験に使用したC言語ソース
上記のアセンブリ言語関数を呼び出すテスト用のCソースが以下に。
#include <stdio.h>
#include <stdint.h>
#define NF (0x80000000)
#define ZF (0x40000000)
#define CF (0x20000000)
#define VF (0x10000000)
extern uint32_t bEQ_NE(uint64_t);
extern uint32_t bCS_CC(uint64_t);
extern uint32_t bMI_PL(uint64_t);
extern uint32_t bVS_VC(uint64_t);
extern uint32_t bHI_LS(uint64_t);
extern uint32_t bGE_LT(uint64_t);
extern uint32_t bGT_LE(uint64_t);
extern uint32_t bAL (uint64_t);
int main(void)
{
uint32_t result;
result = bEQ_NE(ZF);
printf ("EQ: %d\n", result);
result = bEQ_NE(0);
printf ("NE: %d\n", result);
result = bCS_CC(CF);
printf ("CS: %d\n", result);
result = bCS_CC(0);
printf ("CC: %d\n", result);
result = bMI_PL(NF);
printf ("MI: %d\n", result);
result = bMI_PL(0);
printf ("PL: %d\n", result);
result = bVS_VC(VF);
printf ("VS: %d\n", result);
result = bVS_VC(0);
printf ("VC: %d\n", result);
result = bHI_LS(CF);
printf ("HI: %d\n", result);
result = bHI_LS(CF | ZF);
printf ("LS: %d\n", result);
result = bGE_LT(NF | VF);
printf ("GE: %d\n", result);
result = bGE_LT(0);
printf ("GE: %d\n", result);
result = bGE_LT(NF);
printf ("LT: %d\n", result);
result = bGE_LT(VF);
printf ("LT: %d\n", result);
result = bGT_LE(NF | VF);
printf ("GT: %d\n", result);
result = bGT_LE(NF | VF | ZF);
printf ("LE: %d\n", result);
result = bAL(0);
printf ("AL: %d\n", result);
return 0;
}
ビルドして実行
予め代入しておいた条件が、その通りだと判定しているので、動くよなあ。普通。

