ソフトウェアのしくみシミュレーター

コンピュータは、精製した岩(シリコン)に刻んだスイッチのON/OFFだけで、計算し、記憶し、画面を描き、ネットワーク越しに会話しています。 このページでは、その積み上げを25個のインタラクティブシミュレーターで下から順に体験できます。 どれも数クリックで動くので、気になったものから触ってみてください。

大半のシミュレーターは、書籍『CODE コードから見たコンピュータのからくり』(Charles Petzold 著、永山操 訳)を教材化した コンピュータのしくみ講座を作る過程で生まれたものです。 発想のもとになっているのは Faza「Software, from First Principles」(fazamhd.com) のインタラクティブ図解で、アナログ→デジタル変換とネットワークの3点は、そちらを本サイト用にVueコンポーネントとして再実装したものです。

2つの状態でコードを作る

オン/オフの2状態しか送れなくても、並べる個数を増やせば文字は表せる。すべての出発点。

懐中電灯の符号

送れるのは「短い点滅」か「長い点滅」の2種類だけ。それでも並べる個数を増やせば、1個で2文字、2個で4文字と表せる文字が倍々に増えていく。ドットとダッシュを組み立てて「送る」を押すと、符号表と照合して文字が判定される。

さわって確認懐中電灯の符号 — ドットとダッシュで文字を送る
懐中電灯のあかり
いま作っている符号(未入力)

点滅の並びを作って「送る」を押すと、符号表と照合して文字を判定します。

受信した文字(まだなし)
符号表(この教材で使う8文字)
E
T
I・・
A・ー
Nー・
Mーー
S・・・
Oーーー

懐中電灯で送れるのは「短い点滅」か「長い点滅」の2種類だけです。それでも並べる個数を増やすと、 1個で2文字、2個で4文字と、表せる文字は倍々に増えていきます。よく使うEとTに最短の符号が 割り当てられているのも符号表から読み取れます。たった2つの状態の組み合わせで文字が届く—— この構造が、このあとの点字にも電気回路にも、そのまま顔を出し続けます。

リレー中継(弱った信号を作り直す)

距離を伸ばすと信号は右へ行くほど弱まり、ある距離を超えると受信側のサウンダが動かなくなる。そこにリレーを置くと1本だった線路が短い区間に分かれ、切れ目ごとに信号が濃い色へ戻る。届く距離が伸びたのは信号が強くなったからではなく、1区間が短くなったからだという点を確かめられる。

さわって確認リレー中継 — 弱った信号を作り直して、遠くまで届ける
距離 150マイル
中継リレー 0個

送=送信キー / リ=リレー / 受=サウンダ(受信側)

区間 1本 / 1区間あたり 150マイル

リレーなし(送信側から受信側まで1本の区間)

受信側に届いた信号の強さ40%

縦線=サウンダを動かせる最低の強さ(20%)

届く — 受信側のサウンダが鳴る

1区間150マイル。まだ読み取れる強さが残っているので、リレーなしでも届きます。

距離を伸ばすと、線の色が右へ行くほど薄くなります。導線が長くなるほど信号は弱まり、ある距離を超えると 受信側では読み取れなくなります。ここでリレーを置くと、1本だった線路が同じ長さの区間に分かれ、 区間の切れ目ごとに信号が濃い色に戻ります。リレーが弱った信号を受け取り、その動きで 新しい電池につながった次の区間のスイッチを入れ直しているからです。届く距離が伸びたのは、信号そのものが 強くなったからではなく、1区間の長さが短くなったからだ、という点を確かめてみてください。 人間が電文を書き取って送信し直していた中継作業を、この仕組みがそのまま肩代わりしています。

歯車で数える

電気より前の計算機。物理的な仕掛けだけで「数を数えて桁を繰り上げる」を実現していた。

機械式カウンター

車の走行距離計と同じ、桁ごとの数字ホイールと繰り上げピン。ホイールが9から0へ戻る瞬間だけ、ピンが隣の桁を1目盛り押し出す。+1を押し続けて、099→100で2桁が連鎖して動く瞬間を見てみてほしい。パスカル以来の機械式計算機を悩ませた「キャリーの自動処理」そのもの。

