[Subject Prev][Subject Next][Thread Prev][Thread Next][Subject Index][Thread Index]
[OSASK 1362] for more compact(Re: s_world.bin).
- Subject: [OSASK 1362] for more compact(Re: s_world.bin).
- From: Hidemi KAWAI <kawai !Atmark! imasy.or.jp>
- Date: Mon, 15 Jan 2001 10:48:26 -0000
こんばんは、川合です。
Koyanagi Masaaki さんは 2001/01/15 12:58:59 の「[OSASK 1361] Re:
s_world.bin.」で書きました:
>しまった間違えました。INT 0xOD General Protect です。
>何故か 0x0D = 10 と変換してしまいました。
はい、これなら納得です。
今のOSASKは例外が起きたら、システム全体が停止してしまうか、そ
うでなくても表示が乱れてしまいます。これは手抜きのせいで、これは
近いうちに手を付ける予定です。
で、せっかく表示された情報を生かさないのはもったいないので、そ
の読み方を少し説明します。
一般保護例外(INT 0x0D)は、いろんな要因で起こりますが、最も多
いのは、ポインタが狂って妙なところをアクセスしようとした時です。
妙なところというのは、与えられたセグメントサイズ以上の領域をアク
セスしようとした時です。
で、まず、どの命令がそんな妙なことをやろうとしていたのかは、CS
:EIPの値で分かります。CSが0x0007でしたら、例外を起こしたのはアプ
リの中です。それ以外の値の場合は、[OSASK 1007]で少し説明してあり
ます。
さて、CSの値が0x0007でしたら、このエラー表示はデバッグの役にた
ちます(主にASKAで開発している場合に限る)。EIPの表示を見てくだ
さい。それが、例外を発生した命令のアドレス(オフセット)です。MA
SMの時に出力された.LSTファイルで該当の命令が判明するはずです。で
、そのときのレジスタの値も表示されているはずですから、どうしてそ
の命令が例外を起こしたのか、察しがつくのではないでしょうか。
>> ソースは全体的にシンプルで素直な印象を持ちました。まあ、アセン
>> ブラをやるとこうなるのが普通で、だから誰にでもコンパクトで速いプ
>> ログラムが書けるのですが。
>私のアセンブラレベルが初級だというのもあると思いますが(笑)
いえいえ、あれだけ書ければたいしたものです。
>> これは小柳さんへの私信ですが、
>> http://homepage1.nifty.com/dreaming/osask/index.html
>> のページをうちの「OSASKアプリケーションのダウンロードページ」か
>> らリンクしてもよろしいでしょうか?
>はい。よろしくお願いします。
ありがとうございます。
---
さて、せっかく小柳さんがASKAにチャレンジして成功して下さったの
で、これを記念して少しだけASKAのコツみたいなものを書きます。テー
マは「もっとコンパクトにしよう!」です(笑)。
ASKAはわざと変なことをやらない限りC言語で書くよりも実行ファイ
ルがコンパクトになる傾向があります。しかし、少し意識してやると、
さらに小さくできます(これをやると場合によってはソースが読みにく
くなるかもしれませんが)。
実行ファイルの大きさに関しては、小さいことは大きいことよりもい
いことです。今から紹介するテクニックの中には実行スピードが落ちて
しまうような「改良」もあります。そういうものは、速度を必要とする
ところのコードには適しません。しかし、速度を要しないコードが小さ
くなることはそれだけキャッシュの消費を抑え、速度を必要とするコー
ドやデーターをキャッシュできるようになるので間接的に高速化に貢献
できます。
まずは、改良のための基礎知識の説明です。
・レジスタへの即値単純代入
EAX = 2;
のような、reg32 = imm;のタイプに分類される命令は、immが0でない限
りにおいて、5バイトの命令にコンパイルされます。immが0の場合は、2
バイトです。reg16やreg8の場合も含めて書くと以下の通りです。
reg32 = imm; /* 5バイト */ reg32 = 0; /* 2バイト */
reg16 = imm; /* 4バイト */ reg16 = 0; /* 3バイト */
reg8 = imm; /* 2バイト */ reg8 = 0; /* 2バイト */
・レジスタ間の単純代入
ECX = EDX;
のような、reg32 = reg32;のタイプに分類される命令は、2バイトの命
令にコンパイルされます。reg16やreg8の場合も含めて書くと以下の通
りです。
reg32 = reg32; /* 2バイト */
reg16 = reg16; /* 3バイト */
reg8 = reg8; /* 2バイト */
・メモリへの即値単純代入
(int) [DS:EBX] = 0x1234;
のような、mem32 = imm;のタイプに分類される命令は、6バイトの命令
にコンパイルされます。これは、メモリへのポインタをどのように指定
するかによってより長い命令にコンパイルされることがあります。とり
あえず、最低でもこれだけの命令長にはなると理解して下さい。バリエ
ーションを書いておきます。immが0の場合に命令が短くなったりするこ
とはありません。
mem32 = imm; /* 6バイト */
mem16 = imm; /* 5バイト */
mem8 = imm; /* 3バイト */
memの指定方法によって、さらに命令長の長さが変わります。
[DS:EBX]や[DS:ESI]のように、オフセット部がreg32の場合:
命令長は上記バイト数のまま
(例外1:ESPの場合は+2バイト)
(例外2:EBPの場合は+1バイト)
[DS:1234]のように、オフセット部がimmの場合:
命令長は上記バイト+4バイト
[DS:EBX + 4]のように、オフセット部がreg32 + immの場合:
(a) -128 <= imm <= 127 : +1バイト
(b) immが(a)の範囲におさまらない : +4バイト
もちろん、immが0の場合は、オフセット部がreg32の場合として分
類されます。
また、reg32の部分がESPの場合は、さらに+1バイトです。
他にも多くの指定方法がありますが、面倒になってきたので省略(笑
)。
・メモリへのレジスタ値単純代入
(int) [DS:EDI] = EAX;
のような、mem32 = reg32;タイプに分類される命令は、2バイトの命令
にコンパイルされます。これも、memの指定方法によって、追加のバイ
トがあります。追加バイトの計算方法は、「・メモリへの即値単純代入
」の時と共通です。データー長によって基本命令長の長さが変化します
。
mem32 = reg32; /* 2バイト */
mem16 = reg16; /* 3バイト */
mem8 = reg8; /* 2バイト */
ただし、例外が一つだけあります。regの部分がEAX、AX、ALのどれか
で、かつ、memのオフセット部がimmなら、基本命令長を1バイト小さく
みてから追加命令長を加えて下さい。
・レジスタへのメモリ値単純代入
EAX = (int) [DS:EDI];
のような、reg32 = mem32;タイプに分類される命令は、2バイトの命令
にコンパイルされます。これも、memの指定方法によって、追加のバイ
トがあります。追加バイトの計算方法は、「・メモリへの即値単純代入
」の時と共通です。データー長によって基本命令長の長さが変化します
。
reg32 = mem32; /* 2バイト */
reg16 = mem16; /* 3バイト */
reg8 = mem8; /* 2バイト */
ただし、例外が一つだけあります。regの部分がEAX、AX、ALのどれか
で、かつ、memのオフセット部がimmなら、基本命令長を1バイト小さく
みてから追加命令長を加えて下さい。
・・・ふう、長かったですねえ。さて、上記の知識を元に、s_world
のソースの一部が何バイトになるかを計算してみましょう。
work->lib_putnotename.string[0] = 32;/* ' ' */ // 10バイト
work->lib_putnotename.string[4] = 32; // 10バイト
work->lib_putnotename.string[8] = 32; // 10バイト
work->lib_putnotename.string[12] = 32; // 10バイト
(註)
work->lib_putnotename.string[0] = 32;
は、
(int) [DS:0x0c20] = 32;
とみなされる。
さて、これをこのように改良することができます。
EAX = 32; // 5バイト
work->lib_putnotename.string[ 0] = EAX; // 5バイト
work->lib_putnotename.string[ 4] = EAX; // 5バイト
work->lib_putnotename.string[ 8] = EAX; // 5バイト
work->lib_putnotename.string[12] = EAX; // 5バイト
はい、これで、40バイトの命令群が25バイトに改良されました。
ついでに、もっと小さくしてみましょう。
int *putnotename_string == DS:EDX;
EAX = 32; // 5バイト
// (offset) putnotename_string = work->lib_putnotename.string; // 5バイト
LEA((offset) putnotename_string, (int) [work->lib_putnotename.string]);
putnotename_string[ 0] = EAX; // 2バイト
putnotename_string[ 4] = EAX; // 3バイト
putnotename_string[ 8] = EAX; // 3バイト
putnotename_string[12] = EAX; // 3バイト
はい、これなら21バイトです。これで、元の40バイトからみれば、19
バイトだけコンパクトになりました。
・・・こういうことを意識することもできるのが、ASKAの特徴でしょ
う(意識しなければいけないわけではありません)。
それでは。
--
川合 秀実(KAWAI Hidemi)
川合堂社長 / OSASK計画総指揮 / カーネル開発班
E-mail:kawai !Atmark! imasy.or.jp
Homepage http://www.imasy.or.jp/~kawai/