page0043
をテンプレートにして作成
[
トップ
] [
新規
|
一覧
|
単語検索
|
最終更新
|
ヘルプ
]
開始行:
* OSECPUのバイトコードの詳細仕様
-(by [[K]], 2013.06.21)
** (0) はじめに
-これは要するに[[page0032]]の詳細版です。
** (1) 整数レジスタ
-OSECPUは32bitの signed int な整数レジスタを64本持ってい...
-R00~R3F と表記します。
-R00やR01など番号の若いレジスタは、実際のCPUの実レジスタ...
--結局どうなるかは処理系依存で、保障はされていません。
-ということで、特にこだわりがないのならR00やR01を使いまし...
--ちなみにx86版では、R00~R02までが実レジスタに割り当てら...
~
-これらのレジスタはすべて対等で汎用的に使われるというわけ...
--R00~R1F (32本) : 最も汎用的な整数レジスタで、通常は...
--R20~R27 ( 8本) : 汎用ですが、関数ごとにローカルとい...
--R28~R2F ( 8本) : これも汎用ですが、関数ごとにローカ...
--R30~R3B (12本) : 基本的にはこれらも汎用なのですが、...
--R3C~R3E ( 3本) : 将来の拡張のためにリザーブされてい...
--R3F ( 1本) : 後で説明する特別な用途のための整数...
-全体として、OSECPUのレジスタはかなり多いほうだと思います...
** (2) フラグレジスタ
-x86でもARMでも、さらには6502やZ80でさえも、みんなフラグ...
--参考: http://ja.wikipedia.org/wiki/%E3%82%B9%E3%83%86%...
-しかしOSECPUにはフラグレジスタはありません。MIPSの仕様に...
-フラグレジスタがない代わりにCMPcc命令の結果に応じて任意...
-これで設定された値を後述のCNDプリフィクス命令(04 Rxx)...
** (3) 定数即値代入命令
[02] [Rxx] [imm32]
LIMM(Rxx, imm32);
-6バイト命令です。Rxxには0x00から0x3fのレジスタ番号を指定...
** (4) 単純代入命令
[10] [reg0] [reg1] [FF]
CP(reg0, reg1);
-4バイト命令です。Rxxフィールドが複数ある命令では、ここで...
-reg1の値がreg0へコピーされます。
-reg0にR3Fを指定することはできません。reg1にもR3Fを指定し...
-この命令はバイトコード的には、OR命令でreg2にFFを指定した...
-メモリの内容をコピーすることはこの命令ではできません。
** (5) 三項演算命令
[10] [reg0] [reg1] [reg2]
OR(reg0, reg1, reg2);
-4バイト命令です。reg1とreg2がOR演算されて、結果がreg0に...
-他にも以下のような命令がこの形式になっています。
--11 XOR
--12 AND
--14 ADD
--15 SUB
--16 MUL 符号付き乗算
--18 SHL 左シフト
--19 SAR 右シフト(符号付き)
--1A DIV 符号付き除算
--1B MOD 符号付き剰余
-符号なしの演算は一切サポートされていません。OSECPUでは整...
-INCやDECのような命令は持っていません。
-二項演算命令も持っていません。ADD(R00, R01);みたいなこと...
-キャリーやボロー(桁あふれや桁借り)やオーバーフローを検...
-OSECPUでは定数を用いた演算命令を用意していません。もしR0...
-これに関連しますが、整数レジスタを定数の代わりに使う場合...
LIMM(R3F, 1); ADD(R00, R00, R3F);
-R01などではなくR3Fを使うメリットですが、OSECPUはR3Fが使...
-このようにR3Fは定数即値を指定するための整数レジスタとい...
--例外として、SUB,SHL,SAR,DIV,MODに関してはreg1にR3Fを使...
-もしかしたらADD(R00, R00, R00);のように、reg0とreg1やreg...
--この場合、R00 = R00 + R00; ですから、R00は問題なく2倍に...
** (6) 整数比較命令
[20] [reg0] [reg1] [reg2]
CMPE(reg0, reg1, reg2);
-4バイト命令です。reg1とreg2を比較して等しいかどうかを判...
-他にも以下のような命令がこの形式になっています。
--21 CMPNE (等しくなければtrue)
--22 CMPL ( reg1 < reg2 が成立すればtrue)
--23 CMPGE ( reg1 >= reg2 が成立すればtrue)
--24 CMPLE ( reg1 <= reg2 が成立すればtrue)
--25 CMPG ( reg1 > reg2 が成立すればtrue)
--26 TSTZ ( reg1 & reg2 の演算結果が0ならtrue)
--27 TSTNZ ( reg1 & reg2 の演算結果が0でなければtrue)
-三項演算命令と同様にreg2に対してはR3Fの指定が可能です。r...
-そしてなんとreg0に対してもR3Fを指定することができるので...
代入という意味ではなくて、特別な慣用句の指定を意味します...
-これはx86限定の豆知識ですが、20~25の命令については、(5)...
R00--; if (R00 > 0) goto ...; // これは高速化される.
R00--; if (R00 >= 1) goto ...; // これは高速化されない.
--何が言いたいのかというと、可能ならできるだけ0と比較しま...
--まあ些細なことですけどね。
-基本的な整数演算命令はこれでおしまいです。少ないですね。
** (7) 何もしない命令
[00]
NOP();
-1バイト命令です。何もしない命令です。JITコンパイラはこの...
-ここからしばらくは制御系の命令を紹介します。
** (8) ラベル定義命令
[01] [opt] [imm32]
LB(opt, imm32);
-6バイト命令です。OSECPUでは分岐命令を実行する際には、絶...
-当然ですが、同じラベル番号を複数回使うことはできません。...
--DLLとアプリとでラベル番号がぶつかってしまうことに関して...
-optですが、今のところ指定できるのは0か1だけです。0は普通...
--ポインタレジスタについては後述します。
-ラベル番号は本来は32ビットの任意の整数(負の数もOK)なの...
** (9) 無条件分岐命令
[03] [3F] [imm32]
PLIMM(P3F, imm32);
-6バイト命令です。OSECPUではP3Fというポインタレジスタに特...
--しつこいですがポインタレジスタについては後述します。と...
-PLIMMというのはLIMMのポインタレジスタ版で、ラベルの値を...
** (10) 条件分岐命令など
[04] [Rxx] [03] [3F] [imm32]
CND(Rxx); PLIMM(P3F, imm32);
-これは条件分岐命令です。よく見ると無条件分岐命令の前にCN...
-CNDプリフィクスには後続の1命令を条件実行命令化する作用が...
-CNDプリフィクスはRxxの下位1bitしか参照しません。それが1...
--この仕様は注意を要するかもしれません。Cなどでは値が非零...
--もしCみたいに非零がどうかで条件分岐したいのであれば、
TSTNZ(R30, Rxx, Rxx); CND(R30); PLIMM(P3F, imm32);
--みたいにするといいと思います。つまりRxxが非零であるかど...
-CNDプリフィクスは他の命令、たとえばLIMMやADDの前につける...
--他にもいくつかCNDプリフィクスがつけられない命令がありま...
-CNDプリフィクスのRxxに定数即値としてのR3Fを指定すること...
** (11) 条件分岐慣用句
-OSECPU用アプリをいくつか作ってみてわかったことですが、
CMPxx(Rxx, ...); CND(Rxx); PLIMM(P3F, ...);
-のパターンが頻出します。比較して条件分岐ということです。...
-一方でJITコンパイラは、CMP系の命令のときに、結構苦労して...
-そうであれば、もっと単純なコードを生成できるので、そのこ...
CMPxx(R3F, ...); CND(R3F); PLIMM(P3F, ...);
-つまりRxxの部分が形式的にR3Fになっているのです。このR3F...
-この慣用句を使えば、条件分岐のためにわざわざ整数レジスタ...
-なお、ANDやORを含むような複雑な条件では、この構文は使え...
if (R00 <= R02 & R02 <= R01) goto 3;
CMPLE(R30, R00, R02); CMPLE(R31, R02, R01); AND(R30, R30...
-CNDの直後はPLIMM(P3F, ...);でなければいけないので、分岐...
-しかしそれでもこの構文は便利なので本当によく使います。CN...
** (12) ループの構成法
-まず、以下のようにすれば簡単に無限ループが構成できます。
LB(0, 123); // 123である必然性はない.
...
PLIMM(P3F, 123);
-これで...の中は無限に繰り返されることになります。
~
-次に上記のLB命令の直後に適当な条件式を加えて、それが成立...
LB(0, 123);
CMPE(R3F, R00, R01); CND(R3F); PLIMM(P3F, 124);
...
PLIMM(P3F, 123);
LB(0, 124);
-これはつまり while(R00 != R01) { ... } というループです...
-途中でbreakしたいときは、いつでも PLIMM(P3F, 124); すれ...
~
-ループ末尾のジャンプ命令を、条件付きに変更すると、do { ....
LB(0, 123);
...
CMPNE(R3F, R00, R01); CND(R3F); PLIMM(P3F, 123);
** (13) 間接分岐命令
-間接分岐命令というのは、P3Fにラベル番号を直接入れるので...
-たとえばこういうことができます。
PLIMM(P01, 1); PCP(P3F, P01);
-このPCPは P3F = P01; を実行するための命令で、ポインタコ...
[1E] [preg0] [preg1]
-となっていて、 preg0 = preg1; になります。なお、P3Fをpre...
-PCPでP3Fに代入すると、それが間接分岐命令になるというわけ...
** (14) 関数の呼び出し
-OSECPUにおいて、関数の呼び出しとは、R30~R3Bに適当な引数...
-しかしこれでは呼び出し元へ帰ってくることができません。こ...
-ということで、関数呼び出しの際には必ずどこに帰ってきてほ...
... PLIMM(P30, 3); PLIMM(P3F, 関数のラベル番号); LB(1, 3...
-ここでは帰ってくる場所のラベルを3にしましたが、もちろん4...
-ちなみに関数のほうでは、処理が終わって呼び出し元にreturn...
-こういうことになっているので、ポインタ引数を渡したいとき...
** (15) 関数の構成法
-関数の記述は基本的に次の形式でやってください。
[01] [01] [imm32] [FE] [01] [00] [3C] [00] [20] [20] [00...
... 関数の処理内容 ...
[3D] [00] [20] [20] [00] [00] [00] [1E] [3F] [30]
-imm32の部分は関数のラベル番号です。
~
-[01] [01] [imm32] : これはラベル宣言命令なので特に説明は...
-[FE] [01] [00] : これは DEBUG-INFO-1 と呼ばれている3バイ...
-[3C] [00] [20] [20] [00] [00] [00] : この7バイト命令が、...
-[3D] [00] [20] [20] [00] [00] [00] : この7バイト命令は上...
-[1E] [3F] [30] : PCP(P3F, P30); です。これで呼び出し元に...
** (16) 条件処理の構成法
** (17) APIの呼び出し(1)
-ここではR30~R3Bしか引数に使わないAPIの呼び出し方を紹介...
-この方法で呼び出せるAPIは次の10個です。この表に出てくる1...
drawPoint #02: [FE] [05] [01] [0002(32bit)] R30=0xff44; ...
drawLine #03: [FE] [05] [01] [0003(32bit)] R30=0xff45; ...
fillRect #04: [FE] [05] [01] [0004(32bit)] R30=0xff46; ...
fillOval #05: [FE] [05] [01] [0005(32bit)] R30=0xff47; ...
exit #08: [FE] [05] [01] [0008(32bit)] R30=0xff06; ...
sleep #09: [FE] [05] [01] [0009(32bit)] R30=0xff42; ...
inkey #0d: [FE] [05] [01] [000d(32bit)] R30=0xff43; ...
openWin #10: [FE] [05] [01] [0010(32bit)] R30=0xff40; ...
flushWin #11: [FE] [05] [01] [0011(32bit)] R30=0xff41; ...
rand #13: [FE] [05] [01] [0013(32bit)] R30=0xff49; ...
-まず前後の [FE] [05] [01] [00??(32bit)] と [FE] [01] [00...
-P28の値はOS(もしくはVM)が設定した状態でアプリを起動し...
-inkeyとrandについては、結果がR30に格納されて戻ってきます...
-さっき無視してもらったFEで始まる命令群ですが、これはリマ...
~
-リマーク命令というのは、本来の実行には直接関係のない補助...
-[FE] [01] [00] は通称 DEBUG-INFO-1 と呼ばれている3バイト...
-この DEBUG-INFO-1 は、もしエラーが起きた場合に、エラーが...
-裏返せば、もし絶対にバグがないのならこんな情報はあっても...
--これを置き忘れると、エラー扱いになって実行を拒否するモ...
-次は [FE] [05] [01] [imm32] の7バイトのリマーク命令です...
-このリマーク命令の後には、R30からレジスタ番号順にLIMM命...
-値を返すAPIの場合は [FE] [01] [00] の直後に CP(Rxx, R30)...
-なんかめんどくさいなーと思ったら(確かにめんどくさい)、...
** (18) ポインタレジスタ
-ここからしばらくはメモリアクセス関係の命令を紹介します。
~
-OSECPUはメモリアドレスを指し示すためのポインタレジスタを...
-P00~P3F と表記します。
-P00やP01など番号の若いレジスタは、実際のCPUの実レジスタ...
--結局どうなるかは処理系依存で、保障はされていません。
-ということで、特にこだわりがないのならP00やP01を使いまし...
--ちなみにx86版では、P00~P02までが実レジスタに割り当てら...
~
-これらのレジスタはすべて対等で汎用的に使われるというわけ...
--P00 ( 1本) : ベースポインタです。つまり汎用では...
--P01~P1F (31本) : 最も汎用的なポインタレジスタで、通...
--P20~P27 ( 8本) : 汎用ですが、関数ごとにローカルとい...
--P28~P2F ( 8本) : これも汎用ですが、関数ごとにローカ...
--P30~P3B (12本) : 基本的にはこれらも汎用なのですが、...
--P3C~P3E ( 3本) : 将来の拡張のためにリザーブされてい...
--P3F ( 1本) : 「(9) 無条件分岐命令」などで説明し...
-全体として、OSECPUのレジスタはかなり多いほうだと思います...
** (19) データ記述命令
-プログラム内にデータを置くことができます。これらのデータ...
-この命令そのものは何もしないので、プログラムの実行コード...
-(つづく)
** (20) ラベル番号代入命令
[03] [Pxx] [imm32]
PLIMM(Pxx, imm32);
-6バイト命令です。Pxxには0x00から0x3fのレジスタ番号を指定...
** (21) メモリへのアクセス命令
-まず、以下に示す命令は、Pxxがデータをポイントしていると...
--プログラムの分岐先などを指している場合はダメだというこ...
-また型が一致しない場合もセキュリティ違反になります。
[08] [Rxx] [typ32] [Pxx] [00]
LMEM(Rxx, typ32, Pxx, 0);
-これはメモリからデータを読んでRxxに格納する命令で、8バイ...
-基本的にこの命令以外でメモリからデータを読み込む命令はあ...
-つまりこの命令でレジスタに値を持ってきて、そしてレジスタ...
-メモリアドレッシングはPxxの単独指定しかありません。これ...
~
[09] [Rxx] [typ32] [Pxx] [00]
SMEM(Rxx, typ32, Pxx, 0);
-こちらはレジスタの値をメモリに書くための命令で、やはり8...
-基本的にこの命令以外でメモリへデータを書き込む命令はあり...
** (22) ポインタ演算命令
-ポインタレジスタには整数を加えることができます。これは配...
[0E] [preg0] [typ32] [preg1] [Rxx]
PADD(preg0, typ32, preg1, Rxx);
-8バイト命令です。 preg0 = preg1 + Rxx; です。計算結果が...
-この命令の前にLIMM(R3F, ...);を置くのであれば、Rxxの部分...
~
-同じ配列を指し示す2つのポインタレジスタに対しては、差を...
[0F] [Rxx] [typ32] [preg0] [preg1]
PDIF(Rxx, typ32, preg0, preg1);
-8バイト命令です。 Rxx = preg0 - preg1; です。ポインタレ...
-同じ配列に属するというのは、単に型が一致しているだけでは...
** (23) ポインタ比較命令
-2つのポインタに対して、PDIFが計算可能であるような組み合...
-(つづく)
** (24) データの型
** (25) PALMEM命令など
-たとえばP01が配列の先頭を指していれば、
PADD(P30, T_UINT32, P01, R00); LMEM0(R01, T_UINT32, P30);
-とすることで、配列のR00番目の値をR01にとってくることがで...
-この方法の副作用としては、P30というレジスタを一時的に使...
-しかしそれでも副作用がないのならないほうがいいので、こん...
PALMEM0(R01, T_UINT32, P01, R00);
[0E] [3F] [T_UINT32] [P01] [R00] [08] [R01] [T_UINT32] [...
-もちろんP01やR00やR01の部分は他のレジスタに置き換えてもO...
-バイトコードのほうを見ると、これは結局PADD命令とLMEM命令...
~
-同様にPASMEMやPAPLMEMやPAPSMEM命令があります。
** (26) malloc命令
[32] [Pxx] [reg0] [reg1]
-メモリをヒープ領域からアロケートします。reg0には要求して...
-reg1にはその型をいくつ分ほしいのかを指定します。やはりレ...
-そしてPxxにポインタが返ります。
-たとえば、T_SINT32が100個分の配列を確保したいのであれば...
[02] [30] [00000006] [02] [31] [00000064] [32] [01] [30]...
-これでP01にその配列の最初の要素を指したポインタが代入さ...
~
-mallocで確保したメモリ域を使い終わったら、システムに返す...
[33] [Pxx] [3F] [3F]
-このとき指定するPxxはmallocで得たものをそのまま使ってく...
-なお、アプリが終了すればmallocで確保したメモリは全て自動...
** (27) talloc命令
[30] [Pxx] [reg0] [reg1]
-メモリをスタック領域からアロケートすることを除けば、上記...
-解放については
[31] [3F] [3F] [3F]
-を使います。関数の中でスタック域に確保したメモリは関数か...
** (28) APIの呼び出し(2)
-ポインタレジスタを引数に含むAPIについて、呼び出し方を紹...
-(つづく)
** (29) 擬似構造体
-OSECPUは現在構造体をサポートできていません。開発者側の時...
-しかしだからといって、構造体的なことが全くできないわけで...
-構造体というのは、雑な定義でよければ、いくつかのメンバ変...
struct Abc {
int i, a[10];
unsigned char c, d;
};
-これを配列で代用することを考えます。まず配列は要素が全て...
-結局、T_SINT32が13個あればいいのです。だからタイプT_SINT...
-もしAbcの配列を作る必要があれば、その個数を掛け算してく...
~
-この方法には限界があります。それは、ポインタ型はintと互...
struct Node {
int data;
Node *left, *right;
};
-たとえばこんなものがあったとしたら、これを2つの構造体に...
struct NodeInt { // int系のメンバを集めたもの.
int data;
};
struct NodePtr {
NodeInt *leftInt, *rightInt;
NodePtr *leftPtr, *rightPtr;
};
-こうしてしまった上で、配列を二つ作ります。
NodeInt *nodeInt = malloc(...);
NodePtr *nodePtr = malloc(...);
-こうしてしまえば、node[3].dataにアクセスしたいときはnode...
~
-もしくはもっといい方法もあります。ポインタなんて結局は配...
struct Node {
int data;
int leftIdx, rightIdx;
};
** (30)
-(つづく)
* こめんと欄
-(6) 整数比較命令 25 CMPLでなくCMPGだと思います。 -- [[hi...
-(19) データ記述命令のオペコードは多分[34][type32][length...
-そのとおりです。直しました。>CMPG -- ''K'' SIZE(10){201...
#comment
終了行:
* OSECPUのバイトコードの詳細仕様
-(by [[K]], 2013.06.21)
** (0) はじめに
-これは要するに[[page0032]]の詳細版です。
** (1) 整数レジスタ
-OSECPUは32bitの signed int な整数レジスタを64本持ってい...
-R00~R3F と表記します。
-R00やR01など番号の若いレジスタは、実際のCPUの実レジスタ...
--結局どうなるかは処理系依存で、保障はされていません。
-ということで、特にこだわりがないのならR00やR01を使いまし...
--ちなみにx86版では、R00~R02までが実レジスタに割り当てら...
~
-これらのレジスタはすべて対等で汎用的に使われるというわけ...
--R00~R1F (32本) : 最も汎用的な整数レジスタで、通常は...
--R20~R27 ( 8本) : 汎用ですが、関数ごとにローカルとい...
--R28~R2F ( 8本) : これも汎用ですが、関数ごとにローカ...
--R30~R3B (12本) : 基本的にはこれらも汎用なのですが、...
--R3C~R3E ( 3本) : 将来の拡張のためにリザーブされてい...
--R3F ( 1本) : 後で説明する特別な用途のための整数...
-全体として、OSECPUのレジスタはかなり多いほうだと思います...
** (2) フラグレジスタ
-x86でもARMでも、さらには6502やZ80でさえも、みんなフラグ...
--参考: http://ja.wikipedia.org/wiki/%E3%82%B9%E3%83%86%...
-しかしOSECPUにはフラグレジスタはありません。MIPSの仕様に...
-フラグレジスタがない代わりにCMPcc命令の結果に応じて任意...
-これで設定された値を後述のCNDプリフィクス命令(04 Rxx)...
** (3) 定数即値代入命令
[02] [Rxx] [imm32]
LIMM(Rxx, imm32);
-6バイト命令です。Rxxには0x00から0x3fのレジスタ番号を指定...
** (4) 単純代入命令
[10] [reg0] [reg1] [FF]
CP(reg0, reg1);
-4バイト命令です。Rxxフィールドが複数ある命令では、ここで...
-reg1の値がreg0へコピーされます。
-reg0にR3Fを指定することはできません。reg1にもR3Fを指定し...
-この命令はバイトコード的には、OR命令でreg2にFFを指定した...
-メモリの内容をコピーすることはこの命令ではできません。
** (5) 三項演算命令
[10] [reg0] [reg1] [reg2]
OR(reg0, reg1, reg2);
-4バイト命令です。reg1とreg2がOR演算されて、結果がreg0に...
-他にも以下のような命令がこの形式になっています。
--11 XOR
--12 AND
--14 ADD
--15 SUB
--16 MUL 符号付き乗算
--18 SHL 左シフト
--19 SAR 右シフト(符号付き)
--1A DIV 符号付き除算
--1B MOD 符号付き剰余
-符号なしの演算は一切サポートされていません。OSECPUでは整...
-INCやDECのような命令は持っていません。
-二項演算命令も持っていません。ADD(R00, R01);みたいなこと...
-キャリーやボロー(桁あふれや桁借り)やオーバーフローを検...
-OSECPUでは定数を用いた演算命令を用意していません。もしR0...
-これに関連しますが、整数レジスタを定数の代わりに使う場合...
LIMM(R3F, 1); ADD(R00, R00, R3F);
-R01などではなくR3Fを使うメリットですが、OSECPUはR3Fが使...
-このようにR3Fは定数即値を指定するための整数レジスタとい...
--例外として、SUB,SHL,SAR,DIV,MODに関してはreg1にR3Fを使...
-もしかしたらADD(R00, R00, R00);のように、reg0とreg1やreg...
--この場合、R00 = R00 + R00; ですから、R00は問題なく2倍に...
** (6) 整数比較命令
[20] [reg0] [reg1] [reg2]
CMPE(reg0, reg1, reg2);
-4バイト命令です。reg1とreg2を比較して等しいかどうかを判...
-他にも以下のような命令がこの形式になっています。
--21 CMPNE (等しくなければtrue)
--22 CMPL ( reg1 < reg2 が成立すればtrue)
--23 CMPGE ( reg1 >= reg2 が成立すればtrue)
--24 CMPLE ( reg1 <= reg2 が成立すればtrue)
--25 CMPG ( reg1 > reg2 が成立すればtrue)
--26 TSTZ ( reg1 & reg2 の演算結果が0ならtrue)
--27 TSTNZ ( reg1 & reg2 の演算結果が0でなければtrue)
-三項演算命令と同様にreg2に対してはR3Fの指定が可能です。r...
-そしてなんとreg0に対してもR3Fを指定することができるので...
代入という意味ではなくて、特別な慣用句の指定を意味します...
-これはx86限定の豆知識ですが、20~25の命令については、(5)...
R00--; if (R00 > 0) goto ...; // これは高速化される.
R00--; if (R00 >= 1) goto ...; // これは高速化されない.
--何が言いたいのかというと、可能ならできるだけ0と比較しま...
--まあ些細なことですけどね。
-基本的な整数演算命令はこれでおしまいです。少ないですね。
** (7) 何もしない命令
[00]
NOP();
-1バイト命令です。何もしない命令です。JITコンパイラはこの...
-ここからしばらくは制御系の命令を紹介します。
** (8) ラベル定義命令
[01] [opt] [imm32]
LB(opt, imm32);
-6バイト命令です。OSECPUでは分岐命令を実行する際には、絶...
-当然ですが、同じラベル番号を複数回使うことはできません。...
--DLLとアプリとでラベル番号がぶつかってしまうことに関して...
-optですが、今のところ指定できるのは0か1だけです。0は普通...
--ポインタレジスタについては後述します。
-ラベル番号は本来は32ビットの任意の整数(負の数もOK)なの...
** (9) 無条件分岐命令
[03] [3F] [imm32]
PLIMM(P3F, imm32);
-6バイト命令です。OSECPUではP3Fというポインタレジスタに特...
--しつこいですがポインタレジスタについては後述します。と...
-PLIMMというのはLIMMのポインタレジスタ版で、ラベルの値を...
** (10) 条件分岐命令など
[04] [Rxx] [03] [3F] [imm32]
CND(Rxx); PLIMM(P3F, imm32);
-これは条件分岐命令です。よく見ると無条件分岐命令の前にCN...
-CNDプリフィクスには後続の1命令を条件実行命令化する作用が...
-CNDプリフィクスはRxxの下位1bitしか参照しません。それが1...
--この仕様は注意を要するかもしれません。Cなどでは値が非零...
--もしCみたいに非零がどうかで条件分岐したいのであれば、
TSTNZ(R30, Rxx, Rxx); CND(R30); PLIMM(P3F, imm32);
--みたいにするといいと思います。つまりRxxが非零であるかど...
-CNDプリフィクスは他の命令、たとえばLIMMやADDの前につける...
--他にもいくつかCNDプリフィクスがつけられない命令がありま...
-CNDプリフィクスのRxxに定数即値としてのR3Fを指定すること...
** (11) 条件分岐慣用句
-OSECPU用アプリをいくつか作ってみてわかったことですが、
CMPxx(Rxx, ...); CND(Rxx); PLIMM(P3F, ...);
-のパターンが頻出します。比較して条件分岐ということです。...
-一方でJITコンパイラは、CMP系の命令のときに、結構苦労して...
-そうであれば、もっと単純なコードを生成できるので、そのこ...
CMPxx(R3F, ...); CND(R3F); PLIMM(P3F, ...);
-つまりRxxの部分が形式的にR3Fになっているのです。このR3F...
-この慣用句を使えば、条件分岐のためにわざわざ整数レジスタ...
-なお、ANDやORを含むような複雑な条件では、この構文は使え...
if (R00 <= R02 & R02 <= R01) goto 3;
CMPLE(R30, R00, R02); CMPLE(R31, R02, R01); AND(R30, R30...
-CNDの直後はPLIMM(P3F, ...);でなければいけないので、分岐...
-しかしそれでもこの構文は便利なので本当によく使います。CN...
** (12) ループの構成法
-まず、以下のようにすれば簡単に無限ループが構成できます。
LB(0, 123); // 123である必然性はない.
...
PLIMM(P3F, 123);
-これで...の中は無限に繰り返されることになります。
~
-次に上記のLB命令の直後に適当な条件式を加えて、それが成立...
LB(0, 123);
CMPE(R3F, R00, R01); CND(R3F); PLIMM(P3F, 124);
...
PLIMM(P3F, 123);
LB(0, 124);
-これはつまり while(R00 != R01) { ... } というループです...
-途中でbreakしたいときは、いつでも PLIMM(P3F, 124); すれ...
~
-ループ末尾のジャンプ命令を、条件付きに変更すると、do { ....
LB(0, 123);
...
CMPNE(R3F, R00, R01); CND(R3F); PLIMM(P3F, 123);
** (13) 間接分岐命令
-間接分岐命令というのは、P3Fにラベル番号を直接入れるので...
-たとえばこういうことができます。
PLIMM(P01, 1); PCP(P3F, P01);
-このPCPは P3F = P01; を実行するための命令で、ポインタコ...
[1E] [preg0] [preg1]
-となっていて、 preg0 = preg1; になります。なお、P3Fをpre...
-PCPでP3Fに代入すると、それが間接分岐命令になるというわけ...
** (14) 関数の呼び出し
-OSECPUにおいて、関数の呼び出しとは、R30~R3Bに適当な引数...
-しかしこれでは呼び出し元へ帰ってくることができません。こ...
-ということで、関数呼び出しの際には必ずどこに帰ってきてほ...
... PLIMM(P30, 3); PLIMM(P3F, 関数のラベル番号); LB(1, 3...
-ここでは帰ってくる場所のラベルを3にしましたが、もちろん4...
-ちなみに関数のほうでは、処理が終わって呼び出し元にreturn...
-こういうことになっているので、ポインタ引数を渡したいとき...
** (15) 関数の構成法
-関数の記述は基本的に次の形式でやってください。
[01] [01] [imm32] [FE] [01] [00] [3C] [00] [20] [20] [00...
... 関数の処理内容 ...
[3D] [00] [20] [20] [00] [00] [00] [1E] [3F] [30]
-imm32の部分は関数のラベル番号です。
~
-[01] [01] [imm32] : これはラベル宣言命令なので特に説明は...
-[FE] [01] [00] : これは DEBUG-INFO-1 と呼ばれている3バイ...
-[3C] [00] [20] [20] [00] [00] [00] : この7バイト命令が、...
-[3D] [00] [20] [20] [00] [00] [00] : この7バイト命令は上...
-[1E] [3F] [30] : PCP(P3F, P30); です。これで呼び出し元に...
** (16) 条件処理の構成法
** (17) APIの呼び出し(1)
-ここではR30~R3Bしか引数に使わないAPIの呼び出し方を紹介...
-この方法で呼び出せるAPIは次の10個です。この表に出てくる1...
drawPoint #02: [FE] [05] [01] [0002(32bit)] R30=0xff44; ...
drawLine #03: [FE] [05] [01] [0003(32bit)] R30=0xff45; ...
fillRect #04: [FE] [05] [01] [0004(32bit)] R30=0xff46; ...
fillOval #05: [FE] [05] [01] [0005(32bit)] R30=0xff47; ...
exit #08: [FE] [05] [01] [0008(32bit)] R30=0xff06; ...
sleep #09: [FE] [05] [01] [0009(32bit)] R30=0xff42; ...
inkey #0d: [FE] [05] [01] [000d(32bit)] R30=0xff43; ...
openWin #10: [FE] [05] [01] [0010(32bit)] R30=0xff40; ...
flushWin #11: [FE] [05] [01] [0011(32bit)] R30=0xff41; ...
rand #13: [FE] [05] [01] [0013(32bit)] R30=0xff49; ...
-まず前後の [FE] [05] [01] [00??(32bit)] と [FE] [01] [00...
-P28の値はOS(もしくはVM)が設定した状態でアプリを起動し...
-inkeyとrandについては、結果がR30に格納されて戻ってきます...
-さっき無視してもらったFEで始まる命令群ですが、これはリマ...
~
-リマーク命令というのは、本来の実行には直接関係のない補助...
-[FE] [01] [00] は通称 DEBUG-INFO-1 と呼ばれている3バイト...
-この DEBUG-INFO-1 は、もしエラーが起きた場合に、エラーが...
-裏返せば、もし絶対にバグがないのならこんな情報はあっても...
--これを置き忘れると、エラー扱いになって実行を拒否するモ...
-次は [FE] [05] [01] [imm32] の7バイトのリマーク命令です...
-このリマーク命令の後には、R30からレジスタ番号順にLIMM命...
-値を返すAPIの場合は [FE] [01] [00] の直後に CP(Rxx, R30)...
-なんかめんどくさいなーと思ったら(確かにめんどくさい)、...
** (18) ポインタレジスタ
-ここからしばらくはメモリアクセス関係の命令を紹介します。
~
-OSECPUはメモリアドレスを指し示すためのポインタレジスタを...
-P00~P3F と表記します。
-P00やP01など番号の若いレジスタは、実際のCPUの実レジスタ...
--結局どうなるかは処理系依存で、保障はされていません。
-ということで、特にこだわりがないのならP00やP01を使いまし...
--ちなみにx86版では、P00~P02までが実レジスタに割り当てら...
~
-これらのレジスタはすべて対等で汎用的に使われるというわけ...
--P00 ( 1本) : ベースポインタです。つまり汎用では...
--P01~P1F (31本) : 最も汎用的なポインタレジスタで、通...
--P20~P27 ( 8本) : 汎用ですが、関数ごとにローカルとい...
--P28~P2F ( 8本) : これも汎用ですが、関数ごとにローカ...
--P30~P3B (12本) : 基本的にはこれらも汎用なのですが、...
--P3C~P3E ( 3本) : 将来の拡張のためにリザーブされてい...
--P3F ( 1本) : 「(9) 無条件分岐命令」などで説明し...
-全体として、OSECPUのレジスタはかなり多いほうだと思います...
** (19) データ記述命令
-プログラム内にデータを置くことができます。これらのデータ...
-この命令そのものは何もしないので、プログラムの実行コード...
-(つづく)
** (20) ラベル番号代入命令
[03] [Pxx] [imm32]
PLIMM(Pxx, imm32);
-6バイト命令です。Pxxには0x00から0x3fのレジスタ番号を指定...
** (21) メモリへのアクセス命令
-まず、以下に示す命令は、Pxxがデータをポイントしていると...
--プログラムの分岐先などを指している場合はダメだというこ...
-また型が一致しない場合もセキュリティ違反になります。
[08] [Rxx] [typ32] [Pxx] [00]
LMEM(Rxx, typ32, Pxx, 0);
-これはメモリからデータを読んでRxxに格納する命令で、8バイ...
-基本的にこの命令以外でメモリからデータを読み込む命令はあ...
-つまりこの命令でレジスタに値を持ってきて、そしてレジスタ...
-メモリアドレッシングはPxxの単独指定しかありません。これ...
~
[09] [Rxx] [typ32] [Pxx] [00]
SMEM(Rxx, typ32, Pxx, 0);
-こちらはレジスタの値をメモリに書くための命令で、やはり8...
-基本的にこの命令以外でメモリへデータを書き込む命令はあり...
** (22) ポインタ演算命令
-ポインタレジスタには整数を加えることができます。これは配...
[0E] [preg0] [typ32] [preg1] [Rxx]
PADD(preg0, typ32, preg1, Rxx);
-8バイト命令です。 preg0 = preg1 + Rxx; です。計算結果が...
-この命令の前にLIMM(R3F, ...);を置くのであれば、Rxxの部分...
~
-同じ配列を指し示す2つのポインタレジスタに対しては、差を...
[0F] [Rxx] [typ32] [preg0] [preg1]
PDIF(Rxx, typ32, preg0, preg1);
-8バイト命令です。 Rxx = preg0 - preg1; です。ポインタレ...
-同じ配列に属するというのは、単に型が一致しているだけでは...
** (23) ポインタ比較命令
-2つのポインタに対して、PDIFが計算可能であるような組み合...
-(つづく)
** (24) データの型
** (25) PALMEM命令など
-たとえばP01が配列の先頭を指していれば、
PADD(P30, T_UINT32, P01, R00); LMEM0(R01, T_UINT32, P30);
-とすることで、配列のR00番目の値をR01にとってくることがで...
-この方法の副作用としては、P30というレジスタを一時的に使...
-しかしそれでも副作用がないのならないほうがいいので、こん...
PALMEM0(R01, T_UINT32, P01, R00);
[0E] [3F] [T_UINT32] [P01] [R00] [08] [R01] [T_UINT32] [...
-もちろんP01やR00やR01の部分は他のレジスタに置き換えてもO...
-バイトコードのほうを見ると、これは結局PADD命令とLMEM命令...
~
-同様にPASMEMやPAPLMEMやPAPSMEM命令があります。
** (26) malloc命令
[32] [Pxx] [reg0] [reg1]
-メモリをヒープ領域からアロケートします。reg0には要求して...
-reg1にはその型をいくつ分ほしいのかを指定します。やはりレ...
-そしてPxxにポインタが返ります。
-たとえば、T_SINT32が100個分の配列を確保したいのであれば...
[02] [30] [00000006] [02] [31] [00000064] [32] [01] [30]...
-これでP01にその配列の最初の要素を指したポインタが代入さ...
~
-mallocで確保したメモリ域を使い終わったら、システムに返す...
[33] [Pxx] [3F] [3F]
-このとき指定するPxxはmallocで得たものをそのまま使ってく...
-なお、アプリが終了すればmallocで確保したメモリは全て自動...
** (27) talloc命令
[30] [Pxx] [reg0] [reg1]
-メモリをスタック領域からアロケートすることを除けば、上記...
-解放については
[31] [3F] [3F] [3F]
-を使います。関数の中でスタック域に確保したメモリは関数か...
** (28) APIの呼び出し(2)
-ポインタレジスタを引数に含むAPIについて、呼び出し方を紹...
-(つづく)
** (29) 擬似構造体
-OSECPUは現在構造体をサポートできていません。開発者側の時...
-しかしだからといって、構造体的なことが全くできないわけで...
-構造体というのは、雑な定義でよければ、いくつかのメンバ変...
struct Abc {
int i, a[10];
unsigned char c, d;
};
-これを配列で代用することを考えます。まず配列は要素が全て...
-結局、T_SINT32が13個あればいいのです。だからタイプT_SINT...
-もしAbcの配列を作る必要があれば、その個数を掛け算してく...
~
-この方法には限界があります。それは、ポインタ型はintと互...
struct Node {
int data;
Node *left, *right;
};
-たとえばこんなものがあったとしたら、これを2つの構造体に...
struct NodeInt { // int系のメンバを集めたもの.
int data;
};
struct NodePtr {
NodeInt *leftInt, *rightInt;
NodePtr *leftPtr, *rightPtr;
};
-こうしてしまった上で、配列を二つ作ります。
NodeInt *nodeInt = malloc(...);
NodePtr *nodePtr = malloc(...);
-こうしてしまえば、node[3].dataにアクセスしたいときはnode...
~
-もしくはもっといい方法もあります。ポインタなんて結局は配...
struct Node {
int data;
int leftIdx, rightIdx;
};
** (30)
-(つづく)
* こめんと欄
-(6) 整数比較命令 25 CMPLでなくCMPGだと思います。 -- [[hi...
-(19) データ記述命令のオペコードは多分[34][type32][length...
-そのとおりです。直しました。>CMPG -- ''K'' SIZE(10){201...
#comment
ページ名: