2006年度 計算機システム演習 第4回 2005年5月19日 caller-save (前回の方法) caller-save (呼び出し元で 保存) foo: addi $sp, $sp, -8 : # $t0に代入 : sw $t0, 4($sp) jal bar lw $t0, 4($sp) : # $t0を使った処理 : addi $sp, $sp, 8 jr $ra レジスタ $t0〜$t9 データが入っているレジスタ はサブルーチンの呼び出し元 で待避・復帰 サブルーチン内では $t0〜 $t9 を自由に使える bar: # $t0を使った処理 callee-save callee-save (呼び出し先で 保存) レジスタ $s0〜$s7 サブルーチンで使うレジスタ はサブルーチンの先頭・最後 で待避・復帰 呼び出し元では $s0〜$s7 を自由に使える $ra も callee-save foo: jal bar bar: addi $sp, $sp, -4 sw $s0, 0($sp) : # $s0を使った処理 : lw $s0, 0($sp) addi $sp, $sp, 4 jr $ra 効率の違い レジスタを退避・復帰する回数が変わる プログラム # $xに代入 jal foo jal bar # $xを使って計算 # $xに代入 jal foo # $xを使って計算 jal bar # $xを使って計算 caller-save $xはfooの前で退避し、barの 後で復帰(1回) $xはfooとbarの前後で保存・ 復帰(2回) callee-save $xの退避・復帰はfooとbarで $xの退避・復帰はfooとbarで 使うかどうかに依存(0〜2回) 使うかどうかに依存(0〜2回) 例外とは? Java の例外 自分で書いた catch 文の 中の処理が行われる CPU の例外 OSカーネル内で用意され た処理が行われる プログラム 例外発生 try { : 例外発生 : } catch (Exception e) { 例外処理 } 例外処理 OSカーネル 例外の種類 プログラムの不正実行で発生するもの 不正アドレスのアクセス 例: lw $t0, 0x00000001 Exception 4 [Address error in inst/data fetch] プログラムから明示的に発生させるもの OSカーネルでしか使えない 機能を利用するため ファイル入出力など サブルーチンに似ているが サブルーチンでは呼べない 例外番号 内容 8 syscall 例外 13 トラップ例外 例外処理の流れ プログラム : li $v0, 5 syscall move $t0, $v0 : 例外発生 復帰 例外ハンドラ read_int の処理 OSカーネル 例外ハンドラ アドレス 0x80000180 から実行される $k0, $k1 レジスタが使 える .kdata : .ktext 0x80000180 : レジスタの保存 例外ハンドラ内で使うレジスタを保存 例外処理がプログラムの実行に悪影響を与えないよ うにする callee-save .kdata save_t0: .word 0 .ktext 0x80000180 .set noat move $k1, $at .set at sw $t0, save_t0 # $t0を保存するメモリ領域 # # # # $atを扱うことを許可する 疑似命令が使うレジスタを$k1に保存 $atを扱うことを不許可にする $t0を使うなら元の値を保存 疑似命令(再) MIPS にはない命令 blt, bge, ... 命令自体はあるがオペランドが違う命令 sw $t0, A($t0) lui $at, 4097 addu $at, $at, $t0 sw $t1, 0($at) $at レジスタを使って実現 ここで例外が発生して $at が 上書きされると困る コプロセッサ メインプロセッサとは別のプロセッサが例外を管 理する 例外番号など コプロセッサのレジスタには mfc0, mtc0 命令で アクセスできる mfc0 CPUレジスタ, コプロセッサレジスタ コプロセッサのレジスタの内容をCPUレジスタにコピー mtc0 CPUレジスタ, コプロセッサレジスタ CPUレジスタの内容をコプロセッサのレジスタにコピー 例外の種類の判定 コプロセッサの Cause レジスタに発生した例外 番号が入っている $13 レジスタ 例外コード mfc0 $k0, $13 srl $k0, $k0, 2 andi $k0, $k0, 0x1f # 2ビット右シフト # 下位5ビットを取り出す 実際の例外処理 例外に応じた処理をする 不正メモリアクセス エラーを表示 プログラムを終了 syscall $v0 で指定されたサービスを実行 $a0〜$a3を引数とする $v0 に結果を代入 PC の調整 次の命令から実行再開 EPC(Exception Program Counter)レジスタに例外 が発生した命令のアドレスが入っている コプロセッサの $14 レジスタ EPC の値を 4 増やせば次の命令から再開できる mfc0 $k0, $14 addiu $k0, $k0, 4 mtc0 $k0, $14 レジスタの復帰と例外ハンドラ終了 例外発生時と全てのレジスタの内容を同じにし ておく必要がある syscall の場合は $v0 だけ変わる eret 命令で EPC レジスタが指すアドレスから実 行を再開 lw $t0, save_t0 .set noat move $at, $k1 .set at eret # $t0を復帰 # $atを扱うことを許可 # $atを復帰 SPIM での例外ハンドラ デフォルトで /usr/local/share/exceptions.s が 使われている 変更するには -exception_file オプションをつけて xspim を起動すればよい 例: xspim -exception_file myexceptions.s Loaded: myexceptions.s と表示される スタートアップルーチン myexceptions.s には main ルーチンを呼び出 す初期ルーチンも書かなければならない .text .globl __start __start: jal main li $v0, 10 syscall # mainを呼び出す # exitシステムコール 課題1(〆切:6月4日(日)23時) myexceptions.s を作成し、引数の値を10倍す るシステムコールを作れ SPIM では syscall 例外は発生しないので、代わりに トラップ例外を使用する teq $zero, $zero でトラップ例外を発生させられる li li teq $v0, 100 $a0, 55 $zero, $zero move $a0, $v0 li $v0, 1 syscall # システムコール番号100 # 10倍したい値 # トラップ例外を発生させる # 10倍した値を表示 新しいシステムコールを使うプログラム例 課題1のヒント myexceptions.s .kdata : .ktext 0x80000180 # 例外ハンドラで使用するレジスタを退避($atと$t0など) # Causeレジスタから例外番号を取得 # 例外番号が13以外ならdoneにジャンプ # システムコール番号($v0)が100以外ならdoneにジャンプ # 引数($a0)の値を10倍(mul命令)して$v0に代入 done: # EPCレジスタを4進める # 退避したレジスタを復帰 eret : __start: : 課題2 caller-save で書かれた次ページのコードを calleesave で書き直し、コードの効率を論じよ 0 または 1 を 8 回入力させて 8 ビットの2進数とみなし、それ を 10 進数に変換するプログラム 入力1×27 + 入力2×26 + 入力3×25 + 入力4×24 + 入力5×23 + 入力1×22 + 入力7×21 + 入力8×20 結果を2倍しながら入力を足していく $s0〜$s7 の内、サブルーチン内で使わないレジスタを退避す る必要はない main ルーチンもサブルーチンであることに注意せよ main で $s0〜$s7 を使うなら、最初と最後で退避・復帰する必 要がある 2進10進変換プログラム .text addi $t1, $t1, 1 blt $t1, 8, loop main: addi $sp, $sp, -12 sw $ra, 0($sp) li $t0, 0 li $t1, 0 # 結果 # i=0 loop: sw $t0, 4($sp) sw $t1, 8($sp) jal read1bit lw $t1, 8($sp) lw $t0, 4($sp) sll $t0, $t0, 1 # 2倍 add $t0, $t0, $v0 move $a0, $t0 li $v0, 1 syscall lw $ra, 0($sp) addi $sp, $sp, 12 jr $ra # サブルーチン read1bit: li $v0, 5 syscall # read_int jr $ra 課題3 (オプション) 乗算を行うサブルーチンを mul 命令を使わず に作成せよ 単純なアルゴリズム(筆算と同じアルゴリズム) かける数の最下位ビットが1なら かけられる数を結果に足す 1. かけられる数を1ビット左シフト 2. sll $x, $x, 1 かける数を1ビット右シフト 3. 4. andi $x, $y, 0x1(最下位ビットを得る) srl $x, $x, 1 かける数のビット数回繰り返す 0011 × 0110 0000 0011 0011 + 0000 0010010 課題3の難易度 以下の順に難しくなるので、できるところまでや ればよい 16ビットの乗算(結果は32ビット) より高速なアルゴリズム 32ビット×32ビット(結果は64ビット) より多いビット数の乗算 注意 ~/Exercise/compsys/04/ で作業すること できるだけ感想も書いて下さい 5/22 は休講です
© Copyright 2025 ExpyDoc