5. レジスタ-メモリ転送命令(複数レジスタ)
スタックやブロック転送に使用する命令です。複雑そうに思えますが、覚える事は このページの最後の2行です。
LDM / STM 命令
LDM/STM命令は、すべてのレジスタから任意に組み合わせた複数のレジスタ (レジスタリスト) の内容を、 一度にメモリとの間で転送する場合に使用します。 LDM命令で複数ワードをメモリからレジスタに読み込み、STM命令で複数のレジスタの内容をメモリに書き込む ことで大量のデータ転送に使用します。 またスタックデータ構造(Last-In First-Out 型のバッファ)を実現するために、ほとんどのCPUが持つ スタックポインタを任意のレジスタで実現することができます。レジスタの内容をメモリに保存(push)し、 メモリの内容をレジスタに復帰(pop)することで、サブルーチンコールの際の戻り先アドレスや引数を スタックに格納したり、スタックをローカル変数領域とする場合に使用します。
オペコード | オペランド | 動作 | 意味 |
---|---|---|---|
LDM<cd><adm> | Rm<!>, {reg list} | <reg list>=[Rn+=4] | 複数レジスタからロード |
STM<cd><adm> | Rm<!>, {reg list} | [Rm+=4]=<reg list> | 複数レジスタのストア |
STM<cd><adm> | Rm, {reg list}^ | 特権モード用 | 複数レジスタのストア |
LDM<cd><adm> | Rm, {reg list}^ | 特権モード用 | 複数レジスタからロード |
LDM<cd><adm> | Rm<!>, {reg list}^ | 特権モード用 | 複数レジスタからロード |
STM<cd><adm> | Rm<!>, {reg list}^ | 特権モード用 | 複数レジスタのストア |
レジスタリストが「 {reg list}^」となっている命令はユーザモードでは使用しません。
<adm>はアドレッシングモードとして4種類のうちの1つを指定します。 オペランドにあるレジスタRmの示すメモリとの間で複数レジスタの内容を転送しますが、 転送前 (Before) にレジスタRmの示すアドレスを更新するか、転送後 (After) に更新するかを 指定します。 また、更新の方向がレジスタの値を増加 (Increment) させるか、減少 (Decrement) させるかを指定します。レジスタRmに「!」を指定した場合は更新後のアドレスをRmに書き戻します。
モード | 意味 | 動作 |
---|---|---|
DA | Decrement After | メモリアクセス後にポインタを減らす |
IA | Increment After | メモリアクセス後にポインタを増やす |
DB | Decrement Before | メモリアクセス前にポインタを減らす |
IB | Increment Before | メモリアクセス前にポインタを増やす |
レジスタRmをスタックポインタとして使用する場合には、スタックの種類で アドレッシングモードを指定するほうがわかり易いため、次のような別名も用意されています。
モード | 意味 | LDM (pop) | STM (push) | CPUのスタックの例 |
---|---|---|---|---|
FA | Full Ascending | DA | IB |   |
FD | Full Decending | IA | DB | i386,6809,Z80 |
EA | Empty Ascending | DB | IA |   |
ED | Empty Decending | IB | DA | 6800 |
それぞれのモードにおけるスタックの動作を次に示します。最初の Full Descending が 最も重要なモードです。
Full Descending
レジスタをメモリに保存する前にメモリアドレスを保持しているレジスタ(通常は r13, sp)の値を 1ワード分(4バイト)減じた後に保存するレジスタの内容をメモリに書き込みます。スタックポインタ が常に保存済みの値を指しているのでFULL、保存時にメモリアドレスの減る方向にポインタが進むので DECENDING (下降) と呼びます。下の図では上がメモリアドレスの小さい方向となっているので、 上に向かって「下降」することに注意して下さい。 このモードをサブルーチン呼び出し、レジスタの退避などのスタックとして使います。
アドレス小 ┃ ↑ ┃ ┣━━━━━━━┫ ┃ 空 ┃ ┣━━━━━━━┫ ┃ C ┃←SP Full Descending ┣━━━━━━━┫ Push : Decrement Before [stmfd] ┃ B ┃ Pop : Increment After [ldmfd] ┣━━━━━━━┫ ┃ A ┃ ┣━━━━━━━┫ ┃ ↑ ┃←sp
sp は最初にスタックポインタの示していた位置、SPは3ワード保存後にスタックポインタが示す位置 を表しています。
Empty Descending
レジスタの内容をメモリに書き込んだ後、メモリアドレスを保持しているレジスタ(通常は r13, sp)の値を 1ワード分(4バイト)減じます。スタックポインタが常に次に保存する空のメモリ領域 (EMPTY) を指します。 保存時にメモリアドレスの減る方向にポインタが進むのでDECENDINGとなります。
アドレス小 ┃ ↑ ┃ ┣━━━━━━━┫ ┃ 空 ┃←SP Empty Descending ┣━━━━━━━┫ Push : Decrement After [stmed] ┃ C ┃ Pop : Increment Before [ldmed] ┣━━━━━━━┫ ┃ B ┃ ┣━━━━━━━┫ ┃ A ┃←sp ┣━━━━━━━┫ ┃ ↑ ┃
Full Ascending
レジスタをメモリに保存する前にメモリアドレスを保持しているレジスタ(通常は r13, sp)の値を 1ワード分(4バイト)減じた後に保存するレジスタの内容をメモリに書き込みます。スタックポインタ が常に保存済みの値を指しているのでFULL、保存時にメモリアドレスが増える方向にポインタが進むので ASCENDING (上昇) と呼びます。下の図では上がメモリアドレスの小さい方向となっているので、 下に向かって「上昇」となることに注意して下さい。
アドレス小 ┃ ↓ ┃←sp ┣━━━━━━━┫ ┃ A ┃ ┣━━━━━━━┫ ┃ B ┃ ┣━━━━━━━┫ ┃ C ┃←SP Full Ascending ┣━━━━━━━┫ Push : Increment Before [stmfa] ┃ 空 ┃ Pop : Decrement After [ldmfa] ┣━━━━━━━┫ ┃ ↓ ┃
Empty Ascending
レジスタの内容をメモリに書き込んだ後、メモリアドレスを保持しているレジスタ(通常は r13, sp)の値を 1ワード分(4バイト)減じます。スタックポインタが常に次に保存する空のメモリ領域 (EMPTY) を指します。 保存時にメモリアドレスが増える方向にポインタが進むのでASCENDINGとなります。
アドレス小 ┃ ↓ ┃ ┣━━━━━━━┫ ┃ A ┃←sp ┣━━━━━━━┫ ┃ B ┃ ┣━━━━━━━┫ ┃ C ┃ ┣━━━━━━━┫ ┃ 空 ┃←SP Empty Ascending ┣━━━━━━━┫ Push : Increment After [stmea] ┃ ↓ ┃ Pop : Decrement Before [ldmea]
LDM/STM は重要な命令でよく使います。複雑な動きをする命令ですが、 次の形式の2つの命令を覚えておけば十分です。
stmfd sp!, { レジスタリスト } @ 保存 ldmfd sp!, { レジスタリスト } @ 復帰
レジスタリストはレジスタを並べて書いたもので、{r0, r3, r6, lr}、{r0-r10, pc} のようにレジスタ番号の若い方から順に並べたものです。i386の push/pop と pusha/popa の ように「1つ、または全部」よりも柔軟にレジスタをスタックに退避することができます。 レジスタリストの各レジスタはレジスタ番号の小さいほうから順番で転送されます。 レジスタリストのうち最も番号の小さいレジスタは最も低位のメモリアドレスに転送されます。
典型的な使用法はサブルーチンの入り口でリンクレジスタ(r14, lr) と破壊したくない レジスタを保存 (push) し、サブルーチンの出口では保存したレジスタを戻し (pop)、 保存したリンクレジスタをプログラムカウンタ (r15, pc) に戻すことで呼び出し元に 戻ります。
sub: stmfd sp!, {r3, v1-v5, lr} @ 入り口で保存 : 何か処理 ldmfd sp!, {r3, v1-v5, pc} @ lr の代わりに pc に戻してリターン