X 68 k 活用講座 は余裕シャクシャクて、処理していますが , たいと思います ( TabIe 2 ) 。現在のヘッダは ソースに埋め込むのはやめましよう・・ もう少し弾の移動速度を速くするか ( こうす とはいえ , いまだにたくさん、、生の数値〃 List 3 のようになっています。 れば同時に画面に存在する弾数が減るのて、 このデバッグの結果 , 自機の発射する弾 が残っているのは事実・・ 処理に余裕がて、きる )MAX BOM を減らす の数が game. h に記述されている MAX BOM 今月収録されているソースは , これらの バグはフィックスしてあります。 このノヾグ の数になったため , ノーマル 10MHz X680 必要がありそうて、す。また , 割り込み処理 00 て、は連射ジョイスティックを使って攻撃 もかなり肥大してきました。この後さらに フィックスとソースファイルの全面的な見 をすると「ものすごい処理落ち」を起こすよ 直して、ヘッダに大きな変更が起きています。 バックグランド関係の処理を追加する必要 そこて、再度 game. h について説明をしておき うになってしまいました。 XVI や X68030 て、 があるのて、若干の不安がありますが・・ List 3 新しい game. h List 106 : 107 : / * 機種、コンパイラに完全に依存する記述 * / 108 : / * PCG レジスタの構造体 * / 109:typedef union { unsigned short dummy; 110 : struct { 111 : unsigned POSO: 4 ; 112 : unsigned POSI: 4 ; 113 : 114 : unsigned pos2: 4 ; 115 : unsigned POS3: 4 : ) sp; 116 : 117 : ) SP_REG_U; 1 : / * Cmagazine sample ゲームヘッダ * / 2 : 3 : / * コンフィグマクロ * / 4 : / * XC (Ver 1 or Ver 2 ) のライブラリを使用する 5 : 場合はコメントを外してください。 * / 6 : / * #define USE_XC_LIB * / 7 : 8 : / * SP_REGST で引き数の数がエラーになったら 9 : コメントを外してください。 10 : / * #define HAVE_SPREGST—BUGS * / 11 : 12 : / * 溜め打ちで自分の弾の威力が当たった数だけ減らしたい 13 : 場合はコメントを外してください。 * / 14 : / * #define NON_LIKE_R—TYPE * / 15 : 16 : / * 背景データをファイルで読む場合はコメントを外して ください。 * / 17 : 18 : / * #define MAP_IS_FILE * / 19 : 20 : / * 背景描画を DMA で行わない場合にコメントを外してく 21 : ださい * / 22 : / * #def ine SOFT—TRANS * / 23 : 24 : 25 : 26:#incIude く stdio. h> 27:#incIude く stdlib. h> 28:#incIude く string. h> 29:#incIude く io. h 〉 30 : 31:#ifdef HAVE_SPREGIST_BUGS 32:#define SP_REGIST XC2_BUG_PROTOS 33:#incIude く iocslib. h> 34:#undef SP_REGIST 35 : int SP REGST(int, int, int, int, int, int) : 36:#else 37:#include く iocslib. h> 38:#endif 39 : 40:#incIude く interrupt. h> 41 :#include く setjmp. h 〉 42:#ifndef USE_XC_LIB 43:#incIude く signal. h> 44:#eIse 45:#incIude く doslib. h> 46:#endif 47 : 48:#ifndef MAIN 49 : #def ine EXTERN extern 50:#eIse 51 :#define EXTERN 52:#endif 53 : 316:EXTERN int 自機スピード 317:#ifdef MAIN 318 : 319 : # else 320 : ・ 321:#endif 322 : 323 : / 事グラフィックパレット * / 324:unsigned int g—palet[256] : 325:unsigned short c—paletCPAL—C—MAX ー PAL—C—MIN] : 326 : 327 : 328 : / * マップグラフィックへのポインタ * / 329:unsigned short *map—data[256] : TabIe 2 List 3 の解説 ゲームを作成する際に決定するコンバイル条件を決めるマクロ群コメ ントのとおりに機能します XC Ver. 2.1 の iocs ⅱ b. h ではこれだけではコンバイルエラーになりま す XCC は , サイズが 0 の配列は f00 [ ] : と記述するようになっていま すが , GCC では明示的に f00 [ 0 ] : と記述します この部分がコンバイルエラーになりますので , iocs ⅱ b. h を書き換えてく ださい ー連のシステムヘッダを include していますが , interupt. h が interrupt. h と綴りが変更されています 「 X680X0 libc 」の発売に伴って正しい綴りのヘッダに直しました 今までのヘッダはリネームして使ってください また , 「 X680X0 ⅱ bc 」ではバスエラーなどのシグナルを処理できるの で , 「 X680X0 libc 」を使う場合はエラーハンドラを sign を使う形に 変更されています このために ut ⅱ es. c が大幅に書き換えられています EXT というマクロを使っていたところ , 見事に「 X680X0 libc 」のヘ ッダで使われている EXT マクロと衝突してしまいました これはたぶん ( 自信はないけど ) 「 X680X0 libc 」の不備だと思うので すか・・・ グラフィックバレット変更による、、芸″を行うためのバレット番号を指 定するマクロ 割り込み処理で毎回設定しなおすグラフィックバレットのテータを格 納する配列 このために sp 「 ite. c の中に記述されている割り込み処理関数が変更され ています 3 ~ 23 行 26 ~ 46 行 48 ~ 52 行 99:#define 表示 1 100:#define 非表示 0 101:#define MAX_NUM 118 102:#define 最終面 0 103 : 104:#define PAL—C—MIN 64 105:#define PAL—C—MAX 69 104 ~ 105 行 325 行 X68k 活用講座 133
とり早い 解決方法。 List 1 CASPEC/c CASPEC/c は、パソコン上で SPD を用いて、 C 言 語の設計製造、ドキュメントの作成を効率的に 行える開発支援ツールです。プログラムの論理 構造を SPD を用いて対話的に作成し C 言語の ソースコードを自動生成します。また、既存の C ソ ースから SPD によるプログラムの論理構造を生 成することも可能です。 プログラミング作業の効率化 ドキュメンテーションの効率化 プログラム保守の効率化 SPD やプログラム形式の標準化 行入 文・①こ、轆 . C 日 . 万な般 - 田 . 印円 . 一覧日了 / ・データのソートを行う・ / : データのソートを行う 下の拳え字の 上限の画え字の バッフ , メを納ポインタ int ー ー : 上え字め叩 : ー : 上限置カ畋界値より ( / ・下の羅え字の / 上値を下げる ー : 境界備下限値より int ー : ル / ・上のえ字の / 下値を上げる 上値が下以上び満 int 物レ 2 : 叩 : / ・バッファびメー納ポインタ / よ値と下をする ctnr t 当 [ れ・叩” ⅷ厄価ー作朝 t / ・上のえ 下登値を上げる 上ーを下げる i ば 3 レ ( t . 加価 t 幃 : 上が最下限より大きい / ・上県値を下げ / と最下第を交農する 幃 : 下が最上 - より小さい 物 t 瓰ー 下襲値と上をする [ ・デ - タを校み込む 【動作環境】 PC - シリーズ ( ノーマルモード ) MS-DOS Ve 「 . 3.1 以上 【定価】 128 , 000 消費税は含みません * ( S 円「 am Dia 部 a 司とは日本電気株式会社 が開発した設計記述技法です。 * MS - DOS は米国マイクロソフト社の登録商標です。 ドキュメントジェネレータ CDOC , 。、 例応言盟 C ( K & R または AN 日規格準拠 ) 応】 PC - シリース ( ノーマルモート ) ¥田 . 网 ひ価 MS-DOS()e 「 . 引以上 ) KB 以よ A..J - 引シリース \ 圏 . 日本語 MS - DOS ( Ve 「 . 3.0 ) 以上 EWS シリース¥ . VAX シリース¥ . スと情報のいいかたち 日本コンごューター・システハ株式会社 〒ロ 5 東京都江東区東陽 5-29-3 東京本社フロダクト開発部 TEL. 03 ( 5632 ) 0 円 3 ( 直 ) FAX. 03 ( 5632 ) 0 円 4 く資料請求番号 115 〉 C 言語フォーラム 115 i f ( argc く 2 ) { printf( "YnUSAGE: js infile CoutfiIe] Cstartfp] [length]%n" ) : : js infile Cdummyarg] [startfp] [length]*n" ) : printf( Cdummyarg] ニ ***%n" ) : printf( " printf( convert EPWING to DOS TEXTYn ” ) : ex i t ( 1 ) : 80 : 83 : 85 : 86 : 88 : 89 : 90 : 93 : 95 : 96 : 98 : 99 : 100 : 101 : 10 2 : 10 3 : 104 : 105 : 106 : 107 : 108 : 109 : 110 : 112 : 113 : 114 : 118 : 119 : 12 0 : 12 1 : 122 : 123 : 12 4 : 12 5 : 126 : 〃 12 7 : 128 : 12 9 : 130 : 131 : 132 : 13 3 : 13 4 : 13 5 : 13 6 : 13 7 : 138 : 139 : 14 0 : 141 : 14 2 : 14 4 : 1 4 5 : / / 146 : 1 4 7 : 14 8 : 149 : 150 : 15 2 : 構造化プログラミング開発ツール ctrlbrk( ctrl ) ; if( argc 〉 = 5 ) len = atol ( argv[ 4 ] ) : e ー se len ニ LONG_MAX; if( argc 〉ニ 4 ) start = at01( argvC 3 ] ) : e ー se start if( start % 2 ) / / 偶数化 + + start; i f( argc 〉 = 3 ) { ouf = fopen( argv[ 2 ] , i f ( ! ouf ) { printf( "Can't 叩 en output file: %s%n ” , argv[ 2 ] ) : ouf stdout; }else ouf ニ stdout; inf = fopen( argvC 1 ] , i f ( ! i nf ) { printf( "Can't 叩 en input file: %s\n", argv[ 1 ] ) : ex i t ( 1 ) : / / printfbyte() 不具合の間 Tpf = inf; temp = filelength( fileno( inf ) ) : if( start 十 len 〉 temp ) ( start 十 len ー temp ) : len i f( len く 0 ) { printf( ” JS-ERR: Bad parameters on cmd line" ) ; ex i t ( 1 ) ; 早く、分かりやすぐ if( start ) fseek( inf, start, SEEK-SET ) ; ⅶⅱ e ( len ) { printfbyte( inf ) : この関数現状では不具合のため使用せず c 1 = ge tc ( i nf ) : c2 = ge tc ( i nf ) : len ニ EOF ) break; i f ( c2 = EOF & & feo f ( i n f ) ) { putc( cl, ouf ) : break; = c 1 くく 8 : d2 ニ c2 & 0x00ff; ニ d 1 ー d 2 ; j i stOSj i S ( j i S ) : SjiS i f ( s j i s ) { pu tc ( sj i s > 〉 8 , ou f ) : putc( sjis & 0xff, ouf ) ; }else{ / / EPWING peculiari ty = 0x 1 f & & c2 = 0x0a ) ; 0x0a ) / / Th i s is more general than above i f ( c2 = cl ニ 0x0d; / / make DOS'S CRLF putc( cl, ouf ) : putc( c2, ouf ) : fcloseall();
List 1 より CPU の処理速度のほうが速くなってい て , DMA 転送よりソフトウェアて、転送する ほうが高速になります。 余談て、すが , X68030 の SCSI 転送は DMA て、行われていますが , SRAM 上のあるデー タを変更すればソフトウェア転送が可能な ようて、す。 List 1 の説明を TabIe 1 にまとめました。 ご覧ください テノヾッグ 5 , 6 月号のプログラムて、見つかったバグ 修正報告をしておきます。書籍「 X680X0 1 ibc 』の発売時期と重なってしまい 「 libc が バグってない ? 」といった報告も見受けられ libc 作成者にたいへんご迷惑をおかけした とをお詫びします。 ①アニメーションカウンタの初期値を 1 だけ 多く初期設定していた。このためにバス 工ラーて、落ちることがあった (utiles. c)o 割り込み処理内て、アクセスするために 実際にアクセスさえて、きれば落ちること がないのて、動いているかのように見えて ②弾の発射数最大値の上限値をマクロて、な く、、生の数値〃を使っている部分があった。 このために画面に弾が残るようなバグが あった ( mychr. c ) 。マジックナンバを直接 C で書いた DMA 転送の概要 map—data—ptrO = MAP—DATA—PTRO; 152 : now—counter = sizeof (MAP DATA R の : 153 : 154:#endif 面初期化 = 1 ; 155 : 156 : } 157 : 158. ・ VOid 159 : 背景移動処理 (void) 160. 161 : static int cont if ( ! 面初期化 ) 162 : 163 : 164 : cont 十十 ; if ((cont & 1 ) & & ! 面クリア ) 165 : 166 : write-one—block ( ) : 167 : scrol l_data. sc0_y_reg 168 : 169 : scrol l_data. scl_y_reg ー 170 : scrol 1 data. sc2_y_reg s cro 11_data. sc3_y_reg 171 : 172 : if (now—counter くの 173 : 面クリア = 1 : 174 : 175 : return ; 176 : 177 : if ( 面初期化 = 1 ) 178 : 179 : 180 : 面初期化 = 2 ; 181 : for (i = 0 ; i く 256 ; i + + ) 182 : palet (), の ; 183 : for (i ニ 0 ; i く 256 + 16 : i + + ) 184 : write—one—block ( ) : 185 : 186 : else if ( 面初期化く 0X10 圓の 187 : 188 : int i : 189 : 面初期化 + = 1024 ; 190 : for (i = 0 : i く 256 ; i + + ) 191 : if (g—paletCi] 〉面初期化 ) 192 : palet-buf[i] = 面初期化 ; 193 : 194 : else palet—buf[i] = g_palet[i]; 195 : 196 : palet_def 197 : 198 : 199 : 200 : 201 : 202 : 203. else ゲーム開始 = 1 : 面初期化 = 0 : List Table 1 List 1 の解説 3 行 MAP テータ ( 背景のテータ情報 ) をファイルから読み込む場合に # define する マクロてす。今回はコメントにしてあります 8 行 ケームスタート時に背景をだんだん明るくする、、芸〃を行う変数てす テータを V 日 AM に書き込む際の管理用ポインタです 1 0 行 ソフトウェアによってテータを VRAM に書き込む処理関数てす この関数はスーバノヾイザモードでないとバスエラーを起こす ( main. c て SOFT 1 8 ~ 44 行 T 日 ANS マクロを見てスーノヾバイサに入る処理を行う ) DMA 転送によってテータを VRAM に書き込む処理関数です 46 ~ 90 行 この場合はスーババイサモードに移行している必要はありません マップテータに基づいて , 16X 1 6 ドットのテータを VRAM に転送する処 92 ~ 105 行 理を行う関数です マップテータなどをファイルから読み込んて初期化します 107 ~ 156 行 1 58 ~ 203 行 実際に背景の書き換えを行う処理関数てす ケームメインルーチンはこれを呼び出して処理をします 0 : i く num : i + + ) int c = buf[i]. count; unsigned char *dest = addr; = bufCi]. addr : unsigned Char *Src while (c > の if (direction = = *src; else if (dest_inc—mode) dest + + ; else if (dest_dec_mode) dest— i f (src— inc—mode) 十十 ; else if (src_dec mode) src— C MAGAZINE 132 1993 7
98 & 00S / ゲームプロクラミンク 大作 キャラクタ工テイタの作成 ( 2 ) 0 2 野村広 今回は予告どおり , 先月のどうにか点か打 てる程度のマウスの練プログラムみたい なものに , 実寸 ( 1 倍 ) ウインドウとファイル の読み書きと , あと少し追加します。 に渡すのに f ( win. x , win. y) などと書かなけ 則回からだいぶいじってしまったのて、ソ ればなりませんし , 領域は (win. xl, win. x ースのみてくれは変わってしまいましたが , やっていることは同じて、す。前回は感じを 2 , win. yl, win. (2) と長ったらしくなって 擱むための限定的な使用方法の一発プログ 実寸ウインドウを追加する前に将来に備 きます。初期化時はいたしかたないとして も , 一度初期化したらあとは「 win. 領域」や「 w ラム的なものて、したが , 今回はもう少し一 えて少し Window クラスの見直しをしましよ 般性を持たせてみました。 う。前回はただ漠然と「ウインドウとかいう ⅲー > 領域」のような感じて、ほかのルーチンに もの」という感じて、扱っていました。このま 渡したいものて、す。そこて、 , 点と矩形を表 ゲームプログラミングと名がつく記事な のに全然実際のゲーム作りに入りませんが , まガリガリ書くこともて、きるのて、すが , も す Point と Rect を導入します。 Point クラス ツールを作って基礎力を養うということて、 を導入すれば , ウインドウ内の点は f ( Poin うひとっスマートさに欠けます。この先ウ 勘弁してください t p) : と書けて見やすいし , タイプ量も減っ どのみちゲームを作る インドウに機能が追加されるに従って繁雑 ときにはツールが必要になるのて、 , ムダに さは増大する一方て、しよう。 て手が疲れません。このふたつのクラスを はならないと思います。 今の段階て、もウインドウ win 内の点を関数 使って Window クラスを効率よくアクセスす 日 ect クラスを Point クラスから導出 Point クラスを導入 Window クラスの見直し iSt ist class Rect : public P0int { public: lnt ex, ey; int width, height; Rect(int sx, int sy, int awidth' int aheight) point(sx, (Y), width(awidth), height(aheight) 0 : class P0int { int x, y; / / メンパ / / コンストラクタ Point(int ax, int (y) ( X = ax; Y = ay ; 文は Point(int ax, int ay) Rect クラスに Point クラスのメンノヾ変数を持たせる ist class Rect { public: POint origin, end; int width, height; Rect(int ax, int ay, int w, int h); Rect(P0int pl, Point p2); ふたつの点が等しい ist (const Point& pl, p2 ) Point& const = 02. x & & pl. y = p2. y; b001 operator return pl. x 70 C MAGAZINE 1993 7
X 68 k 活用講座 フィックデータ構造が 16 x 16 ドットのタイ ル形式になっているのて、 , ただ単に memcp y を呼び出すように VRAM には転送て、きない のて、 , アレイチェインモードて、転送を行っ ています。 アレイチェインモードは , List 1 の 68 ~ 72 行にある typedef struct List 1 76 : { int i; 77 : 78 : static array—chain buf[16] ; 79 : while (DMA_SENS) for (i = 0 ; i く 16 ; i + + ) 82 : buf[i]. addr 83 : buf[i]. count = 32 : 84 : vr + = 0X200 ; 86 : 87 : DO_DMA (buf, dat) ; 89 : 90:#endif 92:void write_one—block ( ) 93 : { 94 : static int write_count; 95 : write—vram (vram0, map—data[map—data—ptr()[--now—counter]]) ; 96 : vram0 + = 16 : 97 : write count + 十 : if (write—count = 16 ) 98 : 99 : wr i te_count = 0 ; 100 : vram0 ー = ( 32 * 0X100 + 0X10 の ; 101 : if (vramO く (unsigned short * ) 0XC0000 の 102 : vram0 = (unsigned short * ) ( 0XC80000 ー 32 * 256 * 2 ) ; 103 : 104 : 105 : } 106 : 107 : void ロードマップ (int 面 ) 108 : { static char fi le—name ロ 109 : 110 : char name[128]; 111 : unsigned char gbufC256] ; 112 : FILE *file; 113 : file_name[3] + = 面 ; 114 : strcpy (name, LOAD DIR) ; 115 : strcat (name, file—name) ; file = fopen (name, "rb ” ) ; 116 : if (!file) 117 : game-abort ( " マップファイルがありません " ) ; 118 : 119 : fread (g—palet, sizeof (g—palet), sizeof (char), file) : if (!map-data[0]) 120 : 121 : int i; 122 : for (i = 0 ; i く 256 ; i + + ) 123 : map-data[i] = xmalloc (sizeof (short) * 256 ) : 124 : 125 : 126 : 127 : lnt 1 , J; 128 : unsigned short *p; 129 : uns i gned char *q ; for (i = 0 ; i く 256 ; i + + ) 130 : 131 : fread (gbuf, sizeof (gbuf), sizeof (char), fi (e) : 132 : P = map—data[i] : 133 : 134 : q = gbuf; for (j = 0 : j く 256 ; j + + ) 135 : 136 : 137 : 138 : fclose (file); 139 : 140:#ifdef MAP_IS_FILE strcpy (name, LOAD—DIR) ; 141 : 142 : file—name[5] 143 : strcat (name, fi le name) : 144 : fi le_nameC5] 145 : fi le = fopen (name, "rb ” ) : if (!file) 146 : game-abort ( " マップデータファイルがありません " ) : 147 : 148 : map_data—ptrO = x 110C (now_counter = fi lelength ()i leno ()i (e))) ; 149 : fread (map_data—Ptr0, now-counter, sizeof (char), file) : 150 : fclose (file); 151 : # else unsigned short * addr ; unsigned short count ; } array chain のようなデータ構造を渡して転送を行いま す。 array chain FOO. addr には転送元 , あ るいは転送先のアドレス , array chainFO O. count にはそのアドレスへの転送バイト数 を入れてやります。 IOCS て、は , この array chain の配列の先頭ポインタを受け取って , その配列数分だけ転送を行ってくれます。 転送自体はハードウェアが行うのて、 , 転送 が実際に終わらなくても呼び出し元に帰っ てきます。転送のソースアドレス , ディス ティネーションアドレスの増減方法 , 転送 方向などもすべて設定て、きます。 XC 付属の プログラマーズマニュアルは少しわかりに くいのて、ちょっとだけ説明してみます。 IOCS はデータの転送元 , 転送先に C て、書 けば次のようなデータを要求します。 int num = 転送数 array chain bufC 転送数 ] ; unsigned char *addr ・ arry chain bufC] には , それぞれメンバに 転送アドレスと転送バイト数を入れておき ます。 DMA 転送は addr → buf, buf → addr ど ちらも可能て、す。 ソフトウェアて、記述すれば List 2 のような 処理をハードウェアて、勝手に行ってくれま す odest inc mode, ect. は転送時にアドレス を更新するモードを指定してやります。デ ータの転送方向は direction によって指定し ードウェアが行うのだから非常に ます。 高速て、す。 しかし , X68030 て、は , DMA コントローラ ” ScrnO. pat ” ; X68k 活用講座 131
1 JIS コードを shift J 旧コードに変換する ( JS. C ) ね。そして , ついて、に , 「とくに文句のない 人や , システムがまだほとんど処女状態の 人は , 付属ユーティリティを使うとインス トールが楽だよ」と書いてあればよい 何度も言うけど , プロの人びとは , ユー ザの現場のニーズを知らなすぎるよ。ユー ティリティを提供して , 親切をしたつもり て、いるらしいが , 実はそのこと自体がもの すごい不親切。すて、にして相当ややこしい システム状態になっているノヾソコンユーザ は , ユーティリティがやる通りにインスト ールしたいのて、はなくて , 自分の現状にと って都合の良いようにインストールしたい のて、あーる ! えーと , だから , CD-ROM の MS-DOS 上 のアクセスシステムに関する詳しい情報は , 早くても連休明けにしか手に入りません。 そして次は , CD ー ROM の上のデータて、 す。 CD-ROM の , 物理的なフォーマットは , いわゆる ISO 9660 規格て、す。日本て、は , 重 要な standards 文献がなかなか publicly に av ailable て、ないて、すが , アメリカて、はプログ ラミング雑誌などの上て、この規格の詳しい 解説を見いだすことがて、きます。どうして , こう , 何て、もかんて、も , 個人が叩 en に publ ic に入手て、きるものとなると , アメリカの文 献ということになってしまうのか。なさけ 日本て、出版されている CD ー ROM 辞典のた ぐいは , その主要なものは , EPWING とい う日本独自のコンソーシアムの規格による データフォーマットを使用しています。 れは , ISO 9660 の上のデータに対するフォ ーマットて、す。たとえば , イタリック書体 はどう指定するのか , 外字は , 索引ファイ ルはどう作るのか , などなど。 この EPWING の事務局というのが , どう やら実体は 1 台の留守番電話らしい。それは いいけど , 1 か月以上も前に , 技術資料の送 付を一一その留守番電話様に向かって お願いしたのに , 送ってきませんぜ。日本 List 1 : / * JS. C JIS コードのテキストファイルを MS - DOS の shift JIS コート・のファイルに変換する 2 : Apr. 1993 , hiwa * * 現状は特殊ファイル ( EP Ⅵ NG ファイル ) 専用 * * 4 : #include く stdio. h> 5 : #include く stdlib. h> / / for atol(), ワスレ 6 : #include く coniO. h> 7 : # i ncl ude く i 0. h 〉 8 : #include く dos. h 〉 9 : # i ncl ude <l i m i t s. h> 10 : #include く fcntl.h 〉 11 : #include く jctype. h> 13 : #define KARI-LIM 0X26 / / 引 S のキリル文字などを無視 鑿 15 : extern int _fmode, 16 : / / 無用か ? extern int _wscroll : 18 : FILE *Tpf; / / printfbyte() 不具合の間 20 : unsigned short j istosj is( unsigned short c ) 22 : register unsigned cl, c2; c2 = c & 0X00 f f ; 25 : c 1 26 : if( ( ( c2 > ニ 0X21 & & c2 ← 0x7e ) & & ( ( cl 〉 = 0X21 & & cl ← KARI_LIM ) Ⅱ 28 : ( cl > = 0X30 & & cl ← 0X73 ) ) ) い 29 : ニ 0X74 & & c2 ← 0X26 ) ) { i f ( cl & 0X01 ) { 30 : c2 + = 0xlf; i f ( c2 > = 0X7 f ) 32 : 33 : C2 十十 ; }else{ 34 : 35 : c2 + ニ 0x7e; 37 : cl + = 0xel; 38 : c 1 > > ニ 1 : i f ( c 1 > ニ 0xa0 ) 39 : 40 : cl + ニ 0X40 : return ( ( cl くく 8 ) ー c2 ) : 42 : return( 0 ) : 43 : 44 : 45 : 46 : void printfbyte( FILE *f ) 48 : long POS ; 50 : ニ ftell ( f ) : POS gotoxy( 71 , textattr( RED ー REVERSE ) ; 53 : cprintf( ” % 1 d ” POS ) : normvideo() ; 55 : } 56 : 57 : int ctrl( void ) / / printfbyte ( ) 不具合の間ここで位置を表示 59 : printf( " 60 : JS Ex i t ed A t : 別 d _wscroll _fmode : O_TEXT; fcloseall(); 63 : return 0 : 67 : VOid main( int argc, char **argv ) FILE *inf, *ouf; int cl, c2, dl, d2; 70 : unsigned short sjis, jis; 72 : long start, len, temp; _fmode : O_BINARY; 75 : / / -wscroll = b i gb i g ドシ・ , ftell ( Tpf ) ) ; 114 C MAGAZINE 1993 7
プロクラミンク譴場 D 「 . 望洋のプログラミング道場 123 * pf * PI とする。 示をお薦めする。 ptr=calloc()0 , sizeof(int)) ; 性という点でも , このようなキャストの明 int * ptr ; 性という点だけでなく , プログラムの可読 将来的な C 十十への移行を見据えた互換 1 0 個分の領域 ( 配列 ) を確保したいのであれ としなければならない。 の型を持ち , 戻り型が void * である。 int 型 ptr= (int * ) c 訓 oc ( 10 , sizeof(int)) ; 先の例は VOid * calloc(size t n, size t size) ・ する c 訓 oc は , 示的なキャストを必要とする。したがって , たとえば , 空き領域から記憶領域を確保 インタへ代入はできるものの , その逆は明 数の戻り値または引数としてである。 C 十十では , ほかの型へのポインタを void ポ void ポインタがおもに使われるのは , 関 の型のポインタと相互代入が可能であるが , 明されるまでは , cha 「 * が使われていた ) 。 がある。 C 言語では , void ポインタは , ほか インタとして利用される void ポインタが発 でも採用されているが , 若干の仕様の違い あり , 特殊なものである ( いわゆる万能なポ void ポインタは , ANSI C で作られ C 十十 る例としては , memset 関数などがある。 を特定されないという「ただの」ポインタで void ポインタは , 指すオプジェクトの型 また , void ポインタを引数として受け取 void ポインタ Fig. 1 変数とポインタ へのポインタ型 ) 〃を持ち , 同様に pf は、、 floa とめたのが TabIe 1 て、ある。 さて , キャストを用いれば , ある型への のオプジェクトを指すのて、 , 、、 int * 型 (int 型て、あり , * pf は float 型て、ある。以上をま 参照 ) が , これは例外的なものだ。 x は当 nt 型ク , y は、、 float 型〃を持つ。 pi は int 型 このことからもわかるように , * pi は int * 型というポインタも存在する (CoIumn 1 て、ある。 変数て、あり , pi, pf はポインタ変数て、ある。 もちろん , 「ただの」ポインタて、ある void こて、 , x と y は普通の ( ポインタて、ない ) リアス ( 別名 ) て、あり , * pf は y の工イリアス 型を持ち , * ptr は type 型を持つ。 float * pf=&y ; 態になっている。すなわち , * pi は x の工イ オプジェクトを指すとき ,ptr は type * int *pi=&x ; に初期化されているのて、 , Fig. 1 のような状 【重要】ポインタ変数 ptr が type 型の float y ; さて pi と pf は , それぞれ x , y を指すよう int X ; るということだ 次の宣言を考えよう。 指すかによって , 型が区別されるものて、あ は , それぞれ、、 int * 型〃と、、 float * 型クて、あ タを生成する演算子て、あるのて、 , &x, &y なく , 何を ( どういう型のオプジェクトを ) た , 前回説明したように , & 演算子はポイン いうものは , 単なる「ただの」ポインタて、は ポンタと型 t * 型 (float へのポインタ型 ) 〃を持つ。ま こて、筆者がいいたいのは , ポインタと ーする形で , ポインタと型について考察する。 きない難関のひとつカ ; ポインタである。 ー C 言語を習得する上で避けて通ることので一 前回に引き続き , いくっかの質問にお答え ポインタ ( 2 ) Dr. 望洋の 柴田望洋
着目してほしい goukei=sumup(&x) ・ 引数が , x て、なく &x となっている。一般 , 配列 x に対して , ・・・配列の先頭要素へのポインタ ・・・配列 ( 全体 ) へのポインタ て、ある。 したがって , 配列名 x を単独て、記述した場 合は , & x [ 0 ] の略記法とみなされるのだ ( C olumn 4 ) 。 【重要】配列 x に対して , x は配列の 先頭要素へのポインタ ( & x [ 0 ] ) であり , & x は配列そのものへのポインタである。 x と &x は , そもそも型が異なるのて、 , ま ったく別物て、あるが , 原則としてその値は 同一て、ある。 ( 2 ) 受け取る配列の型 List 2 と List 4 の sumup 関数を利用する上 て、の , 決定的な相違は以下の点にある。 List 2 の sumup 関数は , 配列の先頭要素 へのポインタを受け取るため , いろい ろな大きさの配列を取り扱うことがで きるが , List 4 の sumup 関数は , 大きさ が 4 である配列しか受け取ることができ 関数利用者にとって , この違いは重要て、 ある。 ある大きさの配列のみを受け取る関数と いうのは , かなり型づけの強いものて、ある。 「ある大きさの配列しか受け取りたくない」 という場合は多くないて、あろう。したがっ て , 一般には List 4 のようなプログラムにお 目にかかることはない 大多数のプログラマは , 配列の大きさを になっている。 int 型は基本型て、あり , それ 示す番兵を , あまり意識することなく利用 にポインタを適用して派生したのが int * 型 している , と先ほど述べた。同様に , 皆さ て、ある。配列や構造体も , 派生の一種て、あ んは無意識のうちに ( ? ) , 関数の引数とし る。これらの導出は , 原理的には無限に組 多元配列 て List 4 のように配列へのポインタを利用し み合わせることがて、きる。 int 型にポインタ ているのて、ある。 それは , いかなる局面て、 C 言語には , いくつかの基本型があり , そ を適用した型に対して , さらにポインタを あろうか ? こから派生型を導出することがて、きるよう 適用すると int * * すなわち "int へのポイン Column3 不完全型 配列を「正しい」型として扱う局面では , といった具合で , 大きさを指定する必要の その大きさは必ずわかっていなければなら ないこともある。このような , 大きさ不明 ない。しかし , ほかのソースファイルで定 の配列 , 内容 ( メンバ ) 不明の構造体・共用 義された配列を利用するための宣言は , 体を不完全型と呼ぶ。 extern int X [ ] Column4 配列名 ソースプログラム中に , 配列名が単独で intx [ 10 ] ; 現れた場合 , それは & x [ 0 ] のことである と説明したが , 例外が存在する。それは , p 「 intf("%u*n", sizeof(x) ) : は 20 と出力する。もし x を & x [ 0 ] と解釈す sizeof 演算子の被演算数として現れた場合 だ。もし int 型が 2 バイト , int へのポインタ るコンバイラであれば , 3 と出力するかもし 型が 3 バイトであれば , れないが , それは AN 準拠ではない。 配列の全要素の値を合計する ( 配列へのポインタ ) List 1 : double sumup(double (*x)C4]) 6 : 7 : int main(void) double goukei ; double x ロ = { 1.5 , 3. 7 , 0.4 , 15.6 } : = sumup(&x) ; goukei printf( ”合計 =%fYn ” 20 : } int double sum for (i return (sum) : i 十十 ) goukei); return ( の : 元配列とポインタ 126 C MAGAZINE 1993 7
) フ Dr. 望洋の タへのポインタ〃型となる。ほかにも例をあ げると , ポインタの配列 , 配列へのポイン タ , それらをメンバとして持っ構造体・・ したがって , 多元配列を作るには , 配列 を二度適用することになるのて、ある。たと えば , int 型の 4X3 の 2 元配列は , 『「、、 int ″を要素とする大きさ 3 の配列」を 要素とする大きさ 4 の配列』 →→『』と 2 段階 といった具合て、あり , に導出した派生型て、あることがわかる。 の 2 元配列を図示したものが , Fig. 2 て、あ る。一般に , イメージ的に表現すると (a) の ようになるが , 記憶領域上には (b) のように 直列に格納される。 さて , その配列の各行を合計して表示を 行うのが List 5 て、ある。 簡単なプログラムなのて、 , 本連載の読者 て、あれば , 大まかには理解て、きるて、あろう。 ちなみに実行結果は Fig. 3 て、ある。 これて、ようやく , 「明解 ANSIC 言語入門 講座」連載時に送られてきた質問にお答えて、 きるようになった。それは八王子市の渡瀬 さんからのものて、ある。 「 2 元配列 x を渡すとき , x は & x [ 0 ] であ り , & x [ 0 ] [ 0 ] のことではない」とのこ とだが , 理解できない。これらはどう 違うのか ? さて , List 5 のプログラムにおいても , m a ⅲ関数が sum 関数に渡しているのは , &xCOJ て、あり , 決して & x [ 0 ] [ 0 ] て、はない。その理 由を示そう。 (a) 最初に説明したように , 単独て、現れた 配列名は , その配列の先頭要素へのポ インタて、ある。 (b) ここて、 , x は何の配列て、あるかを考えよ う。先ほど説明したように , x は『「、、 in t クを要素とする大きさ 3 の配列」を要素と する大きさ 4 の配列』すなわち「 int 」を要 素とする配列て、はなく , 「、、 int クを要素と する大きさ 3 の配列」を要素とする配列 て、ある。 (c) したがって , x は , 先頭要素 x [ 0 ] への Fig. 2 2 元配列 x [ 0 ] [ 0 ] x [ 0 ] [ 1 ] x [ 0 ] [ 2 ] ⅸ臼 ] [ 0 工 ~ 社 1 コ [ 1 ] x [ 2 ] [ 0 ] x [ 2 ] [ 1 ] x [ 2 ] [ 2 ] x [ 0 x [ 3 ] [ 11 (b) 記憶領域上の実 際の 2 元配列 x [ 0 ] [ 幻 x [ 2 ] [ 2 ] x [ 0 ] [ 1 ] x [ 2 ] [ 1 ] x [ 2 ] [ 0 ] (a) イメージ的な 2 元配列 配列の各行の合計を表示する List 1 : void sum(double x ロ [ 3 ] , 3 : 4 : 6 : 7 : 8 : 9 : 13 : 1 4 : int main(void) 20 : 22 : 23 . 24 : int n) int 0 00 ・••きロ く 1 」ムロ 行 : % 5. lfYn ” i, sum); double goukei; double x ロ [ 3 ] 0 00 0 ワ 0 8 0 0 0 0 sum(), 4 ) ; return ( の ; . 3 List 5 の実行結果 Fig 0 行の合計 0.0 1 行の合計 6.0 2 行の合計 1 5.0 3 行の合計 24.0 D 「 . 望洋のプログラミング道場 127
TDVIDEO ℃ LiSt 力の方々にご協力をいただき , こて、解説 する内容をもとに作成した最新のビデオポ ードに対応する DLL を添付しました。 また , 今後新たにビデオボ、一ドが発売さ れた場合て、も , この解説を元に DLL を作成 すれば TDW が利用て、きるようになります。 各種のビデオポード上て、そのままデバッガ が使えるとなれば , 開発も容易になること て、しよう。 【注意】 Borland C 十十 2.0 や Turbo PascaI for Windows に付属している TDW 2.5 に こうした機能はありません。また , DO は , S / V 版の TDW て、は後述の理由により , この 方法は使えません。 * TDVIDEO. C - - ノーマルモード用 TDVIDEO. DLL 4 : #include くⅵ ndo . h 〉 6 : #pragma argsused 7 : int FAR PASCAL LibMain(HANDLE hlnst, WORD wDSeg, WORD wHeapSize, LPSTR lpcmd) if (wHeapSize ! ニ 0 ) 9 : UnIockData( の : return 1 : 14 : #pragma argsused 15 : int FAR PASCAL WEP(int status) 20 : / * * VideoDone 22 : 24 : int FAR PASCAL VideoDone(void) 28 : 30 : * V i deol n i t 5 32 : 。 33 : int FAR PASCAL VideoInit(void) 35 : return 0 : 36 : 、 39 : * VideoDebuggerScreen ー Turbo Debugger の画面に切り換える 40 : 41 : void FAR PASCAL VideoDebuggerScreen(void) 44 : 第 48 : 49 : 53 : / * 54 : * VideoWindowsScreen - W i ndows の画面に切り換える 56 : void FAR PASCAL VideoWindowsScreen(void) asm { / * テキスト画面の表示停止 * / 59 : mov ah, ODH i n t 18H / * グラフィックス画面の表示 * / 62 : 63 : mov ah, 40H 64 : int 18H return 1 : ー Turbo Debugger の終了時に呼び出される関数 戻り値 : 0 ( 成功 ) 、 1 ( 失敗 ) ノマルモー・用 T VIDEO 98 用の TDVIDEO は , Table 1 に示す工ク スポート関数をサポートしなければなりま せん。逆にいえば , これらの関数をサポー トするだけて、 TDVIDEO はて、きあがります。 List 1 , 2 にノーマルモードのための TDV IDEO のソースコードを示します。ノーマル モードは TDW 自身がサポートしています が , このソースコードをもとにすれば DLL が開発しやすいて、しよう。プログラムにつ いて簡単に説明します。 まず , LibMain と WEP はそれぞれ DLL の こて、は特別な作 ために必要な関数て、す。 業を必要としていないのて、 , もっとも簡単 な内容になっています。 VideoInit と Video Done は無条件て、成功を返します。 VideoIn it は TDW が起動した時点て、一度だけ呼び出 される関数て、 , ビデオポードの検出や必要 なデータの設定 , メモリの確保などをしま す。期待するビデオボ、一ドが検出て、きなけ れば , 1 ( 失敗 ) を返すようにします。 Video Done は TDW が終了するときに呼び出され る関数て、 , 確保したメモリの解放などを行 います。 VideoDebuggerScreen と VideoWindow sScreen は , それぞれ BIOS を使ってグラフ return 0 : いに - ににリリー - Turbo Debugger の起動時に呼び出される関数 戻り値 : 0 ( 成功 ) 、 1 ( 失敗 ) asm { / * グラフィックス画面の表示停止 * / mov ah, 41H int 18H / * テキスト画面の表示 * / mov ah, OCH int 18H 136 C MAGAZINE 1993 7