RAC1プログラム サンプルページを少しだけ更新しました。
サンプルを2つ追加しました。
- サブルーチンを使って x22 + y22 を計算する
- fact(j) + fact(k) を計算する
このサンプル・タイトルからもわかるように、サブルーチン呼び出し機能を追加しました。
多くのハードウェアでは、スタックが1本だけのことが多いので、スタック領域全体をスタックフレームという区画に分けて、スタックフレーム内にサブルーチン(関数、手続き)の引数、ローカル変数、戻り番地などを詰め込みます。
標準的スタックフレームを使う場合のサブルーチン呼び出しは、スタックポインタ、フレームポインタ、プログラムカウンタなどのレジスタを操作します。これはけっこうややこしい。2006年に半分くらい説明した記事があります(残り半分の続きはないです(苦笑))。
ほかにもスタック関係の記事がありますね。
今回の、JavaScriptで書いたオモチャの仮想機械RAC1では、ややこしい方法は避けて、スタックを2本使ってみました。VM(仮想機械)オブジェクトが持っている変数は次のものです。このなかで制御スタックctrlをサブルーチン呼び出しに使います。
this.code = code; // 実行するコード this.pc = 0; // プログラムカウンタ this.stack = stack; // 計算用スタック this.ctrl = []; // 制御スタック
CALL命令とRET命令の実行は次のような処理となります。
case CALL: param = code[this.pc++]; // 呼び出す先のラベル this.ctrl.push(this.pc); // 戻り番地を制御スタックに積む this.scopes.openBlock(); // 局所変数領域を確保する this.jump(param); // サブルーチン入り口に飛ぶ break; case RET: this.scopes.closeBlock(); // 局所変数領域を開放する this.pc = this.ctrl.pop();// 制御スタックトップの戻り番地に飛ぶ break;
変数は計算スタックとは別に管理されているし、戻り番地も専用の制御スタックを使うので、CALL/RETの手順は単純です。実際のハードウェアを忠実にエミュレータしたいなら話は別ですが、好きに作れる仮想機械ですから、スタックをたくさん使ってもいいでしょ。