レジスタ-メモリ転送命令

メモリへの書き込み、メモリからの読み出しはレジスタを経由してロード/ストア命令を使って行う必要があります。

ロード/ストア命令1

データ転送命令は、1バイト(8bit)、ハーフワード (16ビット) または1ワード(32bit)のデータをレジスタからメモリ (ストア)へ、 またはメモリからレジスタ (ロード) へコピーします。

メモリからレジスタへの転送(ロード) では、レジスタの上位の桁は0で埋められます。そのため、ストア命令と違ってロード命令には「lbz」「lhz」「lwz」とニーモニック中に「z」(Zero) が入っています。

1ワード(32bit)の転送命令の「lwz」では上位桁に 0 を埋める余地はありませんが、 64bit PowerPCの場合には0で埋める必要があるため、互換性を考えて「z」が付いているのでしょう。

ハーフワード(16bit)にだけ符号拡張 (16bitの最上位ビットを上位16ビットにコピー) する「lha」 (Load Half word Algebraic) 命令が用意されています。

ロード ストア 意味
0拡張 符号拡張
lbz stb Byte
lbzu stbu Byte with Update
lbzux stbux Byte with Update Indexed
lbzx stbx Byte Indexed
lhz lha sth Half
lhzu lhau sthu Half with Update
lhzux lhaux sthux Half with Update indeXed
lhzx lhax sthx Half indeXed
lwz stw Word
lwzu stwu Word Update
lwzx stwx Word indeXed
lwzux stwux with Update indeXed

アドレッシングモード

転送に使うメモリアドレスを決めることをアドレッシングと呼びます。 PowerPCでは、次の2種類の方法でメモリアドレスを指定します。


PowerPCでは、命令によってアドレッシングが1つに決まり、x86 や ARM のように 1つの命令でもオペランドの書き方で色々なアドレッシングができるCPUとは異なります。

    lwz     r3, 16(r4)      # r3 = mem[r4 + 16]
    lwzx    r3, r4, r5      # r3 = mem[r4 + r5]
    lwzu    r3, 16(r4)      # r3 = mem[r4 + 16] ; r4 = r4 + 16
    lwzux   r3, r4, r5      # r3 = mem[r4 + r5] ; r4 = r4 + r5

u (Update) のついた命令は、アドレスを指定したレジスタを実際に読み書きしたアドレス (実行アドレス) に更新します。

ロード/ストア命令2

複数バイトの転送

次のロード/ストア命令は複雑な動きをします。正確な記述は 玄箱でアセンブリ 1 のリンク先にある資料を参考にしてください。

ロード ストア ニーモニックの意味 動作
lmw stmw Multiple Word 複数レジスタとメモリ間の同時転送
lswi stswi String Word Immediate 複数バイトとレジスタ間の転送
lswx stswx String Word indeXed 複数バイトとレジスタ間の転送

後ほどスタック操作の例で使い方を見ていきます。

エンディアンの変更

レジスタとメモリ間の転送でバイト並びを逆に変更します。

ロード ストア ニーモニックの意味 動作
lwbrx stwbrx Word Byte-Reversed indeXed 4バイトを逆順にして転送
lhbrx sthbrx Half Byte-Reversed indeXed 2バイトを逆順にして転送

排他操作

排他的なメモリ操作に使う命令です。これらの2命令は組み合わせて使います。

命令 ニーモニックの意味
lwarx Load Word And Reserve indeXed
stwcx. STore Word Conditional indeXed

アトミックなメモリ操作(一連のメモリからの読み出しと書き込みが邪魔されない)を行います。複数のCPUで同じメモリ領域をアクセスしたり、別スレッドで同じメモリ領域を変更したりする可能性がある場合に使う命令です。次のように使います。

loop:   lwarx   r5, 0, r3        # メモリを読み出してそのメモリブロックを予約
        addi    r5, r5, 1        # 値を変更
        stwcx.  r5, 0, r3        # 予約変更されていなければ格納
        bne     loop             # 予約変更されていたら繰り返し

ロード/ストア命令の使い方(スタック操作)

ロード/ストア命令をスタック操作を例としてみていきます。特に ロード/ストア命令2 の命令の使い方を試してみます。


PowerPCはスタック専用のレジスタを持たず、スタック専用のpush/pop命令を持っていません。したがって、ロード/ストア命令を組み合わせてスタックを実現します。ほかのOSと同じく Linux でも r1 をスタックポインタとして使用します。 r1 は常に最後に保存した内容を指示することになっています。

push する場合は次のようにします。

    subi  r1, r1, 4   # r1 を減じる
    stw   r3, 0(r1)   # r3 を r1 が示すアドレスに保存

popする場合は逆になります。

    lwz   r3, 0(r1)   # r3 を r1 が示すアドレスから取得
    addi  r1, r1, 4   # r1 を元に戻す

stwu/lwzu を使う

