前回、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が無いしで非対称でないかい、という疑問があると思いますが(勝手に思っている。)
- LDR命令のロード先デスティネーションである第1オペランドには、Xレジスタ(64ビット、ダブルワード)もWレジスタ(32ビット、ワード)もとれるので、LDR命令1つあれば符合無ワードもOK(勿論ダブルワードも。)
- そもそも行先がダブルワードでダブルワードロードの場合は符合拡張する余地がないからダブルワードのS付は不要。
ということだと思います。
前回、いろいろアドレシングモードの図を描いたというのに、今回使用しているのは以下だけっす。
その理由はロードするターゲットメモリには、以下のようなTargetMEMと名付けた構造体を1個おいておいてあるためです。この中にダブルワード、ワード、ハーフワード、バイトのメンバをちりばめてあります。ベースレジスタに構造体の底のアドレスを入れておいてオフセットにメンバへの差分をコードすれば、メンバへのアクセスが可能っと。
なおすべて符合無としてあります。符合拡張する場合は、意図的に拡張できる値を書き込んでおきます。そしてCへの戻り値も符合無としてますが、元の値と返り値を比べて見て符合拡張されていることを確認してね、という段取り(そういう投げやりなのを段取りというのか?)
実験に使ったアセンブリ言語ソース
例によって手抜き(関数プロローグもエピローグもない)な、ほぼ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)は通ります。
実験に使用した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; }
ビルドして実行
以下はビルドして実行したところの画面キャプチャです。
ダブルワードのロードはよし、ワード、ハーフワード、バイトは符合なし、符合ありともOKっと。動けばよいのよ。