前回、メモリアドレスをロードする小技をやったので、今回のロード、ストアでそれを使うかと思っていたですが、その影もありません。単純な命令テストだな。だって時間無かったのだもん。すみません。まあ一応RV32Iが定義しているロードストア命令の全種類を網羅、といっても8個ばかりですが。
※「ぐだぐだ低レベルプログラミング」投稿順indexはこちら
まずは毎回の参照資料へのリンクの再掲載です。
RV32Iのロードストアの全命令
前回、RISC-Vのロード、ストア命令にはアドレッシングモードが一つしかない、という件は書きました。それをうまく活用して実質的にいろいろなアドレシングモードがあるかの如くに使えるという感じでした。そのためロード/ストア命令の種類はロードストアするオブジェクトの種類そのものとなります。まずはロードから。
-
- 符号付きバイトのロード
- 符号無バイトのロード
- 符号付きハーフワード(16ビット)のロード
- 符号無ハーフワード(16ビット)のロード
- ワード(32ビット)のロード
以上の5種類となります。例のごとくレジスタにロードしてしまえば、バイトもハーフワードもワード幅として扱われるので、メモリ上でのオブジェクトの種類を示す、ということであります。
ストアを見てみます。
-
- バイトのストア
- ハーフワードのストア
- ワードのストア
ストアの時は、符号拡張の有無は関係なく、レジスタの下1バイトなのか、下2バイトなのか、4バイト全部なのかという区別だけなので3命令となります。
実験用のアセンブラ関数
前回までアセンブラ関数を書き込んでいた .S ファイルがだんだん見通し悪くなってきたので、今回アセンブラソースを分けました。以下が今回テストするアセンブラ関数ファイルの全文です。
.section .text .align 2 .globl loadBYTE, loadBYTEU, loadHWORD, loadHWORDU, loadWORD, storeBYTE, storeHWORD, storeWORD loadBYTE: lb a0, 0(a0) ret loadHWORD: lh a0, 0(a0) ret loadWORD: lw a0, 0(a0) ret loadBYTEU: lbu a0, 0(a0) ret loadHWORDU: lhu a0, 0(a0) ret storeBYTE: sb a0, 0(a1) ret storeHWORD: sh a0, 0(a1) ret storeWORD: sw a0, 0(a1) ret
ぶちゃけシンプルな各1命令だけ。簡単な読み書きテストです。
ただ、上記をアセンブルすると、例によってRV32Cに解釈されて短いコードにアセンブルされるものが出てきます。以下を御覧じろ。
上記ではワード幅のロード命令と、ワード幅のストア命令が16ビット幅のRV32Cにアセンブルされていることが分かりますか?符号の有無にかかわらずバイトやハーフワードには短縮形の命令が用意されていない(オペコードスペースイの節約のために冷遇?されている)のが分かります。短縮形が使える条件は、
-
- ワード幅のロード、ストア
- 使用するレジスタがx8-x16の8本のどれかであること
上記のアセンブラではCとのインタフェースのため、a0(実はx10のABI的別名)、a1(x11のABI的別名)しか使っていなかったので、2命令が上記の条件に当てはまっていたわけです。
アセンブラ関数の呼び出し側ソース
Cからアセンブラ関数を「みえる」ようにするためのヘッダファイルが以下に。アセンブラ命令上は、バイト、ハーフワードのように命令を使い分けているのですが、各命令の動作の差が見やすいように、Cからは全てレジスタの全幅を使うuint32_tの引数または返り値に見えるようにしています。後で全ビットを標準出力に表示して動作を確認します。
#ifndef TEST_ASM_H #define TEST_ASM_H #include <stdint.h> uint32_t loadBYTE(uint32_t* adr); uint32_t loadBYTEU(uint32_t* adr); uint32_t loadHWORD(uint32_t* adr); uint32_t loadHWORDU(uint32_t* adr); uint32_t loadWORD(uint32_t* adr); void storeBYTE(uint32_t dat, uint32_t* adr); void storeHWORD(uint32_t dat, uint32_t* adr); void storeWORD(uint32_t dat, uint32_t* adr); #endif /* TEST_ASM_H */
Cでの関数呼び出し(テスト・シーケンス)部分は以下のようです。身も蓋もない感じ。
uint32_t loadTarget = 0x1234ABCD; printf("loadBYTE = 0x%08x\n", loadBYTE(&loadTarget)); printf("loadBYTEU = 0x%08x\n", loadBYTEU(&loadTarget)); printf("loadHWORD = 0x%08x\n", loadHWORD(&loadTarget)); printf("loadHWORDU = 0x%08x\n", loadHWORDU(&loadTarget)); printf("loadWORD = 0x%08x\n", loadWORD(&loadTarget)); uint32_t storeTarget = 0; storeBYTE(0xAB, &storeTarget); printf("storeBYTE = 0x%08x\n", storeTarget); storeTarget = 0; storeHWORD(0xABCD, &storeTarget); printf("storeHWORD = 0x%08x\n", storeTarget); storeTarget = 0; storeWORD(0x6789ABCD, &storeTarget); printf("storeWORD = 0x%08x\n", storeTarget);
実験結果
実機上で動作させてみた様子が以下に
loadBYTE = 0xffffffcd loadBYTEU = 0x000000cd loadHWORD = 0xffffabcd loadHWORDU = 0x0000abcd loadWORD = 0x1234abcd storeBYTE = 0x000000ab storeHWORD = 0x0000abcd storeWORD = 0x6789abcd
データ幅と符号の有無で動作が変わっています。当然か。
ロード、ストアの次は、いよいよ?サブルーチンコールとかかね?