⚙️📝🧠
CPU シミュレーター前提知識

アセンブリ
言語入門

CPU が直接理解できる「機械語の一歩手前」

プログラムを書いたことがなくても大丈夫。このページを読めば、CPU シミュレーターの命令がすべて理解できるようになります。

⏱ 所要:20–30分難易度:★☆☆前提:なし
Section 1

アセンブリ言語とは?

CPU が分かる言語の、人間向け表現

アプリ
高水準言語
低水準言語
アセンブリ言語 ⭐
機械語(0と1)

↑ 抽象度(高いほど人間に近い) ← クリックで詳細

← 左のどこかの層をクリックしてください

Python のプログラム

a = 12 b = 7 result = a + b print(result) # → 19 と表示

人間が読み書きしやすい。でも CPU はそのまま実行できない。

同じ処理のアセンブリ

LOAD A, 12 ; A = 12 LOAD B, 7 ; B = 7 ADD A, B ; A = A + B → 19 OUT A ; 19 と表示 HALT ; 終了

CPU への「指示書き」。1行 = 1命令 = CPU が1回やること。

💡 ポイント:アセンブリは CPU の動作を直接コントロールします。 「どのレジスタに何を入れるか」「どのメモリアドレスを使うか」を自分で指定するので、 CPU の中で何が起きているかが手に取るようにわかります。
Section 2

レジスタ — CPU の「手元メモ帳」

計算のたびにメモリに読み書きすると遅い→ CPU 内部の超高速な記憶領域

このシミュレーターのレジスタ
A
汎用
B
汎用
C
汎用
D
汎用
PC
プログラムカウンタ
🏁 Z: ゼロフラグ
N: 負フラグ
💥 C: キャリーフラグ
汎用レジスタ A・B・C・D

どんな計算にも使える「変数箱」。8ビット(0〜255 の整数)を格納できます。
例:LOAD A, 42 → A の箱に 42 を入れる

PC(プログラムカウンタ)

次に実行する命令の番号」を常に指している特殊レジスタ。
通常は自動的に 0→1→2→… と増えますが、JMP 命令で強制的に変えられます。

フラグ(Z / N / C)

直前の演算結果の「性質」を記録する 1 ビットのスイッチ。CMP の後で JEQ / JGT が参照します。

フラグ立つ (=1) 条件
Z結果が 0(= 同じ値を比較した時)
N結果が(Rx < Ry の時)
C加算で 桁あふれ(255 を超えた)
Section 3

メモリ — 「引き出しだな」

レジスタより遅いが、より多くのデータを保管できる

RAM(16 バイト)
0x00
?
0x01
?
0x02
?
0x03
?
0x04
?
0x05
?
0x06
?
0x07
?
0x08
?
0x09
?
0x0A
?
0x0B
?
0x0C
?
0x0D
?
0x0E
?
0x0F
?

STORE A, 0x00 → アドレス 0x00 に A の値を保存

アドレスって何?

メモリの各マス目には住所番号(アドレス)がついています。0x000x0F16進数 の表記で、10進数の 0〜15 のことです。

STORE と LOAD の使い分け
LOAD A, 42 ; 即値→レジスタ STORE A, 0x00 ; レジスタ→メモリ保存 LOAD B, [0x00] ; メモリ→レジスタ読出し

※ このシミュレーターでは LOAD B, [0x00] と書くとメモリから読み込みます

💡 レジスタとメモリの違い:レジスタは CPU のにある超高速な一時置き場(4個だけ)。 メモリはもう少し遅いが 16 マスあります。計算はレジスタで行い、保存したいときだけメモリを使うのが基本パターンです。
Section 3 +

メモリ階層構造

各レイヤーをクリックして速度・容量・用途を確認しよう

