Turbowarpでnandgameのcpuを再現してみた

:::note warn
注意
この記事はnand gameのネタバレ(答え)を含みます。
:::

# 1 きっかけ
cpuの勉強をしたくてnand gameで遊んでいたところ 「これTurbowarpでも作れんじゃね」と思い勉強の実践も兼ねて作ってみることにした。

:::note info
turbowarpには『ビット操作』という便利な拡張機能があるが負の値を-111111...と表現してしまうので16bitの論理ゲートなどは1bitずつ変換してくっつけたもので表すことにする。

:::

# 2.今回作るcpuの構造
![スクリーンショット 2025-03-19 15.54.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3965914/e8f32568-3671-448c-8213-9a20c4097a71.png)
主に、RAM、コントロールユニット、クロック回路、カウンター、rom　でできている
(めっちゃわかりにくい)


## 2.1 memory
このmemoryは二つのレジスタと一つのRAMでできているようだ。

これをさらに分解していく

![スクリーンショット 2025-03-19 15.57.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3965914/d7b82685-2db8-4817-97dd-fa532c1b87fd.png)

### register
![スクリーンショット 2025-03-19 16.00.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3965914/41b09785-afa3-457f-bc2e-a3d73fd3cd2a.png)
N bitレジスタは``DFF``がN個でできているようだ。　(この場合はN=2)
しかし画像中の``D-FF``は`` Dラッチ ``を使用していて、その``Dラッチ``は出力を入力に代入するという方法でできているので今の私の技術力では再現することができない...

なので私は、レジスタをブロックで直接作ることにした。
そして完成したのがこれだ。
![block_2025_3_19-16_13_48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3965914/f3d8afef-a5b6-4330-959e-b90913a22979.png)


このIDというものは複数のレジスタを区別する役割がある。

一応これでもちゃんとnandgameと同じように動作する。


途中で値が変わっても一番最初に記録した値が出る。
説明;
``registerA_``---1番最初の入力を記録する変数

``registerB_``---値をセットしたか（``cl``が1の時,``registerC_``を変更しないようにするため）

``registerC_``---値をセットしたときr``egisterA_``を記録する変数

``registerD_``---出力用の変数(``registerC_``を出力)
### 2.2 RAM
RAMはいくつものレジスタを組み合わせてできている。
いちいちレジスタを何個も組み合わせて作るのは効率が悪い(めんどい)ので
これも一から作っていく
![block_2025_3_19-16_30_54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3965914/38568ea3-02c1-4308-b35d-276380f14331.png)

