レジスタ-メモリ転送命令
メモリへの書き込み、メモリからの読み出しはレジスタを経由してロード/ストア命令を使って行う必要があります。
ロード/ストア命令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種類の方法でメモリアドレスを指定します。
- レジスタの内容に定数 (符号拡張16bit) を加える
- 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 を使ったスタック操作を試してみます。
- レジスタに値を設定 (1)
- PUSH
- レジスタに別の値を設定 (2)
- POP (3)
以上の操作で実際にレジスタの値の変化を見て確認します。
#--------------------------------------------- # レジスタに値を設定 #--------------------------------------------- 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)が復帰している事が確認できます。