3.3 実装ー 55 n レジスタメモリ 任意の長さのメモリ ( ただし、長さは 2 の累乗 ) は、より小さいメモリから再帰的 に構築することができる。この再帰的構造をたどっていくと、ついにはレジスタにた どりつく。このアイデアを視覚化すると、図 3-6 のように表すことができる。一番右 の図は、 64 レジスタ RAM が 8 レジスタ RAM を 8 個並べて作れることを示してい る。 RAM64 メモリから特定のアドレスを選択するためには 6 ビットのアドレスを 用いる。この 6 ビットアドレスを、たとえば、ェェ″″リと表己したとすると、上位の ェ x ェビットは RAM8 回路のひとつを選択するために用い、下位の !JY!J ビットは、そ の選択した RAM8 内から特定のレジスタを選択するために用いる。このような階層 構造に基づいて特定のアドレスにアクセスする論理回路を RAM64 回路は備えなけれ ばならない。 、一三ロ RAM 64 RAM8 8 RAM 8 Register Register 図 3-6 再帰的かっ段階的にメモリを構築する。 w ビットレジスタは w 個の 2 値素子の配列であり、 8 レジスタ RAM は 8 個の w ビットレジスタの配列であり、 64 レジスタ RAM は 8 個の RAM8 回路 の配列であり、と続いていく。これをあと 3 回だけ続ければ、 16KRAM を構築することができる Register RAM8 8 Register Bit Bit Bit カウンタ ひとつは「 w ビットのレジス w ビットのカウンタはふたつの要素から構成される。
7.2 \/M 仕様 ( 第 1 部 ) ー 153 このロジックを VM 上で表すと図 7-11 の ルの radi u s を r に設定するとしよう。 ようになる。 高水準プログラムの視点 ( プログラムの変数について RAM の実際の場所は、 プログラム実行時に決定される。そのため、 ここで示す例は恣意的に決めている ) VM コード / / b オプジェクトと整数の r は関数の最初の 2 つの引数として渡されると想定する r 」を行うコードである / / 次のコードは「 b . r adi u s push argument 0 / / b のべースアドレスを取得する / / this セグメントが b を指すようにする pop pointer 0 push argument 1 / / の値を取得する / / b の 3 番目のフィールドを r に設定する pop this 2 RAM の視点 0 コンバイル b オブジェクト 3012 b 412 2 8 【 0 3012 3013 3014 3015 b オブジェクト 「 b. 「 adius=1 7 」命令直後の 仮想メモリセグメント argument pointer 3012 0 3012 0 1 2 3 「 b. 「 adius=1 7 」命令を行う直前の 仮想メモリセグメント this pointer argument 3012 17 this 120 80 17 3 0 1 0 1 0 0 1 (this の O 番目は RAM[3012 ] にそろえられる ) 図 7 -11 pointer と this セグメントを用いた VM べースのオブジェクト操作 p 〇 inter の 0 番目を argument の 0 番目の値に設定することで、仮想セグメント である th 土 s のべースアドレスをオプジェクトのべースアドレスと一致させることが でき、効率的に作業を進めることができる。これ以降、 VM コマンドはオプジェクト のいかなるフィールドであれ、 thi s という仮想メモリセグメントとべースアドレス からの相対インデックスを用いて、アクセスすることができる。 17 」をどのように図 7-11 で示すような VM しかし、コンパイラは「 b . rad 土 us コードに変換しているのだろうか ? そして、 radius フィールドが、実際の表現上で
3.2 仕様ー 書き込み (write) 51 d という新しいデータをレジスタに書き込むためには、 in 入力に d を入れ、 1 oad 入力を 1 に設定する。次のクロック周期でレジスタには新しい値が送ら れ、出力も d の値が送信され始める。 3.2.3 メモリ 直接アクセスのできるメモリュニットは RAM とも呼ばれる。これは w ビットの レジスタがれ個だけ配列として並べられたものであり、内部に直接アクセスするため の回路を備えている。レジスタの数 ( れ ) とレジスタの幅 (w) は、それぞれ「サイ ズ」、「幅」と呼ばれる。本章ではサイズの異なるメモリをいくつか組み立てることにす る。それらのメモリ幅はすべて 16 ビットである。サイズは全部で RAM8 、 RAM64 、 RAM512 、 RAM4K 、 RAM16K の 5 つのサイズを作成する。これらのメモリはすべ て完全に同じ API である。そのため、パラメータを用いてまとめて示すことにする。 回路名 入力 出力 関数 コメント RAMn / / n とについては以下の別表を参照 土 n [ 16L address[k], load out [ 1 6 ] out (t)=RAM[address(t) ] (t) 工 f 1 oa d ( t ー 1 ) t he n RAM[address(t-1)] ( t ) = 土 n に一 1 ) 「 = 」は 16 ビットの演算である。 Hack プラットフォームに必要な RAM 回路 回路名 RAM 6 4 RAM512 RAM 4 K RAMI 6K n 8 64 512 4096 16384 k 3 6 9 12 14
156 ー 7 章バーチャルマシン # 1 : スタック操作 Hack 機械語の仕様によると、アセンプリプログラム内では、 RAM アドレスの 0 か ら 15 番目は RO から RI 5 のシンポルを用いて参照することができる。さらに、 RAM アドレスの 0 から 4 番目 ( つまり、 R0 から R4 ) はシンポルの sp 、 LCL 、 ARG 、 TH 工 s 、 THAT を用いて参照できることが仕様書に書かれてある。実際、この仕様は、 VM 実 装の可読性を高める目的を見通してアセンプリ言語に導入したものである。これらの VM 環境におけるレジスタは次に示す使われ方を想定する。 レジスタ RAM[()I RAMIII RAM121 RAM[31 RAM141 RAM 一 121 RAM 卩 3 ー 1 司 名前 S P LCL ARG T H 工 S THAT 使用法 スタックポインタ : スタックの最上位の次を指す。 現在の VM 関数における loca セグメントの べースアドレスを指す。 現在の VM 関数における argument セグメントの べースアドレスを指す。 現在の ( ヒープ内における ) this セグメントの べースアドレスを指す。 現在の ( ヒープ内における ) that セグメントの べースアドレスを指す。 temp セグメントの値を保持する。 汎用的なレジスタとして VM 実装で用いることができる。 メモリセグメントマッピング 10Ca1 、 argument 、 this 、 that これらのセグメントは RAM 上に直接マッピング ( 対応づけ ) されている。そ の RAM 上の場所を保持するために、専用のレジスタ ( それぞれ LCL 、 ARG 、 TH 工 s 、 THAT に対応 ) に物理べースアドレスが保持される。そのため、これら のセグメントにおいて乞番目の要素へのアクセスは、 RAM 内の ( se 十り番 目のアドレスへアクセスするアセンプリコードへ変換されるべきである。 で base は各セグメント用のレジスタに格納された値である。
58 ー 3 章順序回路 なるかもしれない。なぜなら、サイズの大きい RAM では何万もの下位レベルの回路 を用いることになり、これらのすべての回路はシミュレータによって ( ソフトウェア のオプジェクトとして ) メモリに展開されるからである。そのため、 RAM512 . hdl 、 RAM4K . hdl 、 RAM16K . hdl の 3 つのプログラムは別ディレクトリに分けて置いて ある。このようにすれば、 RAM4K や RAM16K 回路は、ビルトイン版の RAM512 回路を内部バーツとして用いることになる ( なぜなら、シミュレータが現在のディレ クトリで RAM512 回路を見つけることができないからである ) 。 手順 次の手順で進めることを推奨する。 1 . 2. 3. 4 ・ す 1 本プロジェクトに必要なハードウェアシミュレータは、本書ソフトウェアパッケー レーションを行う。 pro j ect s / 0 3 ディレクトリで指定されているすべての回路を作成し、 V を集中して読む。 「ハードウェアシミュレータ・チュートリアルす 1 」に目を通す。特に、パ 付録 A を読む。特に、 A. 6 節と A. 7 節を集中して読む。 ジの t 001 s ディレクトリに用意されている。 ンミュ ート IV 、 org/tutorials/PDF/Hardware%20SimuIator%20Tutorial.pdf 訳注 : 「ハードウェアシミュレータ・チュートリアル」は次より取得できる。 http://www.nand2tetris.
B. 4 \/M 工ミュレータでの \/M プログラムのテストー 357 例 B -4 VM 工ミュレータでの \/M プログラムのテスト / ☆ FibonacciSeries . vm ファイルは一連の VM コマンドを含み、フイボナッチ数列の最初 の n 個を計算する。プログラムには function/call/return コマンドは含まれないため、 VM 工ミュレータは仮想メモリセグメントをコードによって明示的に初期化しなければならない。 / / プロクラムを読み込み、シミュレーションの準備を行う FibonacciSeries . VIII ′ output—file FibonacciSeries . compare—to FibonacciSeries . output—list RAM [ 4000 ] % DI . 6 . 2 RAM [ 4001 ] DI . 6.2 RAM [ 4002 ] 岩 DI . 6.2 RAM [ 4 0 0 3 ] DI . 6 . 2 RAM [ 4 0 0 4 ] %DI . 6 . 2 RAM [ 4 0 5 ] Dl . 6 . 2 ー / / stack 、 10Ca1 、 argument セグメントを初期化する s et SP 2 5 6 ′ set 10Ca1 300, set argument 400 : / / スタックポインタ ( スタックは RAM [ 256 ] から始まる ) / / 10Ca1 セグメントを RAM のある場所に配置する / / argument セグメントを RAM のある場所に配置する / / テスト用に引数を設定する set argument [ 0 ] set argument[l] 400 の / / RAM[4000] より先に、結果が格納される thi s セグメントの i 番目の値。 セ h 土 s は ] argument セグメントの主番目の値。 argument [ 土 ] 1 〇 cal セグメントの i 番目の値。 10Ca1 [ 土 ] VM セグメントのコンテンツ することができる。 VM 工ミュレータ上で実行されるスクリプトコマンドは、次に示す要素にアクセス B 丸 2 変数 output 7 vmstep; repeat 140 { / / プログラムの実行を完了させるために十分な VM ステップ
B. 2 例 B-2 最上位の Computer 回路のテスト / 大 computerMax . tst スクリプト ☆ / ハードウェアシミ ュレータでの回路テストー 351 max . hack プログラムは RAM[O] と RAM[I] の最大値を RAM[2] に書き込む。 / / computer 回路を読み込み、シミュレータのセットアップを行う load Computer . hdl, output—file Computer . out, compare—to ComputerMax . cmp, output—list RAM16K[0] RAM16K[1] RAM16K[2]; / / Max . hack プログラムを ROM32K 回路パーツに読み込む ROM32K load Max . hack, / / RAMI 6K の最初のふたつの要素に適当な値を設定する s et RAM 16K [ O ] 3 ′ set RAMI 6K [ 1 ] 5 ′ OutPUt ー / / プログラムの計算が終了するのに十分なクロックを実行する repeat 14 { tick, tock, output ー / / プログラムカウンタ ()C 、順序回路 ) は新しいリセット値へ / / リセットするためにクロックを進める 1 ′ / / コンピュータをリセットする output 7 tock, tick, S e t r e S e し output; t i c k ′む 0 ck ′ repeat 14 { output : s et RAMI 6K [ 1 ] 12 3 4 5 ′ s e t RAM 1 6 K [ 0 ] 2 3 4 5 set reset の / / リセットされないように設定する ( 次の tick-tock 時に ) / / 別の値を用いてプログラムを再実行する
B. 2 RAM8 [ 0 . . 7 ] 外部変数 Register[] ARegister[] DRegister[] PC [ ] ROM32K RAM16K RAM 4 K RAM512 RAM 6 4 RAM 8 PC DRegister ARegister Register 回路名 Keyboard Screen S cree n [ O . . 16 3 8 3 ] ROM3 2 K [ 0 . . 3 2 7 6 7 ] RAMI 6K [ 0 . . 163 8 3 ] RAM4 K [ O . . 4 0 9 5 ] RAM512 [ 0 . . 511 ] RAM64 [ 0 . . 6 3 ] Keyboard[] ハードウェアシミュレータでの回路テストー 349 テータ型 / 範囲 1 6 ビット ( -32768...32767 ) 1 6 ビット、読み込み専用 各要素は 16 ビット 各要素は 16 ビット 各要素は 16 ビット 各要素は 16 ビット 各要素は 16 ビット 各要素は 16 ビット 各要素は 16 ビット 1 5 ビット ( O … 32767 ) 16 ビット 1 6 ビット メソッド xxx . asm を読み込む xxx . hack または 図 B-2 本書が提供するすべてのビルトイン回路の A 曰 のようなコマンドを用いる。本書が提供する回路の中で、ビルトイン回路がサポート 専用の命令をテストスクリプトから使用するには、「 R 〇 M32K load Myprog. hack 」 ドはテキストファイル ( 機械語が含まれることが期待される ) を引数に取る。この回路 に、 ROM32K には「 1 〇 ad filename 」というメソッドが備えられている。このメソッ は、 ROM32K 回路に機械語プログラムを読み込まなければならない。これを行うため 命令メモリュニットである。 Hack コンピュータで機械語のプログラムを実行する前に とえば、 Hack コンピュータでプログラムが格納される場所は、 ROM32K という回路の ビルトイン回路は、外部から使用することができるメソッドも持っことができる。た き出す。 に設定する。次のタイムユニットで、レジスタ回路はこの値を取り込み、出力先へは ことができる。このコマンドは、 2 の補数で表現された 135 のバイナリ値をその回路 タ回路をシミュレートするとき、「 set Register [ ] 13 5 」というコマンドを書く 列 ) であれば、 chipName[i] という表記を用いる。たとえば、ビルトインのレジス という表記を用いて、その値にアクセスすることができる。内部状態がべクトル ( 配 もしビルトイン回路が内音囹犬態として単一の値を保持しているとしたら、 c わゴ p Ⅳ ame [ ] 能が備わっているため、その新しい値は画面上にも表示される。 れた 15 のバイナリ値を設定する。さらに、ビルトインの RAM16K 回路には GUI 機 ツであるとすれば、そのコマンドは、メモリの 1017 番目の場所に 2 の補数で表現さ が、現在シミュレートされる回路、もしくは、現在シミュレートされる回路の内部バー たとえば、「 s et RAMI 6K は 017 ] 15 」というコマンドを考えよう。もし RAM16K
B. 3 CPU 工ミュレータでの機械語プログラムのテストー ればよい。これを行うスクリプトを例 B-3 に示す。 例 B - 3 CPU 工ミュレータでの機械語のテスト / / プログラムの読み込みとシミュレータのセットアップ load Mu1t . hack ′ output—file Mu1t . compare—to Mult . cmpt output—list RAM [ 2 ] % D2 . 6 . 2 ー / / RAM の最初のふたつの要素に適当な値を設定する set RAM [ () ] 2 ′ set RAM [ 1 ] 5 ー / / プログラムの計算が終了するのに十分なクロックを実行する repeat 20 { ticktock; } output : / / 別の値を設定して、同じプログラムを再度実行する 353 s et RAM [ 0 ] 8 ′ set RAM [ 1 ] 7 ー repeat 50 { ticktock; } output; B. 3.2 変数 / / Mult . hack は加算を反復して行う / / そのため、先ほどよりクロックサイクルの数を多く設定する CPU 工ミュレータは、 Hack プラットフォームの内部コンポーネントに関連する変 数を認識する。特に、 CPU 工ミュレータ上で実行されているスクリプトコマンドに おいては、次に示す要素にアクセスすることができる。 アドレスレジスタの値 ( 符号なし 15 ビット ) データレジスタの値 ( 16 ビット ) プログラムカウンタ (ProgramCounter) レジスタの値 ( 符号なし 15 ビット ) RAM の乞番目の値 ( 16 ビット ) PC M は ]
に制限を受けることなく、書き込み / 読み込みができる、ということから来ている。 まり、メモリ中のすべてのワードは一一その物理的に存在する場所に関係なく じ時間で直接アクセスできなければならない。 てつ 同 out (word) (word) address (O から n-D load Register 0 Register 1 Register 2 Register n—l RAMn 直接アクセスロジック 図 3-3 ( 概念上の ) RAM 回路。 RAM の幅と長さは変えることができる RAM のどのレジスタにアクセスするかを指定する。メモリ操作が読み込みの場合 ドレス入力、ロードビットの 3 つである。アドレス入力によって、現時刻において、 以上をまとめると、 RAM は次の 3 つの入力を受け取る。それは、データ入力、ア より、論理的な意味でのアドレスが実現される。 が、 RAM 回路は「直接アクセスロジック (direct accesslogic) 」を備える。これに レスによってレジスタの物理的な位置は決定されない。これは後ほど見ていくことだ 物理的な意味でのアドレス ( 所在地 ) ではない、ということである。そのため、アド こで注意してほしいことは、「アドレス」とは ことができる論理ゲートを構築する。 るのに加えて、ツという数に対して、アドレスがツ番目のレジスタを個別に選択する までの間の整数 ) をアドレスとして割り当てる。次に、れ個のレジスタ配列を構築す スタにより構成される ) に対して、他とは重複しないユニークな番号 ( 0 かられ一 1 そのような要求を満たすためには、最初に、 RAM の各ワード ( これはれ個のレジ