Hackコンピュータは、ノアム・ニサンとシモン・ショッケンによって考案され、著書『コンピューティングシステムの要素:第一原理から現代のコンピュータを構築する』で解説されている理論的なコンピュータ設計です。 [1]著者らは「現代の」という用語を用いて、フォン・ノイマン・アーキテクチャ・モデルに従ってパターン化されたデジタルバイナリマシンを指しています。
Hackコンピュータは、コンピュータの構成とアーキテクチャに関する基礎的かつ包括的なコースの一部として、ハードウェアシミュレータアプリケーションで実践的な仮想構築を行うことを目的としています。[2]著者らが作成し、2部構成で配信されているそのようなコースの1つは、「Build a Modern Computer From First Principles: From NAND to Tetris」という大規模公開オンラインコース(MOOC)として無料で利用できます。[3]このコースに含まれる12のプロジェクトでは、学習者は2入力NANDゲートから始めて、ハードウェア(メモリとCPU)とソフトウェア(アセンブラ、VM、Java風プログラミング言語、OS)の両方を含む、完全に機能する仮想コンピュータを完成させます。コンピュータハードウェアの初期実装に使用されるハードウェアシミュレータに加えて、本書とオンラインコースで説明されているプロジェクトをサポートする完全なHackコンピュータエミュレータプログラムとアセンブラも著者のWebサイトで入手できます。[4]
ハードウェアアーキテクチャ

Hackコンピュータのハードウェアは、ブロック図に示すように、3つの基本要素で構成されています。2つの独立した16ビット メモリユニットと中央処理装置(CPU)があります。コンピュータは16ビットワード単位でデータを移動および処理するため、Hackコンピュータは16ビットアーキテクチャに分類されます。
命令メモリは、コンピュータから見ると読み取り専用メモリ(ROM)として実装され、実行用にアセンブルされたバイナリプログラムコードを保持します。ランダムアクセスメモリ(RAM)は、実行プログラムのデータを保存し、コンピュータのメモリマップドI/O機構にサービスと記憶領域を提供します。データ処理とプログラム制御はCPUによって行われます。
3つのユニットはパラレルバスで接続されています。ROMユニットとRAMユニットのアドレスバス(15ビット)とデータバスおよび命令バス(16ビット)は完全に独立しています。そのため、Hackの設計は、メモリユニットとCPU間のバス通信に関してハーバード・アーキテクチャ・モデルに準拠しています。すべてのメモリはワードアドレスのみでアクセス可能です。
読み取り専用メモリ(ROM)
HackコンピュータのROMモジュールは、個別にアドレス指定可能な、シーケンシャルな16ビットメモリレジスタの線形配列として提供されます。アドレスは0(0x0000)から始まります。メモリ要素はシーケンシャルデバイスであるため、システムクロック信号はシミュレーションアプリケーションとコンピュータエミュレータアプリケーションによって供給されます。ROMアドレスバスは15ビット幅であるため、プログラム命令には合計32,768ワードを使用できます。現在アクティブなワードのアドレスは、 CPU内のプログラムカウンタレジスタによって供給されます(下記参照)。特定のクロックサイクルで命令アドレスバスに設定されたアドレスによって識別されるROMメモリレジスタの値は、次のサイクルの開始時に「現在の」命令として使用できます。命令レジスタは存在しません。命令は各サイクルで現在アクティブなROMレジスタからデコードされます。
ランダムアクセスメモリ(RAM)
RAMモジュールは、個別にアドレス指定可能な、シーケンシャルな16ビットの読み書き可能なメモリレジスタの連続した線形配列として捉えることもできますが、機能的にはアドレス範囲によって3つのセグメントに分割されます。アドレス0(0x000)から16383(0x3FFF)には、従来の16ビットの読み書き可能なレジスタが含まれており、汎用プログラムデータのストレージとして使用されます。
アドレス16384 ( 0x4000 ) から 24575 ( 0x5FFF ) までのレジスタは、基本的にはデータRAMに似ていますが、内蔵のスクリーンI/Oサブシステムでも使用されるように指定されています。この範囲のアドレスに書き込まれたデータは、コンピュータの仮想256 x 512画面(I/O参照)に出力を生成するという副次効果があります。プログラムが画面出力を必要としない場合、この範囲のレジスタは一般的なプログラムデータとして使用できます。
RAMアドレス空間の最終アドレス24576(0x6000)には、1ワードのレジスタが1つ含まれており、その現在の値はHackエミュレータプログラムをホストするコンピュータに接続されたキーボードの出力によって制御されます。このキーボードメモリマップレジスタは読み取り専用です(I/O参照)。
24577 ( 0x6001 ) から 32767 ( 0x7FFF ) の範囲のデータメモリアドレスは無効です。選択された RAM メモリレジスタの状態遷移は、システムクロック信号によっても調整されます。
中央処理装置(CPU)