さわって確認機械式カウンタ — 9→0の瞬間だけ繰り上げピンが動く
0百の位
待機
0十の位
待機
0一の位
現在値000

一の位が9→0になる瞬間だけ、繰り上げピンが隣の桁を1つ押し出します。 それ以外のとき、桁同士は独立していて干渉しません。パスカル以来の機械式計算機の設計者たちを 悩ませた「キャリーの自動処理」とは、まさにこの連動機構のことです。後の加算回路のキャリー伝播も、 歯車が電気仕掛けに変わっただけで、原理はこれと同じです。

スイッチで論理を作る

電圧で自動開閉するスイッチを配線でつなぐと、「判断」する回路が生まれる。

スイッチ回路(AND・OR・NOT)

2つのスイッチを直列につなぐと「両方ONのときだけ点灯」(AND)、並列に並べると「どちらかONで点灯」(OR)になる。タブを切り替えて、配線の形がそのまま論理になっていることを確かめられる。手押しのスイッチをリレーの接点に置き換えれば、同じ論理が自動で動き出す。

さわって確認スイッチ回路 — 直列=AND・並列=OR・反転を動かして確かめる

スイッチA・Bの両方を閉じた(ON)ときだけ電流が流れる直列回路

電源
消灯
A(0) AND B(0) = 0非通電(OFF)
AB出力
000
010
100
111

ANDは直列つなぎで、両方のスイッチが閉じないと電流が流れません。 ORは並列つなぎで、どちらか一方が閉じれば電流が流れます。 NOTはスイッチが開いている(0)ときに通電し、閉じる(1)と遮断する「反転」の回路で、 双投式リレーの"逆側"の接点で作るインバータと同じ働きです。手で押すスイッチをリレーの接点に 置き換えれば、同じ論理がそのまま自動で動きます。

ブール式の表

論理は紙の上では「式」として書ける。A×B(AND)・A+B(OR)・A×(1−B) の3つをタブで切り替え、AとBの0/1を動かすと、式へ代入した結果と真理値表の「いまの入力に当たる行」が連動して光る。回路を組む前に、論理が機械的に計算できることを確かめる。

さわって確認ブール式の表 — AとBの0/1を切り替えて行をたどる

×は交わり(AND)。AとBの両方が1のときだけ、式の値が1になる

0×0 = 00(偽)
ABA×B
000
010
100
111

タブで式を選ぶと、A・Bの4通りの組み合わせすべてについて式の値を埋めた表ができあがります。 AとBを切り替えると、いまの入力に当たる行がハイライトされ、上の代入結果と表の行が 常に一致していることを確かめられます。A+Bでは両方を1にしても値は2ではなく1—— ORの表だけの特別ルールです。A×(1−B)のような組み合わせの式も、内側のカッコから順に 当てはめていくだけで機械的に値が決まります。

XOR を組み立てる

「入力が異なるときだけON」になるXORは、単体の部品ではない。同じ入力をORとNANDの両方に入れ、2つの出力をANDでまとめると得られる。ORが「両方0」を、NANDが「両方1」をはじき、残った2通りだけが通る——1マスずつ外れた2つのゲートが互いの穴を塞ぐ様子が見える。

さわって確認XORを組み立てる — OR+NANDの出力をANDでまとめる
ORゲート両方0のときだけ0出力 0
NANDゲート両方1のときだけ0出力 1
ANDゲートでまとめる
最終出力(A XOR B)= 0
ABORNAND出力
00010← 現在
01111
10111
11100

XOR は単体の部品ではなく、同じ入力をORゲートNANDゲートの両方に入れ、 2つの出力をANDゲートでまとめた回路です。ORが「両方0」を、NANDが「両方1」をはじき、 残った「どちらか一方だけ1」の2通りだけで出力が1になります。入力を切り替えて、 どのゲートが反応し、真理値表のどの行にいるかを確かめてみましょう。

二進法で数えて、足す

スイッチは2状態しかないので、0と1だけで数える。それでも足し算はできる。

