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