コンヾイラ の内部を詳解 List 1 switch 文のコード switch 文は , テープルをひいて case ラベ ルに対応するアドレスへジャンプするコー ドに展開されます。ジャンプテープルの工 ントリは , case ラベルとジャンプ先が対に なっています。実際に生成されるオプジェ クトコードに基づいて説明しましよう。 List2 の switch 文をコンパイルして得られ たオプジェクトコードが List3 て、す。 まず , switch 文のカッコ内の式を評価し て , 結果を AX レジスタに得ます ( 1 ) 。次に AX レジスタの内容て、ジャンプテープルをサ ーチして分岐します ( 2 ) 。ジャンプテープル の本体は ( 5 ) に置かれています。また , ジャ ンプテープルのエントリ数は ( 4 ) て、す。さら に , ジャンプテープルの後ろには , どの case ラベルにも一致しなかった場合の飛び先 ( つ まり , default ラベルがある場所 ) がセット されています ( 6 ) 。 このうち , ( 1 ) ~ ( 2 ) は , switch 文の頭部 〔 switch( 式 ) 〕が認識されたときに , 関数 d0Switchhead( ) によって生成されます。 switch 文の内側をコンパイルしているとき に , case 文が現れるとオプジェクトコード 中にラベルを立てます。 その case ラベルの値と出力したラベルの 値を対にしてテープルに保存します。また , その際 , case ラベルがすてにテープルに登 録ずみて、あれば , rcase ラベル 2 重定義工ラ ー』にします。この処理を行うのが , 関数 doCase( ) て、す。また , 同じように default 文についても 2 重定義のチェックを行いま す。これは , 関数 doDefau1t( ) が行います。 ジャンプテープル本体 ( 4 ) ~ ( 6 ) は , switch 文全体が認識されたときに関数 d0Swit chend( ) によって生成されます。 switch 文 内部の処理て、テーカレに登録しておいた case ラベルと飛び先ラベルの組から , ジャンプ テープルを出力します。 yacc による C コンバイラブログラミング 75 106 : 107 : 108 : 109 : 1 10 : 111 : 1 12 : 113 : 114 : 115 : for head ' ( ' expr_opt SC expr_opt SC expr_opt 1 16 : : FOR 1 17 : label,• = gensym() : 1 18 : $ $ = labe 1 gensym ( ) : push し abels() : 1 19 : 120 : break し abe 1 ね be 1 + 2 : contLabel ー label; if ( $ 3 ! = NU しい 121 : genexptop ( $ 3 ) : if ( $ 7 ! = NULL) 122 : genjump(label + I); 123 : gen labe labe l) ; 124 : if ( $ 7 ! = NU しい { genexptop ( $ 7 ) : genlabel (label if ( $ 5 ! = NU しい 125 : genbool (enBooI ( $ 5 ) , 0 , label + 2 ) : resetHeap ( ) 126 : ー FOR 127 : error 128 : resetHeap ( ) 129 : 130 : 131 : 132 : expr-opt : / * empty * / 133 : 134 : ー expr 135 : 136 : 137 : 138 : 139 : switch_head : SWITCH ・ ( ' expr rp 140 : { doSwitchhead($3) 141 : ー SWITCH error 142 : { doSwitchhead(ERROR) : } 143 : 144 : 145 : 146 : compound_stmt 147 : { yyerrok 148 : stmt-l ist rb 150 : breakLabe 1 b 引 + 1 : contLabel genlabel( label) : genbool (enBool ( $ 3 ). 0 , b 引 + 1) : resetHeap ( ) ー WHILE error resetHeap() label; gensym ( ) : switch 文のソース List 2 1 よワ 0 っ 0 4 - ト 0 CD ″ー 8 0 11 int し X , sw i tch ( i ) { case 1 : case 2 : case 100 : X default: X break; break; = 100 : break; = 99 : break;
コンヾイラ の内部を詳解 switch 文はネストすることが可能なた識されるとブッシュ , switch 文の処理が終 けを重複チェック対象にて、きます。これら わるとポップします。このメカニズムによ め , case ラベルーーー飛び先ラベル対応テー の swit 曲文関連の関数は , stmt. c て、定義さ プルもスタック構造にして , switch 文が認 って , つねにいちばん内側の case ラベルだ れています。 switch 文のオプジェクトコード (List 2 をコンバイルしたもの ) goto 文とラベル near 最後に残るのが悪名天下にとどろく goto bp, sp 文て、す。 goto 文のラベルは , 専用のシンポ ax, word ptr —i ルテープルによって管理します。ラベルを 登録する関数は regLabeI( ) , サーチする関 数は searchLabel( ) て、す。 また , 関数処理終了時には endFunc() が , 未定義のラベルの有無をチェックし , もし未定義のラベルがあれば工ラーメッセ ージを表示します。これら 3 つの関数は stmt. c て、定義されています。 最後に これまて、 8 回にわたって , コンパイラの 仕組みについて勉強してきました。筆者に とって 8 回の連載はたいへんな長丁場て、し たが , 顧みると取り上げきれなかった話題 もたくさん残されています。これは , 筆者 の能力が至らなかったのはもちろんて、すが , コンパイラのもっ奧行きの深さを示すもの , ともいえるて、しよう。 この連載が , コンノヾイラというプラック ポックスの仕組みを理解するうえて、 , 少し て、も役立てば筆者の苦労もむくわれるとい うものて、す。苦労といえば , 私事になりま すが , まがりなりにも完結まて、こぎつけら れたのは , 家族の理解 , 協力そして辛抱の おかげて、す。この場を借りて感謝の気持ち を表したいと思います。 最後になってしまいましたが , 最終回ま て、おっき合いいただいた読者の皆様 , 本当 にありがとうございました。 なお , 来月号からはアルゴリズムとデー タ構造を解説する連載を始めます。こちら の連載もよろしくお願いします。 List 3 -4 1 1 よっ 0 -4 -. 0 C..D 行ー 8 9 0 11 っ乙っ 0 -4 - -0 C.D ー 8 0 14 っ 0 っ 0 -4 C..D 々ー 8 9 0 -1 っ 0 っ 0 4 ′ 0 6 ー 8 9 0 1 り 0 00 -4 eD ー 8 9 0 1 つな -4 -0 ー 8 1 よ、 1 1 よ・ 1 1 よ 11 1 よ 11 1 よ 1 っ 0 っ 0 っ 0 っ 0 っ 0 っ 0 ワ】っ朝っ 0 っ 0 CO っ 0 っ 0 っ 0 っ 0 っ 0 00 っ 0 っ 0 00 4 -4 4 、 4 ・ 4 -4 4 -4 4 -4 戸 0 ド 0 -- 0 ′ 0 0 戸 0 -0 ′ 0 -. 0 proc push mov ma 1 n ( 1 ) switch 文の式を評価して結果を AX レジスタに得る mov cx, cs:@2 bx, offset cs:@3 ax,cs:[bx] @6 cs: Cbx + 2] bx, 4 cs: [bx] mov mov Jne Jmp add ー 00P Jmp AX レジスタの内容でジャンプ 2 テープルをひいて分岐する case 1 : word ptr -x, 1 break,• case 2 : mov Jmp word ptr _x, 2 break,• case 100 : mov Jmp = 100 : X break; default: = 99 : X break; switch 文で break しない場合は , ( 3 ) こにくるので , このジャンプ 命令でテープル部分をスキップする ジャンプテープルのエントリ数 4 ) ( case ラベルの数と等しい ) ジャンプテープル本体 case ラベル飛び先 1 2 100 default のときの飛び先 switch 文の出口ラベル word ptr -x, 100 mov Jmp word ptr x, 99 mov JIIP JTIP 3 dw 2.08 100 , 四 010 POP ret endp _mam 76 CMAGAZINE 19 5
は , 「条件が成立している間 , 文を繰り返し 実行する」ということて、すから , これを if と goto を使って表現すると次のようになりま す。 100P . if ( 条件が偽 ) then goto exit 文 goto 100P exit : また , この while 文の内部て、 break 文が現 れたら , ラベル exit にジャンプすればルー プから脱出て、きます。 continue 文に対して は , ラベル 100P にジャンプすればよいこと になります。 同様に , ほかの制御構造を展開したもの をまとめて Fig. 1 に示しましよう。このひな 型に従って , 比較命令と条件分岐命令 , そ して無条件分岐命令をはさみこめば , 制御 構造のコード生成がて、きます。 ループや switch 文が多重にネストされて いるときには , continue 文や break 文は , その時点て、のもっとも内側のループ , また は switch 文に対して作用します。これを実 現する方法を次に説明します。 グローノヾル変数 breakLabe1, cont LabeI に , それぞれ break 文 , continue 文 1 : 2 : 3 : 4 : 6 : 7 : 8 : 9 : 10 : 12 : 13 : 15 : 20 : 22 : 23 : 24 : 28 : 29 : 30 : 33 : 34 : 35 : 39 : 40 : LiSt 文のセマンティックアクション ( cm. y より抜粋 ) 1 stmt_l i st stmt : / * empty * / stmt_l ist stmt { yyerrok; resetHeap ( ) stmt_l ist error { resetHeap ( ) compound_stmt expr SC { expstmt($l) : resetHeap ( ) i f_head stmt pushLabe I s ( ) : label $ $. labe 1 labe に { int p 叩 Labels ( ) genlabel()l + 1): { genjump($l) : whi le_head stmt { genlabel()l + 1) ・ stmt genlabel($l) { genjump()l + 1) : if_head stmt ELSE { genlabel($l) ・ = gensym() : label + 1 : breakLabel gen labe I (labe l) stmt WHILE ' (' expr rp sc { genbool (enB001 ( $ 6 ) , $ 2. label, genlabel($2.labeI + 1) : popLabe ( ) : for-head stmt { genjump($l) : genlabel()l + 2 ) : popLabeI s ( ) : switch_head 3tmt { doSw i tcher,d ( ) の : gensym ( ) : contLabel label; コンノヾイラ switch 文の外なのて、 , 工ラーになる ) 。 だけて、すみます ( 値が 0 だったら , ループや contContlabel へのジャンプ命令を生成する 文に遭遇したときには , たんに breakLable, 以上の方法によって break 文 , continue ます。 ルを breakLabeI, contLabe1 に復帰させ るときに , 先ほどスタックに退避したラベ をセットします。ループ / switch 文が終了す このループ / switc れ文用に発生させたラベル す。その後 , あらためてこれらの変数に Label, contLable をスタックに退避しま ループ / switc れ文に入ったときに , break は 0 をセットしておきます。 文の内側て、ないときには , これらの変数に の飛び先をセットします。ループや switch の内部を詳解 yacc による C コンバイラブログラミング 73 の値は , for 文本体の実行後にループの先頭 セマンティックスタックに積まれます。 り , ノンターミナル for head の値として , ベルの値は $ $ に代入されています。つま 確保されたことになります。また , このラ 1 , label 十 2 の 3 つのラベルがこの for 文用に す。ラベルは整数て、表され , label, label 十 文の展開に必要なラベル 3 個を発生させま 118 行目は , gensym( ) を 3 回呼び出して for という部分が認識されると起動されます。 for ( 初期化 ; 条件 ; 再初期化 ) が for 文の頭部のアクションて、 , これは たのが List1 て、す。 115 行目から 126 行目まて、 cm. y から , 文に関係する部分を取り出し 取り上げてみます。 しよう。例として , いちばん複雑な for 文を 処理がどのようになっているかを見てみま それて、は , 実際に制御構造のコード生成 fo 「文のコード るのが popLabels( ) 関数て、す。 ブッシュするのが pushLabe1s( ) , ポップす 用のスタックは stmt. c て、定義されています。 この break 文 , continue 文のラベル管理
工ル・エス・アイジャパン 0 「 m 面 00 from (ompiler ma 「 5 LSI C -86 LSI C -80 今回はわかりにくいコンパイル 工ラーやコーディングミスのなか て、 , とくに多い質問を集めました。 0 Warning . function assumed printf' undefined to be int なというコン′くイ ) レエラ ーがでるのですが。 A 未宣言の関数を呼び出すとこ のエラーがてます。 printf のよう なライプラリの場合は各ライプラ リのプロトタイプ宣言がヘッダフ ァイルに入っていますのて , 必要 なヘッダファイルをインクルード (#include) してください また , ライプラリてない場合は プロトタイプ宣言が必要になりま す。同一ソース内に関数が定義さ れていても , 参照よりも下に関数 が定義されている場合はこのエラ ーになります。どうしてもこの工 ラーがうるさいという場合は , -w オプションて、エラーメッセージが てなくなります。 Q&A List 1 0 以下のソースで•Warning illegal pointer combination ( = ) ″というコンパイルエラーが でるのですが。 char * p; p = 0X8000 ; ポインタと整数間の代入また は比較を行うとこのエラーになり ます。この場合は以下のようにキ ャストします。 (char * )Ox8000; 0 ー v a ー u e 「 e q u i 「 e d (XXX)& というコン′ヾイルエラー がでるのですが。 代入て、きないものに代入演算 子または & 演算子を摘要した場合 にこのエラーになります。とくに 以下のように , 誤って配列名に & 演 算子をつけるとこのエラーになり 将来的にはこの警告ェラーのみ を出力しないように指定する -wi オ プションをサポートする予定てす。 A P A ます。 Char X gets(&x); [ 128 ] ; ② " ・ #def i ne ABC ABC) printf("hello, worldYn") : : testl. Obj test. exe lcc -0 test. exe testl. Obj getchar()) { switch (c printf(" 改行 Yn"); break: defalt: putchar(c) ; break; 0X10 : getx(n + + ) ー (getx(n) くく 8 ) : #code= 0 #data=8000 test. hex tck test -froml ib #CODE= 0 #DATA=OAOOO test. hex tck test - f 「 0 引 ib #CODE= 0 #DATA=AOOO test. hex tck test -froml ib 0 List ト①のソースの if 文の行で syntax e 「「 0 「 near tax error near ' ) ' ″とし、うコン / ヾ イルエラーがでるのですが。 A もちろんこれは # define の最 後の、、 ; 〃が不要なのて、す。 # define 文がエラー箇所の近くにあればす ぐ気づくのて、すが , ヘッダファイ ルのなかにある場合や #define は 、、 ; 〃が必要だと思い込んて、いる場 合には , なかなかまちがいに気づ かないものて、す。 0 List1—②の makefile で make をかけたら•missing after に c ″というエラーになるのです が。 lcc の行の先頭がタブてない と , このエラーになります。タブ がないと make が正しくコマンド行 を認識て、きません。ェデイタてフ ァイルをセープするとき , タブを スペースにかえるように設定して ある場合は注意が必要て、す。 0 List1- ③の switch 文の default が実行されないのですが。 A defalt のつづりが誤っていま す (default が正しい ) 。この defalt は switch 文の default< はなく , 一度も参照されないラベルになり ます。したがって本当の default の A とき何も実行されません。 0 ListI ー④のソースが正しく実 行されないのですが。 A getx(n 十十 ) と (getx(n) くく 8 ) のどちらが先に評価されるかはコ ンパイラによります ( K & R 「プログ ラミング言語 C 』参照 ) 。したがっ て getx ( n 十十 ) が先に評価されると はかぎりません。このような場合 は , 以下のように 2 行にわけて評価 の順序を明らかにします。 getx(n 十十 ) ; X x ー = getx(n) くく 8 ; 0 LSI C ー 80 で List ト⑤のコマン ドファイルを与えて knil でリンクす ると•syntax e 「「 0 になるので すが。 A ROM や RAM の先頭配置は 16 進数て、指定するのて、すが , 先頭の 文字が数字て、ないとこのエラーに なります。 List ト⑥のように修正し ます。 0 LSI C ー 80 で凵 5t1 ー⑦ ) のコマン ドファイルを与えて knil でリンクす るとエラーにはならないのですが , 正しく配置されません。 A knil て、は ROM や RAM の配置 を指定する code, data は大文字て、 ないと正しく認識しません。つづ りをまちがえても同じ結果になる のて , マップファイルを出力して 配置を確認してください 152 CMAGAZINE 19 5
にもどるジャンプ命令を生成するのに利用 されます。 次に , 119 行目て、 break 文 , continue 文用 のラベルをスタックに退避させます。 120 行 目て、は , break 文のラベルを lable 十 2 に continue 文のラベルを label にします。 まて、て、 , 前準備が終了て、す。 121 行目は , 初期化式の処理て、す。 for 文 て、はカッコ内の 3 つの式のいずれも省略て、き るのて、 , 注意が必要て、す。初期化式が省略 された場合は , たんに初期化を行わないと LiSt 42 : 43 : 44 : 45 : 46 : 48 : 49 : 50 : 52 : 53 : 54 : 55 : 56 : 58 : 59 : 60 : 62 : 63 : 64 : 65 : 66 : 67 : 68 : 69 : 7 72 : 74 : 75 : 77 : 79 : 80 : 82 : 83 : 84 : 85 : 86 : 88 : 89 : 90 : 92 : 93 : 94 : 96 : 97 : 98 : 99 : 100 : 101 : 102 : 103 : 104 : 105 : 1 いうだけて、す。したがって , こて、は , ま ず $ 5 が NULL て、ないことを確認してから genexptop( ) を呼び出します ( 式が省略され たときは , NULL がセットされている ) 。 122 行目から 124 行目は , 再初期化式の処 理て、す。 Fig. 1 ( 5 ) からわかるように , 初期 化式の次には再初期化式が置かれ , 初期化 式を実行した後はジャンプて飛び越せます。 また , 再初期化式が省略されているときに は , Fig. 1 ( 6 ) のように , 再初期化式を飛び 越すジャンプ自体が不要になります。 125 行目は , 条件判定を行う部分て、す。条 件式を enBool ( ) によって , b001 型に変換し てから genbool ( ) に渡して真の場合は下に抜 け , 偽の場合はラベル label 十 2 にジャンプ する条件分岐命令を生成させます。条件式 が省略されているときには , 条件判定を行 いません ( Fig. 1 ( 7 ) ) 。 126 行目て、は , for 文先頭部の処理を終 え , 式のヒープ領域を解放します。 for 文の本体 ( for 文先頭部の後ろにくる 文 ) が認識されると , 36 行目 ~ 39 行目のアク ションが実行されます。 37 行目て、は , ルー プの繰り返しジャンプを生成しています。 38 行目は , ループ出口 ( break 文の飛び先 ) の ラベルを発生します。最後に 38 行目て、 , po pLabels( ) を呼び出して , break 文 , con tinue 文の飛び先のラベルを以前の状態に戻 して終了て、す。 0 CASE expr { doCase ( $ 2 ) stmt ー DEFAULT { doDefauIt() stmt BREAK sc { if (breakLabeI error("break outside 叩 /switch") : genjump (break し abel) ・ CONTINUB sc { if (contLabeI error("continue outside 100P " ) : genjump(contLabeI) RETURN sc { genjump(exitLabel) ・ RETURN expr sc { if ($2->optype ! = !eqType(currentFunction->type->father, error("return type mismatch") : e 1 s e { ト〉 defp = YES : else if (l->defp ー ーニ regLabel($I, YES) : ニ searchLabel ($I) ) { LABE し *l; ー I DENT I F I ER genjump(label->num) label = regLabeI ( $ 2 , (O) : if ((label = searchLabel ( $ 2 ) ) { LABEL *label; GOTO IDENTIFIER sc genjump(exitLabeI) ・ genexp(coerce ( $ 2 , } e 1 se gencode ("YtmovYtax, bxYn") : genexp($2) ; if ($2->optype ー return; ニ NULL) = NULL) error2("labeI '%s' is defined twice" stmt genlabel ( l->num) ・ return; i f_head gensym ( ) : pushLabeI s() : label I ab e I : : WHILE ' ( ' expr rp resetHeap ( ) ・ I F error resetHeap ( ) genbool (enBool ( $ 3 ) , 0 , $ $ ニ label ー gensym() : label; { i nt I F ' ( ' expr rp while head gensym() : label); gensym() : 74 CMAGAZINE 1990 5
: よる C コン、イラブログラミング 創刊特別企画 C コンバイラの内部を詳解 文のコード生成 最終回 近藤嘉雪 法を考えます。またループに関係のある いよいよ連載も最終回を迎えました。今回 break 文 , continue 文の実現法を説明 は , 文のコード生成を説明しましよう。コ します。次に , より複雑な制御構造である ンバイラ全体からすれば , 文のコード生成 fo 「文 , switch 文を実現します。最後に を行う部分は比較的小さいものです。まず if 文 , wh ⅱ e 文などの制御構造を , goto 文 「悪名高き goto 文」をインプリメントして とジャンプ命令の組み合わせに展開する方 Cm コンバイラの完成となります。 Fig. 1 制御構造 E, EI, E2, E3 は , 式 S, SI, S2 は , 文 LI , L2, L3 は , コンバイラが生成するラベル Lc は , continue 文でジャンプするラベル Lb は , b 「 eak 文でジャンプするラベル 文のコード生成 文のコード生成は , 式に比べるとかなり 簡単なものて、す。 Cm の文て、 , いちばん基本 となるのは , 式文て、す。式文のコード生成 は , genexp ( ) を呼び出して行います。式文 については , これて、すべてて、す。 if 文や while 文などの制御構造について考 えてみましよう。 if 文や while 文などの制御構造は構造化文 とも呼ばれ , 近代的なプログラミング言語 には欠かせないものて、す。いまや構造化プ ログラミングは , すっかり定着して常識と なっています。これらの制御構造は , 機械 語て、はサポートされていませんから , コン パイラがコード生成時に比較命令と条件分 岐命令を使って展開しなければなりません。 比較命令と条件分岐命令の組み合わせは , 級て、は föJ に一口ロ if 条件 then goto ラベル に相当します。そこて、 , まず各制御構造を if—then goto ~ を使って表現してみましよ while 文を例にとって説明します。 while 文は while ( 条件 ) 文 という形式をしています。 while 文の意味 72 CMAGAZINE 19 5 do S while (E) 4 if ( E が真 ) then goto LI Lb . fo 「 ()I ;E2:E3) S 5 90t0 L2 E3 if ( E2 が偽 ) then goto L3 90t0 し 1 if (E) ( 1 ) if ( E が偽 ) then LI g 0t0 1 2 0 LI : L3 Lb if (E) 日 S 1 else S2 if ( E が偽 ) then goto LI goto L2 S2 fo 「 ()I : E2 : ) S 6 ※ E3 が省略されている場合 L 1 : Lc ・ if ( E2 が偽 ) then goto L3 90t0 LI LI : L2 : Lb for ()I , :E3) S ( 7 ) ※ E2 か省略されている場合 got0 L2 E3 90t0 LI (E) S while 3 Lc ・ 1 NO ( if ( E が偽 ) then goto L2 goto LI L2 : Lb .
0 「 on from (ompiler ma 「 5 FIJJITSU High C CompiIer VI .4 プラが使用できますが , そのよう な機能はありますか ? High C は , ANSI 準拠のた め , 現在の MS ー C 同様に , インライ 特長と拡張機能 iler VI. 4 は , 80386 マイクロプロセ グラムのほかに , . EXE や . COM の ンアセンプラの機能はありません。 ッサ上の MS-DOS のもとて、動作す リアルのプログラムを作成するこ High C には , リンカやデバッガな るプロテクトモード用 C コンパイラ ・ 80387 コプロセッササポート とはできますか ? どが含まれていませんのて、 , 別に て , 言語仕様は ANSI 規格に準拠し ・ 3 種類の整数精度および 3 種類の 386 ー ASM TOOL KIT のパッケ ています。生成されたプログラム IEEE 浮動小数点精度 High C て、は , ネイテイプコー ージが必要てす。このパッケージ は , 80386 マイクロプロセッサ用の ・厳密な標準チェックをはじめ数 ドしか出力て、きないため , リアル に含まれるアセンプラを使用して ネイテイプコードて , TownsOS 多くのコンパイラ制御オプショ のプログラムは作成て、きません。 High C とのインタフェイスをとる (DOS-Extender) のもとて動作し リアルのプログラムの作成は , ほ ことになります。 ン ・ほかの関数にパラメータとして かのリアルコードの出力て、きるコ メモリモデルは , Small モデルの 渡すことのて、きる入れ子型関数 ンパイラまたはアセンプラを使用 製品の内容について みてすが , もともとコード用 / デー abs, min, max および fill していただくことになります。 タ用におのおの 32 ビットアドレス c れ ar などの効率を考えた組み込 空間を提供しています。 ANSI 規格 み関数 FMTOWNS High C CompiIer ソ に加えて拡張機能として pragma ・通常はメインフレーム ( 大型計算 フトウェア 0 機能 ( コンパイラオプションをソー 機 ) のコンパイラて、しか実現され MS ー C では , 64K パイトを越え ・ High C コンパイラ スプログラムから指定て、きる機 ていないような , 多くの最適化 る配列を取ることはできませんで ・ High C 標準ライプラリ 能 ) , switch/case 文の範囲指定な 機能 したが , High C ではどうなのでし ・ TOWNS C ライプラリ どの独自機能を用意しています。 ー共用サプ , 式の消去 ようか ? マニュアル ーレジスタ内容の保存および再 ・ High C Compiler ユーザーズマ 移植性 High C て、は , small モデルし 使用 ニ . ュアノレ ーデッドコードの削除 ・ C ライプラリリファレンス かありませんが , 64K バイトという コンパイラオプションあるいは ークロスジャンヒ。ング ( 不要な分 制限はありません。制限値は , 4G pragma 指定により ANSI 準拠の指 岐の除去 ) FMTOWNS 38 引 ASM TOOL KIT バイトてす。つまり , 実装てきる 定がてきます。また , 必要に応じ ージャンプ命令サイズの最小化 メモリのことを考えてみると , 制 ソフトウェア て拡張機能を使用して , ほかの C コ 一定数折りたたみ ( 定数になる処 限はまったく気にする必要のない ・ 386 ー ASM ンパイラとの互換が保てます。 理をコンパイル時に計算して 値だということがわかると思いま ・ 386 ー LINK しまう ) す (High C て、は , 64K バイトという ・ 386 ー DEBUG 信頼性と効率 一変数レジスタ自動割り当て 制限に縛られることなく , メモリ ・ 386 ー LIB の許す範囲て、あれば , 好きなだけ マニュア丿レ 関連製品 High C は ANSI て、新たに規定さ 配列を使用することがてきます。 ・アプリケーション開発キットユ れた強力な型チェック機能を用意 それだけても , どれだけプログミ ーザーズガイド しています。さらに , コンパイラ High C Compiler を使用してア ングが楽になるかは容易に想像て、 ・ 386 ー DOS-Extender ユーザーズ は通常は独立した "lint" ( シンタ プリケーションを開発するために きるかと思います ) 。 - マ・ニ . ュアノレ ックスチェッカ ) プログラムて : 得ら は , 別売の 386 ー ASM TOOL ・ 386 ー ASM TOOL KIT ューザ れるチェック機能をたくさん提供 KIT に入っている 386 ー LINK が必 ーズマニュアノレ しています。このため , ひとつの 要てす。 ・ TownsBIOS リファレンス 言語て信頼性と効率の両方がかな 154 CMAGAZINE 19 % 5 A Tu 市 oC ではインラインアセン 0
挿入 / 上書きモードを切り替えます。カー ソルが漢字第 2 バイトにある場合には , 第 1 バイトになるように調整します (List5) 。 [ ] キー処理 —delete 関数ー カーソル位置の 1 文字を削除します (List6)0 まず , カーソル位置より後ろのバ ッフアをチェックします。余白だけの場合 には , 何もしません。カーソル位置が漢字 第 2 バイトの場合には , 第 1 バイトに位置づ けます。 カーソル位置の文字の種類によって , 削 除するバイト数を変えます。 ANK の場合に は , 1 バイト前へ文字列をずらします。漢字 第 1 バイトの場合には , 2 バイト前へずらし ます。削除て、は , 挿入とは逆に , 先頭文字 からずらします。ずらしたぶんだけ maxptr を減じます。最後にバッフア内の文字列を 再表示します。 サンプレプロクラムにつして 今回作成した関数をテストするプログラ ムは , List7 のようになっています。 プログラムの先頭て、 , INS および DEL の 割り付けを変更して , さらに出口て、割り付 けをもとに戻しています。 case4 は ,get key str 呼び出し用の関数 て、す。まず , 入力 / 訂正モードを指定しま す。 0 が入力 , 1 が訂正モードて、す。次に入 カ文字数をバイト単位て指定します ( 60 バイ ト以内 ) 。バイト数に応じて桁数のスケール を表示します。 get key str を呼び出して , 入力させま す。入力モードて、は , 入力域は空欄て、すが , 訂正モードて、は文字列を表示します。入力 後に ret を押すと ,get key str が返してき た文字列を表示します。入力域の内容と同 じて、あれば正常て、す。 訂正モードを実行する場合には , 続けて 実行してください List 7 98 : 100 : 101 : 102 : 103 : 104 : 105 : 106 : 107 : 108 : 109 : 1 10 : 112 : 113 : 114 : 115 : 119 : 120 : 121 : } put ー str ( 25 , 14 , " 処理番号を指定してください。 " , 28 ) : put ー str ( 0 , 16 , " 処理番号 = ". 1 の : color .ORG/ WHITE) : gets(str) : workno=ato i (str) : switch(workno) case 1 : casel ( ) ; break; case 2 : case2() : break : case 3 : case3() : break; case 4 : case4() : break : whiIe(workno!=9); key-asgn ( 1 ) : / * キーポード定義復旧 * / ( ライン N 0 . 1 2 2 ~ 2 2 8 は 4 月号と同じなので省略 ) 229 : 231 : 232 : 233 : 234 : 235 : 236 : 237 : 238 : 239 : 240 : 241 : 242 : 243 : 244 : 245 : 246 : 247 : 248 : 249 : 250 : 251 : 252 : 253 : 254 : 255 : 256 : 257 : 258 : 259 : 260 : 261 : 262 : 263 : 264 : 265 : 266 : 267 : 268 : 269 : 270 : 271 : 272 : 273 : 274 : 275 : 276 : 277 : } VO i d case4 ( ) 230 : / * case4 Char ichr[2] , buffer[80) , work[20] : static char 1 static char sca 厄 10 [ 60 ] ニ 2 sc 引 e [ 60 ト 3 4 5 { " 123456789012345678901234567890123456789012345678901234567890 " } : int count, mode, i : Char for ( i ニ 0 : i く 80 : i + + ) buffer[i]=B し ANK; cls(); / * 全画面クリア * / color(REV,SKY) : put-s tr ( 21 , 2 , " ☆☆☆キー入力ルーチンテスト☆☆☆ " , 38 ) : put-str(30,4,"4 : 文字列入力 " , 14 ) : color(0RG, WHITE) : put-s tr ( 0 , 10 , " モード指定。入力モード = 0 / 訂正モード = 1 " , 42 ) : put-str ( 0 , 8. " I N S で挿入 / 上書きモードを切り換えます。 " , 42 ) : put-s tr ( 0 , 7 , " 訂正モード = 前回の入力データを表示して訂正 " , 42 ) : put-str ( 0 , 6 , " 入力モード = バッフアをクリアしてキー入力 " , 4 の : mode=0; if(yn= putch(yn) ; yn=getch ( ) : put-str(0,11," モード = / * 入力モード * / put_str(), 14 , scale, count) ; put-str(), 13 , scaIe10,count) : mode=l; / * 訂正モード * / e ー se count=atoi (work) ; gets(work) : put ー str ( 0 , 12. " 入力バイト数 = " , 14 ) : return : wh i I e (yn== ' Y ' Ⅱ yn== ' y ・ ) : yn=getch ( ) : put ー str ( 0 , 19 , " 続けますか (Y/N) ? " , 22 ) : put-str(), 17 , buffer, count) : put-s tr ( 0 , 1 6 , " 入力データを確認してください " , 28 ) : / * キー入力 * / get-key-str(), 15 , buffer, count, mode) : 106 CMAGAZINE 19 5