OSECPUのバイトコードの詳細仕様
(0) はじめに
(1) 整数レジスタ
- OSECPUは32bitの signed int な整数レジスタを64本持っています。
- R00~R3F と表記します。
- R00やR01など番号の若いレジスタは、実際のCPUの実レジスタに割り当てるように推奨されているので、R00への代入やR00の値の参照は、R20に対する操作と比べて数倍高速になることが多いです。
- 結局どうなるかは処理系依存で、保障はされていません。
- ということで、特にこだわりがないのならR00やR01を使いましょう。
- ちなみにx86版では、R00~R02までが実レジスタに割り当てられていて、残りはメモリを使ってレジスタをエミュレーションしています。
- これらのレジスタはすべて対等で汎用的に使われるというわけではなく、ある程度の使い方が決まっています。これに逆らってはいけないということはないですが、ライブラリなどで食い違うといろいろ面倒かもしれません。
- R00~R1F (32本) : 最も汎用的な整数レジスタで、通常は関数ごとにローカルとして扱えます。つまり関数を呼び出しても破壊されたりはしません。
- R20~R27 ( 8本) : 汎用ですが、関数ごとにローカルというわけではなく、グローバル変数的に使うことを想定しています。関数呼び出しによって変更される可能性もあります。
- R28~R2F ( 8本) : これも汎用ですが、関数ごとにローカルではなく、グローバル変数的に使われます。主にOSが用途を決定しています。これに対してR20~R27はアプリが自由に用途を決定できます。
- R30~R3B (12本) : 基本的にはこれらも汎用なのですが、関数の引数を渡したり、返値を入れたりするためにも使われるレジスタで、値が破壊されやすいです。ASKAでは複雑な数式を計算しなければいけなくなると、R3BやR3Aをテンポラリとして勝手に使い、値を破壊してしまうこともあります。R39やR38にまで手をつけることだってあります。しかし最悪でもR30までで、R2Fに手出しすることはありません。
- R3C~R3E ( 3本) : 将来の拡張のためにリザーブされています。
- R3F ( 1本) : 後で説明する特別な用途のための整数レジスタです。汎用には使えません。
- 全体として、OSECPUのレジスタはかなり多いほうだと思います。これはOSECPUがメモリ操作を苦手としていて、できるだけレジスタだけで主要な演算が完結できるようにという設計方針によるものです。
(2) フラグレジスタ
- x86でもARMでも、さらに6502やZ80でも、みんなフラグレジスタというものを持っていました。
- しかしOSECPUにはフラグレジスタはありません。MIPSの仕様に似ています。
- フラグレジスタがない代わりにCMPcc命令の結果に応じて任意の整数レジスタを0か-1に変更することができます。つまり普通のレジスタをフラグレジスタの代わりにしてしまったようなものです。
- これで設定された値を後述のCNDプリフィクス命令(04 Rxx)で使えば、条件分岐や条件付き代入などができます。
(3) 定数即値代入命令
[02] [Rxx] [imm32]
LIMM(Rxx, imm32);
- 6バイト命令です。Rxxには0x00から0x3fのレジスタ番号を指定します。imm32は定数即値を4バイトのビックエンディアンで指定します。
(4) 単純代入命令
[10] [reg0] [reg1] [FF]
CP(reg0, reg1);
- 4バイト命令です。Rxxフィールドが複数ある命令では、ここではreg0, reg1のように区別して表記することにします。
- reg1の値がreg0へコピーされます。
- reg0にR3Fを指定することはできません。reg1にもR3Fを指定してはいけません。
- この命令はバイトコード的には、OR命令でreg2にFFを指定した形式になっています。
- メモリの内容をコピーすることはこの命令ではできません。
(5) 三項演算命令
[10] [reg0] [reg1] [reg2]
OR(reg0, reg1, reg2);
(6) 整数比較命令
[20] [reg0] [reg1] [reg2]
CMPE(reg0, reg1, reg2);
- 4バイト命令です。reg1とreg2を比較して等しいかどうかを判定します。trueならreg0には-1が代入されます。falseなら0が代入されます。
- 他にも以下のような命令がこの形式になっています。
- 21 CMPNE (等しくなければtrue)
- 22 CMPL ( reg1 < reg2 が成立すればtrue)
- 23 CMPGE ( reg1 >= reg2 が成立すればtrue)
- 24 CMPLE ( reg1 <= reg2 が成立すればtrue)
- 25 CMPL ( reg1 > reg2 が成立すればtrue)
- 26 TSTZ ( reg1 & reg2 の演算結果が0ならtrue)
- 27 TSTZ ( reg1 & reg2 の演算結果が0でなければtrue)
- 三項演算命令と同様にreg2に対してはR3Fの指定が可能です。reg1に対しては指定できません。
- そしてなんとreg0に対してもR3Fを指定することができるのですが、この場合は定数値
代入という意味ではなくて、特別な慣用句の指定を意味します。それについては後述します。とりあえずは「reg0にはR3Fを指定できない、指定したら意味が変わる」くらいに思っていてください。
- 基本的な整数演算命令はこれでおしまいです。少ないですね。
(7) 何もしない命令
[00]
NOP();
- 何もしない命令です。JITコンパイラはこの命令を翻訳しません(実CPUに対するNOP命令になるわけではない)。
- ここからしばらくは制御系の命令を紹介します。
(8) ラベル定義命令
[01] [opt] [imm32]
LB(opt, imm32);
こめんと欄