基数変換ラボ

同じ1つの数が、10進・8進・4進・2進の4つの表記で同時に切り替わる。桁をタップすると、その桁の重み(基数の累乗)と「桁の値×重み」が展開表示される。基数が変わっても位取りの原理は変わらないこと、基数が小さいほど桁数が長くなることを確かめられる。

さわって確認基数変換ラボ — 同じ数を10進・8進・4進・2進で書く
10進数21
10進法
(10)
8進法
(8)
4進法
(4)
2進法
(2)

桁をタップすると、その桁の重み(基数の累乗)と「桁の値 × 重み」が表示されます。

スライダーや「+1 / −1」で数を動かすと、4つの基数の表記が同時に切り替わります。 どの基数でも、桁の値にその位の重み(基数の累乗)を掛けて全部足すと、必ず元の10進数に戻ります。 基数が小さいほど1桁で表せる量が少ないぶん桁数は長くなりますが、表記が違っても指している量は同じです。 位取りの原理そのものは、基数が変わっても変わりません。

二進カウンター

0と1だけの世界では、1+1で即座に桁が繰り上がる。「+1」を押すと一の位から繰り上げが左隣へ順に伝わり、1111の次には0000へ戻る「桁あふれ」も観察できる。機械式カウンターの繰り上げピンとまったく同じ原理が、2状態でも成立している。

さわって確認4ビットカウンタ — 繰り上げが左へ伝わる様子を見る
Bit30
Bit20
Bit10
Bit00
2進数0000(2)
10進数0(10)

「+1」を押すと一の位(Bit0)から繰り上げが始まります。1だったビットは0に戻りつつ隣のビットへ繰り上げを渡し、0だったビットが1になったところで伝播は止まります。 4bitでは0〜15の16通りしか表せないため、1111の次は0000に戻ります(オーバーフロー)。コンピュータの整数演算が特定の値で「桁あふれ」するのはこの仕組みのためです。

ビット数と組み合わせの数(2ⁿ)

スライダーでビット数を1〜8の間で動かすと、区別できるコードの数が升目の増減としてそのまま見える。1ビット増やすたびに升目はちょうど2倍——1ビットで2通りだったものが、8ビットでは256通りになる。升目をタップすれば、そのマスに対応する2進表記も確かめられる。

さわって確認2ⁿの升目 — ビットを1つ増やすと組み合わせは2倍
ビット数 n = 3

3ビット → 2³ = 8通り

マスをタップすると、そのマスの2進表記がここに出ます

升目の1つひとつが、nビットで区別できるコード1個に対応しています。スライダーを右へ1つ動かすたびに、 升目の数はちょうど2倍——グリッドの縦か横のどちらかが2倍に伸びます。1ビットではたった2通りだった組み合わせが、 8ビットでは256通り。ビット数nを決めると、区別できる可能性の数2ⁿが決まるという関係を、 升目の増減として目で確かめてみてください。

半加算器

1ビットの足し算の「和(S)」はXOR、「繰り上がり(CO)」はANDの振る舞いそのもの。足し算専用の部品はどこにもないのに、ゲート2つで加算が成立する。1+1のときだけCOが1になり、答えが「10」と2桁になる様子を4パターン全部で試してみてほしい。

さわって確認半加算器 — サムはXOR・キャリーはANDで同時に計算する
A=0B=0
XORゲート
S(サム)= 0
A=0B=0
ANDゲート
CO(キャリー)= 0
0 + 0 = 00(2)
ABS(サム)CO(キャリー)
0000
0110
1010
1101

半加算器は、同じ入力A・Bから「XORゲートでサムビットS」「ANDゲートでキャリービットCO」を 同時に計算する回路です。1+1のときだけCOが1になり、10進の「2」が2進数の「10」として出てきます。 入力が2つしかない——前の桁からのキャリービットを足し込めない——ため、使えるのは一番右の桁だけ。 それが「半」加算器と呼ばれる理由です。

スイッチの中身(物理)

論理ゲートの土台になる「電気で電気を制御するスイッチ」は、物理現象そのもの。

