「ぐいぐい01」のAPI再検討
(0)
- OSASK-IRCでneriさんにCOM64plusのフォーマットであれこれアドバイスしていたら、あっという間に非常にコンパクトな仕様になり、helloが25バイトで書けることになってしまった。helloだけならまだしもcharsも26バイトで書けるらしい。
- 一時は「これはしょうがない」とあきらめたものの、いい方法を思いついたのでそれをベースに再設計を試みる。
- このようなハイレベルな競争ができることを本当にありがたく思う。
(1)
- アイデアリスト:
- EBX=-1を正式なものにする。・・・いやまて、EBXではなくEDXで指定することにする。本当はEDIにしたい。・・・やっぱりEDXでいいや。EDXで困るのはIN/OUTとMUL/DIVくらいなもの。EDIだってストリング命令で困るし、手ごろなアドレスレジスタだってほしい。だからこれでいい。・・・いややっぱりEDIにしよう。C言語のことはひとまず忘れる。悪いのはC言語の仕様であって、それに流されるべきではない。
- 最初はEIP自動インクリメント、モードgh4、長さ無限、にする。
- 4bitの6をプリフィクスにする。
- 6_0: 32bitレジスタ、スタック指定プリフィクス
- 6_1: 8bitレジスタ、スタック指定プリフィクス
- 6_2: 式用リザーブ
- 6_3: 各種定数など
- 6_4: ブランチ系0
- 6_4_a_b_c_d: aのbit0は個数式/ターミネータ式, bit1がgosub/goto, bit2がインクリメントありなし、ポインタありなし, bがgh4/8bit/16bit/32bit/64bit..., cが個数もしくはターミネータ, dはポインタ
- 0はgosub, ポインタなし, 個数
- 1はgosub, ポインタなし, term
- 2はgosub, ポインタありインクリメントなし, 個数
- 3はgosub, ポインタありインクリメントなし, term
- 4はgosub, ポインタありインクリメントあり, 個数
- 5はgosub, ポインタありインクリメントあり, term
- 6はgoto, ポインタなし(8bitの6にすること)
- 7はgoto, ポインタありインクリメントあり
- 8はgoto, ポインタありインクリメントなし
- gotoはつまり個数=infだ。gosubでないなら個数もターミネータも関係ない
- 6_4のパラメータはAPIのパラメータとしては完全に透明
- 6_5_a_b_c_d: ブランチ系1 (個数もしくはターミネータがコマンドの動作としても有効)
- これを[個数-データ列]が来る場所に置く。gosubのみ。gotoだとコマンドが終わらない。
- 6_6: 各種拡張(8bitの6にすること)
- そもそもシグネチャの後にタグ総数がないのはおかしい。終わりが分からないじゃないか。それとも終端制にするか?いや個数にする。
- そして0個はありえないので、0個ならイメージのみと判定、サイズも別の方法で取得できるから省略とする。これでオーバヘッドは5バイト。
- 個数0だと4bitあまる。これがもったいない。
- bit0はrjcのフラグ(デフォルト1)。
- bit1は終了時自動改行フラグ(デフォルト1)。
- bit2はHLT->CALL(EBP)置換。悪くないけどエスケープしにくそうだからやめた。コードセクションでもっとも出現頻度の低いバイトを探せ。・・・やっぱF4でやろう(デフォルト0)。
- デフォルトrjc。しかもrjc後にコードセクションの末尾にRETのコードを付加。
- C向きの呼び出し関数を作る。ECXがESIの代わりで、EDXがEDIの代わり。これならPUSHとかはいらないよね。g01_callapi()もCALLじゃなくてJMP。JMPだからスタック汚さない。EIPに直で書ける。・・・やめた。インクルードファイルでgccのインラインアセンブラを使う。
- 書き込み即値のことを考えてない。regならregに書き込む。となると、スタック以外のmemを指定する方法が欲しいか。
- 6_3_0: gh4 [] アドレスが後続する
- 6_3_1: char [] アドレスが後続する
- 6_3_2: short [] アドレスが後続する
- 6_3_3: int [] アドレスが後続する
- 6_3_4: double [] アドレスが後続する -- 未定
- アドレスは、式か、32bit定数か(1)、レジスタで指定(0)
- 6_5の自動挿入機能により随所で1バイト単位の減少が見込まれる。
- その代償としてパケットフォーマットをコマンドに依存せずに解釈することはできなくなった。しかしそれを言うなら既に長さ情報がない時点で3や4のターミネータを認識しそこなえば同じことだし、そもそも長さ情報方式も終端は書いてなかった。
- またこれで6_5の出現頻度は激減するので、6_xにしていい(xは巨大な数)。これで6_5を他の目的に使える。
- 6_x_1だと、おそらくbyteでゼロ末端。6_x_3と6_x_5も。そもそもbはほとんどいつも1。それ以外を指定するほうが稀。
- リンカのアルゴリズムをもうちょっと賢く。ゼロしかないセクションは後ろに固める(zp:1)。その中で_g01_esi0は最優先に。これができれば、G01Mainの最後の配列はbssにはみ出してもいい事になるので便利。これを確実にやりたいときは、問題のゼロ配列だけの.objを作ってOBJSの一番最後にすればいい。ということで、ライブラリは先。
- ESPの値は、CALLした後の値。
- (6_0_)をキャンセルするには5ではなく4。
- 6_2_0_a_bは、reg32(a)+(1.xx<<eeee)の意味(bはプラスだけ)。bが4bitでも+1〜3、8bitなら+0xe000までいける。12bitなら4Gでもなんでも。これはいい。
- コマンドラインで"in"をよこせとかいえる。そもそも本来はファイル名指定する必要ないし。・・・よし、自動挿入許諾。
- COM64plusの真似で初期値ロードを導入:
- モード0:初期値ロードなし
- モード1:EAXへ初期値ロード
- モード2:EAX,ECXへ初期値ロード
- モード3:フルモード
- フルモードでは基本は連番ロード。!5で連番開始番号変更。!6で初期値ロードリスト終了。
- 8bit形式の38-3fはリザーブに。
- 38:
- 39:
- 3A:加算 (6_0)_a (6_0)_b
- 3B:減算 (6_0)_a (6_0)_b
- 3C:単項マイナス
- 3D:符号拡張要請
- 3E:ポインタ演算(6_0)付き
- 3F:4バイトリトルエンディアン整数(主にポインタ用) reg番号8bitの3Fは使用禁止なのでこれはキャンセルせずに使える。
- ボツ案:
- 個数を指定する文脈で6_6_0が来ると、個数ではなくターミネータ指定。・・・っていうかそんなのオプションでいいじゃん。確かにそうだ。
- やはり最初はブランチ・モード指定
- ボツ案の6_4を省いて、aのbit0に個数は非データ/データを追加したもの。
(2)
- 実験:
- hello:
- 5 0 6_5_1_1_0 3 "hello, world\n\0" -- 18バイト
- これに5バイトつけると、23バイト
- HLT変換+自動改行で21バイト
- chars:
- 5 0 1 6_1_0 3 -- 3.5バイト
- 5 0 1 [0a] 3 -- 3バイト
- AL=20; CALL(EBP); DB(4バイト); EAX++; CMP(AL,0x7f); JB do; CALL(EBP); DB(3バイト);
- 5+18=23バイト
- HLT変換+自動改行で17バイト
(3)
こめんと欄
|