Web Analytics Made Easy - StatCounter

工業大学生ももやまのうさぎ塾

うさぎでもわかるをモットーに大学レベルの数学・情報科目をわかりやすく解説! 数式が読み込まれない場合は1回再読み込みしてみてください。

うさぎでもわかる計算機システム Part22 MIPSアーキテクチャ・命令一覧 後編

こんにちは、ももやまです。

今回は前回に引き続きMIPSアーキテクチャの命令について紹介していきます。

後編では、

  1. メインメモリとレジスタ間のデータ転送
  2. 5つのアドレシングモード
  3. データ転送命令
  4. 分岐命令(強制・条件付き)
  5. 比較命令
  6. パイプライン処理

の6つについて説明していきたいと思います。

また、最後に前編、後編をあわせた練習問題を10問用意しているので、練習したい人はそちらもご覧ください!

MIPSアーキテクチャ・命令一覧 前編はこちら!

www.momoyama-usagi.com

1.メインメモリ⇔レジスタ間のデータ転送

(1) なぜメインメモリ⇔レジスタ間のデータ転送が必要か

レジスタに保存できる容量は、4バイト(32ビット)の32本分(合計128バイト)分となっており、メインメモリに比べて非常に容量が少ないです。

そのため、基本的に必要なデータはメインメモリにおかれます

しかし、演算はメインメモリ上ではなくレジスタ上でおかれるため、

1.メインメモリ → レジスタにデータを転送(コピー) 2. レジスタ上で演算 3. 演算終了後、レジスタ → メインメモリを転送(コピー)

のように、計算したいデータをレジスタに転送させたり、計算が終わったデータをメインメモリに戻してあげるデータ転送の処理が必要となります

(2) ロード命令の実行手順(メインメモリ→レジスタ)

ロード命令とは、メインメモリにあるデータをレジスタに転送する命令を指します。

MIPSではロード命令は以下の5ステップで実行されます。

Step1 命令フェッチ (IF)

プログラムカウンタが示しているアドレス値から4バイト分をメインメモリから命令ジレスタに格納します。

また、このタイミングでプログラムカウンタの値を4増加させ、つぎの命令を指しておきます。

Step2 命令デコード (ID)

命令レジスタに格納した4バイトの内容を解読(デコード)します。

Step3 命令実行 (EX)

どのメモリ番地にアクセスすればよいか(どのメモリ番地から読み出すか)を計算します。 (このアドレスのことを実効アドレスと呼びます。)

※ データ転送命令以外(演算命令など)では、このステップでALUによる演算が行われます。

Step4 メモリアクセス (MEM)

Step3で計算されたアドレスからメモリの読み出しをします。

※ メモリの読み書きの必要がない命令(加算やシフトなどの演算命令)ではStep4の操作は行われず、合計4ステップで実行されます。

Step5 書き込み(WB)

読み出されたデータをレジスタに書き込みます。

以上の5ステップでロード命令が行われます。

(3) ストア命令の実行手順(レジスタ→メインメモリ)

ストア命令とは、レジスタにあるデータをメインメモリに戻す命令を表します。

MIPSではストア命令は以下の4ステップで実行されます。

Step1 命令フェッチ (IF)

 (ロード命令と同じです。)

プログラムカウンタが示しているアドレス値から4バイト分をメインメモリから命令ジレスタに格納します。

この段階で、プログラムカウンタの値が +4 されます。

Step2 命令デコード (ID)

 (ロード命令と同じです。)

命令レジスタに格納した4バイトの内容を解読(デコード)します。

 

Step3 命令実行 (EX)

 (ロード命令とほとんど同じです。)

どのメモリ番地にアクセスすればよいか(どのメモリ番地から書き出すか)を計算します。 (このアドレスのことを実効アドレスと呼びます。)

 

Step4 メモリアクセス (MEM)

Step3で求めた実効アドレスにレジスタのデータを書き込みます。

 

ストア命令では、Step5に相当する「レジスタへデータを格納する」必要がないため、以上の4ステップで命令が終了します。

2.アドレシングモード

命令の操作対象(レジスタ、メモリ、定数など)を指定する方法のことをアドレシングモードと呼びます。

