C言語やJavaでは「y = f(x)」という関数でサブルーチン呼び出しを記述します。それに対して,アセンブラの構文には関数などありません。サブルーチン呼び出しの一連の手順を記述する方法を解説しましょう。

矢沢 久雄グレープシティ

なんでキャット(以下,キャット):アセンブラでサブルーチン呼び出しを実現するにはどうするかニャ?

Tデスク:サブルーチンの先頭行にラベルを付け,JUMP命令でそこにジャンプさせるのですか。

キャット:ラベル付けは合ってるけど,JUMP命令じゃダメだニャ。

Tデスク:ええっ,なぜですか?

キャット:サブルーチンの処理が終わったら,呼び出し元のメインルーチンに処理の流れを戻さなければならないからだニャ。正解は,CALL命令でサブルーチンにジャンプし,RET命令でメインルーチンに戻るんだニャ。

Tデスク:JUMP命令とCALL命令は,どこが違うのでしょう?

キャット:とってもいい質問だニャ~。JUMP命令は,オペランドに指定されたラベルのアドレスをPR(プログラムレジスタ)に設定するだけだ。これによって,処理の流れがジャンプするけど,戻っては来れない。それに対して,CALL命令では,オペランドに指定されたラベルのアドレスをPRに設定するのはJUMP命令と同じだけど,その前にスタックに戻り番地をプッシュ(格納)しておくんだニャ。

Tデスク:戻り番地って何ですか?

キャット:メインルーチンでCALL命令が記述されている行のアドレスだ。サブルーチンの処理として最後にRET命令を実行すると,スタックから戻り番地がポップ(取得)されて,そのアドレスがPRに設定される。これによって,サブルーチンからメインルーチンに戻って来れる。後は,通常通りにPRの値がインクリメントされて,CALL命令の次の行に処理が進むんだニャ(図1)。

図1●サブルーチン呼び出しに使われるCALL命令とRET命令
図1●サブルーチン呼び出しに使われるCALL命令とRET命令

Tデスク:なるほど,実に上手い仕組みですね!

キャット:サブルーチンにデータを受け渡す方法や,メインルーチンに処理結果を返す方法も知りたくなったんじゃないかニャ?

Tデスク:はい,ぜひ教えてください。今回は,奮発してカツオブシを2本プレゼントします。

キャット:嬉しいニャ~。いつもよりガンバッテ教えちゃうニャ!!

行き来する処理の流れ

 ある程度規模の大きなプログラムは,複数の部分に分割して作成される。プログラムの実行開始位置を持つ部分を「メインルーチン(主プログラム)」と呼び,メインルーチンから呼び出される部分を「サブルーチン(副プログラム)」と呼ぶ。1つのメインルーチンと複数のサブルーチンがプログラム全体を構成する。

 アセンブラでは,メインルーチンとサブルーチンの構文は同じ。「START命令~END命令」で囲んで記述する。サブルーチンのSTART命令の行には,任意の名前のラベルを付ける。このラベルをCALL命令のオペランドに指定することで,サブルーチン呼び出しが実現される。サブルーチンでRET命令を実行すると,処理の流れがメインルーチンに戻る。

 図2は,メインルーチンからサブルーチンを呼び出し,何もしないで戻って来るだけのサンプルプログラムである。説明の都合上,左端に行番号を付けている。メインルーチンの2行目にある「CALL SUB」が実行されると,戻り番地すなわち2行目のアドレスがスタックにプッシュされてから,PRにラベルSUBのアドレス(サブルーチンの1行目のアドレス)が設定される。サブルーチンの2行目にあるRET命令が実行されると,スタックから戻り番地(メインルーチンの2行目のアドレス)がポップされ,それがPRに設定される。その後,通常通りPRがインクリメントされ,メインルーチンの3行目に処理の流れが進む。

図2●メインルーチンからサブルーチンを呼び出して戻ってくるだけのプログラム
図2●メインルーチンからサブルーチンを呼び出して戻ってくるだけのプログラム