
今回からストア命令に入ります。前回までのロード命令に比べるとお楽です。ロード命令で既にやったからということで面倒なアドレシングモードをパスしてしまったせいもあります。しかし何と言っても符合について考える必要ないのがうれしいです。ストアするときはデータ幅さえ指定すりゃよいと。
※「ぐだぐだ低レベルプログラミング」投稿順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です。
ストア命令
今回、実習してみるストア命令のニーモニックは以下の3つです。
-
- STR
- STRH
- STRB
1のSTR命令はワード(32ビット)とダブルワード(64ビット)のストアに使うもの。ワードなのか、ダブルワードなのかは転送元のレジスタにW(32ビット)を指定するか、X(64ビット)を指定するかで識別します。2のSTRH命令はハーフワード(16ビット)のストアです。転送元はWで指定。3のSTRB命令はバイト(8ビット)のストアです。転送元はやはりWで指定。
Armの場合のオペランドの順番は転送命令などでは右から左へ「デスティネーション, ソース」と指定するのですが、ロードストア命令では「ターゲット、メモリアドレス」という順番なので、ロードはともかくストアでは左から右へという「方向」になります。コマケー話なんだが。
実験用のアセンブリ言語関数のソース
例によって手抜きな(関数プロローグもエピローグもない)1命令1関数スタイルです。第一引数に与えられた値をストアするだけの「やりっぱなし」です。結果はC言語のテスト本体で拾って確認です。
.globl strXReg, strWReg, strXImm, strWImm, strHImm, strBImm
.text
.balign 4
strXReg:
str x0, [x1, x2, LSL 3]
ret
strWReg:
str w0, [x1, x2, LSL 2]
ret
strXImm:
str x0, [x1]
ret
strWImm:
str w0, [x1, #20]
ret
strHImm:
strh w0, [x1, #24]
ret
strBImm:
strb w0, [x1, #32]
ret
実験に使用したC言語ソース
上記のアセンブリ言語関数を呼び出すテスト用のCソースが以下に。アセンブラ関数がやりっぱなしな分、Cソースが多少複雑?
#include <stdio.h>
#include <stdint.h>
typedef struct {
uint64_t dw0;
uint64_t dw8;
uint32_t w16;
uint32_t w20;
uint16_t h24ary[4];
uint8_t b32ary[8];
} TargetMEM;
TargetMEM tgt;
extern uint64_t strXReg(uint64_t, TargetMEM *, uint64_t);
extern uint32_t strWReg(uint32_t, TargetMEM *, uint64_t);
extern uint64_t strXImm(uint64_t, TargetMEM *);
extern uint32_t strWImm(uint32_t, TargetMEM *);
extern uint16_t strHImm(uint16_t, TargetMEM *);
extern uint8_t strBImm(uint8_t, TargetMEM *);
void initTGT() {
tgt.dw0 = 0;
tgt.dw8 = 0;
tgt.w16 = 0;
tgt.w20 = 0;
for (int i=0; i<4; i++) {
tgt.h24ary[i] = 0;
}
for (int i=0; i<8; i++) {
tgt.b32ary[i] = 0;
}
}
int main(void) {
uint32_t uresult;
uint64_t uresultX;
initTGT();
strXReg(0x12345678a5a5a5a5, &tgt, 1); //dw8
strWReg(0x22345678, &tgt, 4); //w16
strXImm(0xAAAA555576543210, &tgt); //dw0
strWImm(0x33345678, &tgt); //w20
strHImm(0x4444, &tgt); //h24ary[0]
strBImm(0x55, &tgt); //b32ary[0]
printf ("tgt.dw0: 0x%016lx\n", tgt.dw0);
printf ("tgt.dw8: 0x%016lx\n", tgt.dw8);
printf ("tgt.w16: 0x%08x\n", tgt.w16);
printf ("tgt.w20: 0x%08x\n", tgt.w20);
printf ("tgt.h24ary[0]: 0x%04x\n", tgt.h24ary[0]);
printf ("tgt.b32ary[0]: 0x%02x\n", tgt.b32ary[0]);
return 0;
}
ビルドして実行
それぞれターゲットにしたメモリアドレスに書き込めていることがわかるかと。
随分と手抜きでやっつけたもんだな、自分。
