今回,私たちはMIPSアーキテクチャについて学びます.
その際の参考書がこちら↓
「パターソン&ヘネシー コンピュータの構成と設計(ハードウェアとソフトウェアのインタフェース)<上>第3版」
http://ec.nikkeibp.co.jp/item/books/P82660.html
「プログラマの基本を身につけたいなら,この本を読めば全てOKだ!」と紹介されたので思い切って購入,それが出会いでした.
情報系の大学,学科では教科書として扱われているようですね.自分は機械系なので,今年の春まで存在を知りませんでした.
どうも,星姫でーす.
以下,この本で学んだMIPSアーキテクチャについて,まとめていこうと思います.
---------------------------------------------------------------------------------------------------------------
0と1で表現する2進数,CPUへ渡す32bitの命令を記述してみると…
00000000000000000000000000000000
うわぁ…悲惨,ケタすら数えたくなくなりますね.
これがいわゆる機械語(machine language)なんです.
マシンが処理するデータであり,0と1は第11話で紹介したように,電圧が「有る」と「無い」で理解できます.
0と1はソフトウェアの最小構成要素,プログラムの原子のようなものです.
MIPSでは命令長が32bitと統一されています.優れた設計は規則性が大切なんだそうです.
その32bitの命令を以下のようにブツ切りにして,この区切られた部分をフィールド(field)と呼ぶことにします.
000000 00000 00000 00000 00000 000000
6 bit 5 bit 5 bit 5 bit 5 bit 6 bit
これらのフィールドにはそれぞれ名前がつけられていて,以下のように対応しています.
000000 00000 00000 00000 00000 000000
op rs rt rd shamt funct
説明は次の通りです.面倒な方は次の太字まで読み飛ばして下さい.
rs: 第1ソース・オペランドのレジスタを表します.オペランドってのは演算の対象となる値や変数のことだよ.
rt: 第2ソース・オペランドのレジスタを表します.a + b, a - b とか,ソース・オペランドは必ず2つあるのが一般的です.
rd: 計算結果格納先・オペランドのレジスタを表します.a + b = c でいう所の c です.
shamt: シフトと呼ばれる操作がありますが,そのシフト量が書かれるフィールドです.
funct: 命令操作のバリエーションです.6bitなので最大64種の機能を記述できます.
opはoperation codeの略,rs,rtはregister source (two?)の略,rdはregister destinationの略.
shamtはshift amountの略,functはfunction codeの略…と勝手に解釈してます.記憶する連想のタネにどうぞ.
さて,ここで一つ疑問が解けます.
どうしてレジスタの大きさが32bitで32本なのか?という疑問です.
答え:命令長が32bitであり,フィールド rs,rt,rd が 5bit だからです.
5bitってことは32までしか表現できません.
つまり,レジスタが32本以上あっても,余分なレジスタへはアクセスできないのです.
ここまで学べば,みなさんは「アーキテクチャが,CPUの設計を決めている」と聞いて,理解できると思います.
逆に「CPUを作ろう〜」という場合,まず最初に,完璧にアーキテクチャを学んでないといけないことになるのです.
さて,重ねて言うようですが,ここまで学んでしまえば,CPUへの命令が,私たちに読めます.さっそく次の命令を読んでみましょう.
00000010001100100100000000100000
うわぁ…やっぱ悲惨,読みづらいです.とりあえず各フィールドへ分割してみましょう.
000000 10001 10010 01000 00000 100000
op rs rt rd shamt funct
op + funct つまり 000000 + 100000 で加算命令を表します.(教科書参照)
rs , rt, rd はそれぞれ 17番, 18番, 8番のレジスタを表しています.
shamt = 00000 ですが,シフト命令では無いので今回は使いません.
よ,読める!私にもコンピュータの言語が読める!
00000010001100100100000000100000
訳↓
レジスタ17番とレジスタ18番の数値データを加算して,結果をレジスタ8番に格納しろ.
きゃはっ,これは自慢出来ちゃうかも.「私は機械語が読めます」ってね.
ここから,記憶力が必要になるので,面倒な方は◆の終わりまで読み飛ばして下さい.
◆バイト(byte)とは?
ここで新しい単位の登場です.
今までbitでやり取りしていましたが,これからは8bitを1byte(バイト)として扱います.
ほぼ全てのアーキテクチャで,8bit,つまり1byte(バイト)単位でアドレスを表現しているからです.
◆語(word)とは?
もうひとつ新しい表現をここで紹介,4byte,つまり32bitを1word(語)と表現します.
MIPSアーキテクチャ,命令長が32bitのアーキテクチャにとって,1wordは有用だからです.
◆ロード(load)とストア(store)
前回の話で,メモリとCPUは32bitのデータをやり取り(データパス)していると述べました.
それがコンピュータの本質であるとも言いました.その際の用語の確認ということで以下のことを覚えて下さい.
メモリからレジスタへデータを転送する命令を一般にロード(load)命令と呼びます.
その逆,レジスタからメモリへデータを転送する命令をストア(store)命令と呼びます.
◆ベース(base)とオフセット(offset)
ロード命令やストア命令は,巨大なメモリ領域のアドレス(address)を指定してデータを転送しています.
数十億本もあるメモリ・アドレスに対して,一本一本を指定してデータをやり取りしているということです.
MIPSでは,そのアドレスの指定に,ベース(base)とオフセット(offset)を使用しています.
ベースはメモリの「とあるアドレス」を指してます.オフセットはベースからの差分量を表しています.
ベースの前後,オフセットの最大幅まで,メモリのアドレスを指定することができるようになっています.
これまで紹介してきたMIPSアーキテクチャの命令形式をレジスタ用ということで,R形式と呼びます.
000000 00000 00000 00000 00000 000000
op rs rt rd shamt funct
これ↑がR形式です.
ロード(load)とストア(store)をR形式で行おうとすると,アドレス表現はどのフィールドを用いても5bitしか確保できません.
一般のプログラムで使用する配列(後で出て来る用語です)は,こんなに小さくはありません.
アドレス空間をもっと広げる必要があります.
新しい命令形式,I形式を以下に紹介します.
000000 00000 00000 0000000000000000
6 bit 5 bit 5 bit 16 bit
op rs rt address
このI形式では,ベース(rs)からオフセット(address)±2^15(±32,768byteすなわち±8,192語)の
範囲内の任意の語をロード出来るようになります.
ここで注意しなければならないのは,データの格納先が(rtフィールド)であることです.
つまり,rtが格納先(destination)になっていることです.(I 形式のI はimmediateのi です.即値を意味しています.)
では今学んだロードとストアを交えた命令を読んでみましょう.
10001101001010000000010010110000
00000010010010000100000000100000
10101101001010000000010010110000
相変わらず,悲惨な作業ですね.頭おかしくなりそうです.
まず最初の6bitを見て,R形式(op = 000000)とI形式にフィールドを分けます.(教科書参照)
100011 01001 01000 0000010010110000
000000 10010 01000 01000 00000 100000
101011 01001 01000 0000010010110000
訳↓
レジスタ9番のベース・アドレス+オフセット1200byte分,すなわち300word後ろのアドレスに入っている32bitのデータを
レジスタ8番へ格納しろ.(ロード命令)
レジスタ18番とレジスタ8番の数値データを加算して,結果をレジスタ8番に格納しろ.
レジスタ8番の32bitデータを,レジスタ9番のベース・アドレス+オフセット1200byte分
すなわち300word後ろのアドレスへ格納しろ.(ストア命令)
そろそろ,みなさん目がチカチカして,頭がクラクラしてくる頃だと思います.(私は目がしばしばしてきました…)
初期のプログラマは,このように2進数を使用してコンピュータと会話していましたが
この煩わしさに頭を悩ませ,ついには精神衛生を保てなくなり,鬱になったり,仕事ができなくなったりしました.
ほどなく,彼らはコンピュータへの命令をもう少し人間に近い言葉で書き表す方法を考案しました.
当時はその表記を,人手で2進数に翻訳していたそうです.(結局,精神衛生が保てなかったとか…)
そこで,記号(シンボル)で書き表されたプログラムをコンピュータを使って2進数に翻訳するプログラムが発明されました.
この種のプログラムは,アセンブラ(assembler) と名付けられています.
アセンブラで翻訳されるシンボル形式の言語は,アセンブリ言語(assembly language) と名づけられました.
この発明により,プログラマの環境は改善されました.先ほどの機械語をアセンブリ言語に書きなおしてみます.
10001101001010000000010010110000
00000010010010000100000000100000
10101101001010000000010010110000
機械語からアセンブリ言語に変換(逆アセンブル)↓
lw $t0,1200($t1)
add $t0,$s2,$t0
sw $t0,1200($t1)
このアセンブリ言語について,少し解説してみます.
lw $t0,1200($t1): load wordの略,$t0,$t1はそれぞれレジスタ8番と9番を表しています.1200はbyte単位のオフセットです.
add $t0,$s2,$t0: additionつまり加算命令であり,第一オペランドが格納先レジスタ,続く二つが第1,第2ソース・オペランドです.
sw $t0,1200($t1): store wordの略.lwで説明しませんでしたが($t1)がベースレジスタです.$t0が格納先レジスタです.
このアセンブリ言語ですが,改善されたとはいえ,コンピュータの動作に依存する言語であるからして,自然な表記とは言えません.
当時,研究者が理論を実現できるとコンピュータに手を出しましたが,彼らはアセンブリ言語の扱いに悩まされました.
そうして,より強力な言語がコンピュータの命令へと翻訳される流れとなったのです.
高水準プログラミング言語(high-level programming language)の登場です.
事務処理用にCOBOL,記号処理用にLISP,科学技術計算用にFortranと高水準プログラミング言語が開発されました.
第009話でさわったC++言語もこの高水準プログラミング言語のうちの一つです.
****************************************************************************
ここまでに学んだコンピュータとプログラムの知識は,今後C++言語を理解する上で使えます.
この先,不安なことがあったら,是非読み返してみて下さい.
さて,まだまだMIPSアーキテクチャについて説明すべきことがあるのですが,次回にやりたいと思います.
2009/09/19 初記.
2009/09/24 追加.
2009/09/27 修正.