CPUを構成する各要素
まず、パソコンの全体像は以下の通り。
この図の内容を理解さえすれば、パソコンが何をする機械なのか概ねわかるはず。
参考 : https://yizhe87.medium.com/from-nand-to-tetris-nand2tetris-project-5-7846812a8dae

- メモリ
- 入力
- writeM(1bit) メモリの値を書き込むかどうかを判定する。1なら書き込む
- adressM(15bit = 32k) メモリの値を「どこに」書き込むかを指定する。
- outM(16bit) メモリに「どんな値を」書き込むかのデータ。
- メモリからの出力 : メモリからの出力データは、基本常に一定。ただし、
writeM が1に設定されたときは、outM の値が次の時間ステップから出力され続ける。
- CPU
- 入力
- inM : メモリから出力されるああ値のこと。
- instruction : どんな処理を実行するべきか?
- 出力
- writeM : メモリに値を書き込むかどうか
- outM : 出力される計算結果
- adressM : どこに書き込むか?
- pc : 次に実行する処理が書いてある場所
- ROM32K
- 入力 : 次に実行する処理が書いてある場所
- 出力 : 実行するコード
CPU
CPUがやっていることは、以下の図のとおり

プロセス一覧
- もし、入力のinstructionがA命令であれば、CPUは16ビットの命令をAレジスタにロードを行う
- もし、入力のinstructionがC命令であれば、
- CPUは指定された計算をALUで行う
- その計算結果を命令で指定されたレジスタ(A、D、M)に格納する。
- 計算結果で、Mにデータが格納されれば、writeM出力は1に設定され、CPUのoutM には、ALUの出力が設定され、
- 計算結果で、Mにデータが格納されていなければ、writeM出力は0に設定される。
- もし、
reset入力が0の場合、
- CPUはALU出力と現在の命令のジャンプビットを使用して、次にどの命令をフェッチするか決める。決める方法は以下の通り
- 現在の命令に、ジャンプ命令が含まれていた場合、ジャンプする宛先は常にAのアドレスに格納されているため、Aアドレスの値にジャンプする
- ジャンプ命令が特に含まれていない場合、プログラムカウンターをインクリメントし、次の行の値を取得しにいく
- もし、
reset入力が1の場合、
CPUの出力である outMとwriteMは組み合わせゲートで実装されるため、入力後瞬時に変化する。
しかし、別のCPU出力である adressMとpcは、クロックによって制御されるため、次の時間ステップのみで新しい値に更新される。
いずれにせよ、どのタイミングで出力されるにせよ、実際に値が評価されるのは、クロック周波数に合わされたタイミングである。
入出力
- 入力
- instruction : 実行する命令(A命令 or C命令)
- inM : データのこと(通常は、Aアドレスに格納されている値)
- reset : 現在のプログラムを最初からに戻すか、続けるか
- 出力
- outM : 命令のM出力
- adressM : outMを書き込む宛先
- writeM : outMにデータが書き込まれたか?
- pc : 次の命令のアドレス
命令メモリ
命令メモリには、名前の通り、プログラムコードが格納されている。
- 入力 : CPUのプログラムカウンターから出力された、命令がある場所(adress)が入力されている。
- 出力 : プログラムの命令コード一行分が出力される

データメモリ
データメモリは名前の通り、データが格納される。
(このデータメモリは、プログラムが格納される命令メモリとは明確に分離されている)

from https://hackaday.io/project/185131-the-hack-computer-from-nand2tetris-on-breadboards/details
データメモリの実装例は、以下のとおり
- RAM16K : 16000(16K)個のレジスタから構成されるRAMチップ。データが記憶される
- Screen : 画面のメモリマップとして動作する
- Keyboard : キーボードのメモリマップとして機能する。
内部

ここで、ALUがこれまでできなかったことを実は実現できるようになっている!
1 1 1 a c1 c2 c3 c4 c5 c6 d1 d2 d3 j1 j2 j3
- instruction[15:13] = 111
- instruction[12] = a ビット
- instruction[11:6] = ALU制御ビット
- instruction[5:3] = destination(どこに結果を書き込むか)
- instruction[2:0] = jump条件
左上のMUX16
このMUX16は、「A命令なら instruction の値を、C命令なら ALU 出力を A レジスタに入れる」
- instructionがC命令でかつ、aビットが1の時に(
instruction[15] AND instruction[12]) instructionを次の 「A registar」チップに流す。
- そうでない場合は、ALUから出力される値を次の 「A registar」チップに流す。
ということをしている。
Aレジスタ(A register) = アドレスレジスタ
A命令のとき MUXの出力(=instruction)をAにロードし、C命令のときはAレジスタは通常更新されず、前回の値を保持(ただしdestビットがAを含む場合は別経路でロード)
ここでのcは、 NOT instruction[15](先頭ビットの否定)
- A命令の時は
NOT instruction[15]は1となり、Aレジスタにデータが格納される。
- C命令の時は
NOT instruction[15]は0となり、前回ALUから計算された結果が素通りされて次チップに渡される。
@21
この場合、CPUは「定数21をAレジスタに格納せよ」という意味。
このあとアドレスレジスタは
- そのまま
adressMとして、データの格納場所として出力される
PCに入力され、次の実行場所として使われる。
- もう一つのMux16に候補の一つとして引数として渡される。
Note
しかし、Aレジスタの正体は「メモリ(M)レジスタの場所(アドレス)」を表すと言うこと。
そして、MレジスタとAレジスタは別個のものではなく、同じものを指している。
- 場所を指すのがAレジスタ
- Aレジスタが指している中身を指すのがMレジスタ
今はたまたま定数が入力されているが、Mレジスタが呼ばれるときは、Aレジスタの場所をMレジスタは見に行っている。
二個目のMUX16
これは、ALUが計算対象とする「値の出どころ」を切り替えるスイッチ。
ALUが「A」か「M(RAM[A])」のどちらを計算に使うか決定する
- 上側入力: Aレジスタからの入力
- 下側入力: inMメモリ(RAM)からの入力
- 選択信号:
instruction[12](C命令のaビット)
C命令のaビットは、Aレジスタの値を使うという意味。
Note:AレジスタとMレジスタはいつ同期されるのか?
Mレジスタ(= RAM[A])がAレジスタの指す場所を「見に行く」のは、
Aレジスタが更新された次のクロック周期。
PCチップ
「PCは命令のアドレスを指す」。
in 入力 次に設定したい命令アドレス
out 出力 現在の命令アドレス(ROMのアドレス入力として使われる)
c ジャンプ命令のとき1になり、inの値でPCを上書き。論理式としては(instruction[1] OR instruction[1] OR instruction[1])ジャンプ命令のいずれかのビットが1であれば、PCがAレジスタの値の値にセットされる。
まずは分岐処理
ALUではできなかった分岐処理が、CPUのレイヤーではできるようになった。
なぜできるようになったかと言えば、プログラムカウンタ の概念が導入されたからだ。
CPUにおける分岐処理は、マルチプレクサで分岐処理を行い、プログラムカウンタで次に実行する処理を変更している。
- サンプルコード (ALUが演算した結果(D)が負(Negative)なら,Aレジスタの値にジャンプ)
D;JLT
page:https://minegishirei.hatenablog.com/entry/2025/04/05/201841