高等学校情報/情報の科学/CPUの処理の仕組み

まず、計算機が必要である。
これは、半加算器や全加算器で作れる。
『高等学校情報/情報の科学/論理回路と半導体』で、半加算器の仕組みを説明した。(※ なお、リンク先の単元で、普通科範囲外の、ダイオードによるAND回路やOR回路の話題も記述しておいた。)
(※ 普通科の範囲外。)
次に、制御のために、データを保存するのに必要なものを考えよう。パソコンでは、メモリ基板だけでなくCPUにも、データ保存のための領域がある。(メモリ基板やハードディスク) なので、まず、データを記憶できる回路をつくろう。
CPUのための記憶の回路をつくるには、フリップフロップ(flip flop)という論理回路を使う。(普通科目「情報」の範囲外。工業高校科目「ハードウェア」などで習う。)なお、フリップフロップのことを別名で「RSラッチ」または「SRラッチ」ともいう。後述のように、セット(S)とリセット(R)があるからである。なお英語でラッチとは、扉(とびら)などの 掛け金(かけがね)のことである。
フリップフロップでは、Qが出力である。Sは「セット」という入力である。Rは「リセット」という端子である。
まず、OR記号の後ろ(図では右側)についている丸「○」は、NOT回路の略記号である。
SとRが両方とも1の場合は、フリップフロップでは禁止されている。
入力の片方をセットといい、Sであらわす。 入力のもう片方をリセットといい、Rであらわす。
フリップフロップでは、上述の動作原理の図のように、いったん入力してしまえば、その出力がまた入力に戻ってくる仕組みなので、ひきつづき同じ出力になる。
つまり、Qの値にかかわらず、R=0の場合、Qの値は保存される。よって、フリップフロップは1ビットのデータを記憶できる。
S=R=1は不定になるので使用禁止とする。
フリップフロップの真理値表は、次のようになる。
なお、フリップフロップによる保存には、電力は消費される。なぜなら、NOT回路のもともとのトランジスタ回路や、AND回路のもともとのダイオード回路には、電源が必要だからである。
フリップフロップの種類は、RSフリップフロップの他にもJKフリップフロップやDフリップフロップなど、たくあんあるが、説明を省略する。

なお、フリップフロップのほかの用途として、下記のようう用途もある。
まず、スイッチなどを入れてオンにする際に、電子回路では、可動部の微細な振動によりオン・オフが何回も変動することによって電圧が変動するチャタリングという現象がある。
フリップフロップの他の用途としては、上述の記録保持のほかにも、フリップフロップを使うと、このチャタリングの除去が出来る。スイッチ回路などの出力側にフリップフロップ的な回路を追加すればいい。、リップフロップの仕組み上、最初にオンになったら、そのまま出力がオンになり続けるので、チャタリングでオフに戻るのを防ぐことが出来るという仕組みである。

なお、クロック信号入力をつけたRSフリップフロップの回路図は、右図のようになる。
さて、CPUで制御用にデータを保存するためのデバイスのことを、パソコン用語でレジスタ(register)という。
なお、メモリ基板にあるデータ保存のための部分は、レジスタとは呼ばない。
さて、フリップフロップを数個並べれば、これにより、その個数ぶんのビットを記憶できる。
つまり、レジスタをつくるには、フリップフロップを必要な個数、並べれば良い。
市販のCPUのビット数について「64ビット」や「32ビット」、「8ビット」などと紹介されるのは、なんのビット数かというと、一般にCPU内のレジスタのビット数である。
つまり、我々は、レジスタの作り方の原理を理解した事になる。
なおパソコンの場合、プログラミングをする際、どのようなデータをCPUのレジスタに保存するかという事は、コンパイラなど言語処理系が自動的に判断する(※詳しくは後述)。プログラマーは判断しない(C言語では キーワード register を変数宣言に用いることでコンパイラに変数をレジスタに割り付けてほしい旨を表現できるなど、完全に判断していないわけではない)。(※ パソコン以外のマイコンとかは高校の範囲外なので説明しない。)
なお、「レジスタ」の用語は、パソコンCPUに限らず、(組み込み機器などの)マイコンCPUでも ほぼ同様の意味である。

