ページへ戻る

+ Links

 印刷 

GUIGUI01​/memo27 :: OSASK計画

osaskwiki:GUIGUI01/memo27

ぐいぐい01に関するメモ-27 anchor.png

  • (by K, 2009.01.13)
  • メモのうち重要な部分をそのうちまとめてまともなページを作る
Page Top

(39) GOで「ぐいぐい01」アプリを作る(5) anchor.png


  • 今回の最初のプログラムは、いわゆるファイルダンプです。ファイルをオープンして、それを16進数でだだーっと書きます。ex0021.cです。
    #include <guigui01.h>
    
    unsigned char cmdusage[] = {
        0x86, 0x50,
        0x01, 'i', 'n', 0x0c, 9, 0x01, 'p', 'u', 't', '-', 'f', 'i', 'l', 'e',
        0x40
    };
    
    void sethex(char *s, int i, int n)
    {
        int j;
        for (j = n - 1; j >= 0; j--) {
            s[j] = '0' + (i & 0xf);
            i >>= 4;
            if (s[j] > '9') {
                s[j] += 'A' - '0' - 10;
            }
        }
        return;
    }
    
    void G01Main()
    {
        unsigned char buf[16], s[8];
        int i, j, k;
        g01_setcmdlin(cmdusage);
        g01_getcmdlin_fopen_s_0_4(0); /* 0はreadモード, 4はslot番号, 0は引数番号 */
        g01_putstr0(" offset   +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +A +B +C +D +E +F  0123456789ABCDEF\n");
        g01_putstr0("---------------------------------------------------------------------------\n");
        for (i = 0;; i += 0x10) {
            j = jg01_fread1(4, 16, buf);
            if (j == 0) {
                break;
            }
            sethex(s, i, 8);
            g01_putstr1(8, s); /* g01_putstr1()は文字数を指定できる。だからs[8]=0;がいらない。 */
            g01_putstr0("  ");
            for (k = 0; k < 16; k++) {
                if (k < j) {
                    sethex(s, buf[k], 2);
                    g01_putstr1(2, s);
                    g01_putc(' ');
                } else {
                    g01_putstr0("   ");
                }
            }
            g01_putc(' ');
            for (k = 0; k < 16; k++) {
                if (k < j) {
                    if (0x20 <= buf[k] && buf[k] <= 0x7e) {
                        g01_putc(buf[k]);
                    } else {
                        g01_putc('.');
                    }
                }
            }
            g01_putc('\n');
        }
    }
  • これをmakeすると375バイトになります。そして実行するとこんな感じです。
    >efg01 ex0021.g01
    usage>ex0021.g01 [in:]input-file
    
    >efg01 ex0021.g01 make.bat
     offset   +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +A +B +C +D +E +F  0123456789ABCDEF
    ---------------------------------------------------------------------------
    00000000  2E 2E 5C 7A 5F 74 6F 6F 6C 73 5C 6D 61 6B 65 2E  ..\z_tools\make.
    00000010  65 78 65 20 25 31 20 25 32 20 25 33 20 25 34 20  exe %1 %2 %3 %4
    00000020  25 35 20 25 36 20 25 37 20 25 38 20 25 39        %5 %6 %7 %8 %9
  • 他にも「>efg01 ex0021.g01 ex0021.g01」とか「>efg01 ex0021.g01 ex0021.org」とかやると結構おもしろいです。375バイトの割になかなか遊べます。
  • さて遊んでばかりではいけないので説明をします。最初はcmdusage[ ]ですが、 0x0c がファイルパス型です。ついでなのでまとめておきましょう。
    0x0cファイルパス型引数
    0x1c整数型引数
    0x2cフラグ型引数
    0x3c文字列型引数
  • 関数sethex()はsetdecを16進数用に改造しただけのものです。
  • それでG01Main()の g01_getcmdlin_fopen_s_0_4(0); で、引数番号0に書かれたファイルをリードモードでオープンさせ、スロット番号4に割り当てています。これは本来は、 g01_getcmdlin_fopen_s(0, 4, 0); と書くべきところなのです。しかしこの関数をguigui01.hに書いておくのをすっかり忘れていました。それで、 g01_getcmdlin_fopen_s_0_4() というのは、スロット番号4にリードモードでオープンするのがあまりによくあるケースなので、関数の引数で指定しなくてもいいことにしたいわば省略形です。
  • スロット番号というのはファイルハンドルの入れ物みたいなもので、ファイルのオープンに使う場合は4番から31番のどれかを使います。しかしまあ、たいていリードは4番で、ライトは5番でやります。どうしてかというと、 g01_getcmdlin_fopen_s_0_4() などの関数を使いたいからですね(サイズが節約できます)。
  • j = jg01_fread1(4, 16, buf); というのは、スロット番号4のファイルから最大16バイトまで読み込んでbufにしまいます。そして読み込めたバイト数がjに入ります。これが0ならファイル終端です。
  • freadのうしろの1が気になるかもしれませんが、「ぐいぐい01」のAPIでは、文字列などを指定するときに、末尾を0にして終端を表現する方法と、文字数を指定して終端を表現する方法の両方をサポートしている場合が多いです。で、ゼロ終端型の場合関数名には0が入ります。そして文字数型の場合は1が入ります。ex0023.cではfread0の例を示そうと思います。

  • ex0021は分かりやすさ重視で簡略表現を避けていましたが、今度はプログラムを短くするためのテクニックを全部使うことにしましょう(本当は?演算子を使うことでもっと短くできるんだけど、それはAPIじゃなくてC言語文法の話が必要になるので今回はパス)。
  • まずはやっぱりcmdusage[ ]です。実はusage内の説明部分で「file」という単語はとてもよく出てくるので、これを1バイトで表現するテクニックがあります( 0x02 )。それを使うとこうなります。
    unsigned char cmdusage[] = {
        0x86, 0x50,
        0x01, 'i', 'n', 0x0c, 6, 0x01, 'p', 'u', 't', '-', 0x02,
        0x40
    };
  • しかししかし。そもそも「in:input-file」という表現そのものが、本当によく出てくるのです。ということで、これ全体を0x88の1バイトだけで表していいことになっています。さらにもしこれを「[in:input-file]」にしたければ、0x89にすることになっています。ということで、今回はこんなに短くなります。
    unsigned char cmdusage[] = {
        0x86, 0x50,
        0x88,
        0x40
    };
  • 次はsethex()です。とりあえず'0'を足して、'9'より大きくなってしまったら'A'以降になるようになにやら補正していますが、実は「ぐいぐい01」ではこんなことをする必要がありません。文字コード0x10~0x1fがまさに「16進数表示用の文字」ということになっているので、以下のようなプログラムで全く同じ結果が得られます。
    void sethex(char *s, int i, int n)
    {
        int j;
        for (j = n - 1; j >= 0; j--) {
            s[j] = 0x10 + (i & 0xf);
            i >>= 4;
        }
        return;
    }
  • ただしこれは注意してほしいのですが、この文字コードが使えるのはg01_putc()やg01_putstr系で表示する場合だけです。ファイルに書き込む場合にはそれが文字コードなのかそれともバイナリファイルの一部なのか分からないので、0x10~0x1fを'0'~'F'と見なしていいのかどうかシステムが分からないのです(というか見なしてはいけないモードになっている)。ということで、モードを変更しない限り、ファイルに出力する場合はex0021のsethex()を使う必要があります。
  • 最後はこれです。 j = jg01_fread1(4, 16, buf); これは j = jg01_fread1_4(16, buf); とできるので(そのほうが小さい)、これを使うことにします。
  • あとはこれです。
        g01_putstr0(" offset   +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +A +B +C +D +E +F  0123456789ABCDEF\n");
        g01_putstr0("---------------------------------------------------------------------------\n");
  • これを以下のように改造しました。
        g01_putstr0(" offset  ");
        for (k = 0x10; k < 16 + 0x10; k++) {
            g01_putstr0(" +");
            g01_putc(k);
        }
        g01_putstr0("  ");
        for (k = 0x10; k < 16 + 0x10; k++) {
            g01_putc(k);
        }
        g01_putstr0("\n---------------------------------------------------------------------------\n");
  • ということで、この変更を全部適用して(ex0022.c)makeすると337バイトになります。38バイト(1割以上)も減りました。ばんざい。
  • おっと忘れていました。もし存在しないファイル名などを指定したらどうなるのでしょうか?・・・こうなります。
    >efg01 ex0022.g01 hoge.txt
    File read open error: hoge.txt
  • これは現在ファイルオープンエラーを継続不能なエラーとしてシステム側で処理するモードになっているせいです。これを解除すればスロット番号4にオープン失敗のハンドルが格納されて処理を続行することもできます。でもたいていはデフォルトのままのほうが自分で面倒なエラー処理をしなくていいので、楽です。

  • おまけ
    • abcdw014以降では(IRCでのmiyasakaさんからの要望のおかげで)efg01に対して拡張子.g01を省略できるようになっています。さらに、こんなファイルを「e.bat」として作って c:\windows\ に入れておくともっと便利になります(efg01.exeの絶対パスについては各自自分の環境に合わせて書き直してください)。
      @c:\tolset\z_tools\efg01.exe %1 %2 %3 %4 %5 %6 %7 %8 %9
    • これを c:\windows\ に入れておく理由は、デフォルトでここにパスが通っているからです。パスさえ通っていればどこでもいいので、windowsディレクトリに自作のものを入れるのが不安な人は、好きなようにしてください。
    • さてこれをやっておけば、
      prompt>e nask
    • とやるだけでnaskのusageが出るようになります(z_toolsに入っているものについてはパスを書く必要がなくなる)。tolset以外の.g01アプリも
      prompt>e アプリのパス
    • で起動できるようになります。なかなか便利なので、興味があれば一度試してみてください。

  • 今度はテキストファイルをただ画面に出力するDOSのtypeコマンドみたいなアプリを作ってみます。最初は難しいことを全部避けて、極力簡単にしてみます。
  • ASCIIのみのテキストファイルしか絶対にin:に指定しないとして、しかも常に2MB未満のサイズだと仮定して問題ないのなら、ここまで単純化できます。ex0023.cです。
    #include <guigui01.h>
    
    unsigned char cmdusage[] = {
        0x86, 0x50,
        0x88,
        0x40
    };
    
    void G01Main()
    {
        char *b = g01_bss1a1; /* mallocを使わなくてもこれで2MBのバッファがもらえる */
        g01_setcmdlin(cmdusage);
        g01_getcmdlin_fopen_s_0_4(0);
        jg01_fread0_4(2 * 1024 * 1024, b); /* 最大2MB, 終端に0がつく */
        g01_putstr0(b);
        return;
    }
  • これをmakeすると91バイトになります。簡単すぎて信じられないかもしれませんがそれでもちゃんと機能します。
    >e ex0023
    usage>ex0023 [in:]input-file
    
    >e ex0023 make.bat
    ..\z_tools\make.exe %1 %2 %3 %4 %5 %6 %7 %8 %9
    
    >e ex0023 Makefile
    TARGET = ex0023
    MODE   = g01
    OBJS   = ex0023.obj
    
    INCPATH    = ../z_tools/guigui01/
    include ../z_tools/com_mak.txt
  • ただしこれではex0023.c自身は表示できません(まあ無理にやっても文字化けする程度で済みますが)。というのはコメント部分に日本語(=非ASCII)が入っているからです。ということで次はそれを何とかしましょう。

  • ということで作ってみたex0024.cです。
    #include <guigui01.h>
    
    unsigned char cmdusage[] = {
        0x86, 0x51,
        0x13, 's', 'j', 'i', 's', 0x20,
        0x88,
        0x40
    };
    
    void G01Main()
    {
        unsigned char *b = g01_bss1a1, *p, sjis = 0; 
        g01_setcmdlin(cmdusage);
        sjis = g01_getcmdlin_flag_o(0);
        g01_getcmdlin_fopen_s_0_4(1);
        jg01_fread0_4(2 * 1024 * 1024, b);
        for (p = b; *p != 0; p++) {
            if (*p < ' ' && *p != '\t' && *p != '\n' && *p != '\r') {
                *p = '.';
            }
            if (*p == 0x7f) {
                *p = '.';
            }
            if (*p >= 0x80) {
                if (sjis != 0 && !(0xa0 <= *p && *p <= 0xdf) && p[1] != 0) { /* SJIS全角 */
                    *p = '.';
                    p++;
                }
                *p = '.';
            }
        }
        g01_putstr0(b);
        return;
    }
  • これをmakeすると181バイトになります。これならちゃんと表示できます。
  • このプログラムで注意しておくべきところは、bで受け取った中身を書き換えているということです。旧OSASKではこのような行為はファイルの書き換えを意味していました(メモリマップトファイルだったので)。しかしjg01_fread系はファイルの内容を「メモリに読み込む」APIなので、書き換えても元のファイルの内容が変更されることはありません。変わるのはメモリの内容だけです(旧OSASKのmapmoduleはファイルにアクセスするために「メモリ空間に割り当てて」いた)。
Page Top

こめんと欄 anchor.png

  • ex0022が351バイトになって我ながら感動したので、後日ASKAかnaskで全部書いてみようと思いました(註:abcdw012での話です)。半分くらいにならないかなあ? -- K 2009-01-13 (火) 23:51:38
  • とりあえず200バイトを切ることは確認。 -- K 2009-01-14 (水) 00:53:13
  • できた。182バイト。半分には行かなかったけど、とりあえず満足。 -- K 2009-01-14 (水) 11:31:04
  • そしてabcdw013で168バイト達成。 -- K 2009-01-14 (水) 19:04:03
  • abcdw014向けの記述に修正。 -- K 2009-01-17 (土) 10:42:06

Last-modified: 2009-11-21 (土) 00:00:00 (JST) (319d) by k-tan