真空管(三極管)

加熱したフィラメントから飛び出す電子の流れを、途中に置いた格子(G)の電圧だけで堰き止める。可動部品ゼロで、リレーの約千倍速く状態が変わるこのスイッチが、初期の電子式コンピュータの主役だった。

さわって確認真空管 — 格子の電圧ひとつで電子の流れを制御する
OFF(遮断)
陰極(K)
加熱中
格子(G)
電子を反発
陽極(A)

豆電球(外部回路): 消灯

格子電圧 -5V ― 格子の負電荷が電子を押し返すため、電子は陽極に届かず陰極側へ跳ね返されます。回路はOFFのまま。

真空管は、フィラメントが陰極を熱し続けていても、格子(グリッド)のわずかな電圧だけで 電流のON/OFFを切り替えられます。可動部品がないぶんリレーより約千倍速く状態が変わります。 この「小さな入力で大きな電流を制御する」仕組みは、次のセクションで見るトランジスタ (ベースの小さな電圧でコレクタからエミッタへの大きな電流を制御)へ、そのまま受け継がれます。

NPNトランジスタ

N型|P型|N型の3層を重ねただけの「固体」のスイッチ。ベース(B)にかける小さな電圧ひとつで、コレクタ(C)からエミッタ(E)へ流れる大きな電流をON/OFFできる。真空管の格子(G)が果たしていた役割を、ベースがそのまま引き継いでいる。真空も加熱も要らないこの部品が、現代のCPUに数十億個詰め込まれている。

さわって確認NPNトランジスタ — ベースの小さな電圧で大きな電流を制御する
OFF(遮断)
CBEコレクタ(C)N型ベース(B)P型エミッタ(E)N型

豆電球(外部回路): 消灯

ベース(B)の電圧が0V ― コレクタ(C)からエミッタ(E)へ電流は流れず、事実上トランジスタはオフです。豆電球は消灯したまま。

ベース電圧コレクタ→エミッタの電流豆電球
0V(電圧なし)流れない消灯
小さな電圧をかける大きな電流が流れる点灯

NPNトランジスタは、N型・P型・N型の半導体を重ねただけの「固体」のスイッチです。 ベースにかける小さな電圧ひとつで、コレクタからエミッタへ流れる大きな電流をON/OFFできます。 真空管との対応でいえば、格子(G)の役割をベース(B)が、陰極(K)側をエミッタが、陽極(A)側をコレクタが受け持っています。 フィラメントの加熱も真空も要らないため、真空管よりずっと小さく、低電力で、長持ちします。

現実をビットに変換する

音や文字のような現実の情報を、0と1の列に変換して運ぶ。

アナログ→デジタル変換

連続した波を一定間隔で切り取り(サンプリング)、決まった段階に丸める(量子化)。サンプリングレート・ビット深度・ノイズの3つを動かして、デジタル化が何を取りこぼし、何をノイズから守るのかを確かめられる。

t=0.00 生値=0.00量子化値=0.13 → 100t=0.06 生値=0.71量子化値=0.63 → 110t=0.13 生値=1.00量子化値=0.88 → 111t=0.19 生値=0.71量子化値=0.63 → 110t=0.25 生値=0.00量子化値=0.13 → 100t=0.31 生値=-0.71量子化値=-0.63 → 001t=0.38 生値=-1.00量子化値=-0.88 → 000t=0.44 生値=-0.71量子化値=-0.63 → 001t=0.50 生値=-0.00量子化値=-0.13 → 011t=0.56 生値=0.71量子化値=0.63 → 110t=0.63 生値=1.00量子化値=0.88 → 111t=0.69 生値=0.71量子化値=0.63 → 110t=0.75 生値=0.00量子化値=0.13 → 100t=0.81 生値=-0.71量子化値=-0.63 → 001t=0.88 生値=-1.00量子化値=-0.88 → 000t=0.94 生値=-0.71量子化値=-0.63 → 001
生の波形(ノイズ込み)量子化されたサンプル値量子化後の再構成波形
出力ビット列(16サンプル × 3bit)
100 110 111 110 100 001 000 001 011 110 111 110 100 001 000 001

