最近の更新 (Recent Changes)

2014-01-01
2013-01-04
2012-12-22
2012-12-15
2012-12-09

Wikiガイド(Guide)

サイドバー (Side Bar)

--
← 前のページに戻る

5.4 命令コードの出力

5.4.1 クロージャの中のコード領域の構造

コード領域は、一次元のリスト構造で出来ています。


  (命令1 命令2 即値 命令3 文字列 ... )

命令コード, 即値, 文字列, クロージャ名などがリスト内に収納されます。

アドレスは、コードリストの要素の数をリストの先頭から数えた値になります。 このとき、コードリストの要素がたとえ長い文字列であってもアドレスは1しか使わないことに注意してください。

リアルマシンとは異なり、Closure BasicのVMの番地のアドレスは固定バイト長ではなく、要素の数になっています。

まず以下のClosure Basicの例題プログラムを見てください。


dim dt[10]

a = 700
print a

for i = 0 to 10
        print i
        dt[i] = i
next

このプログラムをコンパイルすると次に示すようなコードリストにコンパイルされます。


(PUSHI 18 BRK PUSHI 19 BRK PUSHI closure0 PUSHI 19 PUSHI 700 ROT POP PUSHI closure0 PUSHI 19 PUSH PR NL PUSHI 20 BRK PUSHI closure0 PUSHI 20 DUP2 PUSHI 0 ROT POP PUSHI 10 PUSHI closure0 PUSHI 1 POP DUP2 PUSH PUSHI closure0 PUSHI 1 PUSH CMPLE BRZ 115 PUSHI closure0 PUSHI 1 PUSH PUSHI closure0 PUSHI 20 PUSH PR NL PUSHI closure0 PUSHI 20 PUSH DUP PUSHI 11 CMPLE BRZ 79 DUP PUSHI 0 CMPLT BRZ 82 PUSHI illegal index ERR PUSHI 8 ADD PUSHI closure0 SWAP PUSHI closure0 PUSHI 20 PUSH ROT POP PUSHI closure0 PUSHI 1 POP DUP2 DUP2 PUSH PUSHI 1 ADD ROT POP PUSHI closure0 PUSHI 1 PUSH BR 35 DROP DROP STOP)

最初のPUSHIが0番地、18が1番地、BRKが2番地となります。 最後にSTOP命令が入り、コードリストは終了しています。

5.4.2 命令コードの追加

命令コードを追加する述語には2種類あります。


::vm <add code #アドレス 命令コード クロージャ名>

::vm <add code #アドレス 命令コード 引数 クロージャ名>

上の述語は、命令コードのみをコード領域に追加します。 下の述語は、命令コードとその引数をコード領域に追加します。

#アドレスは、この述語を呼び出すときに指定するのではなく、命令コードの置かれるアドレスを返すためにあります。

命令コードを追加すると自動的にアドレスが進められ、次の命令コードの追加時には新しいアドレスに追加されることになります。

例えば以下のようなコード生成処理があるとします。


  ::vm <add code _ PUSHI 18 closure0>
  ::vm <add code _ BRK closure0>
  ::vm <add code _ PUSHI 19 closure0>
  ::vm <add code _ BRK closure0>
  ::vm <add code _ PUSHI closure0 closure0>
  ::vm <add code _ PUSHI 19 closure0>
  ::vm <add code _ PUSHI 700 closure0>
  ::vm <add code _ ROT closure0>

まるで、アセンブラですね。

#アドレスについては、このコードでは使わないので無名変数_を設定しています。

このコード生成処理により以下のコードが生成されます。


   PUSHI 18 BRK PUSHI 19 BRK PUSHI closure0 PUSHI 19 PUSHI 700 ROT 

5.4.3 命令コードの変更

命令コードを変更する述語を次に示します。


::vm <set code アドレス 値 クロージャ名>

クロージャ名のコードのアドレス位置に値を使って強制的に書き直します。

通常の命令コードの出力には、前項で示した::vm <add ~>の命令が便利なのですが、 この::vm <set ~>命令は、分岐やループするコードを出力する場合に飛び先アドレスを 設定するのに主として使います。

例えば以下のコードを見てください。


  ::vm <add code _ BRZ #cl>
  ::vm <add code #braddr1 -1  #cl>

    ~

  ::vm <add code #braddr2 PUSHI 1  #cl>
  ::vm <set code #braddr1 #bradd2 #cl>

  ~

最初のBRZ命令は次のコードに書いてあるアドレスにブランチします。

しかし、このコードを出力している時点では、まだ飛び先のアドレスがわからないので、取り敢えず-1を入れておきます。 アドレス-1は存在しないアドレスなので、もし間違って本当のアドレスと置き換え忘れても、実行時に異常終了するのでわかります。

前の項で説明したとおり、add述語では、第2引数には追加されたコードのアドレスが設定されて返されます。

変数#braddr1には、この-1が入っているアドレスが設定されるので、覚えておきます。

そして、飛び先アドレスが決定されます。上の例では#braddr2アドレスがそのアドレスです。

そのとき、先ほどの#braddr1の-1の入っているコード領域を、#braddr2のアドレスに置き換えます。 これでブランチ先のアドレスが正しくコードに設定されました。

このように、すぐには確定できないアドレスの解決にset述語は使われます。

5.4.4 データ(変数)の初期化

命令コードと同様にクロージャ内のデータ領域の初期化を行うことも出来ます。


::vm <add data #アドレス データ初期値 クロージャ名>

::vm <set data アドレス データ初期値 クロージャ名>

addもsetも命令コード領域への追加と更新と同様に使えます。第1引数が"data"となっていることに着目してください。

実はClosure Basicでは、データの初期化はすべて命令コードで値を設定してしまいます。 データを確保して、値をそのデータ領域のメモリへの値の設定をプログラム上でストアします。 そのため、ここで説明している初期化は使っていません。

しかし、将来の機能追加や別仕様言語への適用などを考えて、データ初期化の方法をVM機能に追加してあります。