CPU内部のレジスタ(PC・IR・ACC・フラグ・SP・インデックスなど)の役割と、命令実行時の値の変化を観察します。
レジスタはCPUの内部にある超高速・超小容量の記憶素子です。アクセス速度はRAM(主記憶)の100倍以上速く、約0.3ナノ秒(10億分の0.3秒)でデータを読み書きできます。ただし容量は非常に少なく、1つのレジスタは通常16〜64ビット(2〜8バイト)しかありません。
CPUが命令を実行するとき、まずメモリからデータを読み出してレジスタに入れ、レジスタ上で計算を行い、結果をレジスタからメモリに書き戻します。メモリに直接アクセスして計算するよりも、一旦レジスタにコピーしてからの方がはるかに高速だからです。
上のツールで各命令を実行すると、レジスタの値がリアルタイムで変化する様子を観察できます。変更されたレジスタには赤い丸印がつくので、どのレジスタが使われたか一目でわかります。
| レジスタ | 名前 | 保持する内容 | いつ変化するか |
|---|---|---|---|
| PC | プログラムカウンタ | 次の命令のアドレス | フェッチごとに+1、分岐で書換 |
| IR | 命令レジスタ | 実行中の命令語 | フェッチでメモリから読込 |
| ACC | アキュムレータ | 演算結果・一時データ | LOAD/ADD/SUB等の命令で変化 |
どんなCPUにも必ず存在する3つの基本レジスタがPC(次の命令のアドレス)、IR(現在の命令)、ACC(演算結果)です。この3つの役割を正確に区別できれば、命令サイクルの仕組みが理解できます。
プログラムカウンタが保持するのは「次に実行する命令のアドレス」です。「現在実行中の命令」ではなく「次の」である点がポイントです(現在実行中の命令はIRに入っています)。
上のツールでADD命令を実行すると、ステップ1でPC・IRが変化し、ステップ3でACCが変化する様子が観察できます。それぞれのレジスタが別のタイミングで使われていることに注目してください。
| フラグ | 名前 | 1になる条件 | 用途 |
|---|---|---|---|
| Z | ゼロフラグ | 演算結果が0 | if (x==0) の判定 |
| N | 負フラグ | 演算結果が負 | if (x<0) の判定 |
| V | オーバーフロー | 符号付き演算で桁あふれ | 計算結果の異常検出 |
| C | キャリー | 符号なし演算で桁あふれ | 多倍長演算の繰り上げ |
フラグレジスタ(FR)は演算結果の状態を1ビットずつ記録する特殊なレジスタです。条件分岐命令(JZ, JNZ, JN等)はこのフラグを参照してジャンプするかを決めます。フラグは演算命令(ADD, SUB等)の実行時に自動的にセットされます。
たとえばSUB命令で5-5=0を計算するとZフラグ=1が立ちます。次にJZ(ゼロならジャンプ)命令が来ると、Z=1を確認してジャンプします。上のツールでADD命令を実行した後のFRの値を確認してみてください。
条件分岐がどのフラグを参照するかを整理しておくと理解しやすくなります。ゼロ判定はZフラグ、負の判定はNフラグ、桁あふれの検出はV/Cフラグです。
スタックポインタ(SP)はスタック領域の「現在の一番上」のメモリアドレスを保持するレジスタです。PUSH命令でSPを-1してデータを積み、POP命令でデータを取り出してSPを+1します。スタックはLIFO(Last In First Out)構造です。
サブルーチン呼び出し(CALL命令)では、戻りアドレスが自動的にスタックにPUSHされ、RET命令でPOPされてPCに復帰します。これが関数呼び出しと戻り値の仕組みの基本です。
上のツールでPUSH/POP命令を実行すると、SPの値が0xFF→0xFE(PUSH)、0xFE→0xFF(POP)と変化する様子を確認できます。スタック操作のインジケータも表示されます。
ベースレジスタ(BR)はプログラムやデータ領域の先頭アドレスを保持し、インデックスレジスタ(IX)は配列のように連続するデータのオフセット(何番目か)を保持します。実効アドレス = BR + オペランド + IX で計算されます。
たとえばBR=0x1000、オペランド=5、IX=2なら実効アドレスは0x1007です。これは「プログラム先頭から5番地先にある配列の2番目の要素」を意味します。プログラムの再配置(リロケーション)のときにBRの値を変えるだけで全アドレスを変更できます。
この仕組みは「ベースアドレス指定方式」「インデックス修飾」と呼ばれます。上のツールのレジスタ表示でBR=0x1000、IX=2の初期値を確認してください。
汎用レジスタ(GR: General Register)は特定の用途が決まっておらず、プログラマが自由に使えるレジスタです。CASLIIでは16個(GR0〜GR15)、x86-64では16個(RAX, RBX等)のようにCPUごとに数が決まっています。
汎用レジスタが多いCPUほど、メモリアクセスを減らしてレジスタだけで計算を進められるので高速に動作します。コンパイラは「どの変数をどのレジスタに割り当てるか」を最適化して、できるだけレジスタを活用します(レジスタ割り当て最適化)。
たとえばCASLIIでは「GR0はインデックス修飾に使えない」「GR1〜GR7が汎用」というルールがあります。上のツールでGR0, GR1の初期値が0であることを確認してください。
| 記憶装置 | アクセス時間 | 容量 | 距離(CPUから) | コスト |
|---|---|---|---|---|
| レジスタ | ~0.3ns | 数十〜数百B | CPU内部 | 最高 |
| L1キャッシュ | ~1ns | 32〜64KB | CPU内部 | 高 |
| L2キャッシュ | ~4ns | 256KB〜1MB | CPU内部 | 中高 |
| L3キャッシュ | ~10ns | 8〜64MB | CPU内/近接 | 中 |
| 主記憶(RAM) | ~100ns | 8〜64GB | マザーボード上 | 低 |
| SSD | ~0.1ms | 256GB〜数TB | ケーブル接続 | 最低 |
記憶装置には「速度が速いほど容量が小さく、コストが高い」という法則があります。これを記憶階層(メモリハイアラキ)と呼びます。レジスタはこの階層の最上位に位置し、最も高速ですが容量は数百バイトしかありません。
レジスタがRAMの約300倍速いという事実を身近な例で表すと、レジスタが「机の上のメモ帳(0.3秒で読める)」なら、RAMは「隣の部屋の書棚(100秒かかる)」、SSDは「市内の図書館(10万秒=約1日)」に相当します。この圧倒的な速度差がキャッシュメモリやレジスタの存在意義です。上のツールで各命令の実行時にレジスタ上で高速にデータが更新される様子を観察してください。