複数のレジスタを push/pop する場合は r1 を更新する stwu/lwzu を使うことができます。


次のコードはスタックへの push/pop の典型的な例です。 r3からr9とリンクレジスタ(lr)をスタックに退避/復帰します。

サブルーチンを呼び出さない場合(leaf関数という末端のサブルーチン)にはリンクレジスタの退避は不要です。

        stwu    r3, -4(r1)      # push r3
        stwu    r4, -4(r1)      # push r4
        stwu    r5, -4(r1)      # push r5
        stwu    r6, -4(r1)      # push r6
        stwu    r7, -4(r1)      # push r7
        stwu    r8, -4(r1)      # push r8
        stwu    r9, -4(r1)      # push r9
        mflr    r9
        stwu    r9, -4(r1)      # push lr

        # 何らかの処理

        lwz     r9, 0(r1)       # pop lr
        mtlr    r9              # set lr
        lwzu    r9, 4(r1)       # pop r9
        lwzu    r8, 4(r1)       # pop r8
        lwzu    r7, 4(r1)       # pop r7
        lwzu    r6, 4(r1)       # pop r6
        lwzu    r5, 4(r1)       # pop r5
        lwzu    r4, 4(r1)       # pop r4
        lwzu    r3, 4(r1)       # pop r3
        addi    r1, r1, 4       # restore r1
        blr

pop する場合は、最初に lwz、最後に addi を使う必要があります。


次の図は、スタックに r3、r0、r6 の順に保存(PUSH)して、r6、r0、r3の順に復帰(POP)する場合のスタックの様子を示しています。

stmw/lmw を使う

複数レジスタを1命令で push/pop することもできます。 stmw/lmw 命令は指定したレジスタから r31 まで連続して転送します。必ずr31までが対象になり、r3からr10といった指定はできません。

push する場合は stmw を使います。 stmw はスタックポインタ (r1) の下から上(メモリアドレスの大きい方)に向かって、レジスタを順次保存するため、オフセットとして「レジスタ数 x 4 x -1」の値 (以下の例では-24)を指定します。 stmw の後に r1 を更新します。

    stmw  r26, -24(r1)    # r26-r31を保存
    subi  r1, r1, 24      # r1 = r1 - 24


pop には lmw を使います。先に stmw を使った位置に r1 を戻した後に lmw を使うことでスタックに保存したレジスタを同じレジスタに戻すことができます。スタックの下から上(メモリアドレスの大きい方)向かって取得するため、オフセットとして「レジスタ数 x 4 x -1」の値(以下の例では-24)を指定します。

    addi  r1, r1, 24      # r1 に加算
    lmw   r26, -24(r1)    # r26-r31を復帰

stswi/lswi を使う

転送バイト数を指定する命令です。最大32ビットなので連続する8レジスタを一度に push/pop できます。したがって r31 までという制限はありません。メモリアドレスを指定するレジスタ(以下の例では r1) にオフセットを加算できないため、先に r1 を更新する必要があります(stmw でも同じ方法が使えます)。

    subi  r1, r1, 28      # r1 を更新
    stswi r3, r1, 28      # r3 からr9 (28バイト) をメモリに転送

    lswi  r3, r1, 28      # r1の示すメモリから28バイトをr3 からr9 に転送
    addi  r1, r1, 28      # r1 を更新

stswx/lswx を使う

Exceptionレジスタの下位7bitを使って転送バイトを指定する命令です。転送バイト数は最大128バイトで、1命令で転送可能です。オフセットはレジスタで指定可能です。


スタックへの push/pop に使う場合にはException レジスタにバイトカウントを設定します。 pushするには以下のコードで実現できます。

    li    r0, 64          # Exception レジスタにバイトカウントを設定
    mtxer r0
    subi  r1, r1, r0      # r1 を更新
    stswx r3, 0, r1       # r3 からr18 (64バイト) をメモリに転送

pop操作は次のようになるでしょう。

    li    r0, 64          # Exception レジスタにバイトカウントを設定
    mtxer r0
    addi  r1, r1, r0      # r1 を更新
    lswx  r3, 0, r1       # r1の示すメモリから64バイトをr3 からr18 に転送

stswx/lswxを試してみる

実際に stswx/lswx を使ったスタック操作を試してみます。

以上の操作で実際にレジスタの値の変化を見て確認します。

#---------------------------------------------
# レジスタに値を設定
#---------------------------------------------
        li      r3, 1
        li      r4, 2
        li      r5, 3
        li      r6, 4
        li      r7, 5
        li      r8, 6
        li      r9, 7
        li      r10, 8
        li      r11, 9
        li      r12, 10
        li      r13, 11
        li      r14, 12
        li      r15, 13
        li      r16, 14
        li      r17, 15
        li      r18, 16
        li      r19, 17
