コンピュータの仕組みを根本から解説する
キー入力から始まる
キーボードの「A」キーを押す。
1ミリ秒も経たないうちに、その文字が画面に表示される。
簡単そうに見える?実際には、この1ミリ秒の間に、電気信号は少なくとも6層の抽象化を通過し、数十のハードウェアコンポーネントを通り抜け、複数のバスを横断して、この「単純な」操作を完了している。
コンピュータの本質は計算ではなく、抽象化である。
各層は下の複雑さを隠蔽し、上の層に対してクリーンなインターフェースだけを公開する。この抽象化があるからこそ、プログラマはトランジスタの中を電子がどのように流れるかを気にすることなく、高水準言語でコードを書くことができる。
今日は、キー入力信号の完全な旅を追いながら、コンピュータがどのようにして層を重ねた抽象化によって物理的な動作を画面上の文字に変換するのかを見ていく。
第1層:物理信号からスキャンコードへ

あなたが押しているのは文字ではなく、スイッチである。
キーボードのすべてのキーの下には、メカニカルスイッチがある。「A」キーを押すと、スイッチが閉じて電流が流れる。これが最も基本的な物理的現実——オンとオフだけであり、「A」は存在しない。
キーボードコントローラの仕事は、この物理信号をコンピュータが理解できる数値に変換することである。
どのようにして?
マトリクススキャン
キーボードには100以上のキーがある。それぞれを個別のケーブルでコンピュータに接続すると、キーボードケーブルは指よりも太くなってしまう。そこでキーボードは賢い設計を採用している:マトリクススキャンである。
チェス盤を想像してほしい。行線と列線が交差している。各キーは交差点に配置されている。コントローラは各行に素早く通電し、各列に電流があるかどうかを検出する。行3が通電されたときに列5が電流を検出すれば、行3・列5のキーが押されたことを意味する。
ここからスキャンコード——キーの位置を表す数値——が生まれる。
「A」を押すと、スキャンコード0x1Eが出力される。「A」を離すと、0x9Eが出力される(「解放」を示す追加の上位ビットが付く)。
第1層完了:物理的动作 → デジタル符号化。
しかし、まだ終わりではない。スキャンコードはまだキーボード内部のバッファにあり、送信されるのを待っている。
第2層:I/Oデバイスからメモリへ

スキャンコードはCPUに到達する必要があるが、CPUはキーボード入力を待ってじっとしているわけにはいかない。
CPUは毎秒数十億回の速度で動作し、キーボード入力は毎秒数十回の速度で届く。もしCPUがすべての命令の前に「キーボード入力はあるか?」をチェックしていたら、99.9999%の時間が無駄になる。
解決策は?割り込み機構である。
割り込み:ハードウェア版の「用事ができたら呼んで」
割り込みの設計思想は日常生活と同じだ:配達員に5分おきに「荷物は届きましたか?」と電話するのではなく、配達員が到着したら電話をくれるようにする。
キーボードコントローラがスキャンコードを生成すると、CPUに割り込み要求(IRQ)信号を送信する。
この信号は割り込みコントローラ(8259Aまたはその現代的な同等品)を経由し、割り込み番号が割り当てられる。キーボードは通常IRQ1、割り込み番号0x21である。
CPUは各命令の実行後、割り込み信号を確認する。もしあれば、現在の作業を一時停止し、割り込みを処理する。
割り込みベクタテーブル
CPUはどのようにキーボード割り込みを処理すべきかを知るのか?
メモリには割り込みベクタテーブル(Interrupt Vector Table)があり、各割り込み番号に対応するハンドラルーチンのアドレスが格納されている。これは電話帳のようなもので、割り込み番号0x21はキーボードハンドラのアドレスに対応する。
CPUがキーボード割り込みを受け取ると:
- 現在のプログラムカウンタ(PC)、ステータスレジスタなどをスタックにプッシュする
- 割り込み番号を使ってテーブルを検索し、ハンドラのアドレスを見つける
- ハンドラにジャンプして実行する
キーボードハンドラはI/Oポート(通常0x60)を通じてスキャンコードを読み取り、メモリ内のキーボードバッファに格納する。
第2層完了:I/Oイベント → メモリ内データ。
スキャンコードは安全にメモリに格納された。しかし、それはまだスキャンコードであり、文字「A」ではない。
第3層:スキャンコードから文字へ

