ページ内コンテンツ
lib_execcmdを活用するためのページ
- (2004.05.04 by K)
- 5/12も近づいて参りました。今年の512コンテストの一位はもはやネタのセンスでMt.orz0ではないかと思うのですが、まあ4/1企画のときのようにすごいのがたくさん出てくるかもしれないので、最後まで油断はできません。
- 512バイトに収めるということになると、どうしてもアセンブラが有利になってしまうので(いやもちろん1KBでもアセンブラが有利ですが)、C言語ファンの人がもっと有利に戦えるように、ここにテクニックをまとめたいと思います。
- まずは最新版<guigui00.h>を入手しましょう。これはosa_dir3にバンドル予定されているものですが、たぶん5/12にosa_dir3が間に合わないので、とりあえず以下のURLからダウンロードしてください。osa_dir2のz_tools/guigui00/guigui00.hを上書きすればOKです。
- http://k.hideyosi.com/test062.lzh (26.3KB)
- さて結局のところ、アセンブラがC言語に対してやたらと有利なのは、lib_execcmdをじゃんじゃん使うからです(と僕は思う)。まあそりゃあもちろん出力されるコードの質も違いますが。しかしとにかくCでもlib_execcmdを活用すれば、その差は縮むのです。ということでここではlib_execcmdの活用を中心に説明します。
原則
- lib_execcmdの使い方の基本形
- 1.lib関数のうち、引数に変数が入っていないものを探す。値を受け取るものもダメ。だたし、staticな変数のポインタ(&)は、変数のうちには数えない(static配列は&をつけなくてもポインタ)。
- たいていはこの状態になっていないと思うので、まずはこの状態に近づけることからはじめる。とりあえずnon-malloc化が非常に有効。詳しくは後述。
- またlib関数が連続しているところが狙い目。単発のところは書き換えるとかえって大きくなっちゃうこともある。
- 2.この世界ではおなじみのVPマクロとVPSマクロを定義。
/* execcmd化ではおなじみのVPマクロ */ #define VP(x) (void *) (x) #define VPS(a, b, c, d) (void *) (a | b << 8 | c << 16 | d << 24)
- 3.次のようにlib関数をstatic void *の配列として展開。その際に全部VP()をつける。
lib_init_nm(&lib_work); lib_openwindow_nm(&window, 0x200, 19 * 8, 4 * 16); lib_opentextbox_nm(0x1000, &wintitle.tbox, 0, 7, 1, 0, 0, &window, 0x00c0, 0); ↓ static void *cmd0[] = { VP(LIB_FN_INIT), VP(&lib_work), VP(LIB_FN_OPENWINDOW), VP(&window), VP(0x200), VP(19 * 8), VP(4 * 16), VP(LIB_FN_OPENTEXTBOX), VP(0x1000), VP(&wintitle), VP(0), VP(7), VP(1), VP(0), VP(0), VP(&window), VP(0x00c0), VP(0), VP(0x0000) /* 最後にこれを忘れずにね */ };
- 4.あとはlib関数を呼んでいた場所で、lib_execcmd(cmd0);とやればOK。
Q&A
- 一ヶ所をexeccmd化したら全部execcmd化しないといけないの?
- そんなことないです。execcmd化したら小さくなるところだけexeccmd化して、あとはいつもどおりlib系の普通のやつを使っていいです。
- なかなかexeccmd化できそうなところが見つけられません。
- 最初のところが狙い目です。lib_initからシグナルボックスをオープンしてウィンドウを開いてウィンドウタイトルをつけて、defsigするあたりまでは変数を使わずに書けるはずです。この部分が一番execcmd化しやすいです。他はやりにくいのでそのままにしておいていいでしょう(苦労してやってもたいしてコンパクトにも高速にもならない)。
- 下の関数表に出てない関数はどうやってexeccmd化するの?
- 簡単にはexeccmd化できないので(やっても多分コンパクトにならない)、そのまま普通のままにしておいてください。
- lib_putstring_ASCIIは、まずlib_putstring1に書き換えておくといいでしょう。
- VPマクロは汚らしいので、こんな風にしてみましたどうですか?
#define _ (void *) #define __(a, b, c, d) (void *) (a | b << 8 | c << 16 | d << 24) static void *cmd0[] = { _ LIB_FN_INIT, _ &lib_work, _ LIB_FN_OPENWINDOW, _ &window, _ 0x200, _ (19 * 8), _ (4 * 16), _ LIB_FN_OPENTEXTBOX, _ 0x1000, _ &wintitle, _ 0, _ 7, _ 1, _ 0, _ 0, _ &window, _ 0x00c0, _ 0, _ 0x0000 /* 最後にこれを忘れずにね */ };
- こっちのほうがきれいかも?
non-malloc化
- 完全にnon-mallocにする必要はなくて、lib_execcmdの妨げになっている部分、おもにtextboxとかgraphboxとかwindowとかシグナルボックスとかをnon-malloc化するとよい。もちろんついでに完全non-malloc化してもいい。
- LIB_WORKのnon-malloc化の例
static struct LIB_WORK lib_work; /* これで lib_init_nm(&lib_work); とできるようになる。 */
- LIB_WINDOWのnon-malloc化の例
static struct LIB_WINDOW window; /* これで lib_openwindow_nm(&window, 0x200, 19 * 8, 4 * 16); とできるようになる。 */
- LIB_TEXTBOXのnon-malloc化の例
static struct { struct LIB_TEXTBOX tbox; int data[7 * 1 * 8 / 4]; } wintitle; /* これで VP(&wintitle) とすることでこのtextboxを表せる */ /* dataの中の式の意味は、 xsize * ysize * 8 / 4 という意味 */
- LIB_GRAPHBOXのnon-mallco化の例
static struct { struct LIB_GRAPHBOX gbox; int data[160 * 100]; } pic; /* これで VP(&pic) とすることでこのgraphboxを表せる */ /* dataの中の式の意味は、 xsize * ysize という意味 */ /* 表示上のサイズとバッファの大きさが違う場合は、 */ /* 表示上の大きさではなくバッファの大きさ */ /* 8bitグラフィックボックスならintではなくchar */ /* 16bitグラフィックボックスならintではなくshort */
- シグナルボックスのnon-malloc化の例
static int signalbox0[256 / 4], *sig_ptr = signalbox0; /* これで lib_opensignalbox_nm(256, signalbox0, 0, REWIND_CODE); */ /* だけでinit_signalbox(); の代わりになる */
- 共通して言えるテクニック:
- 一般に普通のlib関数でnon-malloc化したやつのポインタ(たとえばVP(&textbox)など)を利用すると、普通よりもサイズを食います(まあ違いは1バイトとかその程度ですが)。もし頻繁に普通のlib関数でも使うなら、
などとstaticじゃない普通の変数にいったん代入して、execcmdのときはVP(&textbox)、普通のlib関数のときはptbox、というように使い分けるとさらにコンパクトにできることがあります。
struct LIB_TEXTBOX *ptbox = VP(&textbox);
- 一般に普通のlib関数でnon-malloc化したやつのポインタ(たとえばVP(&textbox)など)を利用すると、普通よりもサイズを食います(まあ違いは1バイトとかその程度ですが)。もし頻繁に普通のlib関数でも使うなら、
void *の配列への変換規則
- 特に説明の必要がないほど簡単なもの
lib_init LIB_FN_INIT non-malloc型 lib_close LIB_FN_CLOSE lib_waitsignal LIB_FN_WAITSIGNAL lib_waitsignaltime LIB_FN_WAITSIGNALTIME lib_openwindow LIB_FN_OPENWINDOW non-malloc型 lib_closewindow LIB_FN_CLOSEWINDOW lib_opentextbox LIB_FN_OPENTEXTBOX non-malloc型 lib_closetextbox LIB_FN_CLOSETEXTBOX lib_opnegraphbox LIB_FN_OPENGRAPHBOX non-malloc型 lib_opnegraphbox2 LIB_FN_OPENGRAPHBOX2 non-malloc型 lib_controlwindow LIB_FN_CONTROLWINDOW lib_drawline LIB_FN_DRAWLINE lib_drawline0 LIB_FN_DRAWLINE0 lib_opensignalbox LIB_FN_OPENSIGNALBOX non-malloc型 lib_opentimer LIB_FN_OPENTIMER lib_closetimer LIB_FN_CLOSETIMER lib_settimer LIB_FN_SETTIMER lib_settimertime LIB_FN_SETTIMERTIME lib_opensoundtrack LIB_FN_OPENSOUNDTRAK lib_controlfreq LIB_FN_CONTROLFREQ lib_loadfontset0 LIB_FN_LOADFONTSET0 lib_makecharset LIB_FN_MAKECHARSET - ちょっと説明を要するもの
- lib_openwindow1 (LIB_FN_OPENWINDOW1)
- slotのところに1を足しておいてください(たとえばVP(0x200)の代わりに、VP(0x200+1)とする)。
- flagのところは256倍した上にさらに1を足してください(たとえばVP(0x29)の代わりに、VP(0x29 * 256 + 1)とする)。
- lib_putstring0, lib_putstring1 (LIB_PUTSTRING)
- 文字列の部分をそのままVP("test060")などとする場合は、optに0x4000を足してください(註:より正確にはORする)。これをVPS('t', 'e', 's', 't'), VPS('0', '6', '0', 0)とVPS展開する場合は、optに0x4000を足す必要はありません。
- 一般にVPS展開するほうがコンパクトになります。VPS展開の場合、4文字づつ書くことになりますが、切りが悪いときは後ろに0を並べておいてください。
- そしてVPSでないときは最後に0x000cを追加します。以下に例を挙げておくので参考にしてください。
- 変換前:
lib_putstring1(0x1000, 0, 0, VP(&wintitle), 0, 0, 0, 7, "test060");
- 変換例1:
VP(LIB_FN_PUTSTRING), VP(0x1000 + 0x4000), VP(0), VP(0), VP(&wintitle), VP(0), VP(0), VP(0), VP(7), VP("test060"), VP(0x000c),
- 変換例2:
VP(LIB_FN_PUTSTRING), VP(0x1000), VP(0), VP(0), VP(&wintitle), VP(0), VP(0), VP(0), VP(7), VPS('t', 'e', 's', 't'), VPS('0', '6', '0', 0),
- lib_openwindow1 (LIB_FN_OPENWINDOW1)
- lib_flushgraphbox (LIB_FN_FLUSHGRAPHBOX)
- 最後に0x000cを追加。
- lib_putblock1 (LIB_FN_PUTBLOCK1)
- 先頭にoptとして1を追加。最後に0x000cを追加。
- lib_drawpoints1 (LIB_FN_DRAWPOINTS1)
- 最後に0x000cを追加。
- lib_drawpoint0 (LIB_FN_DRAWPOINT0)
- xとyのあとに、またxとyを追加(実はdrawline0でごまかしているため)。
- lib_putblock02001 (LIB_FN_PUTBLOCK02001)
- 先頭にoptとして0x2001を追加。
- lib_putblock02001 (LIB_FN_PUTBLOCK02001)
- 先頭にoptとして0x2001を追加。
- lib_putblock02301 (LIB_FN_PUTBLOCK03001)
- 先頭にoptとして0x3001を追加。
- lib_drawpoints0 (LIB_FN_DRAWPOINTS0)
- 最後に0x000cを追加。
- lib_definesignal (LIB_FN_DEFINESIGNAL)
- 0p0の場合は、最後に0を2つ追加
- 1p0の場合は、signalの前に1を追加、signalの後ろに0を追加。
- lib_mapmodule (LIB_FN_MAPMODULE)
- addrの後ろに0x000cを追加、ofsとattrは足し算して一つに(VP(ofs + attr)みたいに)。
- lib_unmapmodule (LIB_FN_UNMAPMODULE)
- addrの後ろに0x000cを追加。
- lib_loadfontset (LIB_FN_LOADFONTSET)
- 最後に0x000cを追加。
- lib_wsjis2gg00jpn0 (LIB_FN_WSJIS2GG00JPN0)
- 先頭にoptとして1を追加、sjisとgg00jpnの後ろにそれぞれ0x000cを追加。
- lib_seuc2gg00 (LIB_FN_SEUC2GG00)
- 先頭にoptとして2を追加、seucとgg00のうしろにそれぞれ0x000cを追加。
- lib_drawlines0 (LIB_FN_DRAWLINES0)
- 最後に0x000cを追加。
- lib_convlines (LIB_FN_CONVLINES)
- 最後に0x000cを追加。
- lib_drawlines1 (LIB_FN_DRAWLINES1)
- 最後に0x000cを追加。
- lib_resizemodule (LIB_FN_RESIZEMODULE)
- sigの前に1を追加。
こめんと欄
- guigui00.hをインラインアセンブラ化するってのはどうかな……ってこりゃ最後の数バイトか -- I.Tak. 2004-05-06 (木) 08:24:17
Counter: 175,
today: 1,
yesterday: 0
初版日時: 2004-05-04 (火) 13:08:34
最終更新: 2009-11-17 (火) 00:00:00 (JST) (350d) by k-tan
|
ぺージ情報 | 閲覧可 | 編集可 | |||
---|---|---|---|---|---|---|
ぺージ名 : | gg00man/execcmd | グループ : | すべての訪問者 | グループ : | すべての訪問者 | |
ページ作成 : | k-tan | ユーザー : | すべての訪問者 | ユーザー : | すべての訪問者 | |
ページ別名 : | 未設定 |