小さなプログラミング処理系を作る インストラクションセット
「オペランド」より「パラメータ」がふさわしいような気がするんで、replaceした。演算の対象物がオペランドで、その意味ではスタック上に載っているデータがオペランドとも言える。
最小限備えるべきインストラクションは、綴りも含めて決めておこう。スタック状態は、書いてある以外のデータが下に積んであってもよい。スタックトップ付近のいくつかを示しているだけ。データは整数値のみで、真偽値は0(偽)とそれ以外で代用する。右がスタックトップ。スタックは右方向に伸びる。
基本インストラクション
スタック操作
名前 | パラメータ | 事前スタック状態 | 事後スタック状態 |
---|---|---|---|
push | n | [] | [n] |
pop | [n] | [] | |
xchg | [n, m] | [m, n] | |
dup | [n] | [n, n] |
算術演算
名前 | パラメータ | 事前スタック状態 | 事後スタック状態 |
---|---|---|---|
add | [n, m] | [m + n] | |
mul | [n, m] | [m * n] | |
sub | [n, m] | [m - n] | |
div | [n, m] | [m%n, m/n] |
divは整除(少数にならない)、余りも一緒に求めてスタックのセカンドに置く。
メモリーIO
名前 | パラメータ | 事前スタック状態 | 事後スタック状態 |
---|---|---|---|
rd | x(アドレス) | [] | [n] |
wt | x(アドレス) | [n] | [n] |
ird | [x] | [n] | |
iwt | [n, x] | [n] |
wt = write してもスタックにはデータが残る。データをそのまま使うこともあるので、たぶんこのほうが便利。irdとiwtのiはindirect(間接)。パラメータではなくて、スタックトップにアドレスを置く。置いたアドレスは事後にスタックからクリアされる。
インストラクションのフォーマット
- インストラクション名をそのまま文字列として使う、pushは"push"など。
- パラメータ(引数)がないなら、名前文字列だけでよい。
- パラメータがあるなら、["push", 3] のような配列。
プログラム例: (100番地データ)の二乗 足す3 を 10番地に書く
[ ["rd" 100], "dup", "mul", ["push", 3], "add", ["wt", 10] ]
使い方の例
自由に考えてよいが、例えば、var m = new Machine(プログラム) で仮想機械を作って、m.run() で走らせる、とか。
プログラムpを、m.run(p) と渡してもいい。あるいは、m.load(p); m.run() てのもある。まー、なんでもいい。
インスペクタ(状態の調査閲覧ツール)やデバッガがあれば便利。データ領域であるメモリも、直接(仮想マシンやデバッガのメソッドで)いじれたほうが便利だろう。
発展的機能
- 論理演算:算術演算と同様に and, or, not
- ミューテーション: inc, dec、スタックトップのインクリメントとデクリメント
- 流れ制御機能: 下の表
- 手続き呼び出し: call, returnを実現する、各自考えよ。
- その他:必要だと思ったり、あると便利だと感じれば、ご自由に。
名前 | パラメータ | 説明 |
---|---|---|
jmp | x(コードアドレス) | jump 指定のコードアドレスに飛んで実行 |
ifzj | x(コードアドレス) | if zero then jump |
ifnzj | x(コードアドレス) | if not-zero then jump |
ijmp | jmpと同様、コードアドレスをスタックトップから取る | |
iifzj | ifzjと同様、コードアドレスをスタックトップから取る | |
iifnzj | ifnzjと同様、コードアドレスをスタックトップから取る |
コードアドレス(コード配列の何番目か)は、当面目視で勘定して指定する。辛いぞー。
間接ジャンプで置いたアドレスはスタックからクリアされる。
[追記]念のため、スタック状態の変化も書いておく。
名前 | パラメータ | 事前スタック状態 | 事後スタック状態 |
---|---|---|---|
jmp | x(コードアドレス) | [] | [] |
ifzj | x(コードアドレス) | [n] | [] |
ifnzj | x(コードアドレス) | [n] | [] |
ijmp | [x] | [] | |
iifzj | [n, x] | [] | |
iifnzj | [n, x] | [] |
nは判断に使う真偽値(とみなす整数)。制御に関連するデータはスタックからクリアされる。後にデータが残っているとたぶん邪魔だろう。
[/追記]
機械語レベルのプログラミング
基本機能だけでも、「算術式を右辺に持つ代入文の並び」はプログラミングできる。
y = x*x + 2; z = (y - 3)*(y + 3); w = x + y + z;
最初に、変数記号(この場合は、x, y, z, w)に対応するメモリのアドレスを決めて(かち合わないなら適当でいい)、それに従って記号を実アドレス(実際の番号)に直したプログラムを書く。
変数記号を含む算術式(ある種のプログラム)のコンパイル/リンク/ロードを人手でやることになる。初期化プログラムを書くか、変数であるメモリを手でセットアップしてからプログラムを走らせると、目的の計算が出来る。やってみる。