スキャンコード0x1Eは「キーボード上のAキーの位置」を表しており、文字「A」ではない。
なぜ?同じキーでも、国によって異なる文字を表す可能性があるからだ。アメリカ人が押せば「A」、フランス人が押せば「Q」になる(キーボードレイアウトが異なる)。
スキャンコードを文字に変換するのはオペレーティングシステムの役割である。
入力サブシステム
最新のオペレーティングシステムにはすべて入力サブシステム(Input Subsystem)があり、以下の処理を行う:
- カーネルバッファからスキャンコードを読み取る
- 現在のキーボードレイアウトに基づいて文字に変換する
- 修飾キーを処理する(Shift+A = 大文字「A」、Ctrl+C = コピーコマンド)
- 文字を現在アクティブなウィンドウに配信する
この変換にはキーボードレイアウトマッピングテーブルが関与する。WindowsレジストリにはHKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Keyboard Layoutsがあり、さまざまな言語のマッピングが格納されている。
変換後、文字「A」はメッセージキューに置かれ、アプリケーションによって読み取られるのを待つ。
第3層完了:ハードウェア依存の符号化 → 統一文字符号化。
第4層:文字からピクセルへ

アプリケーションは文字「A」を受け取り、それを画面に表示する必要がある。
しかし、あなたが見ているのは文字ではなく、ピクセルである。
モニタが理解できるのはピクセル——各点が何色か——だけだ。文字「A」は一連のピクセルで構成されたパターンであり、フォントファイルに格納されている。
フォントレンダリング
「A」を入力すると、アプリケーションは:
- フォントファイルを検索する(例:Microsoft YaHei、SimSun)
- 「A」のグリフ記述(ベクター曲線またはビットマップ)を取得する
- フォントサイズに応じてグリフを拡大縮小する
- ラスタライズ:ベクター曲線をピクセルグリッドに変換する
- アンチエイリアシング処理でエッジを滑らかにする
これらのピクセルデータはビデオメモリ(VRAM)——グラフィックカード上のメモリ——に書き込まれる。
VRAMからスクリーンへ
グラフィックカードの表示コントローラは、毎秒60回(60Hz)の速度でVRAMからデータを読み取り、ビデオ信号に変換してモニタに送信する。
モニタは信号を受信し、各ピクセルの色を制御し、あなたは文字「A」を見る。
第4層完了:文字符号化 → ピクセル行列。
第5層:CPUがこれらの命令をどのように実行するか

上記のすべての操作——スキャン、割り込み、変換、レンダリング——は最終的にCPUによって実行される必要がある。
CPUはどのように命令を実行するのか?
命令サイクル:フェッチ-デコード-実行
命令の実行には3つの段階がある:
-
フェッチ(Fetch)
- プログラムカウンタ(PC)が次の命令のアドレスを保持する
- アドレスをメモリに送り、命令を取得する
- 命令を命令レジスタ(IR)に格納する
-
デコード(Decode)
- 制御ユニットが命令のオペコードを解析する
- 実行する操作を決定する(加算?ロード?ジャンプ?)
- オペランドの場所を決定する
-
実行(Execute)
- 算術論理演算装置(ALU)が計算を実行する
- またはメモリにアクセスしてデータの読み書きを行う
- プログラムカウンタを更新する
そして繰り返す。
例
CPUがADD R1, R2, R3(R2とR3の値を加算し、結果をR1に格納)を実行するとする:
- フェッチ:メモリからこの命令の機械語を読み取る
- デコード:これが加算命令であり、オペランドがR2とR3にあることを認識する
- 実行:ALUがR2とR3の値を加算器に送り、結果をR1に書き込む
各命令はナノ秒単位で完了する。
第5層完了:高水準言語 → 機械語命令 → 電気信号。
抽象化の代償
抽象化は無料ではない。
抽象化の各層には性能上のオーバーヘッドが伴う:
- キーボードスキャン:数マイクロ秒
- 割り込み処理:数十マイクロ秒
- OS変換:数百マイクロ秒
- フォントレンダリング:数ミリ秒
- VRAM書き込み:数マイクロ秒
これらを合わせると、あなたが感じる「入力遅延」になる。通常は10〜50ミリ秒の間である。
一般の人にとって、この遅延は無視できる程度である。しかし、eスポーツプレイヤーにとっては死活問題である。そのため、彼らはメカニカルキーボード(応答が速い)、高リフレッシュレートのモニタ(144Hz以上)、有線接続(無線は追加の遅延がある)を使用する。
一言でまとめると
コンピュータの本質は抽象化である:複雑さをカプセル化し、シンプルなインターフェースだけを公開する。
キー入力信号の旅:
物理スイッチ → スキャンコード → 割り込み → メモリバッファ → 文字符号化 → メッセージキュー → フォントレンダリング → VRAM → ピクセル
各層はただ一つのことだけを行う:下の層から簡略化された出力を受け取り、処理し、上の層に簡略化されたインターフェースを提供する。
この階層的な抽象化こそが、人間に非常に複雑なコンピュータシステムを扱うことを可能にしている。
プログラマはキーボードのスキャン方法を知る必要はなく、getchar()の戻り値を読むだけでよい。
プログラマはグラフィックカードのレンダリング方法を知る必要はなく、DrawText()関数を呼び出すだけでよい。
抽象化の力とは、複雑さを扱いやすくすることである。