添付の図に示すように、Hackコンピュータの中央処理装置(CPU)は、内部構造を備えた統合論理ユニットです。市販のシンプルなCPUに見られる多くの機能を提供します。CPUの最も複雑な要素は、コンピュータの計算機能を提供する算術論理ユニット(ALU)です。ALUは、2つの16ビット入力オペランドと1つの16ビット出力を持つ組み合わせ論理デバイスです。オペランドから出力される計算は、ALUへの6つの順序付けられた1ビット入力のセットによって指定されます。ALUはまた、計算結果がゼロ(zrフラグ)か負数(ngフラグ)かを示す2つの1ビットステータスフラグを出力します。
CPUには、DとAというラベルの付いた2つの16ビットレジスタも搭載されています。D(データ)レジスタは汎用レジスタであり、その現在の値は常にALUのxオペランドに供給されますが、一部の命令ではその値は無視されます。A(アドレス)レジスタは、命令によって指示された場合、その現在の値をALUのyオペランドとして供給することもあります。また、その値はデータメモリのアドレス指定や、分岐命令における命令メモリ内のターゲットアドレスとしても使用されます。この機能を容易にするために、Aレジスタは、ハードウェアに明示的に実装されていないMという「疑似レジスタ」に直接関連付けられています。したがって、このMレジスタは、Aレジスタに含まれる現在の値のアドレスを持つRAMに格納されている値を表します。
CPU における最後の重要な要素は、プログラム カウンタ (PC) レジスタです。PC は 16 ビットのバイナリ カウンタで、下位 15 ビットが次に実行する命令の命令メモリ内のアドレスを指定します。分岐命令によって別の指示がない限り、PC は各クロック サイクルの終了時に値を増分します。CPU には、プログラム制御下で PC を非シーケンシャルな値に設定することにより、コンピュータの命令実行順序を変更するロジックも含まれています。PC には、PC 値が論理 0 から論理 1 に切り替わった後、論理 0 から論理 1 に戻る際に PC 値を 0 (0x0000) に初期化する 1 ビットのリセット入力も実装されています。多くの実際の CPU 設計とは異なり、CPU の外部割り込みや内部割り込み、関数呼び出しのサポートを実装するための、プログラムからアクセス可能なハードウェア メカニズムは提供されていません。
外部入出力(I/O)
Hack コンピュータは、メモリマップ方式の I/O を採用しています。256 x 512 の仮想画面へのビットマップ形式の白黒出力は、目的の出力のビットマップをデータ メモリ位置 16384 ( 0x4000 ) から 24575 ( 0x5FFF ) に書き込むことによって行われます。このアドレス範囲のデータ ワードはビットの線形配列として表示され、各ビット値はコンピュータ エミュレータの仮想画面上の 1 つのピクセルの白黒状態を表します。画面 RAM セグメントの最初のメモリ アドレスのワードの最下位ビットは、画面の左上隅のピクセルを、0 の場合は白に、1 の場合は黒に設定します。最初のワードの次の最上位ビットは、右隣のピクセルを制御します。以下同様に続きます。最初の 512 ピクセル行が画面メモリの最初の 32 ワードで記述された後、次の 32 ワードを使用して同じ方法で 2 番目の行のマッピングが続行されます。コンピュータ外部のロジックが画面 RAM メモリ マップ セグメントを読み取り、仮想画面を更新します。
CPUエミュレータプログラムを実行しているコンピュータにキーボードが接続されていると、エミュレータはプログラム実行中に押されたキーに対応する16ビットのスキャンコードを、RAMアドレス24576(0x6000)のキーボードレジスタに格納します。キーが押されていない場合、このレジスタの値は0です。エミュレータには、キーボードの有効/無効を切り替えるトグルボタンが用意されています。エンコード方式は、印刷可能な文字のASCIIエンコード方式に厳密に従っています。Shiftキーの効果は通常、尊重されます。標準的なPCキーボードによく搭載されている他のキー、例えば方向キー(←、↑、↓、→)やFnキーのコードも提供されています。
動作サイクル
CPU とメモリ ユニットの段階的な操作は、ハードウェア シミュレータとコンピュータ エミュレータ プログラムの両方に組み込まれているクロックによって制御されます。クロック サイクルの開始時に、プログラム カウンタの現在の値によって発行された ROM アドレスの命令がデコードされます。命令で指定された ALU オペランドは、必要に応じてマーシャリングされます。指定された計算は ALU によって実行され、適切なステータス フラグが設定されます。計算結果は、命令で指定されたとおりに保存されます。最後に、プログラム カウンタは、次に必要なプログラム命令の値に更新されます。現在の命令で分岐が指定されていない場合は、PC 値が単純に増加されます。分岐が指定されている場合は、次に実行される命令のアドレスが PC に (A レジスタから) ロードされます。その後、現在の PC 値を使用してサイクルが繰り返されます。
Hackコンピュータは、ハーバードメモリアーキテクチャモデルを採用しているため、現在の命令の実行と次の命令の「フェッチ」を、2つの部分から成る単一のクロックサイクルで実行するように設計されています。クロック速度は、ハードウェアシミュレータとCPUエミュレータの両方にある制御要素によって変更できます。ただし、選択された速度に関わらず、各命令は1サイクルで完全に実行されます。ユーザーはプログラムをシングルステップ実行することもできます。
ROMにロードされたプログラムの実行は、CPUのリセットビットによって制御されます。リセットビットの値が0の場合、上記の動作サイクルに従って実行が続行されます。リセットビットを1に設定すると、PCは0に設定されます。リセットビットの値を0に戻すと、現在のプログラムの最初の命令から実行が開始されます。ただし、RAMにはリセット時の以前のアクティビティの値が保持されています。
いかなる種類の割り込みに対しても、ハードウェアまたはマシン言語のサポートはありません。
データ型
ROM メモリに格納される値は、命令セットアーキテクチャのセクションで説明されているように、有効な Hack マシン言語命令を表す必要があります。
任意の16ビット値をRAMに格納できます。RAMに格納される値のデータ型は、その場所やプログラム内での使用状況によって推測されます。ハードウェアでサポートされる主なデータ型は16ビットの符号付き整数で、2の補数形式で表されます。したがって、符号付き整数の範囲は-32768から32767です。RAM内の値の下位15ビットは、ポインタの意味でROMまたはRAM内のアドレスを表すこともできます。
画面 I/O に割り当てられた RAM メモリ レジスタの値については、画面が「オン」になっている場合、その値はコンピューターの独立した I/O サブシステムによって 256 行 x 512 列の仮想画面の 16ピクセル マップとして解釈されます。
キーボード メモリ内のコード値はプログラムによって読み取られ、プログラムで使用するために解釈される場合があります。
浮動小数点型に対するハードウェア サポートはありません。
命令セットアーキテクチャ(ISA)と機械語
Hackコンピュータの命令セットアーキテクチャ(ISA)と派生機械語は、他の多くのアーキテクチャと比較して簡素です。ALUによる演算処理を指定する6ビットで64種類の命令を記述できますが、HackコンピュータのISAに公式に実装されているのは18種類のみです。Hackコンピュータのハードウェアは整数乗算(および除算)や関数呼び出しを直接サポートしていないため、ISAにはこれらの演算に対応する機械語命令は存在しません。
Hack マシン言語には 2 種類の命令のみがあり、それぞれは 16 桁の 2 進数でエンコードされています。
A-命令
最上位ビットが「0」である命令は、A命令またはアドレス命令と呼ばれます。A命令は、以下のようにビットフィールドでエンコードされます。
0 b 14 b 13 b 12 b 11 b 10 b 9 b 8 b 7 b 6 b 5 b 4 b 3 b 2 b 1 b 0
0 – A命令の最上位ビットは「0」です
b 14 - b 0 - これらのビットは、0から32767までの10進範囲の負でない整数の2進表現を提供します。
この命令が実行されると、残りの15ビットが左ゼロ拡張され、CPUのAレジスタにロードされます。副作用として、その値で表されるアドレスを持つRAMレジスタが、次のクロックサイクルで以降の読み取り/書き込み操作のために有効になります。
C命令
C命令(計算命令)として知られるもう1つの命令タイプは、プログラミングの主力です。最上位ビットは「1」です。残りの15ビットはビットフィールドで符号化され、オペランド、実行される計算、および指定された計算結果の格納場所を定義します。この命令は、最新の計算結果に基づいてプログラム分岐を指定することもできます。フォーマットは次のとおりです。
C 命令: dest = comp ; jump、destまたはjumpのいずれかが空の場合、2 つのオプションdest = compまたはcomp ; jumpが提供されます。
C 命令は次のようにビットフィールドでエンコードされます。
111 a c 1 c 2 c 3 c 4 c 5 c 6 d 1 d 2 d 3 j 1 j 2 j 3
1 – C命令の最上位ビットは「1」です
11 – これらの2つのビットはCPUによって無視され、慣例により常に「1」に設定されます。
a – このビットは、計算で使用されるALUの「y」オペランドのソースを指定します。
c 1 -c 6 – これらの6つの制御ビットは、ALUによって実行されるオペランドと計算を指定します。
d 1 -d 3 – これらの3ビットは、現在のALU出力を格納するための宛先を指定します。
j 1 -j 3 – これらの3つのビットは、算術分岐条件、無条件分岐(ジャンプ)、または分岐なしを指定します。
C 命令の Hack コンピュータ エンコード スキームを次の表に示します。
これらの表では、
- Aは現在Aレジスタに格納されている値を表す
- DはDレジスタに現在格納されている値を表す
- Mは、アドレスがAレジスタに含まれるデータメモリレジスタに現在格納されている値を表します。つまり、M == RAM[A]です。
| 1つの | c 1 | c 2 | c 3 | c 4 | c 5 | c 6 | ALU出力: f(x,y) | ニモニック |
|---|---|---|---|---|---|---|---|---|
| 0 | 1 | 0 | 1 | 0 | 1 | 0 | 0を出力します。すべてのオペランドを無視します。 | 0 |
| 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1を出力します。すべてのオペランドを無視します。 | 1 |
| 0 | 1 | 1 | 1 | 0 | 1 | 0 | −1を出力します。すべてのオペランドを無視します。 | −1 |
| 0 | 0 | 0 | 1 | 1 | 0 | 0 | Dを出力します。AとMは無視されます。 | D |
| 0 | 1 | 1 | 0 | 0 | 0 | 0 | Aを出力します。DとMは無視されます。 | あ |
| 1 | 1 | 1 | 0 | 0 | 0 | 0 | Mを出力します。DとAは無視されます。 | M |
| 0 | 0 | 0 | 1 | 1 | 0 | 1 | Dのビット反転を出力します。AとMは無視されます。 | !D |
| 0 | 1 | 1 | 0 | 0 | 0 | 1 | Aのビット反転を出力します。DとMは無視されます。 | !A |
| 1 | 1 | 1 | 0 | 0 | 0 | 1 | Mのビット反転を出力します。DとAは無視されます。 | !M |
| 0 | 0 | 0 | 1 | 1 | 1 | 1 | Dの2の補数の負を出力します。AとMは無視されます。 | -D |
| 0 | 1 | 1 | 0 | 0 | 1 | 1 | Aの2の補数の負を出力します。DとMは無視されます。 | -A |
| 1 | 1 | 1 | 0 | 0 | 1 | 1 | Mの2の補数の負を出力します。DとAは無視されます。 | -M |
| 0 | 0 | 1 | 1 | 1 | 1 | 1 | D + 1 を出力します(D を増分します)。A と M は無視されます。 | D+1 |
| 0 | 1 | 1 | 0 | 1 | 1 | 1 | A + 1 を出力します(A を増分します)。D と M は無視されます。 | A+1 |
| 1 | 1 | 1 | 0 | 1 | 1 | 1 | M + 1 を出力します(M を増分します)。D と A は無視されます。 | M+1 |
| 0 | 0 | 0 | 1 | 1 | 1 | 0 | D - 1 を出力します(D を減分します)。A と M は無視されます。 | D-1 |
| 0 | 1 | 1 | 0 | 0 | 1 | 0 | A - 1 を出力(A を減算); D と M を無視 | A-1 |
| 1 | 1 | 1 | 0 | 0 | 1 | 0 | M-1 を返します(M を減算します)。D と A は無視されます。 | M-1 |
| 0 | 0 | 0 | 0 | 0 | 1 | 0 | D + Aを出力します。Mは無視されます。 | D+A |
| 1 | 0 | 0 | 0 | 0 | 1 | 0 | D + Mを出力します。Aは無視されます。 | D+M |
| 0 | 0 | 1 | 0 | 0 | 1 | 1 | D - Aを出力します。Mは無視されます。 | ダ |
| 1 | 0 | 1 | 0 | 0 | 1 | 1 | D - Mを出力します。Aは無視されます。 | DM |
| 0 | 0 | 0 | 0 | 1 | 1 | 1 | A - Dを出力します。Mは無視されます。 | 広告 |
| 1 | 0 | 0 | 0 | 1 | 1 | 1 | M - Dを出力します。Aは無視されます。 | 医学博士 |
| 0 | 0 | 0 | 0 | 0 | 0 | 0 | DとAのビットごとの論理積を出力します。Mは無視されます。 | D&A |
| 1 | 0 | 0 | 0 | 0 | 0 | 0 | DとMのビットごとの論理積を出力します。Aは無視されます。 | D&M |
| 0 | 0 | 1 | 0 | 1 | 0 | 1 | DとAのビットごとの論理和を出力します。Mは無視されます。 | D|A |
| 1 | 0 | 1 | 0 | 1 | 0 | 1 | DとMのビットごとの論理和を出力します。Aは無視されます。 | D|M |
| d 1 | d 2 | d 3 | ALU出力を保存する | ニモニック |
|---|---|---|---|---|
| 0 | 0 | 0 | 出力は保存されません | なし |
| 0 | 0 | 1 | M | M |
| 0 | 1 | 0 | D | D |
| 0 | 1 | 1 | MとD | 医学博士 |
| 1 | 0 | 0 | あ | あ |
| 1 | 0 | 1 | AとM | 午前 |
| 1 | 1 | 0 | AとD | 広告 |
| 1 | 1 | 1 | AとMとD | AMD |
| j 1 | j 2 | j 3 | 分岐条件 | ニモニック |
|---|---|---|---|---|
| 0 | 0 | 0 | 支店なし | なし |
| 0 | 0 | 1 | 0より大きい出力 | JGT |
| 0 | 1 | 0 | 出力は0 | JEQ |
| 0 | 1 | 1 | 0以上の出力 | JGE |
| 1 | 0 | 0 | 出力が0未満 | JLT |
| 1 | 0 | 1 | 出力は0ではありません | JNE |
| 1 | 1 | 0 | 出力が0以下 | JLE |
| 1 | 1 | 1 | 無条件分岐 | ジャンプ |
アセンブリ言語
Hackコンピュータには、HackコンピュータのISAを実装するハードウェアプラットフォーム用のプログラムを作成するためのテキストベースのアセンブリ言語が搭載されています。Hackアセンブリ言語プログラムは、ファイル名拡張子が「.asm」のテキストファイルに保存できます。Hackアセンブリ言語のソースファイルは大文字と小文字が区別されます。テキストの各行には、以下のいずれかの要素が含まれます。
- 空白行
- コメント
- ラベル宣言(オプションの行末コメント付き)
- A命令(オプションの行末コメント付き)
- C命令(オプションの行末コメント付き)
これらの行タイプはそれぞれ固有の構文を持ち、定義済みまたはユーザー定義のシンボル、あるいは数値定数を含むことができます。空行とコメントはアセンブラによって無視されます。ラベル宣言、A命令、およびC命令(以下で定義)には、先頭または末尾の空白文字は許可されます(ただし無視されます)。ただし、内部に空白文字を含めることはできません。
コメント
2文字のシーケンス「//」で始まるテキストはすべてコメントです。コメントはソースコードの行に単独で記述することも、他のプログラムソース行の末尾に記述することもできます。コメント識別子文字列に続く行末までのテキストはアセンブラによって完全に無視されるため、機械語は生成されません。
記号と数値定数
Hackアセンブリ言語では、様々な特定の目的のために英数字記号を使用できます。記号は、アルファベット(大文字と小文字)または数字の任意の組み合わせで表すことができます。また、記号には、アンダーバー("_")、ピリオド(".")、ドル記号("$")、コロン(":")のいずれかの文字を含めることができます。記号は数字で始まってはいけません。記号は大文字と小文字が区別されます。ユーザー定義記号は、変数名とラベルの作成に使用されます(下記参照)。
Hackアセンブリ言語アセンブラは、アセンブリ言語プログラムで使用するためのいくつかの定義済みシンボルを認識します。シンボルR0、R1、…、R15は、それぞれ整数0から15にバインドされています。これらのシンボルは汎用レジスタを表すものであり、シンボルの値はデータメモリアドレス0から15を表します。定義済みシンボルSCREENとKBDは、メモリマップされた仮想画面出力の開始アドレス(16384)とキーボード入力の開始アドレス(24756)を表すためにも指定されています。オペレーティングシステムのソフトウェアスタックの構築に使用されるその他のシンボル(SP、LCL、ARG、THIS、THAT)もいくつかあります。
0~32,767 の範囲の非負の 10 進定数を表すために、10 進数 (0~9) の文字列を使用できます。負数を示すためにマイナス記号を使用することはできません。2 進数または 8 進数での表現はサポートされていません。
変数
アセンブリ言語プログラムでは、変数(つまり名前付きRAMレジスタ)を表すためにユーザー定義シンボルを作成できます。シンボルは、アセンブリ時にアセンブラによって選択されたRAMアドレスにバインドされます。したがって、アセンブリ言語のソースコードに現れる変数は、アドレスとして扱う必要があります。
アセンブリ言語のソースコードでは、変数はA命令で最初に参照された際に暗黙的に定義されます。ソースコードがアセンブラによって処理されると、変数シンボルは16番地から始まる一意の正の整数値にバインドされます。アドレスは、ソースコード内で最初に出現した順に変数シンボルにバインドされます。慣例により、プログラム変数を識別するユーザー定義シンボルはすべて小文字で記述されます。
ラベル
ラベルは、左括弧「(」と右括弧「)」で区切られたシンボルです。ラベルは別のソースプログラム行で定義され、アセンブラによってソースコード内の次の命令の命令メモリ位置のアドレスにバインドされます。ラベルは一度しか定義できませんが、プログラム内の任意の場所で、たとえ定義された行の前でも、複数回使用できます。慣例により、ラベルはすべて大文字で表記されます。ラベルは、分岐C命令のターゲットアドレスを識別するために使用されます。
A-命令
A命令の構文は「@ xxxx」です。ここで、xxxxは0から32767の範囲の10進数値定数、ラベル、または変数(定義済みまたはユーザー定義)のいずれかです。この命令を実行すると、AレジスタとM疑似レジスタの値が「xxxx」で表される15ビットのバイナリ値に設定されます。この15ビット値は、Aレジスタ内で左ゼロ拡張され、16ビットになります。
A命令は、3つの目的のいずれかで使用できます。1つは、プログラム制御下にあるコンピュータに(負でない)数値を導入する唯一の手段です。つまり、プログラム定数を作成するために使用できます。2つ目は、M疑似レジスタ機構を使用してRAMメモリの位置を指定し、その後C命令で参照できるようにすることです。3つ目は、分岐を指定するC命令が、Aレジスタの現在の値を分岐先アドレスとして使用することです。A命令は、分岐命令の前に、通常はラベルを参照することによって、分岐先アドレスを設定するために使用されます。
C命令
C言語命令は、HackコンピュータのALU計算エンジンとプログラムフロー制御機能を制御し、命令構文は「comp」、「dest」、「jump」と呼ばれる3つのフィールドで定義されます。compフィールドはすべてのC言語命令に必須です。C言語命令構文は「dest = comp ; jump」です。「=」と「;」は命令フィールドを区切るために使用されます。destフィールドを使用しない場合は「=」を省略します。jumpフィールドを使用しない場合は「;」を省略します。C言語命令では、命令内部にスペースを入れることはできません。
compフィールドは、上記の表で定義されている28個の文書化されたニーモニックコードのいずれかである必要があります。これらのコードは個別の単位とみなされ、すべて大文字で表記し、間にスペースを入れないでください。6個のALU制御ビットは、潜在的に64個の計算関数を指定できる可能性がありますが、アセンブラで認識できるように公式に文書化されているのは、表に示されている18個のみです。
destフィールドは、指定された計算結果を格納する1つ以上の場所を指定するために使用できます。このフィールドと「=」区切り文字が省略された場合、計算結果は格納されません。格納可能な場所の組み合わせは、上記の表で定義されているニーモニックコードによって指定されます。
ジャンプフィールドは、次に実行する命令のROM内アドレスを指定するために使用できます。このフィールドと「;」区切り文字が省略された場合、実行は現在の命令の直後の命令に継続されます。指定された分岐条件が満たされた場合、分岐先アドレス(ROM内)はAレジスタの現在の値によって提供されます。分岐条件が満たされなかった場合、実行はROM内の次の命令に継続されます。現在の計算値に基づいて、6つの異なる比較のためのニーモニックコードが用意されています。さらに、7番目のオプションとして無条件分岐が用意されています。無条件分岐ではcompフィールドの値は必須ではありませんが、必ず指定する必要があるため、この命令の構文は「0;JMP」となります。サポートされている分岐条件は上記の表に示されています。
アセンブラ
Hackコンピュータをサポートする無料で入手可能なソフトウェアには、コマンドラインアセンブラアプリケーションが含まれています。このアセンブラは、Hackアセンブリ言語ソースタイル(*.asm)を読み取り、Hackマシン言語出力ファイル(*.hack)を生成します。マシン言語ファイルもテキストファイルです。このファイルの各行は、ソーステキストファイルの対応する実行可能行のエンコードを表す16文字の2進数文字列です。エンコードは、「命令セットアーキテクチャ(ISA)とマシン言語」セクションで説明されている仕様に従っています。作成されたファイルは、エミュレータのユーザーインターフェースによって提供される機能によって、Hackコンピュータエミュレータに読み込むことができます。
アセンブリ言語プログラムの例
以下は、Hackアセンブリ言語で記述された注釈付きサンプルプログラムです。このプログラムは、最初の100個の連続する整数を合計し、計算結果を「sum 」というユーザー定義変数に格納します。whileループ構造を実装し、1から100までの整数値を反復処理し、各整数を「sum」変数に加算します。ユーザー定義変数「cnt」は、ループ処理中、現在の整数値を保持します。このプログラムは、メモリマップドI/Oを除く、Hackコンピュータの「ドキュメント化された」アセンブリ言語機能のすべてを示しています。これは、C言語のコードをHackアセンブリ言語に翻訳したものです。
// 1+...+100 を加算します
int cnt = 1 ; int sum = 0 ; while ( cnt <= 100 ) { sum += cnt ; cnt ++ ; }
Hackアセンブリ言語ソースファイルの内容は、2列目に太字で示されています。行番号は以降の説明で参照するために提供されていますが、ソースコードには表示されません。アセンブラによって生成されるHackマシンコードは最後の列に示され、その前の列には割り当てられたROMアドレスが示されています。フルラインコメント、空行、およびラベル定義文は、マシン言語コードを生成しないことに注意してください。また、アセンブリ言語命令を含む各行の末尾に記述されたコメントは、アセンブラによって無視されます。
最後の列に表示されているアセンブラ出力は、16 ビットのバイナリ整数表現ではなく、16 バイナリ文字のテキスト文字列です。
| ライン
番号 |
アセンブリ言語プログラムをハックする | 手術記録 | 命令
タイプ |
ロム
住所 |
ハッキングマシン
コード |
|---|---|---|---|---|---|
| 01 | // 1から100までの連続した整数を加算する | プログラムの動作を説明するコメント | 全文コメント | - - - - | コードが生成されませんでした |
| 02 | // 合計 = 1 + 2 + 3 + ... + 99 + 100 | コメントはアセンブラによって無視されます | 全文コメント | - - - - | コードが生成されませんでした |
| 03 | 空白のソース行はアセンブラによって無視されます | 空白行 | - - - - | コードが生成されませんでした | |
| 04 | @cnt // ループカウンタ宣言 | 変数シンボル「cnt」は16にバインドされています | A命令 | 00 | 0000000000010000
|
| 05 | M=1 // ループカウンタを1に初期化する | ラム[16] ← 1 | C命令 | 01 | 1110111111001000
|
| 06 | @sum // 合計アキュムレータ宣言 | 変数シンボル「sum」は17にバインドされています | A命令 | 02 | 0000000000010001
|
| 07 | M=0 // 合計を0に初期化する | メモリ[17] ← 0 | C命令 | 03 | 1110101010001000
|
| 08 | (LOOP) // whileループの開始 | ROMアドレス04にバインドされたラベルシンボル | ラベル宣言 | - - - - | コードが生成されませんでした |
| 09 | @cnt // cntの参照アドレス | 男 ← 16 | A命令 | 04 | 0000000000010000
|
| 10 | D=M // 現在の cnt 値を D に移動する | D ← RAM[16] | C命令 | 05 | 1111110000010000
|
| 11 | @100 // ループ制限をAにロードする | A ← 100 | A命令 | 06 | 0000000001100100
|
| 12 | D=DA // ループテスト計算を実行する | D ← D - A | C命令 | 07 | 1110010011010000
|
| 13 | @END // ブランチのターゲット宛先をロードする | 男 ← 18 | A命令 | 08 | 0000000000010010
|
| 14 | D;JGT //D > 0 の場合はループを終了 | 条件分岐 | C命令 | 09 | 1110001100000001
|
| 15 | @cnt // cntの参照アドレス | 男 ← 16 | A命令 | 10 | 0000000000010000
|
| 16 | D=M // 現在の cnt 値を D に移動する | D ← RAM[16] | C命令 | 11 | 1111110000010000
|
| 17 | @sum // 合計の参照アドレス | 男 ← 17 | A命令 | 12 | 0000000000010001
|
| 18 | M=D+M // 合計にcntを加算する | M ← D + RAM[17] | C命令 | 13 | 1111000010001000
|
| 19 | @cnt // cntの参照アドレス | 男 ← 16 | A命令 | 14 | 0000000000010000
|
| 20 | M=M+1 // カウンタを増分する | RAM[16] ← RAM[16] + 1 | C命令 | 15 | 1111110111001000
|
| 21 | @LOOP // 分岐のターゲット先をロードする | M ← 4 | A命令 | 16 | 0000000000000100
|
| 22 | 0;JMP // LOOPエントリにジャンプ | 無条件分岐 | C命令 | 17 | 1110101010000111
|
| 23 | (END) // 終了ループの開始 | ROMアドレス18にバインドされたラベルシンボル | ラベル宣言 | - - - - | コードが生成されませんでした |
| 24 | @END // ブランチのターゲット宛先をロードする | 男 ← 18 | A命令 | 18 | 0000000000010010
|
| 25 | 0;JMP // ENDエントリにジャンプ | 無条件分岐 | C命令 | 19 | 1110101010000111
|
命令シーケンスはA命令、C命令、A命令、C命令、…というパターンに従うことに注意してください。これはHackアセンブリ言語プログラムに典型的です。A命令は、後続のC命令で使用される定数またはメモリアドレスを指定します。
A命令の3つのバリエーションすべてを示しています。11行目(@100)では、定数値100がAレジスタにロードされます。この値は12行目(D=DA)で、ループ分岐条件のテストに使用する値を計算するために使われます。
4行目 (@cnt) にはユーザー定義変数 "cnt" が初めて出現するため、この文はシンボルを次の未使用のRAMアドレスにバインドします。この場合、アドレスは16で、その値がAレジスタにロードされます。また、M疑似レジスタもこのアドレスを参照するようになり、RAM[16]がアクティブなRAMメモリ位置になります。
A命令の3回目の使用は21行目 (@LOOP) に見られます。ここで命令は、ROMメモリ内のアドレスを表す境界ラベル値をAレジスタとM疑似レジスタにロードします。続く22行目の無条件分岐命令 (0;JMP) は、Mレジスタの値をCPUのプログラムカウンタレジスタにロードし、ループの先頭への制御権移行を実行します。
Hackコンピュータは、プログラム実行を停止するための機械語命令を提供していません。プログラムの最後の2行(@ENDと0;JMP)は、HackアセンブリプログラムがCPUエミュレータで実行するように設計されたプログラムを終了させるために通常使用する無限ループ状態を作成します。
参照
- ヘネシー, ジョン・L.、パターソン, デイビッド・A. (2019). 『コンピュータアーキテクチャ:定量的アプローチ』第6版. マサチューセッツ州ケンブリッジ: モーガン・カウフマン出版社
- ジャスティス、マシュー(2021年)『コンピューターの本当の仕組み』サンフランシスコ、カリフォルニア州:ノー・スターチ・プレス。
- マルヴィーノ, アルバート P.、ブラウン, ジェラルド A. (1993).デジタルコンピュータエレクトロニクス 第3版. ニューヨーク, グレンコー・マグロウヒル
- ヌル、リンダ、ロバー、ジュリア (2019). 『コンピュータの構成とアーキテクチャの基本』 第5版. マサチューセッツ州バーリントン: Jones and Bartlett Learning.
- パット、エール・N.、パテル、サンジェイ・J. (2020). 『コンピューティングシステム入門:ビットとゲートからC言語、そしてその先へ』第3版.ニューヨーク、ニューヨーク:マグロウヒル・エデュケーション.
- ペッツォルド、チャールズ (2009). 『コード:コンピュータハードウェアとソフトウェアの隠された言語』ワシントン州レドモンド:マイクロソフトプレス.
- スコット、ジョン・クラーク (2009) 『でも、どうやって知るの? みんなのためのコンピューターの基本原理』フロリダ州オールドスマー:ジョン・C・スコット
- リチャード・ホイップル著 (2019). 『ゼロから自分だけのコンピューターを作ろう』ワシントン州シアトル: Amazon Kindle.
参考文献
- ^ ニサン・ノアム、ショッケン・シモン (2021). 『コンピューティングシステムの要素:基礎から現代のコンピュータを構築する』第2版. マサチューセッツ州ケンブリッジ:MITプレス.
- ^ Schocken, Shimon (2021)、「NandからTetrisへのシラバス」。
- ^ 「第一原理から現代のコンピューターを構築する:NANDからテトリスまで」 。 2021年8月21日閲覧。
- ^ 「Nandからテトリスへ」 。 2021年8月23日閲覧。