%第1版第9第刷
3 章 ディスクファイルの構造
システムコールを用いてディスクをアクセスする場合にどうしても必要なのは、ディスク上のデータがどのような構造を持ち、どんな方法で管理されているか、という情報です。そこで本章では、まず MSX-DOS におけるディスクとのデータのやりとりの基本単位となる "論理セクタ" の説明から始め、最終的には人間にとって最も便利な "ファイル" によるデータ管理の方法まで話を進めます。
3.1 ディスク上のデータ単位
●セクタ
MSX-DOS は、3.5 インチ 2DD でもハードディスクでも、いかなるタイプのディスクドライブも基本的にはアクセスが可能です。それらの異なるメディアを統一的に扱うため、システムコールでは "論理セクタ" をディスク上のデータの基本単位として考えます。論理セクタは 0 から順に付けられた番号によって指定されます。
●クラスタ
システムコールを使用している限りでは、前述のとおりセクタがデータの基本単位であると考えて間違いはありません。ところが、実際にはディスク上のデータは複数のセクタから成る "クラスタ" を単位として管理されています。FAT の項で述べるように、クラスタは 2 から始まる通し番号で指定され、データ領域の先頭がクラスタ #2 の位置に相当します。1 クラスタが何セクタから成るかという情報を得るためには、ファンクション 1BH (ディスク情報の獲得) のシステムコールを用います。
●クラスタからセクタへの換算
後述のディレクトリや FCB には、ディスク上のデータの位置がクラスタで示されている部分があります。クラスタで示されたこれらのデータをシステムコールでアクセスするためには、あるクラスタが何番のセクタに対応しているか、という関係を求めなければなりません。これは、クラスタ #2 とデータ領域の開始セクタが同一の位置に存在しているという事実をもとに、以下のように計算することができます。
1. 与えられたクラスタ番号を C とする。
2. データ領域の開始セクタを調べ (DPB を読む)、これを S0 とする。
3. 1 クラスタが何セクタに相当するか調べ (ファンクション 1BH)、これを n とする。
4. 求めるセクタ番号 S は S=S0+(C-2)*n の計算で得られる。
MSX-DOS では、ディスクの中のセクタを表 3.9 に示す 4 つの領域に分けています。ディスクに書き込まれたファイルゾーンの本体は "データ領域" の部分に記録され、それ以外の 3 つの領域にはデータを管理するための情報が書き込まれています。これらの位置関係は図 3.9 のとおりです。ブートセクタはかならずセクタ #0 に存在しますが、FAT、ディレクトリ、データ領域の開始セクタの位置はメディアによって異なるため、DPB を参照する必要があります。
ブートセクタ FAT ディレクトリ データ領域 | MSX-DOS の起動プログラムとディスク固有の情報 ディスク上のデータの物理的な管理情報 ディスク上のファイルの管理情報 実際のファイルデータ |
---|
表 3.9 ディスクの内容
●DPB (ドライブ・パラメータ・ブロック) とブートセクタ
MSX-DOS では、接続されている個々のドライブごとに "DPB" という領域がメモリ上のワークエリアに設けられ、各ドライブに固有の情報が記録されます。MSX-DOS は 3.5 インチ 2DD、5 インチ 2D など、どのようなタイプのディスクドライブにも対応可能となっていますが、それはこの DPB を参照して個々のドライブに対応した処理を行うことによって、メディア間の差異が吸収できるからに他なりません。
DPB に書き込まれる情報は、もともとはディスク上のブートセクタ (セクタ #0) に存在しているものであり、それが MSX-DOS の起動時に読み出されてきます。ただし、ブートセクタの内容と DPB の内容には図 3.10 と図 3.11 に示すように、データの並べ方などの点で多少の違いがあります。
DPB をアクセスするためには、ファンクションコール 1BH (ディスク情報獲得) のシステムコールを用います。このシステムコールは、DPB が存在するメモリ上のアドレスとその他ブートセクタに書かれていたドライブごとの情報を返します (詳しい使用法については、 4 章システムコール使用法を参照)。
●FAT
MSX-DOS は、ディスクに書き込みを行う際 "クラスタ" をデータの単位とし、1 クラスタよりも大きなサイズのファイルは、複数のクラスタにまたがって記録されます。しかし、その時連続したクラスタが使用されるとは限りません。特にファイルの作成/削除を何度も繰り返した後になると、使用されなくなったクラスタがディスク上のあちこちに散在します。この状態でサイズの大きなファイルを作成すると、ファイルはまったくバラバラの位置に分割して置かれることになり、何番目のクラスタは何番目のクラスタに続いている、というリンク情報を覚えておく場所が必要になります。それが FAT の役割です。また、不良クラスタが発見された場合、以後そこをアクセスしないように位置を記録する目的にも FAT が利用されます。FAT に記録されるこのようなクラスタのリンク情報や不良クラスタ情報は、ディスクファイルを管理する上で不可欠なものであり、一部でも破損してしまうとディスク全体が使用できなくなる恐れがあります。そのため FAT はつねに複数個が用意され、万一の場合に備えています。
FAT の例を図 3.12 に示します。先頭の 1 バイトは "FAT ID" と呼ばれ、ディスクのメディアタイプを示す値 (表 3.2 のメディア ID と同じ値)、次の 2 バイトは意味のないダミー値が入ります。そしてその次から、1 クラスタにつき 12 ビットという変則的なフォーマットで実際のリンク情報が記録されます。それぞれのリンク情報を記録している 12 ビットの領域は、FAT エントリと呼ばれます。FAT エントリは 2 番目から始まることに注意してください。FAT エントリの番号は、それに対応するクラスタの番号でもあります。FAT エントリに記録された 12 ビットのリンク情報は、図 3.13 の要領で読みとってください。
リンク情報は、次に続くクラスタ番号を示す値です。もし FFFH となっている場合は、そのクラスタでファイルが終了したことを意味します。図 3.12 の例では、クラスタ #2 → クラスタ #3 → クラスタ #4、という 3 クラスタ分の大きさのファイルと、クラスタ #5 → クラスタ #6、の 2 クラスタ分のファイルが存在していることがわかります (クラスタ番号の小さい順にリンクしているのは図を見やすくするためで、実際には番号順である必要はない)。
●ディレクトリ
前述の FAT はディスク上のデータの物理的な位置関係を表すものであり、そこに書き込まれたデータ内容に関する情報は含んでいません。したがって、あるファイルがどのようなデータから構成されているかを知るには、FAT とは別の情報源が必要です。これが "ディレクトリ" です。ディレクトリは 32 バイトで構成され、図 3.14 に示すようにファイル名、ファイル属性、作成の日付、作成の時刻、ファイルの先頭クラスタ番号、ファイルサイズの各情報を記録しています。
ディレクトリ中にある "ファイル属性" は、ファイルに不可視属性を設定するもので、このバイトの下から 2 ビット目を "1" にすると、そのディレクトリで指定されるファイルはシステムコールではアクセス不可能となります (図 3.15 参照)。ファイル属性バイトは MS-DOS のディレクトリにも同じものが存在し、そちらでは 1 バイト中の他のビットを用いて書き込み禁止属性などを設定することもできるのですが、MSX-DOS にはその機能は備わっていません。
日付と時刻は、図 3.16、3.17 に示すように、それぞれ 2 バイトの領域を 3 つめのビットフィールドに分割して記録しています。"秒" 用のビットフィールドが 5 ビットしかなため、時間の最小単位は 2 秒となっています。"年" は 7 ビットに 0〜99 の値を設定することで、西暦 1980〜2079 年を表します。
このディレクトリ情報が実際に記録されている場所が、ディスク上のディレクトリ領域です (図 3.9 参照)。その位置 (先頭セクタ) は、DPB に記録されています。ディレクトリ領域には、図 3.8 に示すように 32 バイトごとにディレクトリ・エントリ (ディレクトリの格納場所) が並んでいます。ファイル作成を行うと、使われていないディレクトリ・エントリの中で、番号がいちばん小さいところに目的のファイルのディレクトリが作られます。ファイルが削除されると、該当するディレクトリ・エントリの最初の 1 バイトに E5H が書き込まれ、そのディレクトリ・エントリが空いたことを示します。ディレクトリ・エントリがすべて使用されてしまうと、データ領域がいくら残っていても新しいファイルを作ることはできません。ディレクトリ・エントリの数、すなわち 1 枚のディスク上に作成できるファイルの数も、DPB に記録されています。
3.2 ファイルのアクセス
●FCB (ファイル・コントロール・ブロック)
ディレクトリ領域に記録された情報を用いると、データを "ファイル" として扱うことが可能となります。この方法の特徴は、データの位置をセクタ番号やクラスタ番号のような具体的数値で表現するのではなく、"名前" を用いてファイルを指定できるという点にあります。目的のファイルがディスク上のどのアドレスに存在しているか、ということはすべてシステムコールにまかせ、人間はただファイル名を指示するだけでそれをアクセスできるのです。この時、ディレクトリとともに大きな役割を果たす者が FCB です。
FCB は、システムコールを用いてファイルを扱う際、必要となる情報を格納しておく領域です。ひとつのファイルを扱うごとに図 3.19 で示すような 37 バイトのメモリが必要となります。FCB はメモリ上のどこに置かれてもかまいませんが、MSX-DOS の機能を活かすため、005CH のアドレスがしばしば用いられます。
・ドライブ情報 (00H)
ファイルの存在するディスクドライブを示します。
(0 → デフォルト・ドライブ、1 → A: 、 2 → B: ……)
・ファイル名前部 (01H〜08H)
ファイル名前部は最大 8 文字まで指定可能です。8 文字に満たない場合は、その分がスペース (20H) で埋められます。
・拡張子 (09H〜0BH)
拡張子は最大 3 文字まで指定可能です。3 文字に満たない場合には、その分がスペース (20H) で埋められます。
・カレントブロック (0CH, 0EH)
シーケンシャル・アクセスの際、参照中のブロック番号を示します (4 章のファンクション 14H、15H、24H 参照)。0CH が下位バイト、0EH が上位バイトを表します。
・レコードサイズ (0EH〜0FH)
1 回のアクセスで読み出しあるいは書き込みを行うデータ単位 (レコード) のサイズをバイト数で指定します (ファンクション 26H、27H 参照)。
・ファイルサイズ (10H〜13H)
バイト単位で、ファイルの大きさを示します。
・日付 (14H〜15H)
最後にファイルに書き込みを行った日付を示します。フォーマットはディレクトリに記録されているものと同様です。
・時刻 (16H〜17H)
最後にファイルに書き込みを行った時刻を示します。フォーマットはディレクトリに記録されているものと同様です。
・デバイス ID (18H)
周辺装置をファイルとしてオープンする場合、このデバイス ID フィールドに表 3.10 のような値が設定されます。通常のディスクファイルの場合には、このフィールドの値は 40H+ドライブ番号です。
デバイス名 | デバイス ID |
---|---|
COM (Consore) | 0FFH |
PRN (Printer) | 0FBH |
LST (List=Printer) | 0FCH |
AUX (Auxiliary) | 0FEH |
NUL (Null) | 0FDH |
表 3.10 デバイス ID
・ディレクトリ・ロケーション (19H)
ディレクトリ領域の中で、何番目のディレクトリ・エントリに該当するファイルであるかを示します。
・先頭クラスタ (1AH〜1BH)
ディスクにおけるファイルの先頭のクラスタを示します。
・最終アクセスクラスタ (1CH〜1DH)
最後にアクセスされたクラスタを示します。
・最終アクセスクラスタの先頭クラスタからの相対位置 (1EH〜1FH)
最後にアクセスされたクラスタの先頭クラスタからの相対位置を示します。
・カレントレコード (20H)
シーケンシャルアクセスの際、現在参照中のレコードを示します (ファンクション 14H、15H、24H 参照)
・ランダムレコード (21H〜24H)
ランダムアクセスおよびランダムブロックアクセスの際、アクセスしたいレコードを指定します。前記のレコードサイズフィールドを 1〜63 の値に設定していると 21H〜24H の 4 バイト全体が使用されますが、レコードサイズが 64 以上の場合には 21H〜23H の 3 バイトしか意味を持ちません (ファンクション 26H、27H 参照)。
●ファイルのオープン
FCB を用いてファイルをアクセスするには、まずファイルをオープンする手続きが必要になります。"ファイルのオープン" とは、システムコールのレベルでは、ファイル名フィールドだけが定義された不完全な FCB を、ディレクトリエリアに記された情報を用いて完全な FCB に変換することを意味しています。図 3.20 に "オープンされていない FCB" と "オープンされた FCB" の違いを示します。
画像ファイルが存在しません。
図 3.20 FCB のオープン前後
●ファイルのクローズ
ファイルをオープンして書き込みを行った場合、それに伴って、ファイルサイズをはじめとする FCB の各フィールドの内容も変更を受けます。この更新された FCB の情報をディレクトリ領域に戻しておかないと、次回ファイルをアクセスする際に、ディレクトリの情報を実際のファイルの内容がくい違ってしまいます。更新された FCB の情報をディレクトリに戻すというこの操作が、システムコールにおけるファイルのクローズに相当します。
●ランダム・ブロック・アクセス (レコードによるファイル管理)
MSX-DOS には、"RANDOM BLOCK READ" および "RANDOM BLOCK WRITE" という、たいへん有能なシステムコールが存在します。このシステムコールを用いると、ファイルを任意の大きさのデータ単位に分割し、その先頭から順に 0,1,2… という番号を付けて管理できます。このデータ単位を "レコード" と呼びます。レコードのサイズは 1 バイト以上であればどんな値を設定してもかまいません。ひとつのファイル全体を 1 レコードとして扱うこと (究極のシーケンシャルアクセス) も、データ 1 バイトずつを 1 レコードとして扱うこと (究極のランダムアクセス) も、128 バイトを 1 レコードとして扱うこと (CP/M 方式) も可能です。
その際、FCB の "レコードサイズ" と "ランダムレコード" の両フィールドがレコードの指定に用いられます。レコードサイズ・フィールドの値は 1 レコードのバイト数を示します。ランダムレコード・フィールドは、アクセスするレコードの番号を任意に指定できます (詳しい使用法については、それぞれのシステムコールの説明を参照)。
●シーケンシャルアクセス
(固定長レコード + カレントレコード + カレントブロックによるファイル管理)
CP/M との互換性を保つため、MSX-DOS では CP/M と同様のファイルアクセスをすることも可能です。そのひとつが、"カレントレコード" と "カレントブロック" で管理されるシーケンシャルアクセスです。この方法では、128 バイト固定長のレコードをデータの基本単位とします。ファイルのアクセスはつねに先頭から順 (シーケンシャル) に行い、アクセスしたレコード数は FCB のカレントレコード・フィールドでカウントされます。カレントレコード・フィールドの値は 128 に達すると 0 にリセットされ、その桁上がりがカレントブロック・フィールドにカウントされます。
●ランダムアクセス
(固定長レコード + ランダムレコードによるファイル管理)
CP/M との互換性を保つための 2 つ目の方法が、ランダムレコード・フィールドを用いたランダムアクセスです。任意の位置のレコードをアクセスすることが可能ですが、レコードサイズは 128 バイト固定長です。