CPU の構造

プログラムカウンタ

メモリには、データとプログラムが両方区別なしに記憶される、という話は 前にした。また、メモリにのっているプログラムは、CPU によって順次によみ 出されて順に実行される、という話もした。

そうすると、今メモリの中のどのアドレスにある命令を読んで実行しよう としているか、を覚えておく仕掛が必要になる。そのために用いられるものがプログラムカウンタ(program counter, PC)と呼ばれるものである。プログラムカウンタは、インストラクションポインタ(instruction pointer, IP)と呼ばれることもある。

プログラムカウンタは、メモリのアドレスが記憶できるようになっているレ ジスタの一種である。そこに現在記憶されているアドレスから、これから実行 しようとするプログラムを読み出すようになっている。プログラムの実行中、 ほとんどの時間は、メモリに入ったプログラムをアドレスの順に順番に実行し ている。例えば、100番地からの4バイト(100〜103番地)に1つの命令が入って いて、今それを実行しているとする。すると、次の命令としては、104番地に 入っているものが実行される。従って、プログラムカウンタの内容は、100, 104, 108,… のように変化して行く。

ただし、分岐(branch, jump)命令というものが実行された時だけは様子が変わる。例えば、20番地先へジャンプ(分岐)せよ、という命令が実行された時は、次の命令としては、(今100番地を実行しているとすれば、)120番地にあるものが実行される。これは、プログラムカウンタの値に20を加えたことになる。 (普段は、プログラムカウンタの値には、命令の長さ(今の例だと4)が順々に加 えられて行く。)

プログラムのくり返しは、分岐を使って行なう。分岐によって、現在実行している所よりも、手前の方(アドレスの小さい方)に分岐すると、先程一度実行した場所にまた戻ることができる。これによって、同じ場所を何度も実行することができる。


            命令1 ←──┐
            命令2       ↑
              :         │
              :         │
            命令n ─→─┘
                   分岐

ここで、無条件に分岐するようにしていると、いつまでたっても同じ所ばか り無限にくり返すことになる。つまり、無限ループになる。 わざと無限ループを作る場合もあるが、多くの場合、何かの条件が成り立っ たらループを終了したいであろう。そのためには、ある種の条件が成立してい るかどうかを調べて、それによって、分岐するかしないかを選べるような分岐 命令を利用する。このような命令を条件分岐命令(conditional branch)という。 条件が成り立たなくなるまでループするには、下図のようにすればよい。

            命令1 ←───┐
            命令2         ↑
              :           │
              :           │
            条件分岐─→─┘条件成立の時
              │
              │
              ↓条件不成立の時

無条件に分岐するほうの命令は無条件分岐命令(unconditional branch)という。

もちろん、分岐の際アドレスの大きい方向にジャンプしてもよい。例えば、

            条件分岐───┐条件成立時
            命令1         │
      ┌──無条件分岐    │
      │    命令2 ←───┘
      └──→↓
              ↓

のような形で条件分岐と無条件分岐を組み合わせて使うと、「条件が成り立っ た場合は命令2を、成り立たなかった場合は命令1を実行する」といった場合分 けの処理ができる。つまり、C 言語で言えば

         if (条件) {
           命令2
         } else {
           命令1
         }

に相当する処理ができる。

色々な場合分けができるように、条件分岐命令には多くの種類が用意されてい るのが普通であり、種類ごとに違う条件の判定を行うようになっている。具体 例はあとで紹介する。

ALU

[春学期の復習] CPU は、算術演算や論理演算を実行するための回路を必ず持っている。これ を算術論理演算ユニット(Arithmetic and Logic Unit, ALU)と呼んでいる。

アキュムレータ

[アキュムレータの話は古い話題なので、興味のある人、あるいは文献でアキュムレータの名前を目にして何だろうと思った人だけが読んでくれればよい。期末試験には出さない。]

「アキュムレータ: レジスタの古い呼び名。この用語を「レジスタ」の同義語として口にする人がいたら、その人が相当の古株であることはまず間違いない。---Eric Raymond, The New Hacker's Dictionary, 1991」

昔の計算機は、ハードウェア製作に非常に大きなコストがかかった。そのた め、演算用のレジスタを多数作ることは難しかった。そこで、演算用レジスタ は一個だけ置くことにしていたコンピュータが多い。そのようなレジスタをア キュムレータ(accumulator)と呼ぶ。アキュムレータというのは、直訳すれば 「累積器」くらいの意味である。演算結果を累積して行く場所というわけであ る。

ここで、「演算用のレジスタ」という言葉の意味であるが、例えば、A - B を計算したいとして、A の値を用意しておく場所と、B の値を用意しておく場 所、それに、A - B の結果をしまう場所が必要である。「演算用のレジスタ」 というのは、そのような場所として使えるレジスタ、という意味である。

