制御の流れ
(教科書3.5節~3.6節)
Copyright (C) 2002, 2004 Tsuneo Nakanishi
プログラム内蔵方式
一般的なコンピュータの内部構造
PCの指す番地から取
得した命令を解読す デコーダ
る.
PC
(主記憶上にある)次に実行
する命令の番地を記憶.
レジスタ
ALU
アドレスバス
データバス
主記憶
プログラムの命令と
データを格納.
・
・
・
Copyright (C) 2002, 2004 Tsuneo Nakanishi
ラベル名
ラベル名: 命令やデータが配置されている主記憶上の番地につ
けられた名前.
100 add
$s0, $s0, $s1
add
$s0, $s0, $s1
104 addi
$s4, $zero, 1
addi
$s4, $zero, 1
108 add
$s1, $s1, $s0
add
$s1, $s1, $s0
112 sub
$s0, $s0, $s4
sub
$s0, $s0, $s4
116 bne
$s0, $s4, 108番地
bne
$s0, $s4, L1
L1:
ラベル定義
ラベル参照
ラベル名はアセンブラによって実際の番地に変換される.
Copyright (C) 2002, 2004 Tsuneo Nakanishi
分岐命令(1)
条件分岐命令の例:
beq
bne
$s0, $s1, L1
$s0, $s1, L2
# $s0 = $s1 なら L1 へ分岐
# $s0 $s1 なら L2 へ分岐
無条件分岐命令の例:
# L3 へ分岐
# $s0 に格納されている番地へ分岐
j
jr
L3
$s0
beq
$s0, $s1, L1
$s0 = $s1
Yes
No
次の命令
bne
$s0, $s1, L2
$s0 $s1
Yes
No
ラベルL1の番地
次の命令
ラベルL2の番地
Copyright (C) 2002, 2004 Tsuneo Nakanishi
分岐命令(2)
slt(set less than)命令:
slt
$t0, $s1, $s2
Yes
$t0 ← 1
# $s1 < $s2 ならば $t0 に 1 を設定.
# そうでなければ $t0 に 0 を設定.
$s1 < $s2
No
$t0 ← 0
Copyright (C) 2002, 2004 Tsuneo Nakanishi
分岐命令(3)
$zero レジスタ: 常に 0 が読み出されるレジスタ.(レジスタ0番)
# $s0 < $s1 なら分岐
slt
$t0, $s0, $s1
bne
$t0, $zero, L
# $s0 <= $s1 なら分岐
slt
$t0, $s1, $s0
beq
$t0, $zero, L
# $s0 > $s1 なら分岐
slt
$t0, $s1, $s0
bne
$t0, $zero, L
# $s0 >= $s1 なら分岐
slt
$t0, $s0, $s1
beq
$t0, $zero, L
Copyright (C) 2002, 2004 Tsuneo Nakanishi
コード例: C言語の goto 文
ラベル名で指定するところに飛ぶ.
L100:
…
goto L100;
...
xxxx
L100:
j
…
xxxx
L100
Copyright (C) 2002, 2004 Tsuneo Nakanishi
コード例: C言語の if 文(1)
式の値が真(0以外の整数)ならば else の前を実行する.式の
値が偽(0)ならば else の後を実行する.文が複数ある場合は
中括弧でくくる.
if (式)
文;
else
文;
省略可能
if (式) {
文;
...
}
else {
文;
...
}
式の値が偽のときに特にすることがなければ,else 以降は省
略してもよい.
Copyright (C) 2002, 2004 Tsuneo Nakanishi
コード例: C言語の if 文(2)
L1:
L1:
if (i == j) goto L1;
f = g + h;
f = f – i;
beq
add
sub
$s3, $s4, L1
$s0, $s1, $s2
$s0, $s0, $s3
変数名
レジスタ
f
$s0
g
$s1
h
$s2
i
$s3
j
$s4
Copyright (C) 2002, 2004 Tsuneo Nakanishi
コード例: C言語の if 文(3)
if (i == j)
f = g + h;
else
f = g – h;
bne
add
j
ELSE: sub
EXIT: …
$s3, $s4, ELSE
$s0, $s1, $s2
EXIT
$s0, $s1, $s3
変数名
レジスタ
f
$s0
g
$s1
h
$s2
i
$s3
j
$s4
Copyright (C) 2002, 2004 Tsuneo Nakanishi
コード例: C言語の while 文(1)
1. 条件式を評価する.
2. 条件式の値が真(0以外の整数)ならば文または中括弧の中
を実行する.偽(0)ならば文または中括弧の中を実行せずに
次に進む(=ループを終了する).
3. 文または中括弧の中を実行した後は 1. に戻る.
while (条件式)
文;
while (条件式) {
文;
...
}
Copyright (C) 2002, 2004 Tsuneo Nakanishi
コード例: C言語の while 文(2)
while (save[i] == k)
i = i + j;
save[i] の番地 = $s6 + $s3 * 4
WHILE: add
add
add
lw
bne
add
j
EXIT:
…
$t0, $s3, $s3
$t0, $t0, $t0
$t0, $t0, $s6
$t1, 0($t0)
$t1, $s5, EXIT
$s3, $s3, $s4
WHILE
変数名
レジスタ
i
$s3
j
$s4
k
$s5
save[] 先頭番地
$s6
Copyright (C) 2002, 2004 Tsuneo Nakanishi
コード例: C言語の switch 文(1)
式の値と同じ値の case のところに飛ぶ.該当する case がな
い場合は default のところに飛ぶ.
switch (式) {
case 値1:
...
case 値2:
...
case 値3:
...
default:
...
}
式=値1のとき
式=値2のとき
式=値3のとき
式=その他の値のとき
省略可能
default 以降は不要ならば省略してもよい.
Copyright (C) 2002, 2004 Tsuneo Nakanishi
コード例: C言語の switch 文(2)
通常,switch 文の各 case のあとには break 文を入れる.
switch 文の中括弧の中で break 文を実行すると,中括弧の残
りの実行を打ち切り,switch 文の外に飛ぶ.
switch (式) {
case 値1:
...
break;
case 値2:
...
break;
default:
...
}
式=値1のとき
式=値2のとき
式=その他の値のとき
Copyright (C) 2002, 2004 Tsuneo Nakanishi
コード例: C言語の switch 文(3)
switch (k) {
case 0:
f = i + j;
break;
case 1:
f = g + h;
break;
case 2:
f = g – h;
break;
case 3:
f = i – j;
break;
}
Copyright (C) 2002, 2004 Tsuneo Nakanishi
コード例: C言語の switch 文(4)
k == 0
Yes
f←i+j
No
k == 1
Yes
f←g+h
No
k == 2
Yes
f←g–h
No
k == 3
時間が
かかる!!
No
Yes
f←i–j
Copyright (C) 2002, 2004 Tsuneo Nakanishi
コード例: C言語の switch 文(5)
表に登録されている
番地に分岐.
添え字
登録番地
0
L0
1
L1
2
L2
3
L3
k
k == 0
k == 1
k == 2
k == 3
L0: f ← i + j
L1: f ← g + h
L2: f ← g – h
L3: f ← i – j
Copyright (C) 2002, 2004 Tsuneo Nakanishi
コード例: C言語の switch 文(6)
範囲検査
多岐分岐
L0:
L1:
L2:
L3:
EXIT:
slt
bne
slt
beq
add
add
add
lw
jr
add
j
add
j
sub
j
sub
…
$t3, $s5, $zero
$t3, $zero, EXIT
$t3, $s5, $t2
$t3, $zero, EXIT
$t0, $s5, $s5
$t0, $t0, $t0
$t0, $s6, $t0
$t1, 0($t0)
$t1
$s0, $s3, $s4
EXIT
$s0, $s1, $s2
EXIT
$s6 →
$s0, $s1, $s2
EXIT
$s0, $s3, $s4
変数名
レジスタ
f
$s0
g
$s1
h
$s2
i
$s3
j
$s4
k
$s5
番地表先頭番地
$s6
定数4
$t2
添え字
登録番地
0
L0
1
L1
2
L2
3
L3
Copyright (C) 2002, 2004 Tsuneo Nakanishi
コード例: C言語の for 文(1)
1. 初期化式を評価する.
2. 条件式の値が真(0以外の整数)ならば文または中括弧の中
を実行する.偽(0)ならば文または中括弧の中を実行せずに
次に進む(=ループを終了する).
3. 文または中括弧の中を実行した後は,更新式を評価し,2. に
戻る.
for (初期化式; 条件式; 更新式)
文;
for (初期化式; 条件式; 更新式) {
文;
...
}
Copyright (C) 2002, 2004 Tsuneo Nakanishi
コード例: C言語の for 文(2)
for (i = 0, j = 15; i < j; i++, j--) {
tmp = a[i];
a[i] = a[j];
a[j] = tmp;
}
i
j
a[0]
a[1]
a[2]
a[14]
a[15]
Copyright (C) 2002, 2004 Tsuneo Nakanishi
コード例: C言語の for 文(3)
初期化式
条件式
FOR:
tmp = a[i]
a[i] = a[j]
a[j] = tmp
更新式
EXIT:
add
add
slt
beq
add
add
add
lw
add
add
add
lw
sw
sw
add
sub
j
…
$s0, $zero, $zero
$s1, $t4, $zero
$t0, $s0, $s1
$t0, $zero, EXIT
$t1, $s0, $s0
$t1, $t1, $t1
$t1, $s3, $t1
$s2, 0($t1)
$t2, $s1, $s1
$t2, $t2, $t2
$t2, $s3, $t2
$t3, 0($t2)
$t3, 0($t1)
$s2, 0($t2)
$s0, $s0, $t5
$s1, $s1, $t5
FOR
変数名
レジスタ
i
$s0
j
$s1
tmp
$s2
a[] 先頭番地
$s3
定数15
$t4
定数1
$t5
Copyright (C) 2002, 2004 Tsuneo Nakanishi
手続き呼出の基本(1)
#include <stdio.h>
int add (int a, int b)
{
int c;
c = a + b;
return c;
}
3, 4
a + b を計算して
c に代入
7
c の値を返す
関数 add 呼出
c に結果代入
関数 add
c: 7, d: 0
c, d の値を表示
int main ()
{
int c = 0, d = 0;
c: 7, d: 16
c = add (3, 4);
printf (”c: %d, d: %d\n”, c, d);
d = add (c * 2, 2);
printf (”c: %d, d: %d\n”, c, d);
…
}
14, 2
a + b を計算して
c に代入
16
c の値を返す
関数 add 呼出
d に結果代入
関数 add
c, d の値を表示
caller
callee
Copyright (C) 2002, 2004 Tsuneo Nakanishi
手続き呼出の基本(2)
#include <stdio.h>
callee の作業:
int add (int a , int b)
{
int c;
1. 実引数の仮引数への代入.
2. 内部の実行.
3. 復帰: caller へのジャンプ.
c = a + b;
return c;
仮引数
}
caller の作業:
int main ()
{
int c = 0, d = 0;
1. 実引数式の値の計算.
2. 呼出: callee へのジャンプ.
3. 結果の受け取り.
実引数
c = add (3 , 4);
printf (”c: %d, d: %d\n”, c, d);
d = add (c * 2 , 2);
printf (”c: %d, d: %d\n”, c, d);
…
}
caller は複数ありうるので単純
なジャンプでは caller へ復帰で
きない.
Copyright (C) 2002, 2004 Tsuneo Nakanishi
手続き呼出の基本(3)
実引数の受け渡し: レジスタ $a0~$a3 (レジスタ番号 4~7)
を使用.
結果の受け取り: レジスタ $v0,$v1(レジスタ番号 2,3)を使
用.
callee へのジャンプ: jal 命令を使用.
caller へのジャンプ: jr 命令を使用.
jal 命令: レジスタ $ra(レジスタ番号 31)に jal 命令直後の命令
の番地(= PC + 4)を格納し,ラベルで指定される callee へジャン
プ.復帰時は jr 命令で $ra が格納する番地にジャンプ.
jal
jr
callee
$ra
# callee 呼出
# caller へ復帰
Copyright (C) 2002, 2004 Tsuneo Nakanishi
手続き呼出の基本(4)
caller 側のコード
int main ()
{
…
c = add (3, 4);
…
d = add (c * 2, 2);
…
}
変数名
レジスタ
c
$s0
d
$s1
…
addi
addi
jal
add
…
add
addi
jal
add
…
第一実引数
$a0, $zero, 3
第二実引数
$a1, $zero, 4
_add
返り値
$s0, $v0, $zero
$a0, $s0, $s0
$a1, $zero, 2
_add
$s1, $v0, $zero
【注意】addi 命令は,レジスタと定数値を加算して,その結果をレジスタに格納
する命令である.(後述)
Copyright (C) 2002, 2004 Tsuneo Nakanishi
手続き呼出の基本(5)
callee 側のコード
int add (int a , int b)
{
int c;
_add:
add
add
jr
c = a + b;
return c;
第一仮引数
$s0, $a0, $a1
$v0, $s0, $zero
$ra
第二仮引数
返り値
復帰先番地
}
変数名
レジスタ
c
$s0
caller が,呼出前に復帰先番地を $ra に設定していることに注意すること.
Copyright (C) 2002, 2004 Tsuneo Nakanishi
手続き呼出とスタック(1)
int add (int a, int b)
{
return a + b;
}
$ra が上書きされ
る.(=main に戻れ
ない!)
int dbl (int a)
{
int c;
c = add (a, a);
return c;
}
caller で使ってい
int main ()
{
たレジスタが上書き
int c;
される.
…
c = dbl (100);
引数が4つ以
foo (a, 3, b, 4, c);
…
上ある場合は?
}
_add:
add
jr
$v0, $a0, $a1
$ra
_dbl:
add
add
jal
add
jr
_main:
…
addi
jal
add
…
#add 第一実引数
$a0, $a0, $zero
#add 第二実引数
$a1, $a0, $zero
_add
$s0, $v0, $zero
$ra
$a0, $zero, 100
_dbl
$s0, $v0, $zero
Copyright (C) 2002, 2004 Tsuneo Nakanishi
手続き呼出とスタック(2)
MIPS のレジスタ使用に関するルール(=紳士協定!)
レジスタ名
レジスタ番号
用途
関数呼出時のレジスタ破壊
$zero
0
$v0~$v1
2~3
関数の返り値
$a0~$a3
4~7
引数用
$t0~$t7
8~15
一時保存用
$s0~$s7
16~23
変数用
$t8~$t9
24~25
一時保存用
$sp
29
スタックポインタ(後述)
不可
$fp
30
フレームポインタ(後述)
不可
$ra
31
戻り番地
不可
定数値 0,不要な結果の破棄
(NA)
可
不可
可
不可
可
関数呼出時のレジスタ破壊が「不可」のレジスタは,関数呼出直前と復帰直後
の値が同じになることを,プログラマが保証しなければならない.
Copyright (C) 2002, 2004 Tsuneo Nakanishi
手続き呼出とスタック(3)
上書き問題の解決策 = スタックの利用
スタック: 他のデータを後入れ先出し型(LIFO: Last-In First-Out)
で格納するデータ構造.データの追加(push),データの取り出し
(pop),最後に格納したデータの参照(top)のみが許される.
push 5
push 7
3
5
7
5
7
7
pop
push 3
pop
pop
Copyright (C) 2002, 2004 Tsuneo Nakanishi
手続き呼出とスタック(4)
MIPS におけるスタック:
メモリ領域の一部をスタックのために使用する.
MIPS では,29番のレジスタに $sp と名前をつけ,スタックの
一番上の番地を記憶するために使用する.(スタックポインタ)
$sp
112
100
104
108
112
$sp
108
–4
+4
push yyyy
xxxx
xxxx
100
104
108
112
116
yyyy
xxxx
xxxx
pop
Copyright (C) 2002, 2004 Tsuneo Nakanishi
手続き呼出とスタック(5)
手続き foo の呼出手順(不完全版):
1. 関数呼出によって破壊されうるレジスタ($t0~$t9,$v0,$v1)
の値を push する.(後に参照するレジスタのみでよい.)
2. foo を呼び出す.(jal foo 命令)
3. foo の内部で他の関数を呼び出す場合は $ra を push する.
4. 関数呼出によって破壊されてはならないレジスタ($a0~$a3,
$s0~$s7)の値を push する.(foo 内部で更新するレジスタの
みでよい.)
caller
callee
手続き foo からの復帰手順(不完全版):
1. 呼出後 4 で push した値を元のレジスタに pop する.
2. 呼出後 3 で $ra を push した場合は,その値を $ra に pop す
る.
3. foo を呼び出した jal 命令の次の命令にジャンプする.(jr $ra
命令)
4. 呼出前 1 で push した値を元のレジスタに pop する.
callee
caller
Copyright (C) 2002, 2004 Tsuneo Nakanishi
手続き呼出とスタック(6)
int foo (int a, int b, int c, int d, int e, int f)
{
int g, h;
…
}
スタック
$a0~$a3
… = foo (3, 8, 2, 5, 4, 6);
手続き foo の呼出手順(完全版):
1.
2.
3.
4.
5.
6.
7.
$sp
ローカル変数
(g, h)
callee 側
レジスタ保存
$ra 保存
caller 側
レジスタ保存
5番目以降の実引数を push
フレームポインタ 第5実引数(4)
caller 側レジスタ保存($t0~$t7 etc.)
第6実引数(6)
$fp
foo 呼出
$ra を push
手続きフレーム
callee 側レジスタ保存($s0~$s9 etc.)
ローカル変数のための領域をスタック上に確保
$fp をスタックフレームの底に設定.
Copyright (C) 2002, 2004 Tsuneo Nakanishi
手続き呼出とスタック(7)
手続きフレームは,関数の呼出ごとに生成される.
int bar ()
{
…
}
int foo ()
{
int x, y;
…
bar ();
…
foo ();
…
}
foo () 1回目
foo () 2回目
bar ()
bar ()
foo ()
bar ()
foo ()
foo ()
foo ()
foo ()
foo ()
呼出ごとに独立したローカル変数の実現.
再帰呼出が可能.
Copyright (C) 2002, 2004 Tsuneo Nakanishi
© Copyright 2026 ExpyDoc