MIPSには、

  1. レジスタアドレシング(レジスタ指定)
  2. 即値アドレシング(定数指定)
  3. ベースアドレシング(メモリ指定)
  4. 疑似直接アドレシング(メモリ指定)
  5. PC相対アドレシング(メモリ指定)

の全部で5つのアドレシングモードがあります。

※ 3〜5はすべてメモリを指定するアドレシングモードですが、メモリの指定の仕方が異なります

(1) レジスタアドレシング

レジスタをオペランドとして指定する最も基本的なアドレシングモードです。

レジスタアドレシングとなっているオペランドの例を紹介します。

例1

add $t0, $s0, $s1 

$t0, $s0, $s1

加算 add ・減算命令 sub (および乗算・除算命令)や論理演算命令 and, or, xor, nor は3つのオペランドすべてがレジスタアドレシングです。

例2

addi $t0, $s0, 5

$t0, $s0 や、

sll $t1, $s1, 2

$t1, $s1

addi, andi, ori, xori の即値を指定しない第1、第2オペランドおよびシフト演算命令 sll, srl, sra の即値を指定しない第1、第2オペランドはレジスタアドレシングです。


他にもレジスタアドレシングでオペランドを指定する命令はたくさんありますが、キリがないので省略します。

(2) 即値アドレシング

指定された定数 n をオペランドとします。

addi $t0, $s0, 5

5(第3オペランド)や、

sll $t1, $s1, 2

2

即値演算命令 addi, andi, ori, xori の定数 n を指定する第3オペランドおよびシフト演算命令 sll, srl, sra の定数 n を指定する第3オペランド。

(3) ベース(相対)アドレシング

レジスタの値と、指定した定数(偏位と呼ばれます)の和でアクセスする番地(実効アドレス)を指定する方式です。

f:id:momoyama1192:20200103004716g:plain

MIPSでは、レジスタ・メモリ間のデータ転送命令(lw, sw など)で読み書きするメモリ番地を指定する際に使われます。

使用例
(プログラムの詳しい操作は下に載せています)

lw $t0, 8($fp)  # メモリ → レジスタに読み込み
sw $t1, 12($fp)  # メモリ → レジスタに読み込み

の第2オペランド 8($fp), 12($fp) がベースアドレシングとなっています。

※ 第1オペランドの $t0, $t1 は読み書きするレジスタを指定しているだけなので、レジスタアドレシングです。

(4) 疑似直接アドレシング

オペランドの下位28ビットを直接指定し、残りの上位4ビットをプログラムカウンタの上位ビットしてメモリ番地を指定する方式です。

※ なぜ下位28ビットだけ直接指定するのか

MIPSの命令長である32ビットのうち、6ビットはどんな命令かを指定するオペコードに使われるので、最大で残り26ビットを使うことができます。

疑似直接アドレシングでは、この26ビットを最大限にアドレス指定に使います。

ここでmipsの命令長は4バイト(32ビット)単位なので、絶対アドレスの値は必ず4の倍数、つまり2進数の下2桁は00になります

なので、移動先の下2桁「00」を追加し、28ビットまで指定をすることができます。

それでも残りの4ビットはどうしようもできないのでプログラムカウンタの上位4ビット、つまり次の命令のアドレスの上位4桁を残りの4ビットとし、プログラムカウンタの上位4ビットと指定した28ビットでアクセスする番地を決定します。

f:id:momoyama1192:20200103004730g:plain

つまり、メモリの番地を下のように16のエリア(アドレスの上位4ビットごと)に分割したときに、プログラムカウンタがあるエリア内であれば自由に絶対アドレス指定ができるのが擬似直接アドレシングです。

f:id:momoyama1192:20200103120106g:plain

MIPSでは後ほど説明する強制分岐命令(j, jal 命令)でのみ疑似直接アドレシングが使われます。

(5) PC相対アドレシング

現在のプログラムカウンタからどれだけ先(もしくは前)の番地にアクセスするかを指定する形式です*1

ただしMIPSでは高速化のために、PC相対アドレシングで実効アドレスを求める前にプログラムカウンタの値が次の命令のアドレス(現在の命令のアドレス+4)になるため、実際にPC相対アドレシングを使う場合は、現在の命令(PCの値)ではなくつぎの命令(PC + 4の値)を基準に「どれだけ先(前)」かを指定します。