サンプリングは連続時間の波を一定間隔で切り取る操作、量子化は切り取った値を 2のビット深度乗個の段階(レベル)のどれかに丸める操作です。サンプリングレートを上げるほど波形の形を取りこぼしにくくなり、 ビット深度を上げるほど丸め誤差(量子化誤差)が小さくなります。ノイズを上げると量子化する前の生信号そのものが乱れ、 その乱れが下の2進数ストリームにそのまま刻まれることが分かります。

文字コード分解

入力欄に文字を打つと、各文字に割り当てられた7ビットのコードが16進と2進で1つずつ並ぶ。同じアルファベットの大文字と小文字を両方入れると、両者の差がちょうど20h——2進ではビット1つ分——であることが光って見える。ASCIIの設計の気配りを指先で確かめられる。

さわって確認文字コード分解 — 打った文字がASCIIのコードになる

4文字 / 最大16文字(1文字 = 7ビットのコード)

C43h
o6Fh
d64h
e65h

同じアルファベットの大文字と小文字(例: C と c)を両方入力すると、ペアがハイライトされます。

キーを1つ打つたびに、その文字に割り当てられた7ビットのコードが1つ増えていきます。英字の2進表示で ピンク色の枠が付いているのが20h(=32)の位のビットで、大文字と小文字はこの1ビットしか 違いません。「大文字⇄小文字」を押すと全部の英字が一斉に入れ替わりますが、変わっているのは 各文字のこのビットだけ——並べ替えも大小変換もコードの計算だけで済むという、ASCIIの設計の気配りを 指先で確かめてみてください。

IEEE754単精度ラボ(小数を32ビットで表す)

32ビットのどれか1つをタップして反転させると、10進の値がその場で計算し直される。符号のビットは正負だけを入れ替え、指数のビットは値を2の累乗の単位で大きく飛ばし、有効部小数のビットは同じ枠の中を細かく刻む——同じ1ビットでも、どの区画にあるかで効き方がまるで違うことが目で確かめられる。

さわって確認IEEE754単精度ラボ — 32ビットをさわって10進の値を動かす
プリセット
符号 s(1ビット)指数 e(8ビット)有効部小数 f(23ビット)

ビットをタップすると0と1が入れ替わり、下の10進の値がその場で変わります(上の数字はビット番号)。

10進の値1正規化数
符号 s
0 → 正の数
指数 e
保存値 127 − バイアス 127 = 実際の指数 0
有効部
1.0
32ビット全体
0x3F800000

正規化された形: 1.0 × 20

このシミュレータの本題である正規化数のパターンです。値は (−1)ˢ × 1.f × 2⁽ᵉ⁻¹²⁷⁾ で決まります。

符号のビットを反転すると、値の大きさはそのままで正負だけが入れ替わります。指数のビットを動かすと、 値は2の累乗の単位で大きく飛びます。有効部小数のビットは、暗黙の1のうしろに続く桁を足し引きして、 同じ2の累乗の枠の中を細かく刻みます——同じ1ビットでも、どの区画にあるかで効き方がまるで違います。 指数のビットをすべて0またはすべて1にすると、ゼロ・非正規化数・無限大・NaNという特別なパターンに入ります。 そのとき (−1)ˢ × 1.f × 2⁽ᵉ⁻¹²⁷⁾ の式は使えないので、どのパターンなのかを値のとなりに表示しています。

記憶して、実行する

「覚える回路」と「命令を繰り返し実行する仕組み」が揃うと、コンピュータになる。

RSフリップフロップ(1ビットの記憶)

NORゲート2つの出力を互いの入力へ戻すだけで、回路は入力が消えたあとも1ビットを覚え続ける。Setを一度ONにしてからOFFへ戻してもQが1のまま残る「保持」が主役。SetとResetを両方ONにすると、QとQ̄が反対でなくなる「禁止状態」も再現できる。

さわって確認RSフリップフロップ — SetとResetで1ビットを覚える

保持中(直前の状態を維持)

