00 ライフラリ開発技法 g. 10 に例を示しています。図て、は ABC. LIB に DE. LIB を結合しています。 D. O BJ および E. OBJ が ABC. LIB に追加され ます。 基本ライプラリへの追加 各処理系には必ずライプラリファイルが 提供されます。そのライプラリファイルの 中には , ANSI 規格て、規定された標準ライプ ラリや , 処理系独自のライプラリなどが登 録されています。このライプラリファイル のことを、、基本ライプラリ〃と呼びましよう。 基本ライプラリのファイル名の一例を Ta ble 5 に示します ( メモリモデルによってファ イル名が違ったり , 複数のライプラリファ イルが用意されている場合があります ) 。 ライプラリアンを使うことにより , 自分 て、作成したライプラリを基本ライプラリフ ァイルに登録したり , 新規にライプラリフ ァイルを作成したりすることがて、きます。 それて、は List 6 て、作成した日付の取得・設 定ライプラリを基本ライプラリに登録しま こて、 List 6 は dosdate. c というフ ァイル名て、あり , すて、にコンパイルしてオ プジェクトファイルになっているものとし ます。 各処理系て、の具体的な操作例を TabIe 6 に示します。もちろんライプラリファイル やオプジェクトファイルには , 必要ならば Fig. 11 ライプラリファイルの構成 基本ライプラリ 自作汎用ライプラリ TabIe 6 の例ては , 登録するオプジェク トモジュールがびとつだけてすが , 大規 模なライプラリ作成においては一度に登 録するモジュールが数十個程度になるこ ともあります。コマンドライン上て、 , 多 くのモジュールを指定することはて、きま せん ( MS ー DOS てはコマンドラインの文字 数に制限があります ) 。 そのために通常のライプラリアンは , 登録・削除などの操作をファイルから読 み込んて、実行する機能を持っています。 このようなライプラリアンが行うべき操 作を記述したファイルのことを応答ファ イル ( レスポンスファイル ) と呼びます。 応答ファイルの記述方法は処理系によ って違いますのて , 具体的な仕様方法な どについては処理系のマニュアルなどを 参照してください パス名の指定などが必要て、す。 コラムⅢ 応答ファイル ライプラリ プロジェクト 1 用 に関しては , 各処理系付属のマニュアルな より詳細な機能や具体的な操作方法など くまて、も一例て、す。 ここに示したライプラリアンの操作はあ どを参考にしてください 【演習 2 】 List 6 の関数および【演習 1 】で作成した 関数を基本ライプラリに追加する作業を 実際に行ってみよ。 ライプラリの構築 日付の設定・取得ライプラリを基本ライ プラリに登録しました。しかし , すべての ライプラリを基本ライプラリに登録する必 要はありません。ライプラリを一本化する と , すべての関数名や変数名などをきちん と管理しなければならず , 保守は容易ては ありません。そこて、私が実践してきた方法 は , ①頻繁に使うライプラリ ( たとえばグラフ ワンポイントアドバイス ライプラリアンによる操作をフロッピ ー上て、行うと , 想像を絶するほど時間が かかります。 RAMi•イスクやハードディ スク上て、操作を行うべきて、しよう ( 試しに 所要時間を比べてみるのもおもしろいて、 しよう ) 。 私個人としては , 高速性ゆえに OptLib を愛用しています。 プロジェクト 2 用 ライプラリ 特集ライプラリ開発技法 55
0 0 ライフラリ開 柴田望洋 工学博士 , 九州大学工学部 ) Part 1 ライプラリ開発の基礎知識 作する前に知っておきたい基礎知識を解説します。 技法が求められます。本ノヾートではライプラリを自 割コンノヾイル技法を理解するなどの基本的な知識 , や、、準″標準ライプラリとの整合性を考慮したりい分 ライプラリを自作開発する上では , 標準ライプラリ はじめに その際の注意点などを解説します。また , して , ライプラリの具体的な開発技法や , 本特集て、は , 中級者を目指す人を対象と て、なく , 自分て、開発することもて、きます。 処理系の提供するライプラリを使うだけ 関数などを集めたもの」と定義しましよう。 こて、のライプラリは「汎用性のある便利な ラリは図書館という意味て、はありません。 二 ) 。もちろん , 本特集のライプ なり「図書館」という訳語に出会ってしまい てみました。まず英和辞典を引くと , いき とは何て、あろうかと考え て、、ライプラリク 本特集記事を執筆するにあたって , 改め 1991 11 各処理系を TabIe 1 のとおり略記します。 44 C M AGAZIN E ライプラリとは 実行ファイルの作成と ライプラリ 系て、は , それぞれのファイルに対して異な ていると考えられます。 MS ー DOS 上の処理 する過程は , 大まかには Fig. 1 のようになっ のソースファイルから実行ファイルを作成 コンパイル方式の処理系て、あれば , C 言語 る拡張子が割り当てられ , ソースファイル・ オプジェクトファイル・・ ライプラリファイル 実行ファイル・・ . C ファイル . OBJ ファイル . L 旧ファイル . EXE ファイノレ といった対応て、あることが多いようて、す。 C 言語て、は , 複数のソースファイルからひ とつのプログラムを構築することがて、きま す。いわゆる分割コンパイルという方式て、 すね。 List 1 は , C 言語を習いたての頃に , およ そほとんどの人が試してみるプログラムて、 す。 5 行目て、 printf 関数を呼び出していま す。この printf の関数の実体 , すなわち定義 はどこにあるのて、しようか。コンパイル・ リンクなどの作業を行うと , 正しく実行フ ァイルを作成することがて、きますから , プ ログラマがどこかて、定義しなければならな いというわけて、はありません。 printf 関数のように , 頻繁に利用する関数 はライプラリファイルに格納されています。 Fig. 1 に戻りましよう。プログラムをコンノヾ イルするとオプジェクトファイルが作成さ れます。 printf の実体はオプジェクトファイ
ようにライプラリを作成て、きます。このよ ています。 イプラリというわけて、はありません。 C 言語 うな観点て、ライプラリを分類すると ,Fig. 3 頻繁に使用する printf や strcpy などは AN には強力な、、マクロクがありますから , マ SI て、定められているものて、す。このような のようになります。 クロもライプラリの一種と考えることがて、 ライプラリを一般に標準ライプラリと呼び きます。 既存ライプラリの利用 List 2 て、は 4 個のマクロを定義していま 通常は ANSI 規格て、定められているもの以 す。これらのマクロの機能概略を TabIe 2 に さて Fig. 3 のように分類されたライプラリ 外にも多くのライプラリが提供されます。 示します。このプログラムは , ヘッダファ の使い分けはどのようにするべきて、しよう イルとしてまとめておけば , そのファイル List 3 は , 現在のディレクトリ上のファイル か。標準ライプラリなどを一切使わず , す をインクルードすることによっていって、も 名をすべて表示するプログラムて、す。この べてのライプラリを自作・利用することも 利用て、きる , 立派な、、ライプラリク プログラムて、用いている dfind および dnext となり 関数は , 、、ファイル検索〃という非常に便利 て、きますが , こうした方法はお薦めて、きま ます。 せん。既存のライプラリを使い分ける場合 な機能を持つ LAT 独自のライプラリて、す。 処理系の提供するライプラリも , マクロ には , ANSI 規格の標準ライプラリをもっと として実現されているものが数多く存在し FILEINFO という構造体は , これらの関数 も優先的に使用し , 次いて、各処理系が供給 て、使用する型て、あり , やはり LAT 独自のも ます。実現形態て、ライプラリを分類すると する標準ライプラリ以外の、、準標準的〃なラ のて、す。 Fig. 2 のようになります。 イプラリ , そして最後にそのほかのライプ プログラマは , ANSI 規格て、ないライプラ ライプラリと ANS 槻格 ラリ , つまり自作ライプラリを使うという リも , 特別な手続きを必要としないて、 , 標 方針が考えられます。 準ライプラリと同じ感覚て、利用することが C 言語の統一を図る目的て、 , ANSI 規格が て、きます。 標準ライプラリ 定められました。 ANSI 規格は言語仕様のみ 処理系によって提供されるライプラリて、 ならず , ライプラリに関しても規格を定め 物足りなくなった場合は , 自分て、も好きな もし目的とする動作を行う ANSI 規格のラ イプラリがあるのならば , そのライプラリ 典型的な ( ? ) C プログラム を素直に使いましよう。標準ライプラリは 厳選されたものて、す。学習を目的とする場 合などはともかく ,printf や strcpy とまった く同機能の関数を作成する必要はありませ ん。移植性などの面においても大事なこと て、す。 標準ライプラリをすべて頭にたたき込む 必要はありませんが , 機会があるたびにマ ニュアルに目を通すなどして少しずっ覚え ていけばよいて、しよう。 標準ライプラリ以外の く準標準的 > ライプラリ たとえば 8086 用の処理系て、あれば , 漢字 処理や割り込みなどに関しては事実上 < 標 準 > ともいえるライプラリが存在します ( た だし一部の処理系て、ヘッダファイルなどの 仕様が違います ) 。これらのライプラリも基 本的には使用してよいてしよう。 LiSI 1 : #include く stdio. h 〉 2 : 3 : int main(void) printf ( ”初めてのプログラム \ n 5 : return( の : マクロで実現するライプラリ LiSt 1 : #define maxof(), b) 2 : #define minof(), b) 3 : 4 : #define square(x) 5 : #define qube(x) Fig. 2 実現形態によるライプラリの分類 関数形式マクロ ( ヘッダファイルて供給 ) ( ライプラリファイルで供給 ) 関数 ライプラリ 46 C MAGAZINE 1991 11
C 言語の人気の秘密のひとっとして , 「豊 富なライプラリの蓄積」があげられる。そ もそもライプラリとは何であろうか。 AN SI 規格などの処理系に標準で提供されるラ イプラリと自作ライプラリはどのように 使い分けるべきであろうか。またライプ ラリ開発の際はどのように作成・保守を 行えばよいであろうか。ライプラリにつ いて考えてみよう。 していないのだな。それて、はライプラリフ て、リンカは , 「この関数はプログラマが定義 ル中に存在しないのて、 , 次のリンクの過程 発技法丁 TabIe 1 処理系とその路称 Tu 「 bo QC Power MS LSI LAT 称 TC 十十 TC C6 C5 LS 旧 .3 L 引試食版 ァイルから持ってこよう」と考え , ライプラ リファイルから printf 関数を探します。うま く見つかればそれを抽出して結合するなど Microsoft C Ver. 6 ℃ Microsoft C Ver. 5.1 LSI C -86 Ve 「 . 3.3 LSI C ー 86 試食版 Ver. 3.2 Lattice C Ver. 4.1 処理 系 Power C Ver. 2.0. OJ Turbo C 十十 Ver. 1 ℃ 1 Turbo C Ve 「 . 2 ℃ Quick C Ver. 2.0 Fig. 1 実行ファイル作成の過程 ソースファイル ソースファイル コンバイル オプジェクトファイル オプジェクトファイル ライプラリファイル の作業を行います。これて、めて、たく実行フ ァイルがて、きあがります。 このようにリンクを行うときに , ライプ ラリファイルの中から必要な関数などが抽 出され , 実行ファイルの中に埋め込まれま す。て、すからプログラマは printf 関数の内部 構造などの実体をまったく知ることなく , いって、もどこて、て、も利用て、きます。 printf などのライプラリがまったく提供さ れていないとしたらどうなるて、しようか ? 簡単な足し算・引き算などの演算を行う だけのことて、も , その計算結果を表示する ことすらて、きません。 ライプラリのない裸の C 言語は , 何もて、き ないといっても過言て、はないて、しよう。 のことから , ライプラリの重要性がよくわ かります。 ライプラリの分類 このような printf などの、、関数クのみがラ リンク 特集ライプラリ開発技法 45 実行ファイル 抽出 結合
00 ライフラリ開発技法 そのほかのライプラリ 標準ライプラリおよび準標準的なライプ ラリをなるべく使ったほうがよいことがわ かりました。 こて、もう一度 List 3 のプログラムを考え ましよう。 dfind, dnext 関数は LAT のみが 供給するライプラリて、す。このように短い プログラムて、すが , LAT 以外の処理系て、は 2 : ような事態をどう受け止めるべきて、しよう 私としては , このような可搬性の乏しい 処理系付属のライプラリをあえて使う必要 はないと思います。 ほかの処理系への移植時に必ず問題が発 生します。自分て、うまく設計・作成したほ うがよほど都合のよいものとなります。 なお List 3 のプログラムの問題点に関して は , 後て、詳しく考察します。 コンパイルすることすらて、きません。 TabIe 2 List 2 のマクロの機能概路 TabIe 3 コンバイル結果 qube(x) square(x) minof(a,b) maxof(a,b) マクロ この 機 能 ふたつの引数 a , b の大きいほうの値を得る ふたつの引数 a , b の小さいほうの値を得る x の 3 乗を得る x の 2 乗を得る コンバイラ コンパイル結果 LAT 〇 LSI C5 C6 〇 Powe 「 QC 〇 TC X TC 十十 〇 Fig. 3 ライプラリの分類 ライプラリ AN 規格て定められた標準ライプラリ 自作ライプラリ 標準ライプラリ以外の処理系が供給するライプラリ ティレクトリ表示 ( い T ) List return( の ; flag = dnext(&buf) : printf( ” Xs}n", buf. while (!flag) { flag ニ dfind(&buf, struct FILEINFO buf; flag; int int main(void) く stdio. h 〉 #include く dos. h 〉 1 : #include 14 : 13 : 12 : 10 : 4 : 3 : name) ; ライプラリの自作 今回は「ライプラリの開発技法」を解説す るのが目的て、す。ライプラリをいかに使う のかて、はなく , いかに作るかという話題を 取りあげるべきて、す。 そこて、いよいよ本題に突入します。要す る「いかに作り , いかに保守するか」という ことて、す。 ライプラリ開発のための ウォーミングアップ ライプラリを作成するには , 分割コンパ コラム I ANSI ライププ丿の実現形態 ANSI 規格のライプラリ (offsetof, setjmp, va 系マクロを除く ) は , 必ず関数 として実現されることが要求されます。 すなわち , もしあるライプラリをマクロ て供給するとしても , 処理系は必ず関数 特集ライプラリ開発技法 47 るかどうかを確かめてみましよう。 している処理系て正しくコンパイルて、き かを TabIe 3 に示します。みなさんも使用 グラムを正しくコンパイルて、きるかどう Table 1 に示した処理系が , このプロ return ( の , (putchar) ( ' vn' ) , int main(void) #include く stdio. h > 呼び出されることになります。 呼び出すことはて、きないのて , 関数版が の回りにカッコがあるのて , マクロ版を 次のプログラムを見てください oputchar ています。 版も供給しなければならないことになっ
00 ライフラリ開発技法 らない人のために , 簡単に説明しましよう。 リンケージ ージを持ちます。すなわち , そのファイルだ 指定子を指定すると , その関数は内部リンケ 関数本体の定義において static 記憶クラス か ( 無リンケージ ) という区別のことて、す。 ( 内部リンケージ ) , 、、その場かぎり〃のもの 、、うちわ〃だけのものか ( 外部リンケージ ) , 別子 ( 名前 ) が , 外部に、、公表クて、きるものか リンケージとは関数名や変数名などの識 けに通用するものとなり , ほかのファイル からはその名前が「見えなく」なります。 sta tic の指定をしなければ外部リンケージを持 ち , その名前をほかのファイルに対して、、主 張〃すなわち、、公表〃し , ほかのファイルから もその名前が「見える」ようになります。 Fig. 8 のような分割コンパイルを行う場合 を考えます。ふたつの f 関数は内部リンケー ジを持ち , それぞれのソースファイルに独 自のものとなり , その名前はファイルの外 に、、公表〃されないのて、 , 名前の衝突とい った事態は起こりません。 Part 2 たとえば h 関数て、 f 関数を呼び出すと , ソ ースファイル 1 の f 関数が呼び出され , i 関数 て、 f 関数を呼び出すと , ソースファイル 2 の f 関数が呼び出されます。 ふたつの g 関数は両者とも外部リンケージ を持ち , 外に対して自分の名前を、、主張〃す るのて、 , リンク時に多重定義のエラーとな ります。 h 関数と i 関数はやはり , 外部に自分の名前 を、、主張〃するのて、 , ソースファイル 1 からて、 もソースファイル 2 からて、も互いに呼び出せ 実践ライプラリ開発 ファイルを活用した処理系間の壁を破る , より具体 たライプラリを快適に活用するノウハウやヘッダ に , 自作ライプラリの保守管理は重要です。作成し 実際に自作ライプラリを開発することと同等以上 的なライフ、ラリ開発技法を紹介します。 ライプラリを 作成しよう ライプラリ自作のためのウォーミングア ップは完了て、す。それて、は具体的にライプ ラリを作成しましよう。 日付の取得および設定 TabIe 4 に示します。 Table 4 にまとめたよ 行う関数群て、す。このライプラリの概要を クションコールを用いて日付の取得・設定を List 6 は私が自作した , MS ー DOS のファン うにメモ程度の簡単なものて、もかまいませ ん。ライプラリ自作の際は , 必すドキュメン トを作成するように習慣づけましよう。 人間の記憶はいい加減なものて、すから , しばらく時間がたてばすぐ忘れてしまいま す。またドキュメントを作成しておけば , 自分以外の人もすぐに利用て、きるようにな ります。 プログラム中に単にコメントを記入す るだけて、はなく , 簡単なものてもよいか ら必ずドキュメント ( 文書 ) を作成する 【演習 1 】 List 6 を参考にして , 時刻の設定・取得を 行う関数 DosGetTime, DosPutTime を作 成せよ。 ( 演習問題の解答は来月号付録ディスクに 収録する予定です ) DosGetDate, DosSetDate という汎用の 関数を作成しました。しかし , 「関数を作れ ばそれて、終わり」というわけてはありませ ん。 さらにふたつの問題点をクリアしなけれ ばなりません。 特集ライプラリ開発技法 51
00 ライフラリ開発技法 8086 ライプラリ開発の秘訣 Part 3 セグメンテーションの概念が導入されている 8086 で は , 64 K バイトの壁を意識したプログラミングが求め られますが , メモリモテルに制限されない開発は不可 能でしようか ? 8086 上での開発に役立つメモリモデ ルと fa 「ポインタ活用法を詳解しましよう。 のが存在するようて、す。高速性追求するた ログラミングが可能となります ( もっとも処 めに「スモールモデル専用」などに制約し 理系により徴妙に仕様が異なります ) 。 8086 ライプラリの開発 なくてはならない特別な事情がある場合は こて、は , 8086 ライプラリ開発のための ともかく , そうて、なければ , ユーザや読者 テクニックや細かい事項を解説します。 市販のライプラリや書籍などて、発表され には納得て、きないものて、す。 るライプラリなどには , 「スモールモデル専 メモリモデ、ルや far ポインタに精通すれ 用」「ラージモデル専用」などの制約のあるも ば , メモリモデルによる制限を受けないプ TabIe 9 メモリモテルの性質 メモリモデル タイ スモール ミティアム コンパクト ジ フ ヒュ ジ メモリモテル 8086 には , セグメンテーションの概念が 導入されており , どこにいっても ( ? ) 64K バ イトの制約がつきまといます。セグメント に関してはコラムⅦを参照してください ポインタの種類と メモリモテ丿分類 , ミディアムの各メ タイニ スモーノレ モリモデルて、は , オプジェクトを指すポイ ンタはオフセットのみ ( 2 バイト ) て、表現て、き るのて、 , このようなメモリモデルをスモー ルデータモデルと呼び , そのポインタを ne ar ポインタと呼びます。一方 , コンパクト , ラージ , ヒュージの各メモリモデルて、は , ポインタをセグメントオフセットの対 ( 4 バ イト ) て、表現しなければならないのて、 , この ようなメモリモデルをラージデータモデル と呼び , そのポインタを far ポインタ ( 一部の 処理系の一部のメモリモデルて、は huge ポイ ンタ ) と呼びます。 タ 別 称 コ S メモリモテル P メモリモテル D メモリモテル L メモリモデル H メモリモテル 〇〇〇 〇〇〇 Table 10 各処理系のサホートするメモリモテル メモリモテル LAT L 試食版 LS 旧 .3 タイニ S スモール 0 P ミティアム 〇 X D コンノヾクト 0 ジ L 〇 X フ H ヒュージ 〇 X Power QC Tu 「 bo C6 C5 0 〇 0 〇〇 0 x 〇〇 0 〇〇 x 〇〇 x 〇 x x 〇〇〇 0 x 特集ライプラリ開発技法 67
00 ライフラリ開発技法 Fig. 25 List 27 の実現 ( 間違った方法 ) ( a ) と ( b ) の CS は違うので , このように実現しても ( a ) 実行時の CS でなく , ( b ) 実行時の CS しか得られない〃 unsigned int getCseg (void) { getCseg ( ) ; (a) getCseg を呼び出すソースファイル Fig. 26 キャストによる DS 値の取り出し (void * 1 ) ↓キャスト (void far* ) セグメント DS オフセット 1 セグメント部分に DS の値がセット ↓ 1 (a) スモールテータモテル て、調査します。 終わりに (b) getCseg を実現するソースファイル セグメント DS ↓変化せず DS 1 1 オフセット (b) ラージデータモテル [ 引用・参考文献 ] [ 2 ] がよろしいのて、はないて、しようカ 大規模なライプラリの例ならば参考文献 解することがて、きたて、しようか ? だし LAT て、は 0 の値を持つ near ポインタを far ポインタにキャストしてもセグメント部が 正しくセットされないのて、 ( ヌルポインタは 特別な扱いを受けるようて、す ) , 1 という値 をキャストしています ( 0 以外だったら何て、 。 Fig. 26 参照 ) 。 も可なのて、す・・・ MS および QC て、は , この方法て、は DS 値を 得ることはて、きないのて、 , CS と同様な方法 CS を返す関数 ( ? ) 1 : #include く dos. h> 2 : 3 : uns igned int getCseg(void) List 27 大規模なライプラリの例を取り上げるこ とはて、きませんて、したが , 数多く示した小 規模な例から , 細かなテクニックなどを理 5 : 6 : 8 : struct SREGS segread(&x) : return(). (s) ; [ 1 ] 柴田望洋著 , 『秘伝 C 言語問答ポイン タ編』 , ソフトバンク , 1991 年 柴田望洋著 , ℃ 98 スーパーライプラ リ』 , ソフトバンク , 1991 年 [ 3 ] 平林雅英著 , TANSI C 言語辞典」 , 技 術評論社 , 1989 年 日本電気編 , TMS-DOS 3.3C プログ ラマーズリファレンスマニュアル Vo 特集ライプラリ開発技法 73 [ 4 ] [ 2 ]
00 ライフラリ開発技法 TabIe 12 List 25 のライプラリの機能概路 ライプラリ名 fptr(s,o) peek(s,o) peekb(). 0) poke(). 0) pokeb(s,o) のになります。 そのほかの処理系 (a) CS の調査 CS を調査するのはちょっとめんどうて、す。 アセンプラて、あれば , スモールコードメモ リモデルのとき一発て、わかるし , ラージコ ードメモリモデルて、も関数を組んてスタッ ク上にブッシュされているリターンアドレ スを調査すれば一発なのて、すが・・ は , 9 行目の宣言により s という名の SR は , こて、は C 言語のみを使って解決する方法 EGS 構造体を持ちます。 これは内部リンケ code= (segread ( & ージを持ちますから , ほかのソースファイ となりますにこて、カンマ演算子を使う理由 を考えましよう。 8086 用のほとんどの処理 ルの識別子 ( 名前 ) と衝突することはありま は , 「明解 ANSI C 言語入門講座」 ' 91 年 9 月 系には ,segread という関数が用意されてい せん。また 15 行目て、定義されたマクロによ 号を参照してください ) 。まず segread 関数を ます。 segread 関数は , <dos. h> て、定義され s. 呼び出して , り , getCseg は (segread ( & s) , s にセグメントをセットし ている SREGS 構造体に現在のセグメント値 0 と置き換、ら 0 ます ! たと、ば , てもらい , コードセグメントに相当するメ を格納する関数て、す。 getCseg ( ) ンバ cs を返します。これによって無事に現在 List 26 をインクルードしたプログラム COde Fig. 23 ポインタのセグメント値 / オフセット値を得る 機 能 セグメント s, オフセット 0 の値を持つ fa 「ホインタを作成する セグメント s , オフセット 0 のアドレスにあるワード値を得る セグメント s , オフセット 0 のアドレスにあるバイト値を得る セグメント s , オフセット 0 のアドレスにワード値を格納する セグメント s , オフセット 0 のアドレスにバイト値を格納する fa 「ポインタ (a) ポインタ s のオフセット値を得る nea 「ポインタ セグメント オフセット (unsigned) にキャスト (b) ポインタ s のセグメント値を得る セグメント オフセット (void far * ) にキャスト DS 上位 16 ピットを 取り出す 特集ライプラリ開発技法 71
00 ライフラリ開発技法 て , そのセグメントおよびオフセットを求 64K バイト以内の空間を表すためのものて、 V 日 AM のアクセス めることがて、きます。機能概略を TabIe 11 す。てすから , ある固定されたセグメント PC ー 9801 のノーマルモードて、は , 0XA000 に示します。 に対して , その先頭からのオフセットしか 0 番地からテキスト VRAM に割り当てられて Fig. 23 て、解説をしましよう (List 24 は LA 持ちません。たとえば near* インタが 0X12 います。 1 文字が 1 ワード ( 漢字を除く ) に対 T て、も動作しますが , 34 という値を持っているとしても , どこを こて、は LAT 以外の 応しています (Fig. 2 の。 基準 ( セグメントの先頭 ) としての値かがわ 処理系についてのみ解説します ) 。 List 23 は PC ー 9801 のノーマルモードて、 , からなければ , その物理アドレスを求める テキスト画面上一杯に 80X25 個文字 A を ことはて、きません。 (a) オフセットの取得 表示するプログラムて、す ( 文字の属性につい そこて、作成したのが List 24 のライプラリ (unsigned) にキャストすることによ ては考慮していません ) 。 ( ヘッダファイル ) て、す。このマクロにより , り , 下位 16 ビットを取り出すことにな , こて物理アドレス 0XA0000 を表現する方 near;tk インタ , far ポインタの両方に対し ります。 near;tk インタ , far;* インタと 法が , LAT とそのほかの処理系て、異なるこ テキスト画面一杯に ' A ' と表示 ( PC ー 9801 ) とに注意してください。 Fig. 21 に示してい るように , LAT て、は 16 進 5 桁の物理アドレス 1 int main(void) をそのまま far;* インタにキャストすればよ int 3 いのて、すが , それ以外の処理系て、は , セグ unsigned far *vram = (unsigned far * ) 4 #if defined(LATTICE) 5 メント 4 桁・オフセット 4 桁の合計 8 桁て、表し 0XA0000 : 6 #else 7 たアドレスを far;* インタにキャストする必 0XA0000000 : 8 #endif 9 要があります。 10 for (i = 0 ; i く 80 * 25 ; i + + ) Ⅱ 12 *vram 十十 return( の ; 13 List 23 LAT とそれ以外の処理系て、は far ポイン タの表現法が違う Fig. 20 テキスト VRAM ( 文字部 ) の構成図 ( 80 , 1 ) 0XA0000 ポインタとアドレス 次に , 与えられたポインタから , 実際の 物理アドレスを得る方法を考えましよう。 多くの処理系て、 FP OFF, FP SEG というラ イプラリが用意されています。しかしこの ライプラリは , ・処理系によってアドレス算出法が異なる ・処理系によっては far ポインタに対しての み正しく動作し , near ポインタでは意味 をなさない という致命的な欠点があります。 そこて、ライプラリを作成します。 far ポイ ンタて、あれば , セグメントオフセットは何 とかして求めることがて、きそうて、す。しか し near ポインタについてはどうて、しよう か。 Fig. 22 に示すように , near ポインタは ※ 1 文字が 1 ワード ( 2 バイト ) に対応 ( 80 , 25 ) ( 1 , 25 ) Fig. 21 0XA0000 . 0000 番地を指す fa 「ポインタ (void far * ) 0XA0000 LAT (void far * ) 0XA0000000 LAT 以外の処理系 特集ライプラリ開発技法 69