f:id:momoyama1192:20200103004719g:plain


PC相対アドレシングで使えるビット数は16ビット(I形式の即値部分)です。

ここで、mipsの命令長は4バイト(32ビット)単位なので、アクセスを行うアドレスの値は必ず4の倍数、つまり2進数の下2桁は00になります。

なので、相対アドレス(基準番地から指定する番地がどれだけ先なのか)も当然4の倍数となり、下2桁は00となります。

なので、相対アドレスを指定する際には下2桁の「00」を省略することで2ビットを追加した18ビットをアドレス指定に使うことができます。

f:id:momoyama1192:20200103131011g:plain

ここで、18ビットのうちの1ビットはアドレスの前後(正負)を指定するために使う必要があるので、残りは17ビットとなります。

そのため、最大でプログラムカウンタの前後  2^{17} 番地を指定、アクセスすることができます。

アドレシングモードごとのメモリアクセス可能範囲

プログラムカウンタの値に対し、

  • 疑似直接アドレシング
  • PC相対アドレシング

でアクセスできる(実効アドレスにできる)メモリ番地の範囲を図で表すと、下のようになります。

f:id:momoyama1192:20200103004726g:plain

なお、レジスタの値を実効アドレスとする jr 命令、およびレジスタの値と偏位を指定するベース(相対)アドレシングでは、メモリのすべての番地をアクセス対象(実効アドレス)にすることができます。


MIPSにおける5つのアドレシングモード

MIPSでのアドレシングモード指定方法は全部で5つである。

そのうちの1つはレジスタを指定、1つは定数を指定、残り3つがメモリを指定のアドレシングモードである。

  1. レジスタアドレシング(レジスタ指定)
    レジスタをオペランドとして指定する。
  2. 即値アドレシング(定数指定)
    定数をオペランドとして指定する。
  3. ベースアドレシング(メモリ指定)
    レジスタの値と定数(偏位)を足した番地を実効アドレスとし、計算された実効アドレスを指定する。
  4. 疑似直接アドレシング(メモリ指定)
    実効アドレスの下位28ビットは直接指定、残りの上位4ビットは現在のプログラムカウンタと同じとし、計算された実効アドレスを指定。
  5. PC相対アドレシング(メモリ指定)
    次の命令のプログラムカウンタからアクセスしたいアドレスがどれくらい先かを相対的に指定。

3.命令紹介6 データ転送命令(メモリ→レジスタ)

では、前編に引き続き命令紹介をしていきたいと思います。

最初はメモリからレジスタへのデータ転送(ロード)命令です。

データの転送量、および「符号あり整数」・「符号なし整数」のどちらに設定したいかによって使う命令が変わってきます。


MIPSの場合、ロード命令・ストア命令に関わらず、実効アドレスの算出にはレジスタの値 $s と偏位 n (nには負の値も指定可能)を用います。

(1) 4バイト読み込み  lw  [load word]

※ 4バイト[32ビット]で1命令なので word です

メモリから連続する4バイトを読み込み、レジスタに格納するI形式の命令です。

lw $t, n($s) は、レジスタ $s の値と定数(偏位と呼ばれます) n を加算した値を番地とし、$s + n 番地から連続する4バイトのデータをレジスタに読み込みます。

※ 注意

$s + n を行う際、n のビット数を $s に合わせるため、32ビットに符号拡張されます。また、1命令は4バイトなので、n の値は必ず4の倍数となります。

f:id:momoyama1192:20200102121145g:plain

例えば、上の命令の $fp の値を 0x00003768、n を4とすると、指定される番地は 0x00003768 に4(正確には 0x00000004)を加算した 0x0000376C 番地から連続する4番地を読み込み、$t0 に格納します。

f:id:momoyama1192:20200102121151g:plain

上の図で示す通り、レジスタに格納する際は上位バイトから順番に1バイトずつ格納されます。

(2) 2バイト読み込み  lh  [load half]

※ 1命令の半分(2バイト)なので half です

メモリから連続する2バイトを読み込み、レジスタに格納するI形式の命令です。