なお、パソコンで「次にどんな命令を実行するか?」(「たとえば次に○○の保存を実行する。その次に、△△のコピー作業を実行する。その次に、××を移動する。」という命令など)という作業も、記憶が必要である。
例えば、命令の種類に番号をつけて、
「たとえば次に○○の保存を実行する。その次に、△△のコピー作業を実行する。その次に、××を移動する。」という命令は、
このように、読込んだ命令を保持するためのレジスタのことを命令レジスタ(instruction register)という。命令レジスタには、「これからCPUが実行する命令1つ」が保存される。
なお、「命令2 → 命令3 → 命令1」という順番で命令を実行するためには、命令レジスタとは別途、メモリ基板などの主記憶装置に「命令2」「命令3」「命令1」などの順番を記憶する必要がある。
このような目的の、メモリへの命令の記憶には、たとえば、(メモリ基板などの)主記憶装置のアドレス200番に命令「2」を保存、アドレス201番に命令「3」を保存、アドレス202番に命令「1」を保存、アドレス203番に命令「99」・・・というふうにする。

などのように、アドレス200〜203番に、それぞれの命令の値を保存するわけである。
そして、処理1「まず、アドレス200番の命令をCPUに読み込んで、命令どおり実行しろ」という処理を行い,それから処理2「命令を実行し終わったら、次のアドレス(つまり前のアドレスに+1した番号のアドレス)を実行しろ」という処理2を繰り返せばいいだけである。
そうすれば、アドレス200番の命令から順に、次のアドレス201の命令、その次のアドレス202の命令、その次のアドレス203の命令、を実行することになる。(「200番」というのは単に説明のために仮に決めた数字である。)そして、アドレス203には終了命令が入っているから、そこで処理が終わる。
このような200から順に201,202,・・・と処理の移動する動作をするためには、「現在、CPUは第何番のアドレスを実行しようとしているか?」という情報を保存するレジスタも必要であり、このような(現在実行中しようとしているアドレスを保存するための)レジスタのことをプログラムカウンタ(program counter)という。なお、上述の説明のように、プログラムカウンタは、命令を実行するたびに、+1されて、次の命令を指すようになる。
このように、命令を実行するには、命令レジスタのほかに、プログラムカウンタと主記憶装置(メモリ)などが必要である。
また、命令レジスタを実行するためには、先ほどの例で、命令を保存したアドレス200〜203番のように、命令を保存したアドレスが必要である。このようなアドレスを命令アドレスという。

