1: 2009-01-13 (火) 22:00:05 [6] | 2: 2009-01-14 (水) 00:53:13 [7] | ||
---|---|---|---|
Line 29: | Line 29: | ||
void G01Main() | void G01Main() | ||
{ | { | ||
- | unsigned char buf[16]; | + | unsigned char buf[16], s[8]; |
- | int i = 0, j; | + | int i, j, k; |
g01_setcmdlin(cmdusage); | 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(" offset +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +A +B +C +D +E +F 0123456789ABCDEF\n"); | ||
g01_putstr0("---------------------------------------------------------------------------\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'); | ||
+ | } | ||
} | } | ||
- | -ex0016では、cmdusage[ ]が合計18バイトにまで増えました。しかしこれは13バイトにまで減らせます。小さいことは「ぐいぐい01」ではとても重要なことなので、最初はそれを説明しようと思います。 | + | -これをmakeすると390バイトになります。そして実行するとこんな感じです。 |
- | -ex0016のcmdusage[ ]の中には、16進数の1の位に「c」が出てくる場所が5ヶ所あります。これは、gh4のコードで「次の1バイトを数値として解釈」させるために使っています。しかし、もし値が0~4の範囲であれば、わざわざこんなことしないで、「c」の位置にその値を直接書いてしまっていいのです。gh4とはそういうものなのです。 | + | >efg01 ex0021.g01 |
- | //ちなみに最初の 0x86, 0x5c, ?, の「c」の部分は4までしか置き換えられませんが、その他の「c」は5もOKです。 | + | usage>ex0021.g01 [in:]input-file |
- | -何を言っているのかよく分からないと思いますが、たとえば、 | + | |
- | 0x86, 0x5c, 0, | + | |
- | -の部分を考えましょう。これは、 | + | |
- | 0x86, 0x50, | + | |
- | -と短く書けるのです。これで1バイト節約できます。同じようにして、他の部分を全部整理するとこうなります。ex0017.cです。 | + | |
- | #include <guigui01.h> | + | |
- | unsigned char cmdusage[] = { | + | >efg01 ex0021.g01 make.bat |
- | 0x86, 0x50, | + | offset +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +A +B +C +D +E +F 0123456789ABCDEF |
- | 0x00, 's', 0x33, 's', 't', 'r', | + | --------------------------------------------------------------------------- |
- | 0x10, 'n', 0x11, '#', | + | 00000000 2E 2E 5C 7A 5F 74 6F 6F 6C 73 5C 6D 61 6B 65 2E ..\z_tools\make. |
- | 0x40 | + | 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 -noadc ex0021.g01 ex0021.g01」とかやると結構おもしろいです。390バイトの割になかなか遊べます。 | |
- | void G01Main() | + | -さて遊んでばかりではいけないので説明をします。最初はcmdusage[ ]ですが、 0x0c がファイルパス型です。ついでなのでまとめておきましょう。 |
- | { | + | |0x0c|ファイルパス型引数| |
- | (ex0015.cと同じ内容) | + | |0x1c|整数型引数| |
- | } | + | |0x2c|フラグ型引数| |
- | -これをmakeすると139バイトになります。でも動作はex0016と完全に同じです。 | + | |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の例を示そうと思います。 | ||
---- | ---- | ||
- | -じゃあもう一つ小さくする技を。ex0017では引数が「s:str」と「n:#」でしたが、これを「str:string」と「num:number」にしたいとしましょう。まあどうせ順序を覚えていれば「str:」や「num:」とタイプする必要はないですしね。それなら分かりやすいほうがいい、と考える人もいるでしょう。 | + | -ex0021は分かりやすさ重視で簡略表現を避けていましたが、今度はプログラムを短くするためのテクニックを全部使うことにしましょう(本当は?演算子を使うことでもっと短くできるんだけど、それはAPIじゃなくてC言語文法の話が必要になるので今回はパス)。 |
- | --そういえば今まで説明してきませんでしたが、引数名は半角で文字コード0x21~0x7eの範囲で(つまり数字はいいけどスペースはダメ)、「"」や「:」を含まないものにしなければいけません。というか記号類は避けたほうがいいです。確実に使っても問題がない記号を以下に挙げておきますが、とにかく記号なんて使うと、OSによっては(というかシェルによっては)、入力しにくくなること請け合いです(後半のものはできれば使わないで置いたほうがいいんじゃないかと思ったもの)。 | + | -まずはやっぱりcmdusage[ ]です。実はusage内の説明部分で「file」という単語はとてもよく出てくるので、これを1バイトで表現するテクニックがあります( 0x02 )。それを使うとこうなります。 |
- | !#'=~+-/@_\^` ;?*& | + | |
- | -ということでex0018.cです。 | + | |
- | #include <guigui01.h> | + | |
unsigned char cmdusage[] = { | unsigned char cmdusage[] = { | ||
0x86, 0x50, | 0x86, 0x50, | ||
- | 0x02, 's', 't', 'r', 0x34, 0x01, 'i', 'n', 'g', | + | 0x01, 'i', 'n', 0x0c, 6, 0x01, 'p', 'u', 't', '-', 0x02, |
- | 0x12, 'n', 'u', 'm', 0x11, 0x01, 'b', 'e', 'r', | + | |
0x40 | 0x40 | ||
}; | }; | ||
- | + | -しかししかし。そもそも「in:input-file」という表現そのものが、本当によく出てくるのです。ということで、これ全体を0x88の1バイトだけで表していいことになっています。さらにもしこれを「[in:input-file]」にしたければ、0x89にすることになっています。ということで、今回はこんなに短くなります。 | |
- | void G01Main() | + | |
- | { | + | |
- | (ex0015.cと同じ内容) | + | |
- | } | + | |
- | -ここでまたサイズを節約するテクニックを使いました。というのは引数説明のところで、0x01というのを使っているからです。これはusage表示するときに引数名に置換されます。これを使うことで 0x3c や 0x1c を使わずに済んでいますし、そもそも置換することでそれぞれ2バイト節約しています。 | + | |
- | -makeすると147バイトになります。一応usage表示を見て確認しておきましょう。 | + | |
- | >efg01 ex0018.g01 | + | |
- | usage>ex0018.g01 [str:]string [[num:]number] | + | |
- | ---- | + | |
- | -こうしてコマンドラインにたくさん引数を使うようになると、usage表示がかなり長くなることがあります。「usageなんかみないぜ!そんなの気にしない」っていう人はそれで問題ないんですが、やっぱりどうせならきれいにusage表示してほしいって思う人もいるでしょう。ここではその方法を説明したいと思います。 | + | |
- | -ということでex0019.cです。 | + | |
- | #include <guigui01.h> | + | |
unsigned char cmdusage[] = { | unsigned char cmdusage[] = { | ||
0x86, 0x50, | 0x86, 0x50, | ||
- | 0x02, 's', 't', 'r', 0x34, 0x01, 'i', 'n', 'g', | + | 0x88, |
- | 0x87, | + | |
- | 0x12, 'n', 'u', 'm', 0x11, 0x01, 'b', 'e', 'r', | + | |
0x40 | 0x40 | ||
}; | }; | ||
- | + | -次はsethex()です。なにやらとりあえず'0'を足して、'9'より大きくなってしまったら'A'以降になるようになにやら補正していますが、実は「ぐいぐい01」ではこんなことをする必要がありません。文字コード0x10~0x1fがまさに「16進数表示用の文字」ということになっているので、以下のようなプログラムで全く同じ結果が得られます。 | |
- | void G01Main() | + | void sethex(char *s, int i, int n) |
{ | { | ||
- | (ex0015.cと同じ内容) | + | int j; |
- | } | + | for (j = n - 1; j >= 0; j--) { |
- | -これをmakeすると148バイトになりますが、注目ポイントは 0x87, です。これがあると、usage表示のときにここで改行します。それ以外の効果はありません。この改造をしてももちろん「num:」は引数番号1のままです(2になったりはしない)。 | + | s[j] = 0x10 + (i & 0xf); |
- | -これをusage表示させるとこうなります。 | + | i >>= 4; |
- | >efg01 ex0019.g01 | + | |
- | usage>ex0019.g01 [str:]string | + | |
- | [[num:]number] | + | |
- | ---- | + | |
- | -さてここまでで整数型引数と文字列型引数を紹介してきましたが、次はフラグ型引数を紹介します。これは値を指定するのではなく、単にその引数名が現れたかどうかだけを問題にします。・・・どんな例にしましょうか。じゃあたとえば0からnまで数えるプログラムを使いましょう。ex0020.cです。 | + | |
- | #include <guigui01.h> | + | |
- | + | ||
- | unsigned char cmdusage[] = { | + | |
- | 0x86, 0x51, /* フラグ型は自動当てはめ対象からははずすようにする */ | + | |
- | 0x13, 'd', 'o', 'w', 'n', 0x20, | + | |
- | /* フラグ型は当然省略可能に。そしてフラグ型には説明を付けない。 */ | + | |
- | 0x00, 'n', 0x11, '#', | + | |
- | 0x40 | + | |
- | }; | + | |
- | + | ||
- | void setdec(char *s, int i, int n) | + | |
- | { | + | |
- | (ex0010.cと同じ内容) | + | |
- | } | + | |
- | + | ||
- | void G01Main() | + | |
- | { | + | |
- | int i, n; | + | |
- | char s[4]; | + | |
- | g01_setcmdlin(cmdusage); | + | |
- | n = g01_getcmdlin_int_s(1); | + | |
- | if (g01_getcmdlin_flag_o(0) == 0) { | + | |
- | /* down指定がない場合 */ | + | |
- | for (i = 0; i <= n; i++) { | + | |
- | setdec(s, i, 3); | + | |
- | s[3] = 0; | + | |
- | g01_putstr0(s); | + | |
- | } | + | |
- | } else { | + | |
- | /* down指定がある場合 */ | + | |
- | for (i = n; i >= 0; i--) { | + | |
- | setdec(s, i, 3); | + | |
- | s[3] = 0; | + | |
- | g01_putstr0(s); | + | |
- | } | + | |
} | } | ||
return; | return; | ||
} | } | ||
- | -これをmakeすると265バイトになります。そしてusageを出してみると、こうなります。 | + | -ただしこれは注意してほしいのですが、この文字コードが使えるのはg01_putc()やg01_putstr系で表示する場合だけです。ファイルに書き込む場合にはそれが文字コードなのかそれともバイナリファイルの一部なのか分からないので、0x10~0x1fを'0'~'F'と見なしていいのかどうかシステムが分からないのです(というか見なしてはいけないモードになっている)。ということで、モードを変更しない限り、ファイルに出力する場合はex0021のsethex()を使う必要があります。 |
- | >efg01 ex0020.g01 | + | -最後はこれです。 j = jg01_fread1(4, 16, buf); これは j = jg01_fread1_4(16, buf); とできるので(そのほうが小さい)、これを使うことにします。 |
- | usage>ex0020.g01 [down] [n:]# | + | -あとはこれです。 |
- | -適当に実行してみるとこんな感じです。 | + | g01_putstr0(" offset +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +A +B +C +D +E +F 0123456789ABCDEF\n"); |
- | >efg01 ex0020.g01 10 | + | g01_putstr0("---------------------------------------------------------------------------\n"); |
- | 0 1 2 3 4 5 6 7 8 9 10 | + | -これを以下のように改造しました。 |
+ | 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すると351バイトになります。39バイト(10%)も減りました。ばんざい。 | ||
+ | -おっと忘れていました。もし存在しないファイル名などを指定したらどうなるのでしょうか?・・・こうなります。 | ||
+ | >efg01 ex0022.g01 hoge.txt | ||
+ | File read open error: hoge.txt | ||
+ | -これは現在ファイルオープンエラーを継続不能なエラーとしてシステム側で処理するモードになっているせいです。これを解除すればスロット番号4にオープン失敗のハンドルが格納されて処理を続行することもできます。でもたいていはデフォルトのままのほうが自分で面倒なエラー処理をしなくていいので、楽です。 | ||
+ | ---- | ||
+ | -(書き途中) | ||
- | >efg01 ex0020.g01 7 down | ||
- | 7 6 5 4 3 2 1 0 | ||
- | -プログラムから推測できるように、 0x2c がフラグ型になります。しかしフラグ型では説明は付けないので、 0x20 を使うことになります。フラグ型を指定するときは、「down:」のように末尾に「:」があっても構いません。 | ||
- | >efg01 ex0020.g01 7 down: | ||
- | 7 6 5 4 3 2 1 0 | ||
- | -フラグがコマンドライン中に存在していれば、g01_getcmdlin_flag_o()の値が0以外になります。存在しなければ0です。 | ||
* こめんと欄 | * こめんと欄 | ||
- | - このシリーズは好評のようです。実際にアプリを作って遊んでくれる人も出てきました。 -- [[K]] &new{2009-01-13 (火) 11:07:16}; | + | - ex0022が351バイトになって我ながら感動したので、後日ASKAかnaskで全部書いてみようと思いました。半分くらいにならないかなあ? -- [[K]] &new{2009-01-13 (火) 23:51:38}; |
+ | - とりあえず200バイトを切ることは確認。 -- ''K'' &new{2009-01-14 (水) 00:53:13}; | ||
#comment | #comment |
(This host) = http://osask.net