ぐだぐだ低レベルプログラミング(95)ARM64(AArach64)、ロードストア命令その6

Joseph Halfmoon

今回からストア命令に入ります。前回までのロード命令に比べるとお楽です。ロード命令で既にやったからということで面倒なアドレシングモードをパスしてしまったせいもあります。しかし何と言っても符合について考える必要ないのがうれしいです。ストアするときはデータ幅さえ指定すりゃよいと。

※「ぐだぐだ低レベルプログラミング」投稿順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つです。

    1. STR
    2. STRH
    3. 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;
}
ビルドして実行

それぞれターゲットにしたメモリアドレスに書き込めていることがわかるかと。strResults

随分と手抜きでやっつけたもんだな、自分。

ぐだぐだ低レベルプログラミング(94)ARM64(AArach64)、ロードストア命令その5 へ戻る

ぐだぐだ低レベルプログラミング(96)ARM64(AArach64)、ロードストア命令その7 へ進む