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 に戻してリターン