さて、CPUが命令を処理しやすいように、命令を「1」や「2」といった数字に置き換えたものが機械語(machine language)である。(※ 上記の命令「1」や命令「2」は、説明用に、この教科書で勝手に決めた番号であるので、専門用語ではないので、暗記しなくてよい。)(※ そもそも機械語はCPUの種類によって異なるので、暗記しなくてよい。)
なお、先ほどは「データの移動命令を「1」として」などと簡単に言ったが、実際には、「どこにあるデータを、どこに移動するか?」という情報が必要である。そのため、「どこのデータを扱うか?」という処理が必要である。
この、「どこのデータを扱うか?」というデータも、レジスタに記憶しとけばいい。
この「どこのデータを扱うか」というデータのことを、アドレス(address)という。
いっぽう、「移動する」「命令する」「コピーする」などのような、アドレスのない、単なる命令の種類のことを、「命令コード」または「命令部」(operation)という。
つまり、命令レジスタでは、命令部とアドレスの2つを記憶する必要がある。
なお、「このパソコンのメモリ基板にあるデータを扱え」というデータも、アドレスの情報である。
で、レジスタは要するに、フリップフロップである。
で、もし読者のあなたが、レジスタさえ理解すれば、命令レジスタもいろいろと分かる事になるから、CPUの命令処理の仕組みも、なんとなく分かるだろう。
説明の都合上、「移動命令」「保存命令」「コピー命令」などと言ったが、実際には、これらの命令は、さらに細かい「読み込み命令」や「書き込み命令」などの組み合わせである。
たとえば、データの「コピー命令」とは、「まずコピー元(メモリ上などにコピー元のデータがある)のデータを読み込み、そして読み込まれたデータをコピー先に書き込む」という命令である。(読み込みをしても、読み込み元のデータは消えない。しかし、書き込み先のデータは上書きされる。)
「保存命令」も、「○○に書き込んでおいたデータを読み込み、それを保存先に書き込む」という命令である。(書き込みをしても、書き込み元のデータは消えない。しかし、書き込み先のデータは上書きされる。)
このように、いくつかの命令は、「読み込み」と「書き込み」という2種類の命令に帰結する。
より正確には、機械語とは、読み込み命令や書き込み命令などの、より根本的な命令に対応するビット列の数値のことを機械語という。
なお上記の説明では、説明の都合上パソコンで説明したが、組み込み機器などのマイコンCPUでも、内部的には機械語で処理している。
(※ 高校の範囲外。普通科だけでなく、たぶん工業高校でも習わないかも?)
中学の技術家庭科の技術分野などで、右図のような条件分岐のフローチャートを見たこともあるだろう。
さて、条件分岐の実現方法の仕組みは、さきほど説明したプログラムカウンタを使うだけである。
「条件が満たされた場合のみに、プログラムカウンタに記憶されたアドレス値を、分岐先の命令のアドレスに書き換える」という仕組みを作ればい良いだけである。
コンピュータは数値しか記憶できないので、
たとえば、コンピュータ設計者が 「
」
・・と決めるわけだが、しかし、設計者以外の一般の人間には「1」とか「2」だけの数字を見ても意味が分かりづらい。
なお、説明のため「コピー命令を「3」」とか書いたが、実際のパソコンでは、命令は何らかのビット列である。たとえば、「10101011000110」とかビットの数字が並んでいても、人間には意味が分からない。
ビット列になると、一般人には、もっと意味が分からない。
そこで、一般の人間が命令を理解しやすいように、
「
」
などにしてみよう。(※ この「MOVE」「SAVE」などの命令語は、説明用に仮に決めただけであり、情報科学の専門用語でないので暗記しなくてよい。)
そして、コンピュータ的な
のかわりに、
のような文章にすれば、人間にとっても「たとえば次に○○の保存を実行する。その次に、△△のコピー作業を実行する。その次に、××を移動する。」という命令だと理解しやすい。
コンピュータにとっても、命令「MOVE」を「1」に置き換え、同様に命令「SAVE」を「2」に置き換え、命令「COPY」を「3」に置き換え、命令「STOP」を「99」に置き換えれば、処理できる。
この「MOVE」「SAVE」「COPY」「STOP」のように、人間が命令を理解しやすいように、機械語を英単語などに書き換えたもののことを「アセンブリ言語」(assembly lamguage)という。(※ アセンブリ言語は普通科「情報」の範囲外。なお、「MOVE」「SAVE」などは説明用に、仮に、この教科書で決めただけの単語であり、けっして、アセンブリ言語の専門用語ではないので、「MOVE」「SAVE」などは暗記しなくてよい。)(※ そもそもアセンブリ言語はCPUの種類によって異なるので、暗記しなくてよい。)
別途、アセンブリ言語の命令「MOVE」を機械語「1」に置き換え、同様に(アセンブリ言語の)命令「SAVE」を(機械語)「2」に置き換え、同様に命令「COPY」を命令「3」に置き換える、という回路を用意しておけば、その回路を使ってアセンブリ言語を機械語に置き換えできる。
同様に、別途、機械語の命令「1」をアセンブリ言語の命令「MOVE」に置き換え、同様に(機械語の)命令「2」を(アセンブリ言語の)命令「SAVE」に置き換え、同様に命令「3」を命令「COPY」に置き換える、という回路を用意しておけば、その回路を使って機械語をアセンブリ言語に置き換えできる。
(※ 範囲外: )なお、アセンブリ言語は、パソコンでもハードウェアを制御する際に使われる。また、マイコンCPUを制御する際にはアセンブリ言語によるプログラミングが一般に使われる。
実際には、アセンブリ言語は、人間が大量のコードを読むには向いていない等の理由があるので、なので、アセンブリ言語をさらに人間が読みやすいように置き換えたプログラミング言語というものが用いられる。
中学で「BASIC」や「C言語」などというプログラミング言語の存在を習ったと思う。
BASICやC言語も、そのプログラムを実行する場合、最終的にはコンピューター内部では、そのプログラミング言語のコードを、機械語の命令に置き換えて、そしてCPUなどが機械語の命令を処理している。
なお、プログラム言語で書かれたコードを、機械語の命令に置き換える装置やソフトウェアのことをコンパイラ(compiler)という。 そして、コンパイラを実行すること(つまり、プログラム言語で書かれたコードを機械語の命令に置き換えること)を「コンパイルする」(compile)などという。
プログラム言語のコードが書かれたファイルのことをソースコードという。あるソフトウェアのソースコードを読めば、もしも読み手に、プログラミング言語の専門知識が充分にあれば、そのソフトウェアのしくみが読み手に分かってしまう。
このため、そのソフトウェアのプログラムの仕組みを秘密にしておきたい場合、ソースコードは非公開にして配布せずにしておき、コンパイル後のソフトウェアのみを配布することで、プログラムのしくみを秘密にできる。
なお、アセンブリ言語の話に戻るが、「読み込み」命令は、仮に「上書きコピー命令」という命令を根本的な命令と考えれば(※ 「上書きコピー命令」という命令名称は仮名なので覚えなくて良い)、つまり「読み込み」命令とは「CPU外部の(メモリ上などの)読み込み元アドレスにあるデータをもちいて、レジスタのデータを上書きコピーしろ」という命令である。(読み込み元アドレスにある読み込み元データと、上書きをされた(レジスタにある)書き込み先データは、同一内容のデータになるので、コピー命令の一種である。なので「上書きコピー命令」と名付けたわけだ。)
そして、「書き込み」命令も、仮に「上書き」命令というのを考えれば、「書き込み」命令とは「今のレジスタのデータを用いて、CPU外部の(メモリ上やハードディスク上などの)書き込み先アドレスにあるデータを上書きしろ」という命令である。(レジスタにある読み込み元データと、上書きをされた(レジスタにある)書き込み先データは、同一内容のデータになるので、コピー命令の一種である。)
このように、「読み込み」命令も「書き込み」命令も、どちらとも「上書きコピー命令」命令である。単に、レジスタからCPU外部に上書きするか、それともCPU外部からレジスタに上書きするか、・・・という方向性が違うだけである。
このため、アセンブリ言語では「読み込み」や「書き込み」「データの移動」「データの複製コピー」、・・・などの命令を、同一のアセンブリ命令(たとえば命令「MOV」(MOVEが由来だが、作業内容は「移動」ではなく「上書きコピー」である) )で扱っている場合も多い。
なお、「a=4」(変数aに数値4を代入)のような代入命令も、じつはコピー命令である。なぜなら、変数aの中身を記録するためのアドレスに、4をコピーするからである。(要するに、代入命令も命令「MOV」で実現できる。)

