R3000特訓講座
第5号
更新日付 1998-02-27
リンク、サブルーチンについて説明します。
これで何とかプログラムが組めるようになるかな?
・リンク
R3000にはサブルーチンコール命令がありません。
当然、リターン命令もありません。
サブルーチンのないプログラムは考えられませんが、
いったいどうすればいいでしょう。
代わりに次のような命令が用意されています。
・呼び出し側
--------------------------------------------------------------------
jal pochi #リンクレジスタraに戻り番地を入れ、
nop #遅延スロットを殺す pochiにジャンプ。
--------------------------------------------------------------------
jalはリンクレジスタraに戻り番地を入れ、ジャンプする命令です。
戻り番地は遅延スロットの次の命令です。
リンクレジスタraが戻り番地を覚えているので、
サブルーチンでの処理が終わったら、
raが示すアドレスへジャンプすることで、
元のプログラムに戻ることができます。
サブルーチン側の処理はこうなります。
・サブルーチン側
--------------------------------------------------------------------
pochi:
うたって
おどる
la a0,pochi
jal a0 #レジスタraが示すアドレスにジャンプ
nop #遅延スロットを殺す
--------------------------------------------------------------------
これを使えばサブルーチンのようなものは作れそうです。
しかし、リンクレジスタraは一つしかありません。
このままではサブルーチンからサブルーチンを呼べません。
もう少し改良してみます。
リンクレジスタraをサブルーチン側で保存してみましょう。
--------------------------------------------------------------------
pochi:
addi sp,sp,-4 #スタックを1ワード分確保
sw ra,$0000(sp) #raをスタックにストア
うたって
おどる
lw ra,$0000(sp) #raをスタックからロード
addi sp,sp,4 #スタックを元に戻す(1ワード分開放)
jr ra #レジスタraが示すアドレスにジャンプ
nop #遅延スロットを殺す
--------------------------------------------------------------------
これで基本的なサブルーチンの構造は理解できたと思います。
それでは実際に簡単なサブルーチンを作ってみましょう。
問題1.データ転送を行なうサブルーチン。
ただし以下のような引き数を受け取るものとする。
a0=転送元アドレス、a1=転送先アドレス、a2=転送バイト数。
--------------------------------------------------------------------
mcopy:
addi sp,sp,-4 #スタックを1ワード分確保
sw ra,$0000(sp) #raをスタックにストア
[謎の4行] #次の問題で使う
add a2,a0,a2 #転送終了時の転送元アドレスを計算
_loop:
lb at,$0000(a0) #1バイトロード
addi a0,a0,1 #転送元アドレスを+1
sb at,$0000(a1) #1バイトストア
bne a0,a2,_loop #転送終了でなければ_loop
addi a1,a1,1 #転送先アドレスを+1
_exit: #次の問題で使う
lw ra,$0000(sp) #raをスタックからロード
addi sp,sp,4 #スタックを元に戻す(1ワード分開放)
jr ra #レジスタraが示すアドレスにジャンプ
nop
--------------------------------------------------------------------
R3000にもそろそろ慣れたでしょうから、
遅延スロットもプログラムに使ってみました。
問題2.上記プログラムは1バイトづつロード/ストアしている。
1ワード(4バイト)づつロード/ストアすれば高速化できる。
ロード/ストアするアドレスと、転送バイト数が、
共に4の倍数であれば、ワード単位で転送する処理を付加せよ。
ワード単位でロード/ストアするためには、
アドレスがワード単位(4の倍数)でなければならない。
というわけで、[謎の4行]部分で、その検査を行なうことにする。
--------------------------------------------------------------------
mcopy:
addi sp,sp,-4 #スタックを1ワード分確保
sw ra,$0000(sp) #raをスタックにストア
or at,a0,a1 #a0とa1を論理和
or at,at,a2 #a2も論理和
andi at,at,4-1 #下位2bitだけを残す
beq at,zero,_word #下位2bitが0(4の倍数)なら_wordへ分岐
add a2,a0,a2 #転送終了時の転送元アドレスを計算
_loop:
lb at,$0000(a0) #1バイトロード
addi a0,a0,1 #転送元アドレスを+1
sb at,$0000(a1) #1バイトストア
bne a0,a2,_loop #転送終了でなければ_loop
addi a1,a1,1 #転送先アドレスを+1
_exit:
lw ra,$0000(sp) #raをスタックからロード
addi sp,sp,4 #スタックを元に戻す(1ワード分開放)
jr ra #レジスタraが示すアドレスにジャンプ
nop
_word:
lw at,$0000(a0) #1ワードロード
addi a0,a0,4 #転送元アドレスを+4
sw at,$0000(a1) #1ワードストア
bne a0,a2,_word #転送終了でなければ_loop
addi a1,a1,4 #転送先アドレスを+4
j _exit #_exitへジャンプ
nop
--------------------------------------------------------------------
いきなりR3000らしいプログラムになっています。
追加した4行の中に「beq at,zero,_word」という行があります。
これは分岐命令ですから、
遅延スロットをnopで埋めなければなりません。
そこで両方のプログラムで必要となる「add a2,a0,a1」を、
そのまま遅延スロットに置いているのです。
どさくさに紛れて、見慣れない命令がいくつか登場していますが、
たぶん気のせいです。
あと2回しか講座が残っていない(笑)ので、
文句を言わずに覚えましょう。
・おまけ
書き切れなかったことを補足しておきます。
この欄に書かれている内容は無理に覚える必要はありません。
・スキあり?
--------------------------------------------------------------------
lw ra,$0000(sp) #raをスタックからロード
addi sp,sp,4 #スタックを元に戻す(1ワード分開放)
jr ra #レジスタraが示すアドレスにジャンプ
nop
--------------------------------------------------------------------
これはサブルーチンから戻る処理ですが、
この部分は高速化できるかもしれません。
nopのところに「addi sp,sp,4」を移動すれば、
遅延スロットを有効に使えるということですね。
しかし、そうすると、raをロード完了しないうちに、
次の命令「jr ra」でraを使うことになります。
というわけで、このようなプログラムにしたのですが、
私にもよくわかりません。
・飛行機と電車
jalでサブルーチンコール、jrでリターン。
これは「行きは飛行機、帰りは電車」と覚えてください。
え、意味がわからない?
わからない人は大文字で書いてみましょう。
お仕事ください(笑)...
koh@inetmie.or.jp