2014.11.26 コンピュータアーキテクチャI 課題解答例 (第 8 回) 1. 講義ノート 5 の演習 1 から演習 5 を行え. 演習 1: 答えはノートにある通り.自力でコンパイルして,ノートのような答えが出てきたか.出てこな かったら何が間違っていたのかじっくり考えることが重要. 演習 2: 同上 演習 3: 省略 演習 4: ハノイ関数:この関数は,棒 a にある n 枚の円盤を,棒 b をワークスペースとして使って,棒 c に移す関数である. 関数本体でやっていることは,円盤 (n) が 1 枚だったら,それを棒 a から棒 c に移動して完了. そうでなければ,1) 棒 a にある上から (n-1) 枚の円盤を棒 c をワークスペースとして使って棒 b に 移動し,2) 一番下にある円盤 (n) を棒 c に移動し,3) 棒 b にある (n-1) 枚の円盤を棒 a をワークス ペースとして使って,棒 c に移動する,である. 関数 hanoi は再帰的に呼び出される. 関数 move は,円盤を移動するメッセージの出力を行う. 演習 5: 1) 省略 2) hanoi: #ここから 6 行でレジスタ退避 addi sw sw $sp, $sp, -20 $a0, 0($sp) $a1, 4($sp) sw sw $a2, 8($sp) $a3, 12($sp) sw slti beq $ra, 16($sp) $t0, a0, 2 $t0, $zero, hanoi1 add $a2, $a3, $zero jal lw lw move $a2, 8($sp) $ra, 16($sp) # $a2 は値が変わったので,回復 # $ra は move を呼んだとき値が変わったので、回復 addi jr $sp, $sp, 20 $ra #呼び出し元にジャンプ hanoi1: addi $a0, $a0, -1 #n-1 # n<2? n が 1 かどうかをチェック # n>1 だったら分岐 # move(n,a,c) を呼ぶための準備.$a2 # に$a3 の値をコピー. 1 #ここから 3 行で$a2 と$a3 のスワップ add add $t0, $a2, $zero $a2, $a3, $zero add jal $a3, $t0, $zero hanoi #hanoi(n-1,a,c,b); lw lw jal $a0, 0($sp) $a2, 12($sp) move #n 回復 #c を$a2 に #move(n,a,c) addi lw $a0, $a0, -1 $a1, 8($sp) #n-1 #$a1 に b lw lw jal $a2, 4($sp) $a3, 12($sp) hanoi #$a2 に a #$a3 に c #hanoi(n-1,b,a,c) lw lw $a0, 0($sp) $a1, 4($sp) #ここから 6 行でレジスタ回復 lw lw lw $s2, 8($sp) $s3, 12($sp) $ra, 16($sp) addi jr $sp, $sp, 20 $ra #呼び出し元にジャンプ 参考 1 move 関数のコンパイル. move: addi sw $sp, $sp, -20 $a0, 0($sp) sw sw sw $a1, 4($sp) $a2, 8($sp) $a3, 12($sp) sw add $ra, 16($sp) $a3, $a2, $zero #$a2 を$a3 にコピー add add la $a2, $a1, $zero $a1, $a0, $zero $a0, str1 #$a1 を$a2 にコピー #$a0 を$a1 にコピー #la ロードアドレス命令 #文字列 str1 の先頭アドレスを$a0 に jal printf lw lw lw $a0, 0($sp) $a1, 4($sp) $a2, 8($sp) lw lw $a3, 12($sp) $ra, 16($sp) addi jr $sp, $sp, 20 $ra 静的データ領域 2 str1: db "move %d from %c to %c\n" 参考 2 printf 文などの可変引数関数のコンパイルについて printf 文を例に話します.可変引数関数では,第 1 引数に第 2 引数以下をどのように使うかを指 示する情報を与えねばならない.たとえば, printf("x= %d, y=%d\n", x, y); なら,第 1 引数の内容から,第 2 引数と第 3 引数は整数であるとして処理することになる.具体的 には,printf 関数の本体において,第 1 引数の文字列を解読していって,%が来たら,その次の文 字を解析し,d なら第 2 引数を整数として読み出して,10 進数の文字列に変換して出力する.そし て,第 1 引数の解析を継続する.というようにプログラムが組まれる. (このあたりの詳細は,カー ニハン,リッチィー:プログラミング言語 C 第 2 版の7.3節 (p.189) を見よ. ) この関数の呼び出し部分のコンパイル例は以下のようになる.ただし,x,y はそれぞれ$s0,$s1 に 割り当てられているものとする. add $a2, $s1, $zero add $a1, $s0, $zero #y を$a2 にコピー #x を$a1 にコピー la #la は load address 命令 #str1 のアドレスを$a0 にロードする. $a0, str1 jal printf 静的データ領域 str1: db x= %d, y=%d\n #db は文字列を定義する擬似命令 #str1 のアドレスから"x= ..."の文字列が格 #納される. このようになります. 引数の数が合わなかった場合どうなるか? 上の例で y を忘れた,すなわち,printf("x= %d, y=%d\n", x); と書いたとしましょう.そう するとコンパイラは,次のようなコードを出します. add $a1, $s0, $zero #x を$a1 にコピー la #la は load address 命令 #str1 のアドレスを$a0 にロードする. $a0, str1 jal printf この場合,printf 本体では,第 1 引数の 2 番目の%d を見たとき,$a2 が y の値を持っていると解 釈して,その値を出力します. (だから,y の正しい値は出力されないことになります. ) 3
© Copyright 2024 ExpyDoc