スイッチー \ はルート (SHELL=/P 指定 ) の COMMAND.COM の環境変数 ( デフォル トは親プロセスの環境変数 ) を変更します。 スイッチは環境変数の手前に指定してく ださい。編集て、きる環境変数は 環境変数名 = 定義 の合計て、 254 文字まて、て、す。また , コマンド ラインの環境変数の指定に = を含むときは , 編集モードに入らずに直接設定します。 のときは環境変数領域いつばいまて、設定て、 きます。 envset には以下のようなモードがあ ります。 ( 1 ) envset [ ー \ ] 環境変数を表示します。スイッチー \ を指 定するとルートの COMMAND ℃ OM の環境 変数を , 指定しなければ親プロセスの環境 変数を表示します ( スイッチー \ に関しては以 ド同様 ) 。スイッチー \ を指定しないときは SET コマンドと同じ機能になります。 ( 2 ) envset [ ー \ ] envnam=define 環境変数 envnam を define に設定します。 スイッチー \ を指定しないときは SET コマン ドと同じ機能になります。 ( 3 ) envset [ ー \ ] envnam 十 =define 環境変数 en Ⅷ am の最後に define を追加し ます。コマンドラインから指定するため , Fig. 7 ルートの COMMAND.COM 以外の環境変数領域 ( MS ー DOS 3.2 ) 低位アドレス 个 MCBI MCBn MCBx MCBy 高位アドレズ ロ t2Ch—2Dh] ロ [ 16h ~ 17h ] M PSPy SIZy M PSPy SlZx M PSPm SlZn [2Ch—2Dh] ロ [ 16h ~ 17h ] MCBm M pspm SlZm M PSPm SIZI ー 0 1 2 3 4 環境変数領域その 1 ( ダミー ) PSPm COMMAND.COM の PSP COMMAND.COM 環境変数領域その 2 (MS-DOS 3.2 以降 ) 子プロセスの環境変数領域 PSPm 1 回て、は 128 バイトという制限があります 動作がおかしくなるものもあるようて、す。 ョンによっては PATH が 128 バイト以上だと することがて、きます。なお , アプリケーシ 環境変数領域いつばいまて、環境変数を設定 が , この機能を繰り返し用いることにより , 注意してください コラム COMMAND.COM のスイッチ / E : MS-DOS 3.1 以降では , 起動時にスイツ チ /E : で環境変数領域の大きさを指定する ことができます。 CONFIG. SYS で SHELL.COM/AND.COM /E : 512 /P というように指定します。 / E : で指定する数 値は MS-DOS 3.1 ではバラグラフ単位でし たが , MS-DOS 3.2 以降ではバイト単位に 変更されています。 MS-DOS 3.1 の場合 , スイッチ / E : は子 プロセスとして起動する COMMAND.COM で は無効で , COMMAND /E : 512 /C name 92 C MAGAZINE 1991 8 などのように起動しても , 環境変数領域の 大きさを変えることはできませんが , MS- DOS 3.2 以降では可能になりました。 MS-DOS 3.2 以降で環境変数の仕様が変 更されたのは , 子プロセスとして COMMAND. COM を起動するときに , /E : で環境変数領 域の大きさを変更できるようにするためだ と思われます。親プロセスから渡された環 境変数領域は拡張することはできませんが , プログラム本体以降に作成した環境変数領 域は容易に拡張することができます。 例 ) envset path 十 = ; A : *USR ( 4 ) envset [ ー \ ] envnam— =define 環境変数 envnam から define の部分を除去 します。 例 ) envset path— ; A : *USR ( 5 ) envset [ ー \ ] envnam 環境変数 envnam を編集するモードに入り ます。入力は cgets 関数を使用しているた め , 環境変数の編集中はテンプレート機能 が使用て、きます。編集て、きる環境変数は envnam=define の合計て、 254 文字まて、て、す ocgets 関数は DOS ファンクション 0Ah ( Table 4 ) を呼び出して おり , 254 文字の制限は cgets 関数 ( DOS ファ ンクション 0Ah ) の制限て、す。 ( 6 ) envset -h 簡単なヘルプメッセージを表示します。 環境変数工テイタの説明 本ューティリティて、も get_envseg 関数が ポイントとなります。この関数さえ理解す れば , 子プロセスの PSP 子プロセス
語具 用 応 もに , MS ー C コンパイラのディレクトリ < MS C> ドの INCLUDEi' ィレクトリの中に config. h として右の内容のファイルを入れて おく必要がある。 CL /AL /Od /W3 /Zi /J /c 関数 . c ( ラージメモリモード /CodeView を使用 / 警告レベルは 3/char を unsigned char に する / リンクは行わずコンパイルのみ ) bkdraw2( ) 関数 ( CM910801 ) Number (MS-C 用 config. h) #define MSC #ifndef MSDOS #define MSDOS 参考文献 [ 1 ] 豊国永健著『 C 文解釈』 ( 翔泳社 ) [2]WiIIiam James Hunt 著 fThe C Tool box 』 (Addison-WesIey Publishing Com pany) #endif #ifndef PROTOTYPE #define PROTOTYPE #endif List 2 List 1 3 : 4 : #include ” config. h" 5 : 6 : #define EXTERN extern 7 : 8 : #include く stdio. h> 9 : #include く stdlib. h> 10 : #include く string. h> 1 1 : #include く ctype. h> 13 : #include ” fkey. h" 14 : #include menurd. h ” 16 : #ifdef PROTOTYPE 17 : #include "cboxprot. h" 18 : #endif 20 : bkdraw2(moj i) 21 : Char *moji : 22 : { 23 : char *tokenC40] : 24 : char kugiri[10]; 25 : 26 : i nt X, y 27 : char col 28 : char att 29 : int ura; 30 : unsigned char str[256] : 31 : char attrib 32 : strcpy(kugiri, 33 : 34 : 35 : token[i] 36 : strtok(moji,kugiri) : while( token[i] ! ニ NU しい { 38 : tOken [ + + i ] ニ strtok (NULL, kugiri) : 39 : 40 : atoi (token[l]) : atoi (token[2] ) : 41 : 42 : strcpy( str,token[3]) : 43 : (char) atoi (token[4]) : col (char)atoi (token[5]) : 44 : att 45 : ( (col くく 5 ) ー att ) : a t t r i b 46 : atoi (token [ 6 ]. ) : ura printv(str. attrib,x,y, ura) : return(0) : 48 : 49 : 50 : } 1 1 : # i ncl ude く ctype. h> 13 : #include "fkey. h" 14 : # i ncl ude ” pldwn. h" 15 : #include ” menurd. h ” 16 : #include "window. h ” 17 : # i nc lud e winvar. h ” 18 : 19 : #ifdef PROTOTYPE 20 : #include "cboxprot. h" 21 : #endif 22 : 23 : inpdraw2(moji , fp, cmd. arg) 24 : Char *moji : 25 : FILE *fp; 26 : Char *cmd : 27 : Char *arg : 28 : { 29 : char *token [ 40 ] ; 30 : char kugiri[IO]: i i , n , wn : 32 : int initl=1,linenum=0; 33 : int ret : 34 : strcpy(kugiri, 35 : 36 : while ( (fgets(moji,MOJILEN,fp)) ! = NU しい { 38 : if(moji [O]!=SUBLINE){ 39 : 40 : continue; switch(tolower(moji [ 1 ] ) ) { 42 : 43 : case C 44 : token[i] 45 : strtok(moji. kugiri) : while( token[i] 46 : ! = NU しい { token[ + + i] 47 : = strtok()U しし kugiri); 48 : 49 : 50 : 52 : 53 : 54 : 55 : 58 : 59 : 60 : 62 : 63 : 64 : 65 : 66 : 68 : 69 : 76 : Copyr ight 1991 E. Toyokuni CM91 W n Gwbase[wn]. col Gwbase[wn] . rcol break; token[i] : strtok(moji,kugiri) : while( token[i] ! = NU しい { token[ + + i] = strtok()U しし kugiri) : atoi (token[l]) : (char)atoi (token [ 2 ] ) : (char)atoi (tokenC3]) : atoi (token[l]) : atoi (token [ 2 ] ) : W n Gwinmn[wn] [n] . x atoi (token[3]) : G. winmn[wn] [n] . y atoi (token[4]) : strcpy (Gwinmn [wn] (n) . def , token[5]) : GwinmnCwn] [n].len atoi (token[6]) : Gwinnn[wn] [n] . style atoi (token[7]) : if(Gwbase[wn].In く n + 1 ) { GwbaseCwn].ln n + 1 b reak : case •e return(ret) : inpdraw2( ) 関数 ( CM910802 ) List 2 3 : 4 : #include ” config. h" 5 : 6 : #define EXTERN extern 7 : 8 : #include く std i 0. h > 9 : #include く stdlib. h> 10 : #include く string. h> Number Copyr ight 1991 E. Toyokuni CM91 return((int) NU しい : 応用 C 言語 111
8 クイーン ( 最初のひとつの解を表示 ) List 1 30 : { 41 : } 45 : { 58 : } 63 : { 1 : 2 : 3 : 4 : 5 : 7 : 8 : 13 : 14 : 16 : 17 : 19 : 20 : 22 : 23 : 25 : 26 : 27 : 31 : 32 : 33 : 34 : 35 : 36 : 38 : 39 : 40 : 42 : 44 : 46 : 48 : 49 : 50 : 51 : 52 : 53 : 54 : 55 : 56 : 59 : 60 : 62 : 64 : 65 : 66 : 68 : 69 : 71 : 6 : #define SUCCESS 1 #define FAI し 9 : #define FREE 10 : #define NOT_FREE 0 12 : # def i ne N #include く stdio. h> #include く stdlib. h> #include く string. h> 0 1 8 / * 成功 * / / * 失敗 * / / * この場所は利き筋になっていない ( 置ける ) * / / * この場所は利き筋になっている ( 置けない ) * / / * クイーンの数 * / 15 : / * 各行に置かれたクイーンの位置 * / int pos[N] : 18 : / * クイーンが垂直方向に利いているかを示す配列 * / i nt col CN] ; 21 : / * クイーンが右斜め下向きに利いているかを示す配列 * / int downC2 * N 24 : / * クイーンが右斜め上向きに利いているかを示す配列 * / i nt up [ 2 * N 28 : / * クイーンの位置。利き筋を初期化する * / 29 : void init-board() int for for for fo r i く N : i 十 + ) i く N : i + + ) = FREE; i く 2 * N ニ FREE; i く 2 * N = FREE; up[i] ( i ニ 0 : down[i] ( i = 0 : coICi] ( i = 0 : pos [ i ] i 十十 ) i 十十 ) 43 : / * クイーンの位置を出力する * / VOid print-queens() i nt i , j : printf("Yn") ; printf("Yn"); printf(". e I se printf()Q " ) : if (pos[i] for (j = 0 : j く N : j + + ) for ( i = 0 : i く N : i + + ) 61 : / * a 行目以降すべての行にクイーンを置いてみる * / int try(int a) i nt b : = FREE & & up[a + b] = FREE & & if (colCb] / * a 行目の b 番目に置けるかどうか調べる * / for (b = 0 ; b く N : b + + ) { / * 左から右に向かって順番にクイーンが置けるかどうかを調べる * / アルゴリズムアータ構造入門 一定になっています。たとえば , ( 4 , 1 ) を て、は縦方向の位置 a と横方向の位置 b の和が します ( Fig. 6 ) 。また , ここては各利き筋上 右斜め上向きの利き筋も , やはり 15 本存在 同様な考え方を適用することがて、きます。 また , 右斜め上向きの利き筋についても ばなりません。 として , 利き筋になったことを示さなけれ down [a—b 十 (N—I)]==NOT FREE (), b) にクイーンを配置するときには , き筋になっていないことになります。また , て、あれば , 位置 ( a , b ) は右斜め下向きの利 down [a—b 十 (N—I)]==FREE 配列 down を参照して , となります。 2 ー 4 十 ( 8 ー 1 ) = 5 ます。たとえば , ( 2 , 4 ) を通る利き筋は , きさて、す。 8 クイーンの場合には N = 8 となり っていることに注意 ) 。ここて , N は盤の大 となります ( 座標は , ( 行位置 , 列位置 ) とな a ー b 十 ( N ー 1 ) こて、 , 座標 ( a , b ) を通る利き筋の番号は , 示すように 0 から 14 まて、の番号をふります。 るかどうかを表すために , 利き筋に Fig. 5 に これらの利き筋にクイーンが置かれてい す。 筋上て、は , つねに a ー b = = 1 が成り立ちま ます。たとえば Fig. 5 て、 , ( 1 , 0 ) を通る利き 置 a と横方向の位置 b の差が一定になってい 本あります。各利き筋上ては , 縦方向の位 ように , 右斜め下向きの利き筋は全部て 15 について考察しましよう。 Fig. 5 からわかる 話は複雑て、す。まず右斜め下向きの利き筋 斜め方向の利き筋については , ちょっと れていることを意味します。 のとき , x 列目にはすてにクイーンが配置さ を表します。つまり coICx]==NOT FREE C01 は垂直方向に利き筋が利いていること す NOT FREE のいずれかをセットします。 っている ( = 置くことはて、きない ) ことを表 アルゴリズムとデータ構造入門 ちます。 通る利き筋上ては , 常に a 十 b = = 5 が成り立 85
す ) 。日本やアメリカて、は不要て、すし , かえっ 記述て、きません ( MS ー C て、はサポートされて と思われます。 て邪魔になる場合も少なくありませんから , いる仕様て、す ) 。 LSIC て、は通常の関数は引 List 6 は Ver. 3.3 て、修正されたバグて、 , サポートしないほうがよいかもしれません 数がレジスタ渡しとなっていますが , 可変 Ver. 3.20 て、はレジスタ変数に & をつけてア (GCC もデフォルトて、はトライグラフシーケ 引数の関数は引数がスタック経由て、渡され ドレスを取り出すと , 警告も出さずに register ンスを抑制しています ) 。 日言を無視したコードを生成していました るため , このような仕様になっているもの なお , ANSIC に完全に準拠していない Fig. 2 プロファイル情報を作成する手順 との意志表示として , マクロ STDC が スイッチー p を指定する E:*>lcc -0 -0 sieve. c 定義されていません。 cpp-DLSI-C- 旧 :/LS ℃ /INCLUDE -j ー 0 E:*I . $ $ $ sieve. c 生成コードか ip86 E : \ 3. $ $ $ E : \ 2. $ $ $ sieve. c 向上する例 「 86 ー 0 sieve. obj -m sieve. c E:*2 $ $ $ link @link.i ANSI C への準拠度はよくなりましたが , Mic 「 0S0升 (R) Segmented-Executable Linker Version 5.10 Copyright (C) Microsoft CO 「 p 1984 ー 1990. AII rights reserved. もっとも気になる生成コードは , 最初に述 Object Modules [. OBJ] /st:Ox2800/cp: 0X1000 / noi B :*LS ℃ *L 旧 *s*cpro. obj 十 べたとおり , ほとんど変わっていません。 Object ModuIes [. OBJ] sieve. Obj その中て、 , 次の 2 点については向上している Run File [E:cpro. exe] cdOS. 0bj の代わりに cp 「 0. Obj をリンク Sleve List FiIe [NUL. MA 円 ことに気づきました。 Libra 「 ies [. L 旧 ] B :*LSIC*L 旧 *s*knjlib 十 Libraries [. L 旧 ] B:*LSIC*L 旧 *s*doslib; ①ビットフィールドの生成コードがよくな E:*>sieve った (List 3 参照。 List 3 のアセンプリリ 100 iterations 1899 primes スト ()3 asm. txt) は付録ディスク収録 ) 。 ② #pragma optimize space を指定していな E:*>prof sieve. c >p いときには , グローバル変数や static 変数 をワード境界に割り付ける ( 付録ディスク 収録 13 asm. txt て、変数の直前に EVEN が 挿入されていることに注意 ) 。 ただし , 構造体は今まて、どおりパックさ れ , ワード境界に調整されることはありま せん。開始位置がワード境界に割り付けら れるだけて、す。この変更て、実行速度が気持 だけ向上するてしよう。 ノおよび仕様に関して Ver. 3.20 て、気づいていた不具合を調べて みました。 List 4 は① near 関数を far ポイン タに代入すると CS レジスタて、はなく , DS レ ジスタをセットする , ② signedchar の変数 cs を cs&0xff としても符号拡張される ( 編集 部注 : このバグは製品版て、はフィクスされ ています ) , という不具合て、す (List 4 のアセ ンプリリスト ( 14 asm. txt) は付録ディスク収 録 ) 。 バグて、はないのて、すが , 可変引数の関数 はプロトタイプと関数定義を同一ソースに ←プロファイラ用のツール ↑ プロファイル情報の作成 工ラトステネスのふるい ( sieve. c ) のプロファイル情報 凵 st / * Eratosthenes S ieve prime Number program * / #define TRUE 1 #define FALSE 0 #define SIZE 8 190 #define ITER 100 # i ncl ude く s td i 0. h 〉 char flags[SIZE + 1 ] : 1 ma i n ( ) 1 1 1 1 100 100 100 819100 189900 189900 1499900 1499900 189900 8 1 9 100 819100 100 1 int i, prime, k, count, iter; printf ("%d iterationsYn",lTER) : for (iter i ter く = ITER; iter + + ) { count for (i 0 : i く = 引 ZE : i + + ) ロ ags [ i ] for ( i = 0 : i く = S I ZE ; i + + ) { i f ( 日 ags [ i ] ) { p r ー me for (k i + prime; k く = SIZE; k + = prime){ flags Ck] ニ FA し SE; / * primes found * / = TRUE; ー第ーブをー当亂を亂亂ーーブをーをーを亂ー count 十十 : printf ("%d primesYn" を count) : / * primes found on 10th pass * / 42 C MAGAZINE 1991 8
TabIe 1 配列要素の配列とポインタによる表現 Table2 配列要素のアドレスの配列とポインタによる表現 &x [ 0 ] &p [ 0 ] p x [ 0 ] p [ 0 ] &x [ 1 ] x 十 1 p [ 1 ] &PCI] x [ 1 ] &x [ 2 ] x 十 2 &p [ 2 ] x [ 2 ] p [ 2 ] &x [ 9 ] x 十 9 &p [ 9 ] p 十 9 p [ 9 ] 50 点の学生の学籍番号を求める ( 先月号より ) と配列 x の先頭要素へのポインタを代入する と , p は x [ 0 ] を指すことになり , *p は x[0] の工イリアスとなります (Fig. 9 ) 。 C 言語て、はポインタ変数 p に対して , p 十 i は p の指すオプジェクトの i 個後ろの要素を指 し , p ー i は i 個前の要素を指すことになって います。たとえば p 十 2 は x [ 2 ] を指しま す。ポインタに * を適用すると , そのポイ Fig. 9 配列とポインタ x [ 9 ] LiSt 1 : #include く stdio. h> 2 : 3 : #define AMAX / * A 組の人数 * / 6 4 : #define BMAX / * B 組の人数 * / 7 5 : #define / * 探索失敗 * / FAI し ED 6 : ー ten 点の学生の学籍番号を求める関数 8 : int search(int mark ロ , int max, int ten) 12 : 14 : 17 : / * ー メイン 18 : int main(void) 19 : { 20 : 22 : 23 : 24 : 25 : 26 : 28 : 29 : 30 : 32 : 33 : 34 : 35 : 36 : 38 : 39 : 40 : 42 : 43 : 44 : } int for ( i 0 : i く max : i 十十 ) if (mark[i] = ten) break : return (i = max ? FA I LED ・ 町 0 x[l] x[2] x[8] x[9] int int int 1 , no : atensu [AMAX] : / * A 組の点数 * / btensu [BMAX] : / * B 組の点数 * / puts ( " A 組の点数を入力して下さい " ) ; for (i = 0 : i く AMAX; i + + ) / * AMAX 人の点数を読み込む * / scanf("Xd", &atensu[i]) : puts ( ” B 組の点数を入力して下さい " ) : for (i = 0 : i く BMAX; i + + ) / * BMAX 人の点数を読み込む * / scanf("Xd", &btensu[i]) : no = search(atensu, AMAX, 5 の ; if (no = FAILED) puts ( " A 組に 50 点の学生はいません” ) : else printf ( " A 組の 50 点の学生の学籍番号は % d です \ n " , no + 1 ) : no = search(btensu, BMAX, 5 の : if ()o ー = FAILED) puts ( " B 組に 50 点の学生はいません " ) : e I se printf ( " B 組の 50 点の学生の学籍番号は % d です \ n " , no + 1 ) : return ( の : 配列とポインタの表記法 * ( x 十 2 ) のカッコの中は足し算ですね。 足し算はオペランドの順番を交換しても よいはすです。したがって , * ( 2 十 x ) と も書けます。この形式のオペランドの順 番が可換であるのならば , x [ 2 ] も 2 [x] と書けるはずです。実際にこのように記 述することもできます。 [ ] は演算子で あり , 一方のオペランドが「ある型 T への ポインタ型」 , もう一方のオペランドが「整 数」であれば順序は問われません。本文で は四つの記述が同じ意味であると説明し ましたが , 実は次の八つが同じ意味とな ります。 x [ 2 ] 2[x] * ( x 十 2 ) * ( 2 十 x ) p[2] 2 [p] * ( p 十 2 ) * ( 2 十 p ) なお [ ] が配列添え字演算子と呼ばれる 演算子の一種であるということは , 5 月号 で説明しています。 ンタの指すオプジェクトの実体を表します。 とになり , さらに * ( x 十 2 ) は x [ 2 ] となり つまり * ( p 十 2 ) は x [ 2 ] のこととなりま ます。 す。また * ( p 十 2 ) を p [ 2 ] と書いてよいとい 話がややこしくなりましたのて , まとめ う決まりもあります。 ましよう。ポインタ p が配列 x の先頭要素 x [ 0 ] さらに C 言語て、は配列 x に対して , 単独に を指すとき , x [ 2 ] は * (x + 2 ) とも p [ 2 ] と 現れた x ( x [i] て、はなく x という意味 ) を先 も * ( p 十 2 ) とも表現て、きるわけて、す。すな 頭要素へのポインタとみなします ( 文脈によ わち TabIe 1 に示すように , 同じものを表す っては例外もあります ) 。 x がポインタとみ のに 4 通りの記述がて、きます。 なされるのて、すから , たとえば x 十 2 は x [ 0 ] また , 各要素のアドレス ( 各要素へのポイ の 2 個後ろの要素 , すなわち x [ 2 ] を指すこ ンタ ) に対しても同様に TabIe 2 のように 4 通 132 C MAGAZINE 1991 8
1 : 2 : 3 : 4 : 5 : 6 : 7 : 8 : 10 : 1 1 : 13 : 14 : 15 : 16 : 19 : 20 : 22 : 23 : 24 : 25 : 26 : 28 : 29 : 30 : 32 : 33 : 34 : 35 : 36 : 37 : 38 : 39 : 40 : 42 : 43 : 44 : 45 : 46 : 49 : 50 : 52 : 53 : 54 : 55 : 56 : 58 : 59 : 60 : 62 : 64 : 65 : 66 : #define EXTERN extern # include #include #include # include # include #include 9 : # i ncl ude #include #include 12 : # i ncl ud e #include inpmenu( ) 関数 ( CM910803 ) List 3 22 : { 43 : } List 4 く stdio. h> く stdlib. h> く string. h> く dos. h 〉 menurd. h ” ” window. h ” winvar. h ” く pldwn. h> く fkey2. h> く input. h> く inputvar. h 〉 inpmenu (wn) i nt wn ; 18 : char moji [ 20 ] [ 20 ] : unsigned long key int i,n,len; int xl,yl; len ニ 15 ・ ニ 0 : / * 挿入モード for ( i : 0 : i く n : i + + ) { n Gwbase[wn] .ln : CURON; yl= Gwin[wn]. yl + 1: xl= Gwin[wn]. xl + 1: tlwfresh(&Gwin[wn], ,Gwinmn[wn][i]. def) ; printvs(Gwinmn[wn][i] . x + xI,Gwinmn[wn][i]. while(!(key & CR)){ 15 : 20 : 21 : 25 : 26 : 28 : 29 : 30 : 31 : 32 : 33 : 34 : 35 : 36 : 38 : 39 : 40 : 41 : 42 : 43 : 44 : 46 : 48 : 49 : 50 : 17 : # i ncl ud e "cboxprot. h" 16 : #ifdef PROTOTYPE int kind,ura; char attrib char att 27 : char col int xl , yl , x2. y2 ・ i nt i ; 24 : char kugiri[10]; 23 : char * toke 矼 40 ] : Char *moji; keidraw2(moji) 18 : #endif strcpy(kugiri' ニ strtok(NUL し . kugiri); tOken [ 十十 i ] while( token[i] ! ニ NU しい { = strtok(moj i, kugi (i) : token[i] x2 col att : atoi(token[l]) : = ato i (token[2] ) : = ato i (token[3]) : = atoi(token[4]) : (char)atoi (tokenC5]) : (char)atoi (token[6]) : y 十 yl superin3(Gwinmn[wn] [i] . x + xl,GwinmnCwn] (i) . Y + Y1,Gwinmn[wn] [i]. def. (Gwbase[wn]. col くく 5),NONSEC. &key,CDOWNlCU 円 ESCAPE,Gwinmn[wn] [i]. style) : Gwinmn[wn] [i].len. (Gwbase[wn]. rc 引くく 5 ) , REVERSEEI NONSEC. if(key & CDOWN) { if(key & CUP) { break if(key & ESCAPE) { ( (col くく 5 ) ー att ) : attrib kind= atOi (token[7]) : ura ニ atoi (token[8]) : textkei3(xl,yI,x2,y2. attrib,kind,ura); return ( 0 ) : keydraw2( ) 関数 ( CM910805 ) List 5 Copyright 1991 E. Toyokuni Number CM91 if( i く 0 ) { if( i ” n ) { (!strcmp(moji [ n ー 1 ]. "Y")) i f ( CUROFF; Ⅱ (!strcmp(moji [n-l]," break 3 : 5 : 7 : 1 1 : 12 : 13 : 14 : 19 : 21 : 25 : 26 : 27 : 6 : #define EXTERN extern "config. h" 4 : # i ncl ude 8 : # i nc lude 9 : # i nc lude 10 : # i nc lude #include #include #include く s td i 0. h > く stdlib. h> く string. h> く ctype. h> "fkey. h" menurd. h" werase(&Gwin[wn]) : return(0) : List 4 keidraw2( ) 関数 ( CM910804 ) 16 : #ifdef PROTOTYPE 17 : # i nc lude "cboxprot. h" 18 : #endif 20 : keydraw2(moji) char *moji; 23 : char *token [ 40 ] : 24 : char kugiri[10]; i nt i : char COI int ura; 28 : unsigned char str[256] : 3 : 4 : 5 : 6 : 7 : 8 : 9 : 10 : 1 1 : 13 : 14 : Copyright 1991 E. "config. h" Toyokuni Number CM91 #include #define EXTERN extern #include # include #include #include # include # include く stdio. h> く stdlib. h> く string. h> く ctype. h> "fkey. h" menurd. h ” 29 : 30 : 32 : 33 : 34 : 35 : 36 : 38 : 39 : 40 : 42 : strcpy(kugiri, ! ニ NULL) { while( token[i] strtok(moji . kugiri) : token[i] return ( 0 ) : chgfnc3(str, col, ura) : u ra atoi (token[3J ) : col (char)ato i (token[2]) : str,token[l]) : strcpy( : strtok(NULL,kugiri) : tOken [ + + i] 112 C MAGAZINE 1991 8
なった。老化防止にはパズルがいちばん。 ば静的に与えておけばよいのて、すが , さま これからもおもしろく解けるパズルをお願 ざまなケースの検索を可能にするために起 ( 八王子市松本元 ) いします。 動時に作成するようになっています。 プログラムリストの最初て定義されている 今回て、解答を送るのは 2 回目てす。今回の #define SQUARE ( 6 ) / * 格子数 * / プログラムは , 愛機 VX て、計算すると 5 時間 #define COINS ( 6 ) / * ・ - コインの数 * / 30 分かかります。それて、出た答えが 2 通りて、 を適当な値に変えることて、 , 希望するケー すから , はたしてあっているかどうか不安 スについて調べることがて、きます。しかし , ( 愛知郡北村克史 ) があります。 これらの値の正当性については , まったくチ ェックしていませんのて、注意してください 格子数は 3 以上を指定し , コインの数は 10 を 越えないようにしてください。 Table 1 に私 ネイバーズ が検索したケースの一例を示します。 機種 : PC-286VE 東洋ガラスの六ズルシテイというシ 応募の規定 コンパイラ : Turbo C Ver 2.0 ( 統合環境版 ) リーズに , 「ネイバーズ」というムズいパズ ルをみつけた。 パズルの解答を導き出すプログラムを作 ガラスの円盤に縦 4x 横 4 , 合計 16 個の丸 成し , 下記の応募規定に従い , ディスクて、 いクポミがあり , その底にはどーこも赤 , 黄 , 下記の宛先 , または電子メールて、 R & R に記 青 , 白 , 黒のどれかの色が塗ってある (Fig. されている ID 宛に解答をお寄せください 5 ) 。周囲のトレンチには , 色つきのビー玉 なお電子メールの場合は , LHA て、の圧縮フ が 16 個 ( 赤 , 青 , 黄 , 黒各 3 個い自 4 個 ) 入っ ァイルや ish ファイルてお送りいただいても ている。すべてのビー玉を 1 個ずっクボミに けっこうて、す。締め切りは 8 月 18 日必着とし 置けば完成なのだが , 置き方に以下の制約 ます。 がある。 ・パズルの標題 , 解答の明記 ①玉と同じ色のクポミには置けない。 ・作成するプログラムは C 言語のみ ②玉と同じ色のクポミのトナリには置けな ・使用機種 , コンパイラおよびオプション い。この場合 , 45 度方向のトナリも含ま スイッチの明記 ・プログラム作成に参考とした文献 , プロ ③ビー玉を置いたら以降そのクポミの色は グラム 置かれた玉の色に変わる。 ・ 800 字以内のプログラムの説明と感想 つまりあるクポミに , ある色のビー玉が ・連絡先 ( 住所 , 電話番号 ) いま置けなくても T 周囲にほかのビー玉が置 ・上記をすべてテキストファイルにすること かれて , その場所の色が変われば」置けるよ なお , お送りいただいたディスクなどは うになる・・・、・・順序が問題なのてある。 返却て、きません。ご了承ください。 最終的な盤の配色が同じぞも , それにい 宛先 たる道筋は何通りもあるだろラ。しかし今 〒 108 回は仕上げの配色だけを問題にする。 東京都港区高輪 2 ー 19 ー 13 NS 高輪ビル 仕上げの配色には何通りあるか。それぞ ソフトノヾンク株 れの仕上がり図と , それに至る道筋を 1 通り C マガジン編集部「 C マガ電脳クラブ」係 だけ例として添えてください。 い第を今月のノヾズル TabIe 1 格子数 3 x 3 4X4 5 x 5 7 X 7 解 5 通り 1 5 通り 20 通り 1 通り コイン 3 4 5 7 ■正解者からのメッセージ 少々プログラムが大きくなってしまいま したが , それだけに解答の図が最初に画面 に描かれたときの感激はひとしおて、した。 来月号をまた楽しみにしています。 ( 八代郡荒木周一 ) おかげて 10 日間の GW のいいヒマつぶしに C マガ電脳クラブ 153
応用 C 言語 C の道具箱 List 7 menurd2( ) 関数 ( CM910806 ) List 6 24 : { 23 : { Number CM91 3 : 5 : 6 : 7 : 9 : 10 : 1 1 : 13 : 17 : 19 : 21 : 22 : 23 : 25 : 28 : 29 : 30 : 32 : 33 : 34 : 35 : 36 : 38 : 39 : 40 : 41 : 42 : 43 : 44 : 45 : 46 : 48 : 49 : 50 : 52 : 54 : 55 : 56 : 58 : 59 : 60 : 62 : 63 : 64 : 14 : # i ncl ude # include # include #include #include 8 : #include Copyr ight 1991 E. TOY0kuni 4 : #include "config. h" #define EXTERN extern 17 : 19 : 20 : 21 : 22 : 24 : 25 : 26 : 27 : 28 : 29 : 30 : 32 : 33 : 34 : 35 : 36 : 38 : 39 : 40 : 42 : 43 : 44 : 45 : 46 : 48 : 49 : 50 : 52 : 53 : 54 : 55 : 56 : 58 : 59 : 60 : 62 : 64 : 65 : 66 : 68 : 69 : 70 : 71 : 75 : 77 : 78 : 79 : 80 : 82 : 85 : 86 : 88 : 89 : 90 : 92 : 94 : 95 : 96 : 98 : 99 : 100 : 101 : 102 : 103 : 104 : 105 : 106 : 107 : } #endif selbar2(mn,color,rcolor, linenum, initl,ura) struct Gmnstr mn[] : Char color, rcolor: く stdio. h> く stdlib. h> く string. h> く ctype. h> "fkey. h" menurd. h ” int i nt i nt i nt int ab ー i ne. cu r ⅱ ne : int outkey[50] : zen : int selmod Y : i nt *pluskey ニ 0 ー i nenum , i n i t l, ura : 16 : #ifdef PROTOTYPE # include ” cboxprot. h" 18 : #endif 20 : menurd2 (filename, cmd, arg) Char *fi lename Char *cmd; Char *arg; 26 : FILE *fp; 27 : char moji [ 256 ] : i nt i = 0 cmd [ 0 ] = NULL; arg [ 0 ] = NULL; i f ( (fp fopen(filename, "r ” ) ) = NUL し ) { printf("can't open Yn ” ) : exit(l); while ( (fgets(moji,MOJILEN,fp)) MWINDOW *wx; Char *savearea; outkey[0] = ESCAPE ・ outkey[2] ニ 0 : outkey[l]= CR; Y for (i i n i 凵 ー inenum if(moj i [ 0 ] ! =CMD い (E) { continue; switch(tolower(moji[l))) { keidraw2(&moji[ + + i]); break; case bkdraw2 (&moji [ + + i ] ) : break; keydraw2(&moj i [ + + i ] ) : ニ間しい { arg) ; break: inpdraw2(&moj i [ + + i ] , fp, cmd, break; case W w indraw2(&moj i [ + + i ] , fp, cmd, arg) break; case ・ S seIdraw2(&moji [ + 十 i],fp,cmd,arg) break; case ' ー fclose(fp) : return(O) : List 7 selbar2( ) 関数 ( CM910807 ) ” config. h" 1 : 2 : 4 : 6 : 7 : 8 : 10 : 14 : 16 : #include 3 : #define EXTERN extern 13 : # i ncl ude 12 : # i ncl ude 11 : #include ” pldwn. h ” 9 : #include ” fkey. h ” #include #include 5 : #include く stdiO. h> く string. h> く conio. h> #include "menurd. h ” ” window. h ” winvar. h ” 15 : # ifdef PROTOTYPE # include "cboxprot. h ” pr i ntv ()n [ i ]. moj i . co ⅵ NONSEC, mn [ i ] . x, mn [ i ] . y, u (a) : printv(mn[y]. moji.rcoIorlREVERSEElNONSEC,mn[y]. x,mn[y]. y,ura) : sw i tch ( i i ) { scandi r(pluskey) zen do { case F2 break; werase(&GwinC2] ) : getch() : keybfcl ( ) ; tIwfresh(&Gwin[2], case FI return(ESCAPE) case ESCAPE ・ break : case CDOWN: break y 十十 case SPACEE ・ break case RO しし UP: break : case CUP.• (y > linenum linenum : if (y く 1 ) continue default: return(ii) ; case F10 case F9 case F8 case F7 case F6 case F5 case F4 break; inpmenu(3) : case F3 break; werase(&Gwin[11]) : werase(&Gwin[1]) ; outkey, ” F2") : barscroI(wx,YE しし 0WlREVERSEE,&abline,&curline, W X = &Gw i n [ 1 ] : tIwfresh(&Gwin[1], tIwfresh(&Gwin[ll], curline : 0 abline i f 応用 C 言語 return(selmod) : selmod ニ y ! ニ CR ) : } ⅶⅱ e ( i i printv(mn[y). moji,rcolorlREVERSEElNONSEC,mnCy). x,nn[y). y,ura) : printv(mn[zen]. moji, colorlNONSEC. mn[zen]. x, mn[zen]. y, ura) : 113
249 行から 268 行まて、の , 関数 write graph ( ) が , マップデータを実際に VRAM に展開 する関数て、す。マップデータとして文字列 のポインタを受け取り , その文字をインデ ックスとして X 方向に 256 ドット分 VRAM に 転送します。今回は , かなり時間的にシビ アなのて、 , IOCS を経由しないて、直接 VRAM に転送しています。これらの下請けルーチ ンが 192 行から 247 行て、す。少して、も処理時 間を稼ぐために X 方向への書き込みはループ にしないて、 16 個ステイトメントを並べてい ます。この部分は , アセンプラて、記述する 場合てもマクロを用いて一切ループさせず に 256 処理をいっきに行う場合すらある , 処 理のいちばん重い部分て、す。このプログラ ムて、はスクロールしか行っていないのて、 , GCC て、コンパイルして最大限の最適化を行 った場合には 1 垂直同期期間に 8 ドット単位 て、スクロールさせても時間的には間に合っ ているようて、す。環境変数 GCC OPTION を FMLO set GCC OP 引 ON gcc map. c ー 0 -lbas -liocs ー旧 OS -DSCR UNlT=8 てコンパイルしてみてください。超高速ス クロールが楽しめます。いちおう XC Ver. 2 てもコンパイルてきるように記述してあり ますが , 目に見えて最初の画面設定が遅い のて、スクロール量にかなりの制限がて、そう てす ( XC てコンパイルする場合は , 割り込 み処理は GCC が生成したアセンプラコード が挿入されます ) 。 190 行目の変数 vram の設定と 265 行から 267 行の怪し気な処理は , X68000 の VRAM が球面スクロールてあることと , 縦方向ス クロールて最初に書き込むべき位置が VRAM の終端部分てあることによるものて す。アドレス計算て、注意しなければならな いのは , 表示は 256X256 て、すが , VRAM 自 体は 512X512 あります。とくに Y 方向へ移動 する場合は間違えやすいのて注意しましよ , よく間違えたのて・・・・・・ ) 。 341 行から 349 行が割り込み処理関係のワ ークてす。 volatile 宣言してあるのは GCC に 最適化の制限を要求するためて、 , 実際には 必要ないのかもしれませんが念のためにつ けてあります。変数 scro Ⅱ flag て、は , 割り込 み処理は実際の処理に関係なくスクロール を行いますのて , 表の処理て、 VRAM への転 送が間に合わなくなった場合にスクロール を一時的に停止するためのフラグて、す。変 数 vsync_counter は割り込みによって何ドッ ト分スクロールしたのかを表処理て、認識す るためのカウンタて、す。 関数 main( ) の 432 行から 443 行がメインの ループて、す。変数 map line は , 次回転送て、 どのマップデータを VRAM に書き込むかを 管理するインデックスて , これが 0 より大き い間はマップが継続することを示します。 436 行目て、画面スクロール量をチェックし て , 次のスクロールまて、余裕をみてデータ を転送します。これが何らかの原因て、処理 が遅れた場合には scroll ー flag を落としてスク ロールを一時的に停止して対処します。 れと同様なことが市販のゲームて、も行われ ていて , 処理が間に合わなくなればスクロ ールが重くなるのて、す。 main( ) の先頭て、スーパーバイザに移行し ていますが , コメントのように明示してユ ーザに戻していません。これは「手抜き」と 同時に -fno-defer-pop を GCC に指定したく なかったからて、す。 EXIT するとユーザモー ドに戻るのて、 , 最適化を制限してまて、ユー ザモードに戻すことはしませんて、した。ス ーパーバイザに移行するのにも最初の回て、 は IOCS ライプラリ , 今回は DOS ライプラリ といささか無節操を否めないて、すが筆者の 性格ということて笑って許してください 自前のデータて、スクロールさせたい人の ためにグラフィックのデータ構造を説明し ておきます。単純明快て、 , 最初の 256 ロング ワード (int) がパレット 0 から 255 にそれぞれ 対応するパレットデータて , そ - れから後が 実際のキャラクタグラフィックデータて、す。 こちらは unsigned char<, X 方向に 16 ドッ ト並んだ後 , Y 方向に移動する 16 x 16 ドット のデータになります。データの大きさを変 更した場合は構造体 gdata ( 179 行 ~ 182 行 ) X 68 k 活用講座 の g data の部分を変更してください 試験用マクロについて いろいろなスクロールを実体験て、きるよ いくっかのマクロて、制御て、きるよう になっています。実際にコンパイルして変 化を見てください。 SCR UNIT 1 / 60 秒に何ドットスクロールさせるかを 決定します。 2 の倍数てないとうまく動作 しないのて、注意してください。あまり大 きな値を指定すると , 書き込みが間に合 わなくなります 0List 1 は完全に割り込み を停止させていないのて、 , 気にいらない 場合は修正してください ( すべて答えを出 さないのもいいかな ? と思って・・・・・・ ) 。 HALF これを #define するとスクロールインター バルを 1 / 30 秒に 1 回にします。 . これて、スク ロール速度が半分になります。スクロー ル単位を 2 倍にしてこれを指定すると見た 目は同じ速度て、スクロールしますが , あ まり美しいスクロールて、はありません。 OFFSET これを #define すると , 実際に CPU がグラ フィックを書き込む部分が見える処理ま て表示をオフセットさせます。通常は見 えない CPU の作業を実際に見ることがて きます。 XC Ver. 2 のノヾグ 今回のプログラムは XC Ver. 2 て、もコン パイルて、きるようにしておきましたが , そ の移植の際にバグを発見したのて、報告して おきます。 XCC' コンパイルした場合はレジ スタへの変数自動割り付けはないのて , と くに VRAM にアクセスする関数が GCC に比 べて相当遅くなります。そこて変数を register 宣言したのて、すが , List 2 のような関数が List 3 のようにコンパイルされてしまい動きませ ん。これはかなりひどいバグて , プロック 内部て宣言された register 変数に割り当てた X68k 活用講座 95
ばなりません。 MASM6.0 て、は , 128 バイト せた命令を自動的に生成します。 以上への条件ジャンプをそのまま書くこと mov ax, CX がて、きます。 80386 以上の命令を生成すると skip JZ きはそのままのコードを生成します。 80286 ; 128 バイト以上のコード 以下の命令て、コードを生成するときは ,short の条件ジャンプと無条件ジャンプを組み合 C のヘッダファイル 左のプログラムを 80286 以下のコードて、アセ ンプルした場合 , MASM は次のコードに置 き換えてコード生成を行います。 mov ax, CX temp$l Jnz skip Jmp temp$l : skip . LiSt 13 ; 128 バイト以上のコード #define ESC 0xlb int count; double sum; struct data{ int i i : long Ⅱ ; f 10 at ff : skip . farCALL の最適化 同一セグメント内にある far ラベルへの CALL は , farCALL を使用せずに , CS をプ ッシュしてから nearCALL するような命令に 置き換えます。 push CS call near ptr testsub fortran FuncI(int n0, int nl, double x): double _far List 14 コンバートした MASM 用インクルードファイル ESC EQU 00000001bh EXTERNDEF count:SWORD C EXTERNDEF sum :REAL8 C data STRUCT 2t SWORD SDWORD REA し 4 ENDS TYPEDEF PROTO FAR FORTRAN :SWORD, PROTO @proto_0 フロー制御コードの 自動生成 ff data @proto 0 Funcl アセンプラブログラムの大きな欠点のひ とつに , プログラムの読みにくさがありま す。よほどしつかりとコメントを書いてお かないと , 後て、読む場合や他人が読む場合 にたいへん苦労します。 MASM6.0 には , 条 件判断やループ制御のためのディレクティ プが用意されており , 自動的に見合った命 令を生成します。ディレクテイプを使えば プログラムの流れを見やすく記述すること がて、きます。次のプログラムはフロー制御 のディレクテイプを使用した例て、す。キー 入力を 10 回受け付け , 数字のみ表示させま す。途中て、キーが押下されるとそこて、 中止します。 include dOS. inc 286 MODEL small, os dos DOSSEG STACK DATA ℃ ODE . STARTUP :REA し 8 :SWORD, List 不要な nop 命令の生成をしない ( MASM6.0 ) 0005 0007 0009 000C 000E 0 0 10 sk i p ah, 09h dx, OFFSET msg 21 h sk i p 10 dup(?) っ 0 11 「 0 、 1 0 0 ワ 0 < 一 4 ・っ 0 0 ハしつー 0 JITIP mov mov int Jnc db 001 A s k i p : 不要な nop 命令の生成をしない ( MASM5.1 ) List 0005 0008 000A 000D 000F 0011 11 0 0 ワ 3 0- < -4 ・っ 0- 【じハしつー 0- sk i p ah. 09h dx, OFFSET msg 21 h sk i p 10 dup(?) J m P ßlOV ß10V int Jnc db 001 B s k i p : 46 C MAGAZINE 1 1 8