ページへ戻る

+ Links

 印刷 

gg00man​/execcmd :: OSASK計画

osaskwiki:gg00man編集/execcmd

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です。
  • さて結局のところ、アセンブラがC言語に対してやたらと有利なのは、lib_execcmdをじゃんじゃん使うからです(と僕は思う)。まあそりゃあもちろん出力されるコードの質も違いますが。しかしとにかくCでもlib_execcmdを活用すれば、その差は縮むのです。ということでここではlib_execcmdの活用を中心に説明します。
Page Top

原則

  • 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。
Page Top

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 /* 最後にこれを忘れずにね */
        };
    • こっちのほうがきれいかも?
Page Top

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関数でも使うなら、
          struct LIB_TEXTBOX *ptbox = VP(&textbox);
      などとstaticじゃない普通の変数にいったん代入して、execcmdのときはVP(&textbox)、普通のlib関数のときはptbox、というように使い分けるとさらにコンパクトにできることがあります。
Page Top

void *の配列への変換規則

  • 特に説明の必要がないほど簡単なもの
    lib_initLIB_FN_INITnon-malloc型
    lib_closeLIB_FN_CLOSE
    lib_waitsignalLIB_FN_WAITSIGNAL
    lib_waitsignaltimeLIB_FN_WAITSIGNALTIME
    lib_openwindowLIB_FN_OPENWINDOWnon-malloc型
    lib_closewindowLIB_FN_CLOSEWINDOW
    lib_opentextboxLIB_FN_OPENTEXTBOXnon-malloc型
    lib_closetextboxLIB_FN_CLOSETEXTBOX
    lib_opnegraphboxLIB_FN_OPENGRAPHBOXnon-malloc型
    lib_opnegraphbox2LIB_FN_OPENGRAPHBOX2non-malloc型
    lib_controlwindowLIB_FN_CONTROLWINDOW
    lib_drawlineLIB_FN_DRAWLINE
    lib_drawline0LIB_FN_DRAWLINE0
    lib_opensignalboxLIB_FN_OPENSIGNALBOXnon-malloc型
    lib_opentimerLIB_FN_OPENTIMER
    lib_closetimerLIB_FN_CLOSETIMER
    lib_settimerLIB_FN_SETTIMER
    lib_settimertimeLIB_FN_SETTIMERTIME
    lib_opensoundtrackLIB_FN_OPENSOUNDTRAK
    lib_controlfreqLIB_FN_CONTROLFREQ
    lib_loadfontset0LIB_FN_LOADFONTSET0
    lib_makecharsetLIB_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_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を追加。
Page Top

こめんと欄

  • guigui00.hをインラインアセンブラ化するってのはどうかな……ってこりゃ最後の数バイトか -- I.Tak. 2004-05-06 (木) 08:24:17

Last-modified: 2009-11-17 (火) 00:00:00 (JST) (319d) by k-tan