レジスタ(CPU 内)
~1 クロック  |  数十バイト
CPU チップの中に物理的に存在する最速の記憶域。
このシミュレーターの A・B・C・D・PC がまさにレジスタです。
容量は極めて小さいが、演算は 1 クロックサイクル以内 で完了する。
🔥
L1 キャッシュ
~4 クロック  |  32–64 KB / コア
CPU コアごとに持つ最小・最速のキャッシュ。
「よく使う命令・データ」をここに置いておき、メモリアクセスを削減する。
命令キャッシュ(L1-I)とデータキャッシュ(L1-D)に分かれていることが多い。
🌡️
L2 キャッシュ
~12 クロック  |  256 KB–2 MB / コア
L1 より大きく、L1 にないデータを L2 から探す(キャッシュミス時)。
多くの CPU では L1・L2 は コアごと専用、L3 は複数コアで共有される。
💾
L3 キャッシュ(LLC)
~30–50 クロック  |  4–64 MB(共有)
Last Level Cache。全コアで共有する大容量キャッシュ。
L3 をミスするとメインメモリへのアクセスが発生し、大きなレイテンシが生じる。
Apple M4 は最大 72 MB の L3(System Level Cache)を搭載。
🟩
メインメモリ(RAM)
~100 ns  |  4–128 GB
このページの Section 3 で扱う「引き出しだな」がここ。
プログラム実行中データ・スタック・ヒープはすべてここに乗る。
電源を切ると揮発する(データが消える)ため、SSD に保存が必要。
💿
SSD / NVMe フラッシュ
~50–100 µs  |  256 GB–8 TB
不揮発性ストレージ。電源を切ってもデータが残る。
OS・アプリ・ファイルが保存されている。起動時に RAM に読み込まれる。
NVMe(PCIe 接続)は SATA SSD より約 5〜10 倍高速。
🗄️
HDD(磁気ディスク)
~5–10 ms  |  1–20 TB
回転するディスクに磁気で記録。レイテンシは SSD の 100 倍以上
大容量・低コストのため、バックアップやアーカイブ用途で今も現役。
機械的な可動部品があるので衝撃に弱い。
速度 vs 容量のトレードオフ
速度 容量 単価
レジスタ~1 cy~512 B最高
L1~4 cy32–64 KB非常に高
L2~12 cy256 KB–2 MB
L3~40 cy4–64 MB
RAM~100 ns4–128 GB
SSD~100 µs256 GB–8 TB非常に低
HDD~8 ms1–20 TB最低
💡 キャッシュのポイント:CPU はまず L1→L2→L3→RAM の順で探す。
見つからない場合をキャッシュミスと呼び、下の層を探すたびに大幅に遅くなる
プログラムの局所性(近いアドレスを繰り返し使う性質)がキャッシュ効率に直結する。
🎯 このページとの関係
Section 2 で学んだ レジスタ(A/B/C/D) は最上位の超高速層。
Section 3 で学んだ RAM(0x00〜0x0F) は STORE/LOAD でアクセスするメインメモリ層。
キャッシュは CPU が自動的に管理するため、アセンブリから直接操作はしない。
Section 4

命令セット(ISA)全解説

12 命令すべてを例付きで理解しよう

📦 データ転送命令

LOAD Rx, 値

レジスタ Rx に即値(数字)を入れる。

LOAD A, 10 ; A ← 10 LOAD C, 255 ; C ← 255(最大値)
LOAD Rx, [addr]

メモリのアドレス addr から値を読み込む

STORE A, 0x00 ; まず保存 LOAD B, [0x00] ; B ← mem[0x00]
STORE Rx, addr

Rx の値をメモリアドレス addr に保存する。

LOAD A, 42 STORE A, 0x03 ; mem[3] = 42
MOV Rx, Ry

Ry の値を Rx にコピーする。Ry は変わらない。

LOAD A, 99 MOV B, A ; B = 99(Aも99のまま)

🧮 算術命令

ADD Rx, Ry

Rx = Rx + Ry

LOAD A, 3 LOAD B, 5 ADD A, B ; A = 8
SUB Rx, Ry

Rx = Rx − Ry

LOAD A, 10 LOAD B, 3 SUB A, B ; A = 7
MUL Rx, Ry

Rx = Rx × Ry(8ビット下位)

LOAD A, 6 LOAD B, 7 MUL A, B ; A = 42
⚠️ 注意:結果は必ず最初のレジスタ(Rx)に書き込まれます。 例えば ADD A, B の後、A の値は変わりますが B は変わりません。

🔀 比較・分岐命令

「もし〜なら」を実現するために使います。CMP で比較 → JEQ/JGT で分岐という流れが基本です。

CMP Rx, Ry

Rx と Ry を比較してフラグを更新。値は変わらない。

Rx = Ry → Z=1
Rx < Ry → N=1
Rx > Ry → Z=0, N=0
JMP label

無条件で label の行にジャンプ(ループに使う)。

loop: ADD A, B JMP loop ; 永遠にループ
JEQ label

Z=1(直前の比較が「等しい」)なら label へ。

CMP A, B JEQ equal ; A==B ならジャンプ
JGT label

N=0 かつ Z=0(直前の比較が「より大きい」)なら。

CMP A, B JGT bigger ; A>B ならジャンプ

📤 出力・終了命令

OUT Rx

Rx の値を出力欄に表示。Python の print() に相当。

LOAD A, 42 OUT A ; 「42」と出力
HALT

プログラム終了。必ず最後に書くこと。

OUT A HALT ; ← これがないと暴走
Section 5

サンプルプログラム解説

シミュレーターの 3 つの例を 1 行ずつ読んでみよう

例1:足し算(12 + 7)