lh $t, n($s) は、レジスタ $s の値と定数(偏位と呼ばれます) n を加算した値を番地とし、$s + n 番地から連続する2バイトのデータをレジスタの下位2バイトに読み込みます。

また、読み込んだ際32ビットに符号拡張されます。
(符号あり整数として考えます)

$s + n を行う際、n のビット数を $s に合わせるため、32ビットに符号拡張されます。また、1命令を半分にしたものを読み込むので、n の値は必ず2の倍数となります。

f:id:momoyama1192:20200102121155g:plain

例えば、上の命令の $fp の値を 0xA3490270、n を6とすると、指定される番地は 0xA3490270 に6(正確には 0x00000006)を加算した 0xA3490276 番地から連続する2番地を読み込み、$t0 に格納します。

f:id:momoyama1192:20200102121159g:plain

上の図で示す通り、レジスタの下位2バイトに格納する際は上位バイトから順番に1バイトずつ格納され、符号拡張されます。

上の例の場合、16進数での最上位の桁が 9 のため、符号ビットは1となります*2。そのため、1で桁埋めされます(16進数だと上位桁がFになります)。

(3) 2バイト読み込み (絶対値)  lhu  [load half unsigned]

先程の命令 lhu をつけることで、$t に読み込まれるビットが符号なし整数になります。

※ ただし偏位を指定する n は符号なし整数にはならないので、n の値を負の値にすることはできます。

f:id:momoyama1192:20200102121203g:plain

読み込む番地を計算し、下位2バイトを格納する部分は lh と同じです。

f:id:momoyama1192:20200102121207g:plain

ただし、符号なし整数として考えるので、符号拡張ではなくゼロ拡張が行われます。

(4) 1バイト読み込み  lb  [load byte]

メモリから1バイトを読み込み、レジスタに格納するI形式の命令です。

lb $t, n($s) は、レジスタ $s の値と定数(偏位と呼ばれます) n を加算した値を番地とし、$s + n 番地から1バイトのデータをレジスタの下位1バイトに読み込みます。

また、読み込んだ際32ビットに符号拡張されます。
(符号あり整数として考えます)

$s + n を行う際、n のビット数を $s に合わせるため、32ビットに符号拡張されます。

f:id:momoyama1192:20200102121211g:plain

例えば、上の命令の $fp の値を 0x11451410、n を-4とすると、指定される番地は 0xA0x11451410 に-4(正確には 0xFFFFFFFC)を加算した 0xA1145140C 番地のデータを $t0 に読み込みます。

f:id:momoyama1192:20200102164302g:plain

上の図で示す通り、レジスタの下位1バイトに格納され、符号拡張されます。

上の例の場合、16進数での最上位の桁が 4 のため、符号ビットは0となります。そのため、0で桁埋めされます。

(5) 1バイト読み込み (絶対値)  lbu  [load byte unsigned]

先程の命令 lbu をつけることで、$t に読み込まれるビットが符号なし整数になります。

※ ただし偏位を指定する n は符号なし整数にはならないので、n の値を負の値にすることはできます。

f:id:momoyama1192:20200102121220g:plain

読み込む番地を計算し、1バイトを格納する部分は lb と同じです。

f:id:momoyama1192:20200102164306g:plain

ただし、符号なし整数として考えるので、符号拡張ではなくゼロ拡張が行われます。

データ読み込み命令のまとめ

lw, lh, lhu, lb, lbu の5つの命令をまとめると下の図のようになります。

f:id:momoyama1192:20200102121259g:plain

4.命令紹介7 データ転送命令(レジスタ→メモリ)

レジスタからメモリへのデータ転送(ストア)命令も紹介したいと思います。

(1) 4バイト書き込み  sw  [store word]

※ 4バイト[32ビット]で1命令なので word です

レジスタの中身を上位から順にメモリに格納します。

sw $t, n($s) は、レジスタ $s の値と定数(偏位と呼ばれます) n を加算した値を番地とし、$s + n 番地から連続する4バイトにレジスタの中身を上位バイトから順番に格納します。

※ 注意

1命令は4バイトなので、n の値は必ず4の倍数となります。

f:id:momoyama1192:20200102121230g:plain

