演習第3回:アセンブラプログラミング(1)サブルーチン(説明資料

概要

MIPSのシミュレータspim上で実行可能なアセンブリ言語のコードを用いてプログラムを作成する。

実用的な処理を行うためのプログラムでは、ひとまとまりの処理を独立なものとして定義し、それを再利用するサブルーチンを用いる。

アセンブリ言語においては、レジスタの種類と数およびその使用目的に厳格な決まりがあるため,特にサブルーチンの使用に当たっては注意が必要である.サブルーチンにデータを渡すためにはレジスタ$a0~$a3を用いる。レジスタ$v0と$v1はサブルーチンの戻り値を格納することになっているし、$v0はシステムコール番号を設定するためにも使う。

作業用レジスタは$s0~$s7および$t0~$t9の2種類があるがサブルーチンの使用に当たっては区別して使わなければならない。$s0~$s7はサブルーチンを呼んでも呼び出し側では書き換えられない決まりになっているので、サブルーチン内で書き換える必要がある場合には、サブルーチン内で退避と復元が必要である。それに対し、$t0~$t9はサブルーチン内で書き換えても良いことになっているため、必要な場合はサブルーチンを呼び出す側で退避および復元が必要となる。

サブルーチンの使用に当たって必須であるレジスタの退避の方法としてスタック(stack)を用いる。

サブルーチンでデータを渡すレジスタは$a0~$a3の4つと決まっている。データの個数が4を超える場合はどうしたらよいのか?サブルーチンの処理結果のデータを呼び側に渡すためのレジスタは$v0と$v1の2つであるが、3つ以上のデータを返す場合はどうしたらよいか?メモリに必要な分のデータ領域を確保し,その領域のアドレスをメインルーチンとサブルーチンの間で受け渡しすれば,この問題は解決する。スタックを使うこともこの問題を解決するための必須テクニックです。

レポートの提出期限:2024年7月4日(木)23:59:59

課題1(メモリアクセス・大域配列)必須課題

キーボードから読み込んだ複数の整数値をメモリー上に一時保存し、読み込みが完了してから総和の計算を行ないたい。これを実現するための以下の2つのサブルーチンを作成せよ。実行して正しく機能することを示すこと.

その1:サブルーチンsum

【機能】引数$a0で指定されたアドレスから始まるメモリーに、引数$a1で指定された個数の32ビット整数値が格納されているとし、これらの総和を求めて、その値を$v0で返す。データ数を示す$a1は1以上の整数と仮定してよい。

sumへ渡す2つの引数は、C言語で言うと配列の先頭のポインタと配列の要素数に当たる。総和を求める対象は配列の要素に当たり、$a0 で渡されたアドレスを1ワードずつ (4バイトずつ) 増加させることで、その要素に順にアクセスすることが可能になる。C言語における、ポインタ変数 p を増加させながら値を参照する*p++ の振舞いと同等である。

sumの機能を確かめるために、下記のテスト用メインルーチンを使用しても良い。ただし、sumはこのテスト用メインルーチンに特化した実装を行なってはならない。あくまでも、上記【機能】を満たす汎用のサブルーチンである。

sumの機能を確かめるプログラムは testsum.s とする.

# サブルーチン sum のテスト .data list: .word 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 .text .globl main main: addi $sp, $sp, -4 # スタックに1ワードの領域を確保 sw $ra, 0($sp) # 戻り先アドレスの退避 la $a0, list # 第1引数=データ領域listのアドレス li $a1, 10 # 第2引数=10 (データ数) jal sum # サブルーチンsumの呼び出し move $a0, $v0 # システムコールの引数=sumの戻り値$v0 (総和) li $v0, 1 # print_integerの指定 syscall # 総和の出力 lw $ra, 0($sp) # 戻り先アドレスの復元 addi $sp, $sp, 4 # スタックに確保した領域の開放 jr $ra # ここから sum の定義を書く

その2:サブルーチンread

【機能】引数$a0で指定されたアドレスから始まるメモリ領域に対し、引数$a1で指定された個数の32ビット整数をキーボードから読み込み順次格納する。C言語で言う配列に整数値を順次読み込む機能と考えてよい。データ数を示す$a1は1以上の整数と仮定してよい。

サブルーチンreadでは整数値の読み込みのためにシステムコールが必要になる。また引数として受け取った$a0、$a1もシステムコールで値が変更される可能性があるため、そのまま継続して利用するのは不適切である。

readとsum を組み合わせたの時の動作を確かめるために、下記のメインルーチンを使用してもよい。ただし、readとsumはこのテスト用メインルーチンに特化した実装を行なってはならない。あくまでも、上記【機能】を満たす汎用のサブルーチンである。

readとsumの機能を確かめる以下のプログラムは testreadsum.s とする.

# サブルーチン read, sum のテスト # スタック上に5ワード分の読み込み用バッファ領域を確保して、 # 5つの値の読み込みと総和の計算を実行する。 .text .globl main main: addi $sp, $sp, -24 # スタックに 6 ワードの領域を確保 # 0($sp)は$raの待避用 # 4($sp)から5ワード分は配列変数として利用する sw $ra, 0($sp) # 戻り先アドレスの退避 addi $a0, $sp, 4 # 第1引数 配列変数用領域のアドレス ($sp + 4) li $a1, 5 # 第2引数=5 (データ数) jal read # サブルーチン read の呼び出し # → 配列変数にデータが読み込まれる addi $a0, $sp, 4 # 第1引数=配列変数用領域のアドレス ($sp + 4) li $a1, 5 # 第2引数=5 (データ数) jal sum # サブルーチン sum の呼び出し # → $v0に総和が入る move $a0, $v0 # システムコールの引数 = sumの戻り値$v0 (総和) li $v0, 1 # print_integerの指定 syscall # 総和の出力 lw $ra, 0($sp) # 戻り先アドレスの復元 addi $sp, $sp, 24 # スタックに確保した領域の開放 jr $ra # ここから sum と read の定義を書く

課題2(サブルーチンの入れ子)「パスカルの三角形」必須課題

階乗と組み合わせの数を計算するサブルーチンfactとcombi を作成し、これを用いてn個からr個を取り出す時の組合せnCrを計算できるようにする. これを利用してパスカルの三角形を計算するプログラム pascalTri.s を作成せよ。メインルーチンの処理は以下のようなものとする。

  1. パスカルの三角形の高さmをキーボードから読み込む.
  2. mの値が0以下の場合は何も出力しない.
  3. mの値が1以上の場合はパスカルの三角形を計算して表示する(表示例は以下参照).
  4. 三角形の各段、各列に対応するnCrをサブルーチンcombiを呼び出して計算
  5. m≦13に対応すればよい.

※ 本課題における制約条件:
汎用的なプログラム作成の演習とするため、 main, fact, combiの各ルーチン内で使用する作業用レジスタは それぞれ$s0〜$s7を番号の若い方から順に使用すること。 もちろん各ルーチンの実行前後でレジスタの値が変わらないよう一時待避が必要である 。

サブルーチンfact

【機能】引数$a0で指定された非負整数の階乗を求め、その値を$v0で返す。再帰計算ではなく,繰り返し計算を用いること。

サブルーチンcombi

【機能】引数$a0を n、引数$a1をrとした時の組合せの数nCrを上記のサブルーチンfactを用いて求め、その値を$v0で返す。なお、n個からr個を取り出す時の組合せの数nCrは、factによる階乗の計算を使い以下のように求めること。

組み合わせの数の計算式

補足:整数除算はdiv $rs, $rtで計算できる。結果は商がLoレジスタ、余りがHiレジスタに入る。

% spim SPIM Version 8.0 of January 8, 2010 Copyright 1990-2010, James R. Larus. All Rights Reserved. See the file README for a full copyright notice. Loaded: /usr/lib/spim/exceptions.s (spim) load "pascalTri.s" spim) run m = -1 (spim) run m = 0 (spim) run m = 1 1 (spim) run m = 2 1 1 1 (spim) run m = 3 1 1 1 1 2 1 (spim) run m = 4 1 1 1 1 2 1 1 3 3 1 (spim) run m = 13 1 1 1 1 2 1 1 3 3 1 1 4 6 4 1 1 5 10 10 5 1 1 6 15 20 15 6 1 1 7 21 35 35 21 7 1 1 8 28 56 70 56 28 8 1 1 9 36 84 126 126 84 36 9 1 1 10 45 120 210 252 210 120 45 10 1 1 11 55 165 330 462 462 330 165 55 11 1 1 12 66 220 495 792 924 792 495 220 66 12 1

課題3自由課題

課題2では作業用レジスタの使用に制約を課した。この制約を外すことによりレジスタの退避・復元の手間を最少化したプログラムを作れ.

プログラムのファイル名は課題2のファイル名に適当な文字列を追加したもの(例えば pascalTri_opt.s )とすること.

これを完成して提出した場合はレポート内容点に加点する.

SPIMプログラム開発と動作確認について

★ MIPSシミュレータSPIMの使い方については SPIM を参照のこと。

★ SPIMをリセットするためには reinitialize コマンドを使ってください。このコマンドは、SPIMを起動したまま修正したプログラムを読み込む前に実行します。

★ 実行例は複数示す必要があります。その他、課題実行、レポート作成にあたっては レポート に従うこと。