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

Joseph Halfmoon

前回、Armのロードストア命令のアドレシングモード複雑怪奇、などと書きながら、今回実習に使用いたしますのは「ベース+オフセット」一択です。人生複雑にしたくない人はそれが一番、違うか。ロードストアは1命令テストするのにもメモリを用意しないといけないので、準備のCのコードがメンドイのよね。文句が多いな。

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

今回実験する命令

今回実験する命令のニーモニックを列挙すると以下のように結構な数です。しかしどれも単純なメモリロード命令です。サイズと符合拡張のありなしで組み合わせが増えているだけ。

    • LDR
    • LDRSW
    • LDRH
    • LDRSH
    • LDRB
    • LDRSB

バイトとかハーフワードは、符合拡張のありなしでLDRBとLDRSBのようにニーモニックにサイズと符合有無がエンコードされているのに、ワードはSしかないし、ダブルワードはSが無いしで非対称でないかい、という疑問があると思いますが(勝手に思っている。)

  1. LDR命令のロード先デスティネーションである第1オペランドには、Xレジスタ(64ビット、ダブルワード)もWレジスタ(32ビット、ワード)もとれるので、LDR命令1つあれば符合無ワードもOK(勿論ダブルワードも。)
  2. そもそも行先がダブルワードでダブルワードロードの場合は符合拡張する余地がないからダブルワードのS付は不要。

ということだと思います。

前回、いろいろアドレシングモードの図を描いたというのに、今回使用しているのは以下だけっす。bimm12

その理由はロードするターゲットメモリには、以下のようなTargetMEMと名付けた構造体を1個おいておいてあるためです。この中にダブルワード、ワード、ハーフワード、バイトのメンバをちりばめてあります。ベースレジスタに構造体の底のアドレスを入れておいてオフセットにメンバへの差分をコードすれば、メンバへのアクセスが可能っと。

なおすべて符合無としてあります。符合拡張する場合は、意図的に拡張できる値を書き込んでおきます。そしてCへの戻り値も符合無としてますが、元の値と返り値を比べて見て符合拡張されていることを確認してね、という段取り(そういう投げやりなのを段取りというのか?)

TargetMEM

実験に使ったアセンブリ言語ソース

例によって手抜き(関数プロローグもエピローグもない)な、ほぼ1命令1関数スタイルです。

.globl	uldrXImm, uldrWImm, uldrHImm, uldrBImm, sldrWImm, sldrHImm, sldrBImm
.text
.balign	4

uldrXImm:
    ldr x0, [x1]
    ret

uldrWImm:
    ldr w0, [X1, #8]
    ret

uldrHImm:
    ldrh W0, [X1, #16]
    ret

uldrBImm:
    ldrb W0, [X1, #24]
    ret

sldrWImm:
    ldrsw x0, [X1, #12]
    ret

sldrHImm:
    ldrsh W0, [X1, #18]
    ret

sldrBImm:
    ldrsb W0, [X1, #25]
    ret

なおソースコードの編集にはVS Codeを利用しており、Armのアセンブリ言語ソースのシンタックス・ハイライトには以下のプラグインを使わせていただいております。これで編集もお楽。

Arm assembly highlighting for Visual Studio Code v1.7.4

ただね、以下みるとわかるとおり、LDRSW命令(符合拡張付きロード、ワードからダブルワードへ)のニーモニックをプラグインが認識していないようですのじゃ。コマケー話なんだが。勿論、アセンブラ(gas)は通ります。plugInError

実験に使用したC言語ソース

上記のアセンブリ言語関数を呼び出すテスト用のCソースが以下に。いつもと変わらぬ手抜きのソースです。

#include <stdio.h>
#include <stdint.h>

typedef struct {
    uint64_t dw0;
    uint32_t w8;
    uint32_t w12;
    uint16_t h16ary[4];
    uint8_t b24ary[8];
} TargetMEM;

TargetMEM tgt;

extern uint64_t uldrXImm(uint64_t, TargetMEM *);
extern uint32_t uldrWImm(uint32_t, TargetMEM *);
extern uint32_t uldrHImm(uint32_t, TargetMEM *);
extern uint32_t uldrBImm(uint32_t, TargetMEM *);
extern uint64_t sldrWImm(int64_t, TargetMEM *);
extern uint32_t sldrHImm(int32_t, TargetMEM *);
extern uint32_t sldrBImm(int32_t, TargetMEM *);

void initTGT() {
    tgt.dw0 = 0x0123456789ABCDEF;
    tgt.w8 = 0x10111213;
    tgt.w12 = 0x80818283;
    tgt.h16ary[0] = 0x2021;
    tgt.h16ary[1] = 0xB0B1;
    tgt.b24ary[0] = 0x30;
    tgt.b24ary[1] = 0xC0;
}

int main(void) {
    uint32_t uresult;
    uint64_t uresultX;

    initTGT();

    uresultX=uldrXImm(0, &tgt);
    printf ("uldrXImm: 0x%016lx\n", uresultX);
    uresult =uldrWImm(0, &tgt);
    printf ("uldrWImm: 0x%08x\n", uresult);
    uresult =uldrHImm(0, &tgt);
    printf ("uldrHImm: 0x%08x\n", uresult);
    uresult =uldrBImm(0, &tgt);
    printf ("uldrBImm: 0x%08x\n", uresult);
    uresultX=sldrWImm(0, &tgt);
    printf ("sldrWImm: 0x%016lx\n", uresultX);
    uresult =sldrHImm(0, &tgt);
    printf ("sldrHImm: 0x%08x\n", uresult);
    uresult =sldrBImm(0, &tgt);
    printf ("sldrBImm: 0x%08x\n", uresult);

    return 0;
}
ビルドして実行

以下はビルドして実行したところの画面キャプチャです。

BuildAndResult

ダブルワードのロードはよし、ワード、ハーフワード、バイトは符合なし、符合ありともOKっと。動けばよいのよ。

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