例えば、上の命令の $fp の値を 0x33411924、n を4とすると、指定される番地は 0x33411924 に4(正確には 0x00000004)を加算した 0x33411928 番地から連続する4番地に $t0 を上位のバイトから1バイトずつ格納していきます。

f:id:momoyama1192:20200102121236g:plain

なお、残りの上位16ビットは無視されます。

(2) 2バイト書き込み  sh  [store half]

※ 2バイト[16ビット]で1命令なので half です

レジスタの下位2バイトの中身を上位から順にメモリに格納します。

sh $t, n($s) は、レジスタ $s の値と定数(偏位と呼ばれます) n を加算した値を番地とし、$s + n 番地から連続する2バイトにレジスタの下位2バイト(下位半分)の中身を上位バイトから順番に格納します。

※ 注意

1命令の半分は2バイトなので、n の値は必ず2の倍数となります。

f:id:momoyama1192:20200102121246g:plain

例えば、上の命令の $fp の値を 0x1192ACE4、n を-2とすると、指定される番地は 0x1192ACE4 に-2(正確には 0xFFFFFFFE)を加算した 0x1192ACE2 番地から連続する2番地に $t0 の下位2バイトを上位のバイトから1バイトずつ格納していきます。

f:id:momoyama1192:20200102121241g:plain

(3) 1バイト書き込み  sb  [store byte]

レジスタの1バイトの中身を上位から順にメモリに格納します。

sb $t, n($s) は、レジスタ $s の値と定数(偏位と呼ばれます) n を加算した値を番地とし、$s + n 番地から1バイトにレジスタの下位1バイト(下位1/4)の中身を格納します。

f:id:momoyama1192:20200102121250g:plain

例えば、上の命令の $fp の値を 0x1129E50A、n を4とすると、指定される番地は 0x1129E50A に4(正確には 0x00000004)を加算した 0x1129E50E 番地に $t0 の下位1バイトをを格納します。

f:id:momoyama1192:20200102121255g:plain

なお、残りの上位24ビットは無視されます。

データ書き込み命令のまとめ

sw, sh, sb の3つの命令をまとめると下の図のようになります。

f:id:momoyama1192:20200102121303g:plain

5.命令紹介8 強制分岐命令

ここからは分岐命令についてです。

強制分岐命令と条件分岐命令の2つがあるので、まずは強制分岐命令について説明していきます。

(1) 強制分岐 j [jump]

j は強制分岐命令の基本的な形です。また、J形式の珍しい命令です。

j LABEL とすることで、LABEL がついている命令に無条件で分岐します。

f:id:momoyama1192:20200103005711g:plain

例えば、

    j    GO             # この命令を実行
    addi $t0, $t0, -1
GO: add  $t0, $s0, $s1 # ここにワープ

という命令の j GO を実行すると、GO のラベルがある2行下にワープします。
addi $t0, $t0, -1 が無視されます)

なお、j GOGO には、分岐先の命令の下位28ビット〜下位3ビットが入ります(アセンブルの際に自動的に計算してくれます)。

※ 詳しくは第2章の(4)疑似直接アドレシングの部分をご覧ください

jal はサブルーチン(関数)用のJ形式強制分岐命令です。

どっかの航空会社じゃないですよ。

jal LABEL とすることで、LABEL がついているサブルーチンに無条件で分岐し、分岐する際にレジスタ $ra に次の命令のアドレスを格納します。

f:id:momoyama1192:20200103005715g:plain

次に紹介するレジスタで示された番地に分岐する jr 命令とレジスタ $ra を使うことで、サブルーチンから元に戻ることができます。

(3) レジスタ強制分岐 jr [jump register]

jr はレジスタで指定されたアドレスに分岐するR形式の命令です。

どっかの鉄道会社じゃないですよ。

jr $r とすることで、$r 番地に強制分岐します。

レジスタの値を使うので、j 命令などと異なり、すべての番地にワープすることができます

f:id:momoyama1192:20200103005721g:plain

$ra には、jal 命令の次の番地が記録されているため、jr $ra とすることで、サブルーチンから元に戻ることができます。

6.命令紹介9 条件分岐命令

つぎは条件分岐命令について説明していきます。

値が等しいときに分岐する命令 beq と、等しくないときに分岐する命令 bne の2つがあります。

