前章までで、部品は揃った。計算する論理ゲート、記憶するレジスタ、時を刻むクロック。この章では、これらを組み上げて、ついにCPU(中央処理装置)を動かす。
CPUは魔法の箱ではない。「メモリから命令を1つ読んで、実行する」を、ただひたすら繰り返すだけの機械だ。その繰り返しの正体を、部品レベルから組み立てていこう。
CPUの5大部品
最小構成のCPUは、次の部品でできている。
| 部品 | 役割 | 厨房の比喩 |
|---|---|---|
| ALU | 計算する(加減算・論理演算) | 実際に調理する手 |
| レジスタ群 | 計算中のデータを保持 | まな板の上の食材 |
| プログラムカウンタ (PC) | 次に読む命令の位置を指す | レシピの「今どの行か」を指す指 |
| 命令レジスタ (IR) | 今読んだ命令を保持 | 今読んでいるレシピの一行 |
| 制御装置 (Control Unit) | 命令を解読し各部に指示 | レシピを読んで手に指示する脳 |
これらがクロックの号令で連携する。
ALU — 計算の心臓
ALU(Arithmetic Logic Unit)は、前章までの加算器・論理ゲートを束ねた「計算専門ユニット」だ。2つの入力と「何をするか」を指示するオペコードを受け取り、結果を出す。
def alu(a: int, b: int, op: str) -> tuple[int, dict]:
"""簡易ALU。結果とフラグを返す"""
a &= 0xFF; b &= 0xFF
if op == "ADD": r = a + b
elif op == "SUB": r = a - b
elif op == "AND": r = a & b
elif op == "OR": r = a | b
elif op == "XOR": r = a ^ b
else: raise ValueError(op)
result = r & 0xFF # 8ビットに収める
flags = {
"zero": result == 0, # 結果が0か(分岐命令で使う)
"carry": r > 0xFF or r < 0, # 桁あふれ
}
return result, flags
print(alu(200, 100, "ADD")) # (44, {'zero': False, 'carry': True}) 200+100=300→桁あふれ
print(alu(50, 50, "SUB")) # (0, {'zero': True, 'carry': False}) 結果0
注目はフラグだ。ALUは計算結果だけでなく「結果がゼロだったか」「桁あふれたか」という付随情報を出す。この zero フラグが、後述の条件分岐を可能にする。
フェッチ・デコード・実行 — すべての根源にあるループ
CPUの動作は、3ステップの無限ループに集約される。
┌──────────────────────────────────┐
│ │
▼ │
[Fetch] PCが指すメモリから命令を取得 │
│ │
▼ │
[Decode] 命令を解読(何をする命令か?) │
│ │
▼ │
[Execute] ALUやレジスタを操作して実行 │
│ PCを次の命令へ進める │
└──────────────────────────────────┘
- フェッチ(Fetch):プログラムカウンタ(PC)が指すアドレスから命令を読み、命令レジスタ(IR)へ。
- デコード(Decode):制御装置が命令を解読。「これは加算命令だ」「対象はレジスタ2番だ」と各部品への配線を決める。
- 実行(Execute):ALUで計算、結果をレジスタへ。そしてPCを次へ進める。
この3ステップが、クロックに合わせて延々と回る。あなたが今使っているPCも、スマホも、この瞬間に数十億回/秒でこのループを回している。
自作CPUをコードで組む
概念だけでは腑に落ちない。動く最小のCPUを書いてみよう。数種類の命令だけを持つ、教育用の仮想CPUだ。
まず命令セット(このCPUが理解できる言葉)を定義する。
| オペコード | 命令 | 意味 |
|---|---|---|
| 0x01 | LOAD r, val | レジスタrに即値valを入れる |
| 0x02 | ADD r, s | r = r + s |
| 0x03 | SUB r, s | r = r - s |
| 0x04 | JZ addr | 直前の結果が0ならaddrへジャンプ |
| 0x05 | OUT r | レジスタrを出力 |
| 0xFF | HALT | 停止 |
class CPU:
def __init__(self, program):
self.mem = program # 命令列(メモリ)
self.reg = [0, 0, 0, 0] # レジスタ4本 (r0〜r3)
self.pc = 0 # プログラムカウンタ
self.zero = False # ゼロフラグ
self.running = True
def step(self):
op = self.mem[self.pc] # ── Fetch: PCが指す命令を取得
# ── Decode & Execute
if op == 0x01: # LOAD r, val
r, val = self.mem[self.pc+1], self.mem[self.pc+2]
self.reg[r] = val
self.pc += 3
elif op == 0x02: # ADD r, s
r, s = self.mem[self.pc+1], self.mem[self.pc+2]
self.reg[r] = (self.reg[r] + self.reg[s]) & 0xFF
self.zero = self.reg[r] == 0
self.pc += 3
elif op == 0x03: # SUB r, s
r, s = self.mem[self.pc+1], self.mem[self.pc+2]
self.reg[r] = (self.reg[r] - self.reg[s]) & 0xFF
self.zero = self.reg[r] == 0
self.pc += 3
elif op == 0x04: # JZ addr
addr = self.mem[self.pc+1]
self.pc = addr if self.zero else self.pc + 2
elif op == 0x05: # OUT r
print("OUT:", self.reg[self.mem[self.pc+1]])
self.pc += 2
elif op == 0xFF: # HALT
self.running = False
def run(self):
while self.running:
self.step() # フェッチ・デコード・実行を繰り返す
# 「3 + 4 を計算して出力する」プログラム
program = [
0x01, 0, 3, # LOAD r0, 3
0x01, 1, 4, # LOAD r1, 4
0x02, 0, 1, # ADD r0, r1 → r0 = 7
0x05, 0, # OUT r0
0xFF, # HALT
]
CPU(program).run() # OUT: 7
たった数十行だが、これは本物のCPUと同じ原理で動いている。program という数字の列(=機械語)を、フェッチ・デコード・実行のループが解釈し、計算を実行する。
分岐 — CPUが「判断」する仕組み
上のCPUには JZ(Jump if Zero)命令がある。これが条件分岐、つまりプログラムの「もし〜なら」の正体だ。
仕組みはシンプルで、PCの値を書き換えるだけ。通常PCは命令1つ分ずつ増えるが、ジャンプ命令はPCを任意のアドレスに飛ばす。ゼロフラグを見て飛ぶかどうかを決めれば、if 文やループが実現できる。
# 「5 から 1 ずつ引いて、0になるまでループ」
program = [
0x01, 0, 5, # LOAD r0, 5 (カウンタ)
0x01, 1, 1, # LOAD r1, 1 (減算する値)
0x05, 0, # OUT r0 ← ループの先頭 (アドレス6)
0x03, 0, 1, # SUB r0, r1 → r0 = r0 - 1、ゼロならzeroフラグON
0x04, 6, # JZ ... 実際は「0でなければ6へ戻る」ロジックを組む
0xFF, # HALT
]
高級言語の while も for も、機械語レベルでは「フラグを見てPCを書き換える」という、この単純な操作に還元される。「ループ」という抽象概念の物理的正体が、PCの巻き戻しなのだ。
まとめ — 「おまじない」の消し方
- CPU=計算+記憶+制御の統合体:ALU(計算)、レジスタ(記憶)、PC/制御装置(進行管理)の連携で動く
- フェッチ・デコード・実行:メモリから命令を読み、解読し、実行する。この無限ループがあらゆる処理の根源
- 機械語=ただの数字の列:CPUはその数字を命令として解釈しているだけ
- 分岐=PCの書き換え:
ifやforの正体は、フラグを見てプログラムカウンタを操作すること
「プログラムが動く」という現象の、最も物理に近い層が見えた。CPUは賢いのではなく、単純な操作を高速に繰り返しているだけだ。
次章からはPart 2へ。この機械語を、人間がどう書き、Cのような言語がどうやって機械語に翻訳されるのかを解体していく。
「コンピュータができることは、驚くほど少ない。ただ、それを驚くほど速くやる。」