演算用レジスタがアキュムレータしかない場合、例えば、A をアキュムレー タに置き、B をメモリに置き、A - B の結果はアキュムレータに入れる(従っ てアキュムレータに以前入っていた値 A は消される)、といった形の機械語命 令を使って演算しなければならない。

   例:                A                               B
                 ┌───-┐                     ┌───-┐
   アキュムレータ│   3   │     メモリの100番地 │   2   │
                 └───-┘                     └───-┘
                         │
                         │  Sub 100 命令  (100番地の内容を引け)  ※ これは架空のコンピュータの命令
                         │
                     A-B ↓                           B
                 ┌───-┐                     ┌───-┐
   アキュムレータ│   1   │     メモリの100番地 │   2   │
                 └───-┘                     └───-┘

しかし、現在では、CPU 内に多数のレジスタを置くことができる。従って、 複数のレジスタを演算用に用いることができるのが普通である。そのため、ア キュムレータという言葉は段々聞かなくなってきたが、今だに教科書では時々 目にする言葉である。

フラグ(フラグレジスタ)

ある演算の結果がどのようであったか(0になったか、負になったか、桁あふ れが起こったか)によって条件分岐ができるようにしたいとする。その場合、 一つの考え方は、演算結果がどのようだったかを覚えるための小さなレジスタ を用意し、演算を行うたびに、その結果に応じてそのレジスタに適切な値を設 定するようにする、というものである。条件分岐命令はそのレジスタの中のい くつかのビットを見て、それらの値に応じて分岐するか否かを決める。このよ うな目的のために設けられるレジスタを「フラグレジスタ」などと呼んでいる。 (CPU によっては違う名前かも知れない。)

一般に、フラグという言葉は、ある条件が成立しているか否かを表すために用 意された小さな記憶領域を指して言う。一つのフラグには1ビットの領域があ れば十分である。フラグレジスタは、いくつかのフラグを1まとめにしたもの なので、全体としては、数ビットの大きさになる。フラグの語源はもちろん 「旗」である。旗が立っているか、いないかという1ビットの区別で何らかの 合図をすることから来ている。

フラグが「条件成立」を表す状態の時、「フラグはオンである」とか、「フラ グが立っている」という。「条件不成立」の時は、「フラグはオフである」と か、「フラグが降りている」などという。

(C のような言語でプログラムを書いている時も、ある変数をフラグとして 利用することがよくある。その場合、例えばその変数に0が入っているか、0以 外が入っているかでフラグのオン・オフを区別したりする。この場合、変数の サイズは 1 ビットより大きいかも知れないので、1つのフラグに複数ビット使 うこともあるわけである。)

話を戻すと、例えば N フラグ(negative flag)というフラグビットをフラグレ ジスタに用意しておき、演算命令を実行するたびに、その結果が負なら N フ ラグを立て、負でなければ N フラグを降ろす、といったことをするわけで ある。そして、「直前の演算結果が負なら分岐する」という分岐命令を実行す る時は、N フラグを参照して分岐するかどうかを決めるわけである。他によく 設置されるフラグとしては、演算結果が 0 だったかどうかを示すフラグ、桁 あふれが起きたかどうかを示すフラグなどがある。

また、そのようなフラグレジスタを持たない CPU もある。そういう CPU の場 合、分岐命令として例えば

「レジスタ R0 と R1 を比較して R1 のほうが小さければ分岐する」

といったものが用意されていることがある。R0 に 0 を入れておき、R1 に演 算結果を入れておけば、この分岐命令により、「演算結果が負なら分岐する」 という処理が実現できるわけである。そのような考え方を使えば、フラグの必 要な局面は減る。また、あとで紹介する MIPS CPU ではフラグ専用のレジスタ を置いていないが、フラグが必要な時のために、32個の汎用レジスタのうちの どのレジスタでもフラグに使えるようになっている。

演算ユニットについて

乗除算回路は整数演算の場合でも加減算や論理演算の回路よりもずっと複雑 である。そのため、昔は乗除算回路を持たない CPU もよくあった。そのよう な場合、乗除算はソフトウェアによって実現していた。つまり、加減算命令や 論理演算命令、条件分岐などを組み合せたプログラムを実行することで乗除算 を行なっていた。しかし、今日の CPU には整数の乗除算を行なう回路が入っ ているのが普通である。整数演算回路を IU (Integer Unit)ということがある。 浮動小数点演算回路FPU (Floating Point Unit) と呼ばれる。 浮動小数点演算は整数演算回路よりもさらにずっと複雑になる。そのため、 浮動小数点演算回路は色々な点で整数演算回路と別の扱いをされることも多い。 まず、浮動小数点演算回路を持たない CPU も存在する。その場合、

演算回路を複数持ち、それらを同時に動かして計算速度をかせぐ CPU も多 いが、整数演算回路と浮動小数点演算回路の個数が違うことはまれではない。

コプロセッサ

メインの計算処理を行う CPU に付加する形で、その手伝いをするプロセッサを置いているコンピュータがよくある。それらを「コプロセッサ(coprocessor)」と呼んでいる。("co" は「共に」を意味する接頭辞。)

コプロセッサの例としては、先に説明した外付け FPU がある。また、入出力処理専門のコプロセッサを置いていたコンピュータもある。