0
Q
1
SR動作
00保持(直前のQを維持)
10Set → Q = 1
01Reset → Q = 0
11禁止(同時ONは不可)

RSフリップフロップはNORゲート2つを互いにフィードバックさせただけの、最も単純な1ビットの記憶回路です。 SetでQ=1、ResetでQ=0にでき、両方OFFの間は最後にセット/リセットした状態を保ち続けます。 Setを一度ONにしてからOFFへ戻しても、Qが1のまま変わらない——この「保持」が、 コンピュータが1ビットを覚えておくための最小構成の原型です。

8×1 RAMアレイ

3個のスイッチが作る3ビットのアドレスが、8個のラッチのうちどれを読み書きするかを決める。デコーダが運ぶのはデータの値ではなく「ライト信号」で、選ばれた1個だけが書き換わる。いくつかのラッチに違う値を書いてからアドレスを切り替えると、それぞれの値が保存されたまま好きな順で読み出せる。

さわって確認8×1 RAMアレイ — アドレスで選んだラッチを読み書きする
アドレス

アドレス 000 → ラッチ0 を選択中

3線-8線デコーダ(ライト信号の届け先を1個だけ選ぶ)
00
10
20
30
40
50
60
70
8線-1線セレクタ(読み出し元を1個だけ選ぶ)
0
データアウト(ラッチ0 の値)

3個のスイッチが作る3ビットのアドレスが、8個のラッチのうちどれを読み書きするかを決めています。 書き込みのとき、デコーダが運ぶのはデータの値ではなくライト信号です。 データインの値は8個すべてのラッチに共通配線で届いていて、アドレスで選ばれた1個のW端子だけが 有効になるので、そのラッチだけが書き換わります。読み出し側では、同じアドレスを受け取ったセレクタが 8本のデータアウトから1本を選びます。いくつかのラッチに違う値を書き込んでからアドレスを切り替えると、 それぞれの値が保存されたまま、好きな順序で読み出せることを確かめられます。

バスの読み込み(部品をつなぐ4本の信号)

マイクロプロセッサがRAMから1つの値を読み込むとき、4本の信号は同時にではなく決まった順序で働く。アドレスを出す→制御信号で「読み込む」ことを伝える→RAMが中身を返す→受け取る、の一巡を1ステップずつ追える。アドレスを出しただけではRAMが応答しない点に注目してほしい。

さわって確認バスの読み込み — アドレスを出してから中身が返るまで
ステップ 0 / 4 — 待機中
読み出すアドレス
マイクロプロセッサ
受け取った値
バス(4種類の信号)
アドレス信号 どこを読むか
データ出力信号 書き込み用(今回は出番なし)
データ入力信号 読み込んだ中身
制御信号 何をするか
RAM(4か所・中身は8ビット)
0001101001
0100010111
1011001010
1100111100

バスのどのレーンにも信号は出ていません。読み込むアドレスを選び、「次のステップ」を押すと読み込みの一巡が始まります。

「次のステップ」を押すたびに、読み込みの一巡が1段階ずつ進みます。 まずマイクロプロセッサがアドレス信号にアドレスを出しますが、 それだけではRAMは応答しません。続いて制御信号が「読み込む」ことを伝えて、 はじめてRAMが中身をデータ入力信号に出します。アドレス信号と制御信号は、RAMが応答するまで 出したままである点にも注目してください。ここで値が流れる線が 「データ入力」と呼ばれるのは、マイクロプロセッサを基準にした呼び名だからです。 同じ1本の線でも、RAMの側から見れば自分が出しているデータ出力になります。読み出すアドレスを 選び直すと、その場所の中身が届くまでを何度でも確かめられます。

自動加算器(止まれない機械)

RAMに並べた数をカウンタが順に指し、累算器へ足し込んでいく。正しい合計が出てもカウンタは止まらず、末尾を過ぎると先頭へ巻き戻って同じ数を二重に足してしまう。「止まれ」をメモリの中に書いておく=停止命令という発想の転換が、なぜ必要だったのかを体験できる。

さわって確認自動加算器 — 足し終わっても止まれない機械を動かす

待機中