#---------------------------------------------
#(1) PUSH 前
#---------------------------------------------
        li      r0, 64          # Exception レジスタにバイトカウントを設定
        mtxer   r0
        subi    r1, r1, r0      # r1 を更新
        stswx   r3, 0, r1       # r3 からr18 (64バイト) をメモリに転送
#---------------------------------------------
# PUSH 後
#---------------------------------------------
        li      r3, 3
        li      r4, 4
        li      r5, 5
        li      r6, 6
        li      r7, 7
        li      r8, 8
        li      r9, 9
        li      r10, 10
        li      r11, 11
        li      r12, 12
        li      r13, 13
        li      r14, 14
        li      r15, 15
        li      r16, 16
        li      r17, 17
#---------------------------------------------
#(2) POP 前
#---------------------------------------------
        li      r0, 64          # Exception レジスタにバイトカウントを設定
        mtxer   r0
        addi    r1, r1, r0      # r1 を更新
        lswx    r3, 0, r1       # r1の示すメモリから64バイトをr3 からr18 に転送
#---------------------------------------------
#(3) POP 後
#---------------------------------------------

上のコード中の (1)、(2)、(3) におけるレジスタの値を実際に調べてみました。レジスタ名の次にレジスタの内容を2進数表記、続いて16進数表記、最後に10進数で表記しています。

(1)
 r2: 00000000000000000000000000000000 00000000           0
 r3: 00000000000000000000000000000001 00000001           1
 r4: 00000000000000000000000000000010 00000002           2
 r5: 00000000000000000000000000000011 00000003           3
 r6: 00000000000000000000000000000100 00000004           4
 r7: 00000000000000000000000000000101 00000005           5
 r8: 00000000000000000000000000000110 00000006           6
 r9: 00000000000000000000000000000111 00000007           7
r10: 00000000000000000000000000001000 00000008           8
r11: 00000000000000000000000000001001 00000009           9
r12: 00000000000000000000000000001010 0000000A          10
r13: 00000000000000000000000000001011 0000000B          11
r14: 00000000000000000000000000001100 0000000C          12
r15: 00000000000000000000000000001101 0000000D          13
r16: 00000000000000000000000000001110 0000000E          14
r17: 00000000000000000000000000001111 0000000F          15
r18: 00000000000000000000000000010000 00000010          16
r19: 00000000000000000000000000010001 00000011          17
r20: 00000000000000000000000000000000 00000000           0

(1) の時点では r3 から r19 に1から17が書き込まれていることが確認できます。

(2)
 r2: 00000000000000000000000000000000 00000000           0
 r3: 00000000000000000000000000000011 00000003           3
 r4: 00000000000000000000000000000100 00000004           4
 r5: 00000000000000000000000000000101 00000005           5
 r6: 00000000000000000000000000000110 00000006           6
 r7: 00000000000000000000000000000111 00000007           7
 r8: 00000000000000000000000000001000 00000008           8
 r9: 00000000000000000000000000001001 00000009           9
r10: 00000000000000000000000000001010 0000000A          10
r11: 00000000000000000000000000001011 0000000B          11
r12: 00000000000000000000000000001100 0000000C          12
r13: 00000000000000000000000000001101 0000000D          13
r14: 00000000000000000000000000001110 0000000E          14
r15: 00000000000000000000000000001111 0000000F          15
r16: 00000000000000000000000000010000 00000010          16
r17: 00000000000000000000000000010001 00000011          17
r18: 00000000000000000000000000010000 00000010          16
r19: 00000000000000000000000000010001 00000011          17
r20: 00000000000000000000000000000000 00000000           0

(1) では r3 から r18 をスタックに保存した後、r3 から r17に3から17を書き込んでいます。レジスタの値も変化していますが、r18とr19は変化していません。

(3)
 r2: 00000000000000000000000000000000 00000000           0
 r3: 00000000000000000000000000000001 00000001           1
 r4: 00000000000000000000000000000010 00000002           2
 r5: 00000000000000000000000000000011 00000003           3
 r6: 00000000000000000000000000000100 00000004           4
 r7: 00000000000000000000000000000101 00000005           5
 r8: 00000000000000000000000000000110 00000006           6
 r9: 00000000000000000000000000000111 00000007           7
r10: 00000000000000000000000000001000 00000008           8
r11: 00000000000000000000000000001001 00000009           9
r12: 00000000000000000000000000001010 0000000A          10
r13: 00000000000000000000000000001011 0000000B          11
r14: 00000000000000000000000000001100 0000000C          12
r15: 00000000000000000000000000001101 0000000D          13
r16: 00000000000000000000000000001110 0000000E          14
r17: 00000000000000000000000000001111 0000000F          15
r18: 00000000000000000000000000010000 00000010          16
r19: 00000000000000000000000000010001 00000011          17
r20: 00000000000000000000000000000000 00000000           0

(3) では保存したレジスタ(r3-r18)が復帰している事が確認できます。