(1) 等号ならば分岐 beq [branch on equal]

beq は2つのレジスタの値が等しいときに指定されたラベルの命令に分岐するI形式の命令です。

f:id:momoyama1192:20200103185826g:plain

beq $s1, $s2, LABEL は、$s1$s2 の値を比べ、等しければ LABEL がついている命令に分岐します。

※ 等しくなければそのまま次の命令を実行します

LABEL に入る値は?

beq $s1, $s2, LABELLABEL には、分岐元(比較命令の次の命令)から何命令先かが入ります。

※ このようなオペランドの指定方法をPC相対アドレシングモードと呼びます*3

例えば、

       beq  $s1, $s2, LABEL
       addi $s1, $s1, -1
       sra  $s2, $s2,  1
LABEL: add  $t0, $s1, $s2

となっている場合、分岐元(比較命令の次の命令)から見て分岐先(ラベル LABEL がある命令)2命令先なため、LABEL の値は2となります。

f:id:momoyama1192:20200103185834g:plain

なお、beq $s1, $s2, LABEL をわざわざ beq $s1, $s2, 2 としなくても、アセンブラから機械語に変換する際に自動的に何命令離れているかを計算してくれます。

便利ですね!


もちろん、下のような「前に戻る条件分岐命令」も設定することができます。

f:id:momoyama1192:20200103185839g:plain

この場合、分岐元(beq の次の命令)から見て分岐先は2命令前(-2命令先)なので、LABEL は -2 となります。


※ しかし前に戻る場合は、beq のような条件分岐ではなく j 命令のような強制分岐が使われることが多いです。

(2) 等号でなければ分岐 bne [branch on not equal]

bne は2つのレジスタの値が等しくないとき、指定されたラベルの命令に分岐するI形式の命令です。

f:id:momoyama1192:20200103185830g:plain

bne $s1, $s2, LABEL は、$s1$s2 の値を比べ、等しくなければ LABEL がついている命令に分岐します。

※ 等しければそのまま次の命令を実行します

LABEL の値の計算法は beq と同じです。
(PC相対アドレシングモードです)

比較命令における注意

例えば、$s1$s2 の値が等しいときに $s1 の値を1減らし、$s2 の値を2増やす処理をしたいとします。

C言語のような高級言語であれば、

if (s1 == s2) {
    // 条件に合致するときの処理
    s1 -= 1;
    s2 += 2;
}

のようにカッコの中に条件に合致するときの処理($s1 を1減らし、$s2を2増やす)を書きます。

しかし、アセンブラ(MIPS)の場合、

       beq  $s1, $s2, LABEL  # s1 == s2 ならば LABEL へ
       addi $s1, $s1, -1
       addi $s2, $s2,  2
LABEL: add  $t0, $s1, $s2

のように書いてしまうと、s1 == s2 のときに LABEL にワープしてしまい、思った通りの処理がされません。

$s1$s2 が等しくないときに $s1 を減らし、$s2 を増やしてしまい、真逆の操作になる)

そのため、アセンブリで条件分岐を用いた書く場合、下のように条件分岐とラベルの間に条件を満たさない場合の処理を書く必要があります。

f:id:momoyama1192:20200103185843g:plain

上で紹介した処理を書きたい場合、$s1$s2 が等しくないときに LABEL にワープさせ、等しいときに行う処理をさせないようにします。

7.命令紹介10 比較命令

比較命令は、2つのレジスタの値を比較し、どちらが大きいかを0か1の値で返します。

先程紹介した条件分岐命令とセットで使われます。

(1) 比較命令 slt [set on less than]

slt は2つのレジスタの値を比較するR形式の命令です。

slt $rd, $rs, $rt は、$rs$rt の値を比べ、$rs のほうが小さければ1、$rs のほうが大きい or 等しければ0を $rd に代入します。

f:id:momoyama1192:20200103185847g:plain

例えば上の命令の場合、$s0 の値が 0xFFFFFFF0(10進数で-16)、$s1 の値が 0x00000003(10進数で3)であれば、$s0 のほうが小さいので $t0 の値は1となります。


なお、逆に $rs のほうが大きければ($rs > $rt であれば)1を返す smt (set on more than) に相当する命令はありません。