今回は保存、出力先をRAMというリストにしてみた。
registerのブロックの出力先(register_D)を リスト``RAM``にしただけ。
仕組みは同じ。
## 2.3 counter
![スクリーンショット 2025-03-19 16.19.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3965914/5238d432-e0f5-495c-b33d-1da107a1e2c0.png)
見ての通りカウンターも一部出力を入力に代入しているところがあるので一から作った。
結果↓
![block_2025_3_19-16_22_10 (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3965914/8b405967-0a42-4f25-b966-bd673a4535b7.png)

説明;
``カウンタ1`` 特に意味はない　制作過程で出てきたやつ　今は修正済み。

``カウンタ2 ``Xを書き込み中か(clが更新されたか)

``カウンタ3``モード判別
                0の時インクリメントモード
                a,bの時setモード

``カウンタ4`` Xの値の保存

``カウンタ5`` カウンタに書き込み可能か(clが更新されたか)

## 2.4 control unit
さぁここからが本番だ。
control unitはCPUにとって心臓部なのでパーツも多いし複雑になってくる。
なので、重要なもののみを解説することにする。

ここはわかりやすいように本来の順番で解説していこう。

<details><summary>論理ゲート</summary>
どれも基本的な仕組みは同じだ。
最初にも言った通り一つひとつ変換して出力する。

説明;

``〇〇``出力
``〇〇 counter``変換するビット
``〇〇 answer``変換結果

### and
![block_2025_4_27-13_08_13 (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3965914/0c07cae0-88a4-468b-b1a5-0c4f170598ef.png)

### or
![block_2025_4_27-13_12_02 (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3965914/92fb1992-1893-4dcc-b678-6e179b48ad2a.png)
### xor
![block_2025_4_27-13_12_06 (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3965914/d23c2637-df0c-47e3-8100-f5b2fa08d53b.png)

### not
![block_2025_4_27-13_11_44 (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3965914/afba4eb7-fae2-472c-836b-8185e6f9d3b9.png)


- - -
</details>
<details><summary>計算</summary>

### HA
半加算機(Half Adder)は1ビットの2つの入力された値を加算、出力(2ビット)する回路だ。

![block_2025_4_27-9_56_13 (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3965914/b67d18ec-1f2f-427a-8693-9134d937d23e.png)

これを再現している↓

![スクリーンショット 2025-04-27 9.57.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3965914/66a7f1d9-a4ee-4471-9924-ed96f9a75afd.png)


### FA
全加算機(Full Adder)は2つの入力された値と繰り上げ(両方1ビット)を加算、出力(2ビット)する回路だ。

半加算機が三つでできる(頑張れば二つで行けるらしい)
![block_2025_4_26-22_52_57 (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3965914/7a68ece6-c48f-4e1f-b46e-c64789d9c6fc.png)

非常にわかりにくい。
丁寧によんでいけばこうなっているとわかるだろう。
![スクリーンショット 2025-04-26 22.57.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3965914/25aafb1c-2947-4743-adc3-2731d9faea10.png)

ちなみに、
x=a
y=b
z=c
である。


### MA
多ビット加算器(Multi-bit Adder)は多数のビットを加算、出力する回路である。

今回は16ビットcpuなので16個必要だ。
しかし、ram同様いちいちパーツを組み合わせて作るのは効率が悪い(めんどい)のでこれも一から作っていく。↓
![block_2025_4_27-12_55_34 (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3965914/94a4b947-604b-4952-b2ea-6814f93f3c59.png)
説明;
``adder answer`` :1ビット加算機の結果
``adder counter``:何ビット目を計算するか
``adder``        :出力
``adder carry``  :「1ビット加算機の結果」の繰り上げの値
手順;
//start
①16ビット目を加算する。``adder carry``を設定する。
//↑loop↓
②15ビット目、``adder carry``を加算する。　``adder carry``を更新する。
③14ビット目、``adder carry``を加算する。　``adder carry``を更新する。
④13ビット目、``adder carry``を加算する。　``adder carry``を更新する。
...(1ビット目まで繰り返す)

### Increment
![block_2025_4_26-22_30_24 (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3965914/e4fb2c5c-843c-4edd-8158-1d4f73992388.png)


インクリメントは入力された数+1を出力する回路だ。
加算機を利用している。
### Sub
![block_2025_3_19-22_59_03 (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3965914/eff184ba-d04a-4f88-ae87-ac267949cba7.png)

Subtraction(減算)はX-Yの結果を出力する回路だ。
これの考え方を変えるとX+(-Y)となり加算器と2の補数を使って結果を出すことができる。
2の補数とは、二進数で負の数を表す代表の方法である。
2の補数は負の数にしたい値を反転(not)して1を足せばできる。

参考<a>https://www.seplus.jp/dokushuzemi/ec/fe/fenavi/kind_basic_theory/basic-theory2/</a>



### EZ(eq)
Equal to Zeroの略でX=0の時1を出力する回路だ。
![block_2025_3_19-22_48_34 (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3965914/6749c04e-f543-49d0-8db2-8aac027f251e.png)


### LZ(lt)
Less than Zeroの略でX<0の時1を出力する回路だ。
![block_2025_3_19-22_43_18 (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3965914/40a5ad89-a637-4770-88e2-cd2e75e4979e.png)

第15ビットを符号ビットとして利用している。
符号ビットが1の時は数値が負の数だということを表している。

- - -
</details>

<details><summary>切り替え&スイッチ</summary>

### セレクタ
![block_2025_3_19-21_14_08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3965914/23392775-2971-4e6b-9f42-31dd9dd24fe4.png)

セレクターは`` s ``が1の時``d1``を、0の時``d0``の値を出力する回路である。

### スイッチ
![block_2025_3_19-21_13_35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3965914/23cfa8a9-6b0a-4437-8256-6cf7e2197a63.png)
スイッチは``s``が1の時``d``の値を``c1``に、0の時``d``の値を``c0``に出力する回路である。
(今回は``c1``、``c0``を一つの値としてまとめた)
- - -
</details>

<details><summary>演算装置</summary>

### ビット演算装置
このビット演算装置の入力は二つのオペコード(``op0``,``op1``)と二つの16ビットの値(``X``,``Y``)の4つで構成されていて出力は、一つだけである。

入出力の関係は以下のようになる。
| ``op1`` | ``op0`` | 出力(``j``) |
|:-:|:-:| :-:|
| 0  | 0  | X AND Y |
| 0  | 1  | X OR Y |
| 1  | 0  |  X XOR Y |
| 1  | 1  | Xをビット反転 |

これをturbowarpで実装するとこうなる
![block_2025_3_19-21_56_15 (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3965914/c0c1c595-04d6-45bb-8709-beb04a2ef2f3.png)


### 算術演算装置
この算術演算装置の入力も二つのオペコード(``op0``,``op1``)と二つの16ビットの値(``X``,``Y``)の4つで構成されていて出力は、一つだけである。
| ``op1`` | ``op0`` | 出力(``R``) |
|:-:|:-:| :-:|
| 0  | 0  | X + Y |
| 0  | 1  | X - Y |
| 1  | 0  |  X + 1 |
| 1  | 1  |  X - 1 |

これをturbowarpで実装するとこうなる
![block_2025_3_19-22_04_27 (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3965914/f45b4dbd-66ff-4690-925d-7c01daff19f7.png)

### 算術論理演算装置(ALU)
この算術論理演算装置はさっき紹介した二つの演算装置を一つにまとめたもので、入力は二つのオペコード(``op1``,``op0``),二つのフラグ(``sw``,``zx``),回路選択用の入力(``u``)の5つから構成されていて、出力は一つだけである。
``u``が1の時は算術演算装置の結果を、0の時はビット演算装置の結果を出力する。
これをturbowarpで実装すると...?
![block_2025_3_19-22_11_01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3965914/0025ef11-0eba-4b39-8130-7a396858519d.png)
(自分で作ったのに意味わからん)

### 条件(condition)
この『条件』は最大3つのフラグを使用してXがそのフラグの条件を全て満たしているかを判断する回路だ。

| フラグ | 条件 |
|:-:|:-:|
|``lt`` | 0未満 |
|``eq`` | 0と等しい  |
|``gt`` | 0より大きい  |

わかりやすくするために例を挙げよう
X ≧ 0　の時1を出力させたい場合``eq``と``gt``を1にすればいい
(≧を分解すると=と>に分かれ、それを条件とするフラグは``eq``と``gt``しかないためだ)
| ``lt`` | ``eq`` | ``gt`` | 1を出力する条件 |
|:-:|:-:|:-:|:-:|
| 0 | 0 | 0 | 絶対に出力は0  |
| 0 | 0 | 1 |  X > 0 |
| 0 | 1 | 0 |  X = 0 |
| 0 | 1 | 1 |  X ≧ 0 |
| 1 | 0 | 0 |  X < 0 |
| 1 | 0 | 1 |  X ≠ 0 |
| 1 | 1 | 0 |  X ≦ 0 |
| 1 | 1 | 1 |  絶対に出力は1 |

これをturbowarpで実装するとこうなる(今気づいたけどltがitになっている↓)
![block_2025_3_19-22_31_34 (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3965914/ba6f0ad9-150f-43fc-b30a-5c3ea07c0730.png)

</details>





# 3.おまけ
1.今回使用した拡張機能

・ビット操作
・Lilyの道具箱
・カスタム定義

2.実際にできたcpu(+エディター)のファイル
[製作中(エディター)]

2.5 scratchに対応させたcpu(+機械語の説明のスライド、エディターはなし)
[もうすぐ完成!!]


3.機械語の解説
[cpu機械語解説メモ](https://qiita.com/yamatya6/items/59f82e90847f73f8dfad)

4.これからやりたいこと(目標)
・画面作成
・グラフィック命令の追加
・OSの制作(CUI)

# 4.終わりに
今回はnand gameのHardwareレベルをクリアして学んだことを活かしてこの作品(CPU)を作ったが、今度はSoftwareレベルをクリアしてこの作品をもっとアップデートしていきたいと思っている。

最後まで見てくれてありがとうございました。