プログラミング言語の種類は、さまざまである。 実務で、用途に沿ったプログラミング言語が用いられる。
たとえば、ウェブアプリケーションの用途では、「PHP」(ピー エイチ ピー)あるいは「Perl」(パール)や「Python」(パイソン)・「Ruby」(ルビー)・「Java」(ジャバ)・「Go」(ゴー)などのプログラミング言語が用いられる。 ウェブページのプログラミングでは、「JavaScript」(ジャバ スクリプト)などのプログラミング言語が用いられる。 また、オフィススイートなどのアプリケーションでは「マクロ言語」や「VBA」の様な簡易言語が用いられるが、保守性などの理由から TypeScript などの相対的に高度なスクリプティング言語が用いられることもある。 組み込みソフトウェアなどでは、「C言語」・「C++」(シープラスプラス)あるいはアセンブリ言語などのプログラミング言語が用いられる。
※ 主にパソコンの話題。
一般に、オペレーティングシステムが他の企業の製品などに変わってしまえば、アプリケーションは動作しなくなる。
たとえば、マイクロソフト社のオペレーティングシステムwindows向けに作られたアプリケーションの実行ファイルの多くは、アップル社のオペレーティングシステム Mac OS(マック オーエス)の上では動作しない。また、windows向けアプリケーションの実行ファイルの多くは、オープンソースOSのLinux(リナックス)上でも動作しない。
同様に、Linux 向けに作成されたアプリケーションの実行ファイルの多くは、 windows上 やMac OS上では、動作しない。
なぜ、こうなるのか?
アプリケーションは、ハードウェアを制御しやすいように機械語に変換してあるのに、なぜ、特定のオペレーティングシステムが必要になるのか?
実は、世間のアプリケーションのほとんどは、OSがアプリケーション向けに提供する機能(一般に「API」(エーピーアイ、application programming interface)という)を利用しているだけである。
そして、アプリケーションのプログラムの内容は、そのOSの提供する複数の機能(API)を、組み合わせて、目的の処理を作っているだけである。
アプリケーションの動作内容を、プロセッサが理解出来るように翻訳することが、ほとんどの場合のアプリケーションでのコンパイルの目的である。
アプリケーションは顧客であり、OSがサービス提供者である。アプリケーションは単に、サービス提供者であるOSに、要望を提出しているだけにすぎない。
そしてOSは、読み込んだアプリケーション動作要望を実現するため、プロセッサなど各種資源の調整を行い要求されたサービスを実施する。
アプリケーションが各種サービスをOSに依頼するように、OSの機能をアプリケーションが依頼することをシステムコールという。
メモリの操作やCPUの操作などの、OSの機能の中枢のプログラムのことをカーネル(kernel)という。
メモリ基板のように、CPUとデータを直接やりとりするためのデータを記憶している記憶装置のことを、主記憶装置という。メモリ基板は主記憶装置である。
いっぽう、ハードディスクは補助記憶装置である。補助記憶装置となるものは、ハードディスクのほかにも、CDやDVDなどがある。
なぜ、メモリだけ特別に「主記憶装置」と区別するのかというと、CPUとデータを直接やりとりするのはメモリ基板だから、である。
いっぽう、ハードディスクは、CPUとは直接はデータをやりとりしない。 ハードディスクのデータをCPUで処理したい場合、まずハードディスクのデータをメモリに読みこみ、それからCPUでメモリのデータを1つずつ処理するという方法である。つまり、メモリを仲介して、間接的にハードディスクのデータを処理するという方法が用いられる。
CDやDVDのデータを処理する場合も同様に、メモリを仲介して処理する方式である。なので、ハードディスクもCD、DVDもまとめて補助記憶装置に分類される。