頑張って slt だけで判定しましょう。

(2) 絶対値比較命令 sltu [set on less than unsigned]

sltu をつけ、sltu とすることで $rs$rt の値を符号なし絶対値としてみなし、判定を行います。

※ 判定基準は slt と同じく $rs のほうが小さければ1、$rs のほうが大きい or 等しければ0を $rd に代入します。

f:id:momoyama1192:20200103185851g:plain

例えば、上の命令の $s0 の値が 0xFFFFFFF0$s1 の値が 0x00000003 であれば、$s0 のほうが(明らかに)大きいので $t0 の値は0となります。

(3) 即値比較命令 slti [set on less than immediate]

slti はレジスタの値と定数を比較するI形式の命令です。

slti $t, $s, n は、レジスタ $s と定数 n の値を比べ、$s のほうが小さければ1、$s のほうが大きい or 等しければ0を $t に代入します。

f:id:momoyama1192:20200103185856g:plain

例えば上の命令の場合、$s00x00000007n が7(正確には 0x0007)であれば、s0n が等しいので t0 の値が0となります。

(4) 即値絶対値比較命令 sltiu [set on less than immediate unsigned]

sltiu をつけ、sltiu とすることで $s を符号なし絶対値としてみなし、判定を行います。

f:id:momoyama1192:20200103185900g:plain

※ 判定基準は slti と同じく $s のほうが小さければ1、$s のほうが大きい or 等しければ0を $t に代入します。

8.命令紹介11 なにもしない命令

最後に少し特殊な「なにもしない命令」を紹介したいと思います。

なにもしない命令 nop [no operation]

この命令は何もしない命令です。

f:id:momoyama1192:20200103214612g:plain

じゃあ何に使うんだよ!!! と思うかもしれません。

nop は意図的に命令の実行を遅らせるときに使います。

詳しい使用例は次のおまけ「パイプライン処理」で説明したいと思います。

9.おまけ パイプライン処理

プロセッサがアセンブリなどの命令を実行する過程は、

  1. 命令フェッチ (IF)
  2. 命令デコード (ID)
  3. 命令実行 (EX)
  4. メモリアクセス (MEM)
  5. 書き込み (WB)

の5つに分類することができます。

アセンブラなどの命令が実行される際、CPUは1〜5の処理を1つずつ直列的に処理するのではなく、下の図のようにそれぞれの処理を並列的に実行していくことで処理効率を上げます。

f:id:momoyama1192:20200103214558g:plain

しかし、パイプライン処理はいつもうまくいくとは限りません。

例えば、下のような処理過程の場合、$s1$s2 の書き込み(WB)が終わるまで3行目の add 命令が実行できず、処理効率が悪くなってしまいます。

f:id:momoyama1192:20200103214603g:plain


これを防ぐために先程紹介した「なにもしない命令 nop」を何個か入れ、命令実行をわざと遅らせることで処理効率が悪くならないようにします。

f:id:momoyama1192:20200103214608g:plain


他にもパイプライン処理がうまくいかない例として分岐命令があります。

分岐命令があると、分岐命令の処理結果によってつぎに実行する命令が変わってしまうため、パイプライン処理を止めてつぎに実行する命令が判明するのを待つ必要があります。

10. さいごに

今まで紹介した命令の一覧を仕様書にしたものをこちらに用意しました。


今回は、前編と後編の2回にわけてMIPSアーキテクチャの基本的な仕組みと命令内容についてまとめていきました。

この記事を見て、アセンブラやMIPSアーキテクチャの基本を理解していただければ幸いです。


次回は、前回と今回で説明したMIPSアーキテクチャの知識を確認するための演習問題とその解説をしていきたいと思います。


演習問題はこちら!

www.momoyama-usagi.com

なお、次回が計算機システムの最終回記事です。

*1:先のアドレスを指定する場合は正の値、前のアドレスを指定する場合は負の値となります。

*2:16進数での最上位の桁が8(0x1000)以上であれば符号ビットは1、7(0x0111)以下であれば符号ビットが0となります。

*3:当然ですがPC相対アドレシングモードを使っているのは第3オペランドの LABEL だけで、他の $s1 や $s2 はレジスタアドレシングです。