LOAD A, 12 ; ① A = 12 LOAD B, 7 ; ② B = 7 ADD A, B ; ③ A = A+B = 19 OUT A ; ④ 「19」出力 HALT ; ⑤ 終了
レジスタ A に 12 を入れる。「変数 a = 12」と同じ意味。
レジスタ B に 7 を入れる。
ADD A, B → A = 12 + 7 = 19。B は 7 のまま変わらない。
OUT で A の値(19)を画面に表示。
HALT でプログラム終了。CPU が止まる。

例2:ループ(1〜5 の合計 = 15)

LOAD A, 0 ; ① sum = 0 LOAD B, 1 ; ② i = 1 LOAD C, 5 ; ③ limit = 5 loop: ADD A, B ; ④ sum += i LOAD D, 1 ; ⑤ D = 1 ADD B, D ; ⑥ i += 1 CMP B, C ; ⑦ i > 5? JGT done ; ⑧ なら終了へ JMP loop ; ⑨ 繰り返す done: OUT A ; ⑩ 「15」出力 HALT ; 終了
①②③
A=合計値、B=カウンター、C=上限(5)を初期化。Python では sum=0; i=1; limit=5 に相当。
sum += i — A に B を足す。
⑤⑥
B を 1 増やす(インクリメント)。D=1 を一時レジスタとして使う。
⑦⑧
CMP B, C で B と 5 を比較。B > 5 なら JGT done で出口へ。
まだ B ≤ 5 なので JMP loop で先頭に戻る。
🔄 このパターン(初期化→ループ本体→インクリメント→条件チェック→JMP)が for ループの基本形です!

例3:条件分岐(max(15, 20) = 20)

LOAD A, 15 ; ① A = 15 LOAD B, 20 ; ② B = 20 CMP A, B ; ③ A > B? JGT a_win ; ④ A>B ならa_winへ OUT B ; ⑤ B が大きい→出力 HALT ; ⑥ 終了 a_win: OUT A ; ⑦ A が大きい→出力 HALT ; 終了
①②
比較する 2 つの値をロード。
CMP A, B → 15 − 20 = −5 → N フラグが立つ(負)、Z は 0。
JGT a_win — N=1 なので ジャンプしない(A は B より大きくない)。
ジャンプしなかったので次の行 OUT B を実行 → 20 を出力。
🔀 if-else の基本形:
「CMP → ジャンプ命令 → else ブロック → HALT → label: → if ブロック → HALT」
Section 6

よく使うパターン集

コピー&改造して使えるテンプレート

🔁 カウンターループ

for i in range(1, N+1) に相当

LOAD B, 1 ; i = 1(開始値) LOAD C, N ; 上限 N に変更 LOAD D, 1 ; インクリメント用 loop: ; ← ここにループ処理 ADD B, D ; i++ CMP B, C JGT done JMP loop done: HALT
❓ if A > B / else

大きい方を使う分岐

CMP A, B JGT a_bigger ; A>B ならジャンプ ; B が大きい場合 OUT B HALT a_bigger: ; A が大きい場合 OUT A HALT
💾 値をメモリに保存・復元

レジスタが足りない時のテクニック

LOAD A, 42 STORE A, 0x00 ; A を保存 ; A は別の計算に使う LOAD A, 100 ; 後で復元 LOAD B, [0x00] ; B ← 42(復元)
🟰 if A == B

等しいかどうかの分岐(JEQ を使う)

LOAD A, 7 LOAD B, 7 CMP A, B JEQ equal ; A==B ; 等しくない場合 HALT equal: ; 等しい場合 OUT A HALT
Section 7

よくあるミスと対処法

❌ HALT を書き忘れる

最後に HALT がないと、命令リストの外を読み続けて予期しない動作になります。必ず最後の行に HALT を入れましょう。

❌ ADD の結果の向きを間違える

ADD A, B は「A を更新する」命令です。B には何も書き込まれません。「B に A を足した結果を入れたい」なら ADD B, A と書きます。

❌ ラベル名のスペルミス

loop: と定義して JMP lop と書くと「ラベルが見つからない」エラーになります。定義と参照のスペルを一致させましょう。

❌ CMP なしで JEQ/JGT を使う

フラグは直前の演算の結果です。JEQ/JGT の直前には必ず CMP を置きましょう。

✅ コメントを活用する

; 以降はコメントとして無視されます。各行に「何をしているか」を書いておくと、後で見直したときに理解しやすくなります。

✅ 1ステップ実行で確認する

シミュレーターの「1ステップ」ボタンを使えば、1命令ずつ実行してレジスタの変化を確認できます。バグを見つけるのに最適です。

🚀

準備できたら、シミュレーターへ!

実際に命令を入力して、CPU がどう動くか体感してみよう。
パイプラインのアニメーションで Fetch→Decode→Execute が見えます。

🖥 CPU シミュレーターを開く →