なお、パソコンのマザーボードには、そのパソコンの動作監視用のために温度や電圧などを測定して制御する監視用の専用チップも入っており、そのマザーボード監視用の専用チップのことをサービスプロセッサという。(※ 工業高校の科目『ハードウェア技術』で、ここまで習う。)
フリップフロップの教科書などを見ると、前提としてクロック信号が存在しているのだが、そもそもクロック信号は、どうやってつくるのだろうか?
CPUのクロック信号の発振方法は、「水晶発振子」または「セラミック発振子」といった電子部品を使って、クロック信号を発生させる。(「セラミック発振子」でネット検索すれば、電子部品メーカーのホームページなどが出てくるので、それを読むのが、理解には手っ取り早い。) 一般に、IC(集積回路)やLSIでは、水晶振動子やセラミック発振子を用いて、高いクロック周波数を発生させる。(※ 参考文献: 電波新聞社『電子回路入門講座』、見城尚志・高橋久、2012年第1版10刷、235ページ)
パソコンに接続された外付けハードディスクやディスプレイモニターなど、いろんなハードウェアがありますが、これらはどういう仕組みで制御されてるのでしょうか?
詳しい仕組みは企業側が非公開にしていますが、一般には、機械語で制御してると考えられています。
つまり、パソコンから、たとえば「1101 0100」などの信号が送られてきて、ハードウェアは単に、送られた信号にもとづいて、動作をしているだけです。
では、そのような動作をできるするためには、どうすればいいでしょうか?
とりあえず、読者は中学校でロボット車を制御する原理を習ったと思うので、ここwikibooksでもロボット車の例で考えてみましょう。
説明の簡単化のため、デジタル制御のモーターとそのモータの接続されたタイヤがついた、ロボット車で考えましょう。
簡単のため、
としましょう。
こうすれば、パソコンはロボット車にどのモータを動かすかの指令を送るだけで、車を前後左右に自由に動かせます。
(※ 実際には、正面方向に進むためのモータと、後ろ方向に進むためのモータとは、ひとつのモータに流す電流の向きを変えれば前後の両方にも使えるだろうから、1つのモータでも可能かもしれない。同様に左右のモータも、1つのモータでも可能かもしれない。よって、前後用に1つのモータと、左右用に1つのモータとで、合計2つのモータで対応可能かもいれない。
だが、本書では、説明の簡単化のため、各方向ごとに4つのモータを使うことにしよう。)
これを機械語の視点で考えてみましょう。
まず、ロボット車の設計者は(※ あなたが、仮に、中学生用の技術家庭科のためのロボット車の実技教材を設計する企業の技術者だったと仮定しよう)、4つあるモータのうち、どのモータを動かすかを、機械度で指定しなければなりません。
そのため、下記のように、まずは仕様を決めて、モータと機械語を対応させる必要があります。
少なくとも、5つの命令が必要そうです。
なお、この仕組みだけだと、「モータAとモータBを同時に動かす」という事ができません。ですが、簡単のため、とりあえず、ひとつづつモータを動かす仕組みにしましょう。
さて、モータを何秒間、どの程度の強さで具体的にどのていど動かすかは、モータの番号だけでは不明です。
とりあえず、モータの強さを「弱、中、強」の3段階にしましょう。きっと、モーターに長す電流の大きさを3段階に設定しとけば、モータの出力の強さも三段階に分けられるでしょう。
機械語は
としましょう。
さて、「ロボット車の前方やや右側にある目的地につくため、とりあえずモータを強度『中』で、とりあえず15秒間ほど、前進させたい」としましょう。
どうすればいいか、考えましょう。
とりあえず、「何秒間、モータを動かすか」というのも命令できるように設計する必要がありそうです。
とりあえず1秒単位で走行時間をセットできるように設計したとしましょう。
作業「ロボット車の前方やや右側にある目的地につくため、とりあえずモータを強度『中』で、とりあえず15秒間ほど、前進させたい」を実行するには、
のように、作業を分解する必要があります。
そして、この作業を機械語に(設計者が)翻訳する必要があります。翻訳すると、
のようになります。
ハード側の視点では、まず、 どの ハードを制御するかの命令が必要なので、
という情報がまっさきに必要です。
つづいて、出力の強さを決める必要があるでしょう。そのあとに、その強さで何秒間動かすかの情報が必要でしょう。
つまり、ハード側からすると、情報を知りたい順序は、
のような順序になります。
もし、これを機械語にそのまま並べると、
となりますが、よみづらいので、とりあえず
としましょう。
しかし、これでも、3桁の「001」と2桁の「01」と8桁の「0000 1110」が混ざってて分かりづらいので、
4桁づつに
という機械語を送ることにしましょう。
そのためには、まず、モータの機械語の設計を
のように修正する必要があります。
また、モータ出力強度の機械語も修正し、
としましょう。
こうすれば、
の信号を送ることによって、このロボット車は作業「ロボット車の前方やや右側にある目的地につくため、とりあえずモータを強度『中』で、とりあえず15秒間ほど、前進させたい」を実行できます。
上述のように、ロボットに命令をする場合には、設計者が、作業を機械語で処理できるように、設計する必要があります。
一般に、アセンブラには、機械語の信号をパソコンなどのポートから出力する機能があります(OUT命令というのがある)。それを使って、原理的には信号を出力できますので、上述のような制御もできます。(というか、そうでないとパソコン周辺装置などを製造しているハードメーカー企業がデバイスドライバを作れない)