RAMアレイ(8番地に縮小)

027
114
29
30
40
50
60
70

色付きのマスが、カウンタの指している番地です

カウンタが指す番地0
累算器0
正しい合計50

0〜2番地に3つの数が入っています。合計は50になるはずです。マシンを止める仕組みは、どこにもありません。

この自動加算器には「止まる」という動作そのものがありません。3つの数を足し終えて正しい合計50が出ても、 カウンタは素知らぬ顔で進み続け、末尾の次には0番地へ巻き戻って、足したはずの数をまた足してしまいます。 機械を正しく止めるには、部品を増やすのではなく、「止まれ」という指示=停止命令をメモリの中に 書いておくという発想の転換が必要です。次のセクションで、この「命令」という発想が マシン全体を作り替えていきます。

CPUシミュレーター(フェッチ・デコード・実行)

「1ステップ実行」を押すたびに、プロセッサがフェッチ(命令を読み込む)→デコード(解読する)→実行の3段階を1つずつ進める。命令は1〜3バイトの可変長なので、プログラムカウンタは命令の長さのぶんだけ進む。条件ジャンプでループが回り、停止命令で止まるまでを追える。

さわって確認CPUシミュレータ — フェッチ→デコード→実行を1ステップずつ回す
待機中
PC0000hプログラムカウンタ
A0累算器
命令フェッチ結果
Zeroフラグ
ループ 1 周目
フェッチ命令を読み込む
デコード命令を解読する
実行実際に動かす

PC=0000h の命令「MVI A,5」(2バイト)をメモリから読み込む

番地ラベル命令バイト数
0000hMVI A,52
0002hloop:STA 2000h3
0005hDCR A1
0006hJNZ loop3
0009hSTA 2000h3
000ChHLT1
メモリ 2000h 番地(STAの書き込み先)(未書き込み)

「1ステップ実行」を押すたびに、プロセッサはフェッチ(命令を読み込む)→デコード(命令を解読する)→ 実行(実際に動かす)の3段階を1つずつ進めます。MVIが累算器Aに即値5をロードし、STAがその値を メモリの2000h番地へ書き込み、DCRがAを1減らします。結果がまだ0でなければZeroフラグはセットされず、 JNZ(条件ジャンプ)がプログラムカウンタをloopの番地へ書き戻すので、同じ区間が繰り返されます。 Aが0になるとジャンプは行われず、最後のSTAが0を書き込んでHLTで停止します。 番地の進み方にも注目してください——命令は1〜3バイトの可変長なので、 PCは命令の長さのぶんだけ進みます

フレームバッファ

8×8=64ピクセルをタップすると、メモリ上の対応するビットが1つだけ書き換わる。1ピクセル=1ビットなので、ちょうど1行が1バイト。メモリに並んでいるのはただの0と1の列にすぎず、ディスプレイという文脈がそれを白と黒として解釈することで、初めて「絵」になる。

さわって確認フレームバッファ — ピクセルに描いた絵はメモリのビット列

① 画面:8×8=64ピクセル(タップで白黒が反転)

② メモリ(フレームバッファ):64ビット=8バイト

行000000000
行100000000
行200000000
行300000000
行400000000
行500000000
行600000000
行700000000

ビット1(黒いピクセル): 0 / 64

このグリッドは1ピクセルに1ビットを割り当てているので、色数は2の1乗=2色(白黒)です。 ピクセルをタップするたびに、フレームバッファの対応するビットが1つだけ書き換わります。 8×8のグリッドではちょうど1行が1バイトです。メモリに並んでいるのはただの0と1の列に すぎず、ディスプレイという文脈がそれを白と黒として解釈することで、初めて「絵」になります。

人間の言葉でソフトウェアを書く

CPUが理解するのは二進数の命令だけ。人間が読めるコードとの橋渡しをするのがコンパイラ。

コンパイラの対応表

高級言語の1行が、アセンブリ言語の何命令に展開されるか。宣言・代入・数式・ループ・条件分岐をタブで切り替え、行をクリックすると相手側の対応行が光る。宣言文は命令を1つも生まないこと、for文のたった1行が10行以上の命令に膨らむことが目で見える。

