車輪の再発明は楽しい~Nand to Tetris編~

## はじめに

こんにちは、[42tokyo Advent Calendar 2022](https://qiita.com/advent-calendar/2022/42tokyo)の2日目を担当する、在校生のものです。

今回は、42とも縁が深い「車輪の再発明」として、Nand to Tetris……つまり、ハードウェアの最も基本的な要素から、簡単なゲームが動くところまでを、こちらの書籍を読みながら実装したので、そのまとめを書こうと思います。

https://www.amazon.co.jp/dp/4873117127/

この記事の内容は以下のような感じになります。

- 車輪の再発明と42について
- この本で作ったもの、学べることについて
- この本をおすすめしたい人（＋42をおすすめしたい人）

ちなみに、書籍を読みながら作成したものはこちらにあります。（とりあえず完成することを目標にしたので、非常に汚い＆エラー処理全然してない＆読みにくい＆……）

https://github.com/kotabrog/Theory-and-Implementation-of-Computer-Systems

※ この記事はプログラミングやコンピュータの知識が全然ない人でも読めるように心がけていますが、この本自体がある程度知識のある人向けだと思うので、その点はお気を付けください


## 車輪の再発明

車輪の再発明とは、すでに世にあるものを、再度一から作ることを言います。

これは「もうそれあるのに、自分で発明した気になっているの？」と皮肉の意味で使われることもありますが、一部の界隈では、「そのものを理解するために自分で一から作る」という勉強方法として、好意的に使われることもあります。

例えば「パソコン」は誰でも知っていると思いますが、パソコンがなぜ動いているのかを説明できる人はそんなにいません。
またそれを「知識」として知ることと、実際に作るという「体験」として知ることとでは、知っていることの深みが全然違うと思います。

もちろん、実際に作るためには、単に知ることよりも膨大な時間がかかるため、目的によってどのように知るかを選択する必要があるでしょう。

例えば42では、車輪の再発明をたくさん行い、（記事を書いた時点では）共通課程の最終段階では結構しっかりとしたWebアプリを作ります。

具体的には、

- もっとも基本的な関数をまとめたライブラリを自作する
- 簡単なグラフィック系のプログラムを作成して、どのように描画するかなどの手法を学ぶ
- 「プログラマーといえば」な黒い画面でユーザーの入力を受け付けてプログラムを実行するShellを自作する
- 並列処理の問題として有名な「食事する哲学者の問題」を実装する
- Webサーバーを自作する

などなど、色々なものを車輪の再発明します。（何を学ぶかについて詳しくはこちら：[42 Tokyoを（ほぼ）卒業したので、今までの活動をすべて振り返ってみた](https://note.com/kotabrog/n/n292c0b5ef071#9aa1ae5f-1ba3-499e-af2e-697b78fbdd66)）

これらのことについては、一切知らない、知っている、体験として学んでいる、という3段階（とそのグラデーション）で考えられると思いますが、後者の方がより広い視野でWebアプリ制作に取り組むことができるでしょう。  

もちろんこれらの知識はWebアプリに限らず、ソフトウェアエンジニアリング全般に活かせる知識であり、42ではそのような深い知識を持ったエンジニアを育てるためにこういった課題を行わせているのだと思います。

また、実際に作る体験は、開発の手順やチーム開発の方法、見積もりはたいてい誤るということを知ることなど、副次的な学びもたくさんあります。

## この本で学ぶこと

ここまで車輪の再発明について話してきましたが、今回私が学んだこの書籍は、「コンピュータを再発明する」ということを目標にしています。

ちなみに、先ほどあげた42で学ぶことの例と比較すると、ちょうど42で学ぶことの手前の部分＋少しライブラリの自作に食い込む感じで、学ぶことができるようになっています。

この本で学ぶことは大きく分けて以下のようになっています。

- NANDというかなり基本的な要素から、コンピュータを作成する「ハードウェアパート」
- コンピュータに読み込ませる操作を、人間が一応読める状態からコンピュータが読み込むための01の状態に変換する「アセンブラパート」
- 普段プログラマーが使うような言語から、機械で行う操作に近いアセンブリに変換する「高級言語 to アセンブリパート」
- 自作した言語でOSのかなり基本的な要素を作成する「OSパート」

これらを順番に作ることで、コンピュータがどのようにできているかを学びつつ、ダウンロードできるシュミレーターを使うことで、以下のように実際に動くものを作ることができます。

![pong.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/533384/68ae1d50-caf3-90c0-5aa6-9413e4aab236.gif)


それぞれについて少し深堀りしていきます。

### ハードウェアパート

ハードウェアは実際に作るのは大変なので、シュミレーター上で動せるものを作ることになります。

例えば以下のような感じで、構成する要素を並べたファイルを作ることが目標になります。

```
CHIP And {
    IN a, b;
    OUT out;

    PARTS:
    Nand(a=a, b=b, out=nand);
    Not(in=nand, out=out);
}
```

こういったものを、仕様にそって、[NAND](https://ja.wikipedia.org/wiki/NAND%E3%82%B2%E3%83%BC%E3%83%88)というとても基本的な要素から順に作成していくようになっていて、最終的にはメモリーやCPUなどを作っていきます。

このあたりの分野についての初学者でもわかるように、しっかりとその仕組みは説明しつつ、実際に作る部分に関しては、仕様ベースで作成します。

つまり「ADDという名前で、こういった機能をもつものを作ってください」というような詳細な取り決めがあり、それにそって読者は作成していくような感じです。

ヒントはありつつも、自分で考える余白も残されているので、章にもよりますが結構歯ごたえを感じるかもしれません。

ちなみに42でも、仕様が与えられそれにそったものを課題として作成することになるので、結構進め方は似ているかもしれません。
とはいっても、42では優しいヒントや背景の説明がほとんどなく、そこを調べることも含めて課題となっているので、この本よりは大変だと思います。

ですので、もしこの本を読んで楽しかったら、42にも向いているかもしれません。（逆も同様に）

### アセンブラパート

このパートでは、上で作ったハードウェアで動かすために、簡単な命令をコンピュータが読み込むことができる01に変換することを行います。

具体的には例えば以下の命令は

```
   @R0
   D=M
   @R1
   D=D-M
   @OUTPUT_FIRST
   D;JGT
   @R1
   D=M
   @OUTPUT_D
   0;JMP
(OUTPUT_FIRST)
   @R0             
   D=M
(OUTPUT_D)
   @R2
   M=D
(INFINITE_LOOP)
   @INFINITE_LOOP
   0;JMP
```

以下のように変換されます。

```
0000000000000000
1111110000010000
0000000000000001
1111010011010000
0000000000001010
1110001100000001
0000000000000001
1111110000010000
0000000000001100
1110101010000111
0000000000000000
1111110000010000
0000000000000010
1110001100001000
0000000000001110
1110101010000111
```

### 高級言語 to アセンブリパート

このパートでも、コードの変換を行います。  
今度は、普段プログラマーが触るような言語で書かれた以下のようなプログラムを

```
class Main {
   function void main() {
      do Output.printInt(1 + (2 * 3));
      return;
   }
}
```

アセンブラパートでお見せしたようなアセンブリ言語に変換します。

さらにこのパートは実際には一度中間言語をはさんで、

高級言語->中間言語->アセンブリ

というような順番で変換することになります。  
具体的には上記でお見せしたコードは一度以下のような形に変換されてからアセンブリに変換されます。

```
function Main.main 0
push constant 1
push constant 2
push constant 3
call Math.multiply 2
add
call Output.printInt 1
pop temp 0
return
```

このようにする理由についてですが、例えば「高級言語->アセンブリ」だと、言語やそのバージョンが変われば変換器を変更、修正しなければなりませんし、またコンピュータ（正確にはOSやCPU）が変わっても、同様に修正しなければなりません。

このように変換器が2方向から修正を迫られると、対応させることが大変になるため、クッションとして中間言語を用意しているようです。（詳しくは書籍を……！）

ということで、全体の流れをおさらいすると、以下のような流れで、書いたプログラムをコンピュータに読み込ませることができるようになります。

高級言語->中間言語->アセンブリ->01->ハードウェア

### OSパート

ここまでだと、例えばキーボード操作やディスプレイに描画すること、またプログラムを読み込ませる方法なども毎回プログラムとして書かなければならず、大変です。

ですので、そういった基本的な機能をここでは作成していきます。

ここまで完成すれば、ハードウェアに依存するような操作からもだいぶ解放され、最初にお見せしたようなプログラムも書くことができるようになります。

## おわりに

ということで、ここまででこの本について学べることを大まかにご紹介しましたが、いかがでしたでしょうか？

もしここまでを読んで「コンピュータの仕組み気になる！」ですとか、「実際に作ってみたい！」と思えたら、ぜひぜひこの本を読んでチャレンジしてみてください。（そう思えなかったら、私の拙い文章のせいなので、この本に罪はありません。ということでやっぱり読んでみてください！笑）

https://www.amazon.co.jp/dp/4873117127/

ちなみにオライリーの公式サイトからは電子書籍版も手に入れることができます。

https://www.oreilly.co.jp/books/9784873117126/

またこちらはこの本のもとになった本のwebサイトになります。

https://www.nand2tetris.org/


チャレンジする際に１点だけ注意なのですが、ハードウェアパートを終えてからの変換器作りは、自分でプログラミング言語を選択して作成することになるので、ある程度はプログラミングができることが前提となっています。
量はそこそこあるので、プログラミングの良い練習にもなると思います。

それでは、次の担当はこちらになりますので、ぜひこちらも->https://qiita.com/X-IA/items/41d893d766f3315a67b1