さわって確認コンパイラの対応表 — 1つの命令文が何命令に展開されるか

行をクリック(タップ)すると、対応する相手側の行がハイライトされます

高級言語(ALGOL風)
アセンブリ言語

宣言文 integer a; は、変数の名前と種類(整数)をコンパイラに知らせるだけで、マシンコード命令には展開されません。いっぽう代入文 a := 5; は「アキュムレータAに即値5を転送する」「Aの値を変数aの番地へ保存する」の2命令になります。変数の名前はコンパイル後には残らず、ただのメモリの番地になります。

1つのCPUを分け合う

複数のプログラムが「同時に」動いて見えるのは、OSによる高速な入れ替えのおかげ。

時分割(マルチタスク)

3つのプログラムが同時に動いて見えるのは、1つのCPUを持ち時間ごとに順番に使っているだけ。「1ステップ」で進めると、持ち時間切れ→割り込み→実行位置の退避→次のプログラムの復元、という切り替えが1回ずつ進む。切り替え回数が積み上がるほど「同時実行」に見えてくる。

さわって確認時分割 — 1つのCPUを3つのプログラムで順番に使う
切り替え回数: 0
ブラウザ
動画を再生中
残り 6
エディタ
コードを編集中
待機中
音楽アプリ
曲を再生中
待機中

CPUを使用中:ブラウザ

  • 「再生」または「1ステップ」を押すと、割り込みと切り替えのログがここに表示されます

3つのプログラムは同時に動いているように見えますが、実際は1つのCPUを持ち時間ごとに 順番に使っているだけです。持ち時間が切れると割り込みが入り、オペレーティングシステムが 実行位置やレジスタの中身を退避してから、次のプログラムの状態を復元してCPUを渡します。 複数の端末に同時対応しているように見せる時分割(タイムシェアリング)も、 複数のプログラムを同時に走らせるマルチタスクも、この高速な切り替えの繰り返しでできています。

コンピュータ同士を会話させる

データを小さな断片に分けて運ぶ設計が、インターネットの頑健さを支えている。

パケット交換

データは宛先付きの小さなパケットに分割され、ルーターからルーターへそれぞれ自分の経路で運ばれる。途中のリンクを切断しても、パケットが自動で迂回して届く様子を試せる。

「▶ 5パケット送信」を押すと、youtube.com からブラウザへ5つのパケットが送られます。

パケット交換では、送信順とネットワーク内の経路は必ずしも一致しません。ルーターBを切断すると、そこを通るはずだったパケットは自動的に別の経路(A・C)へ迂回します。経路ごとに通過にかかる時間が違うため、先に送ったパケットの方が後に届くこともあります。それでも受信側では届いた順にパケットを並べ直すことで、元のデータを正しく復元できます。

ページが表示されるまで(DNS・TCP・TLS・HTTP)

URLを入力してからページが描画されるまでに、DNSでの名前解決、TCPの3ウェイハンドシェイク、TLSでの暗号化鍵の合意、HTTPのやり取りという8ステップが毎回走っている。その全体を1ステップずつ再現する。

ステップ —/8

「送信」を押すと、ブラウザが wikipedia.org に接続するまでの通信を1ステップずつ再現します。

ブラウザ
DNSサーバー???.???.???.???
Webサーバーwikipedia.org
DNS
DNS問い合わせ
DNS
IPアドレス応答
TCP
TCP SYN
TCP
TCP SYN-ACK
TLS
TLS ClientHello
TLS
TLS 証明書・鍵合意
HTTP
HTTP GET /(暗号化)
HTTP
HTTP 200 OK

Webページを開くたびに、まずDNSでドメイン名をIPアドレスに変換し(電話帳を引く作業)、 次にTCPで接続を確立し(電話をかけて相手が出るのを待つ作業)、 TLSで相手の身元を確かめて暗号化鍵に合意し(盗聴されない専用線に切り替える作業)、 最後にHTTPでページの中身をやり取りします。URLを入力した瞬間、この4層の通信が舞台裏で毎回行われています。