1: 2005-07-21 (木) 15:48:00 |
現: 2024-01-06 (土) 22:39:10 |
| | DW| 2| ;ヘッドの数はいくつか(一般に1440KB-FDの場合は2)| | | | DW| 2| ;ヘッドの数はいくつか(一般に1440KB-FDの場合は2)| |
| | DD| 0| ;このBPBセクタはデバイスの先頭から数えてどこにあるか(HDDのパーティション開始位置を表すために用いられる、セクタ単位、一般に1440KB-FDの場合は0)| | | | DD| 0| ;このBPBセクタはデバイスの先頭から数えてどこにあるか(HDDのパーティション開始位置を表すために用いられる、セクタ単位、一般に1440KB-FDの場合は0)| |
- | | DD| 2880| ;| | + | | DD| 2880| ;セクターが全部で何個あるか| |
| | DB| 0,0| ;よくわからないけどとりあえず0にしておくといいらしい| | | | DB| 0,0| ;よくわからないけどとりあえず0にしておくといいらしい| |
| | DB| 29H| ;よくわからないけどとりあえず0x29を書くらしい| | | | DB| 29H| ;よくわからないけどとりあえず0x29を書くらしい| |
| | | |
| |IPL|予約領域|FAT領域|ルートディレクトリ領域|データ領域| | | |IPL|予約領域|FAT領域|ルートディレクトリ領域|データ領域| |
| + | |
| + | この各領域はおのおのどのくらいあるのでしょう? |
| + | |
| + | 本来は、IPL以外は可変できるような構想だったようです。逆に言えば、IPL以外の領域は、先のBPBの値から計算しなければいけないものということになります。 |
| + | |
| + | ただ、こと「WindowsやDOSで作ったFAT12のFD」に関しては、事実上決め打ちと考えていいようです。念のため、計算法をご照会。 |
| + | |
| + | -IPL |
| + | --これはセクター0と決め打ちされています。大きさも1セクタ(512バイト)です。 |
| + | |
| + | -予約領域 |
| + | --おそらくなにか特別な用途に使うために用意されたものでしょう。ファイルとしては絶対にアクセスできない領域になるようです。算出は、「FATがどこから格納されているか」の数字で求めます。IPLはセクター0。で、この数字を例えば10にすると、FAT領域はセクター10からとなります。そうすると必然的に、セクター1〜セクター9が予約領域となり、通常は使うことができない特殊な領域となります。 |
| + | --上記の例では「1」ですね。つまり、FAT領域はセクター0の次から(セクター1から)ということになり、事実上予約領域は0となります。 |
| + | |
| + | -FAT領域 |
| + | --ファイルの情報(データではない)が格納されます。ここは、「FATの長さ(1つ分の長さをセクタ数で)」の値から解ります。9です。つまり、上記のスタート地点(セクター1)から9個分のセクターを使うということになります。なので、「セクター1〜セクター9」ということになります。 |
| + | --さらに、「FATがいくつあるか」という情報も参照します。2ですね。もう一個は予備用らしいですが、ちゃんと領域を喰います。二つ目の予備FATも同じく9個なので、「セクター10〜セクター18」となります。 |
| + | --結果、FAT領域は、「セクター1〜セクター18」となります。 |
| + | |
| + | |
| + | -ルートディレクトリ領域 |
| + | --文字通り、ルートディレクトリにあるファイルの情報を格納するところです。 |
| + | --FAT12では、ルートディレクトリにはBPBで決められています。「ルートディレクトリの長さ」の値です。00e0h、十進数に直すと224です。つまり、ルートディレクトリには、0〜224番までの225個しかファイルを置くことができません。逆に言えば、この領域はあらかじめ225個分のファイルのデータを格納できなくてはいけません。ファイル一個に使用するデータは32バイトです。つまり 32*225=7200バイト。7200バイトは何セクタで格納できるでしょう?。 7200÷512=14.0625。つまり、14セクターあれば全部格納できます。(端数がなぜ切り捨てになるのかちょっと解りません。調査中・・・) |
| + | -上記の例で、FAT領域は「セクター1〜セクター18」なので、ルートディレクトリ領域は「セクター19〜セクター32」となります。 |
| + | |
| + | -データ領域 |
| + | --実際にファイルの内容がバラバラに分解されて書き込まれる領域です。スタート位置は上記から必然的に、「セクター33から」となります。 |
| + | --では、いくつまであるのでしょう。「このドライブ(FDの場合はディスク、HDDの場合はパーティション)の総サイズ(セクタ数で)」という所に総数が書かれています。「2880」ですね。なので、データ領域は、「セクター34〜セクター2879」ということになります。 |
| + | --※セクターは0から始まりますので、2880個だと、最後の番号は2879になります。 |
| + | |
| + | |
| + | つまり、こうなります。 |
| + | |
| + | |IPL|予約領域|FAT領域|ルートディレクトリ領域|データ領域| |
| + | |0|--|1〜18|19〜32|33〜2879| |
| + | |
| + | |
| + | こう考えると、例えばカーネル(OS本体)やブートローダーを起動時に読み込ませるにはこういう手順になります。 |
| + | |
| + | +カーネルやブートローダの大きさをとりあえず決める(例えば64KBまでとします。) |
| + | +64KBとはすなわち、1024*64=65536バイト。512で割れば、65536÷512=128。すなわち128個のセクターが必要となります。 |
| + | +データ領域はセクター33からですから、「セクター33〜セクター161」までにカーネルやブートローダーを書き込みます。 |
| + | +FAT領域とルートディレクトリ領域に、「「セクター33〜セクター161」はxxx.xxxというファイルで使われている」という情報を書き込みます。 |
| + | +IPLのBPB以降の位置に、「「セクター33〜セクター161」をメモリのある領域に読み込み、実行しろ」という命令を書き込みます。 |
| + | |
| + | こういう流れになるはずです。 |
| + | |
| + | |
| + | *論理セクタと物理セクタ [#gcd8fb39] |
| + | 上記のFAT構造などで、「セクター0」とか「セクター161」とか書いてますね。でも、実はこれは「論理セクター」といい、単なる概念なのです。 |
| + | |
| + | IPLの段階では、各セクターをBIOSの命令などで読み出すことになりますが、これらの命令では、「セクター140を読め」という指定はできません。もっと物理的。すなわち、実際にFDの状態に合わせた指定が必要になります。 |
| + | |
| + | 実際(物理的)のFDは、まず丸く仕切られています。ちょうど、木の年輪・あるいは輪切りにしたたまねぎのような状態です。この輪を「シリンダ」といいます。(本当はシリンダとはトラックの集まりのことですが、FDの場合はとりあえずシリンダ=トラックでいいでしょう。) |
| + | |
| + | シリンダは内側から「シリンダ0、シリンダ1・・・・シリンダ79」という番号が振られており、0〜79まで。すなわち80本あります。 |
| + | |
| + | シリンダはさらに孤の状態に仕切られています。輪切りにしたたまねぎを、さらにケーキを切るように切っていったと考えてください。こうして切られた1つひとつを「セクタ」と呼びます。 |
| + | |
| + | ※シリンダは0から数えますが、なぜかセクタは1から数えます。 |
| + | |
| + | セクタは1〜18まで。つまり、1つの輪(シリンダ)が18個のセクタに仕切られます。 |
| + | |
| + | さらに、通常のFDは、これが裏表にあります。 |
| + | |
| + | なので、通常のFDには、80(シリンダ) × 18(セクタ) × 2(裏表)= 2880。2,880個のセクタがあるというわけです。 |
| + | |
| + | ・・・カンのいい人はもうおかしいと思いますね。 |
| + | |
| + | 「セクター161ってのは、じゃどこのことなんだよ?」と。 |
| + | |
| + | |
| + | これまで書いてきた「セクター0」とか「セクター161」という書き方は、「論理セクタ」といい、話を簡単にするための概念なのです。 |
| + | |
| + | で、上記の説明が実際のFD上での状態。すなわち、「物理セクタ」です。 |
| + | |
| + | |
| + | 「論理セクタ」で言うと、セクターは0〜2879まで。 |
| + | |
| + | しかし、「物理セクタ」は、最大で18番までしかありません。また、BIOSなどで実際に指定する場合は、この「物理セクタ」で指定してやらないといけないのです。 |
| + | |
| + | ***論理セクター0を物理セクタに換算する [#q28b6739] |
| + | 論理セクター0。(IPLですね)これを、物理セクタに換算すると、こうなります。 |
| + | |
| + | |シリンダ| 0| (一番内側)| |
| + | |セクタ| 1 | (詳しいことは省略。FDはちゃんとスタート位置があります。)| |
| + | |ヘッド| 0 | (表面のこと)| |
| + | |
| + | では、論理セクター1はっていうと、 |
| + | |
| + | |シリンダ| 0| (一番内側)| |
| + | |セクタ| 2 | (詳しいことは省略。FDはちゃんとスタート位置があります。)| |
| + | |ヘッド| 0 | (表面のこと)| |
| + | |
| + | |
| + | 論理セクター2は |
| + | |
| + | |シリンダ| 0| (一番内側)| |
| + | |セクタ| 3 | (詳しいことは省略。FDはちゃんとスタート位置があります。)| |
| + | |ヘッド| 0 | (表面のこと)| |
| + | |
| + | と、こう続いていきます。 |
| + | |
| + | で、そうなると、論理セクター17は |
| + | |
| + | |シリンダ| 0| (一番内側)| |
| + | |セクタ| 18 | (詳しいことは省略。FDはちゃんとスタート位置があります。)| |
| + | |ヘッド| 0 | (表面のこと)| |
| + | |
| + | となります。・・・じゃ、論理セクター18は?っていうと、 |
| + | |
| + | |シリンダ| 0| (一番内側)| |
| + | |セクタ| 1 | (詳しいことは省略。FDはちゃんとスタート位置があります。)| |
| + | |ヘッド| 1 | (裏面のこと)| |
| + | |
| + | と、こう続いていくわけです。もうお解りですね? (^^) |
| + | |
| + | 同じくセクタ34はどこかっていうと、 |
| + | |
| + | |シリンダ| 0| (一番内側)| |
| + | |セクタ| 18 | (詳しいことは省略。FDはちゃんとスタート位置があります。)| |
| + | |ヘッド| 1 | (裏面のこと)| |
| + | |
| + | じゃ、セクタ35は? というと・・・・ |
| + | |
| + | |シリンダ| 1| (一番内側から2番目)| |
| + | |セクタ| 1 | (詳しいことは省略。FDはちゃんとスタート位置があります。)| |
| + | |ヘッド| 0 | (表面のこと)| |
| + | |
| + | とこう続いていくわけです。 |
| + | |
| + | |
| + | |
| + | |
| + | |
| + | |
| + | |
| + | |
| + | |
| + | |
| + | |
| + | |
| + | *コードを考えてみる [#we928a72] |
| + | [[OS-WikiのBIOS一覧:http://community.osdev.info/index.php?cmd=read&page=%28AT%29BIOS]]で調べてみると、ディスクを読むためのBIOSコールは「INT 13H」であることが解る。すこし具体的にコードを考えてみよう。 |
| + | |
| + | |
| + | これまでの考察や調査(パクリとも言う・・・ :P)を元に、以下を仮定してみる。 |
| + | |
| + | -OS本体を、とりあえず最大64KBと仮定しておく |
| + | -FAT上でも仮ファイルとして成り立つようにしておきたい。 |
| + | |
| + | --FAT上でファイルとして成り立たせるためには当然FAT領域やルートディレクトリ領域にも情報を書かなくてはならないが、それはちょっとあとで・・・ |
| + | |
| + | --FAT上でファイルとして成り立たせるためには、当然「データ領域」にデータを置かなくてはいけない。データ領域の開始位置は、論理セクタで33番から。OSが64KBなら、161番までとなる。つまり、BIOSで論理セクタ33〜161までを連続で読み込み、メモリに格納すればいい。 |
| + | |
| + | --ただし、BIOSコールの詳細を見れば解るが、実際の指定は「物理セクタ」で行わなくてはいけない。 |
| + | |
| + | --実際には読み込んだデータを格納するメモリのアドレスが必要だが、これはちょっと一旦はしょっておく・・・(^^;) |
| + | |
| + | そんなわけで、まず論理セクタ33を読み込んでみる。論理セクタ33は、シリンダ2・セクタ14・ヘッド0となるので、こういうコードになるはず。 |
| + | |
| + | MOV AH,02H ;モード指定。(読み込みを指定) |
| + | MOV AL,01H ;連続処理するセクタ数(とりあえず1) |
| + | MOV CH,01H ;シリンダ番号(シリンダ1を指定) |
| + | MOV CL,14 ;セクタ番号(セクター14を指定) |
| + | MOV DH,0 ;ヘッド番号(0すなわち表を指定) |
| + | MOV DL,0 ;ドライブ番号(0すなわちAドライブを指定) |
| + | MOV ES,xxx ;データを読み込むアドレスのセグメント値 |
| + | MOV BX,xxx ;データを読み込むアドレスのオフセット値 |
| + | INT 13H |
| + | |
| + | ・・・あとはこれを繰り返していけばいいんだけど、すぐに思いつくよね。 |
| + | |
| + | ''めんどくせーーー!!!'' |
| + | |
| + | これを、ちょうどベーシックのFor〜Nextみたいにできないものだろうか・・・ |
| + | |
| + | ここで、論理セクタから自動的に物理セクタに変換して、かつ、とりあえず連番なら自動で読み込んでくれるような式を試案してみる。 |
| + | |
| + | ;AXレジスタに開始番号、DXレジスタに終了番号を入れてコールすると読んでくれる |
| + | ;なんてできないものかな・・・ |
| + | |
| + | readsec1: |
| + | PUSH AX ;AXの値を一旦バックアップ |
| + | |
| + | SUB AX,DX ;まず引き算して、AXに「個数」を入れる |
| + | MOV CX,AX ;CXレジスタに個数を入れる。(LOOP用のカウンタ) |
| + | |
| + | POP AX ;リストア |
| + | |
| + | ;ヘッダの値を計算する |
| + | PUSH AX ;AXの値を一旦バックアップ |
| + | DIV AX,2 ;値を半分に割る。 |
| + | SUB AX,1439 ;片面のセクタ数を引き算する。 |
| + | |
| + | ;・・・あれぇ。引き算の場合、マイナスになるような場合って・・・ |
| + | |
| + | あかん・・・だめだ。ちょっと整理しよう。 |
| + | |
| + | -読み込みたいセクターの位置指定を求める計算式 |
| + | --ヘッド |
| + | +++読みたい論理セクターの値 ÷ (総セクター数÷2) |
| + | --シリンダ |
| + | +++読みたい論理セクター値 ÷ セクター数(18) |
| + | --セクター値 |
| + | +++読みたい論理セクター値 mod セクター数(18) |
| + | |
| + | えーっと・・・これでいいかな? ちょっと検算中・・・ |
| + | |
| + | あークソ!だめだ。ちょっと別に式を組み立てないとわからんわい!(あったまわる・・・) |
| + | |
| + | えーっと。逆に、論理セクタを計算する式を考えてみる。 |
| + | |
| + | -論理セクタ : R |
| + | -シリンダ :c |
| + | -セクタ :s |
| + | -ヘッド :h |
| + | |
| + | だとすると・・・こうかな?? |
| + | |
| + | R = c × 18 + s +(2880/2*h) -1 |
| + | |
| + | おぉぉ! これでいいらしいぞ!!! (^^;) |
| + | |
| + | では、この式を展開して・・・・あ”だめだ。ちょっとこれじゃ解りにくいな。ちと変更する。 |
| + | |
| + | |
| + | -論理セクタ : R |
| + | -シリンダ :c |
| + | -セクタ :s |
| + | -ヘッド :h |
| + | -シリンダ内のセクター数:A |
| + | -セクター総数:B |
| + | |
| + | R = cA + s +(B/2*h) -1 |
| + | |
| + | こうだ。これを展開。(なつかすいなぁ。こんなの中学以来かも・・・(^^;)) |
| + | |
| + | |
| + | h = (R-cA-s+1)÷B÷2 |
| + | s = R-cA+1-(B÷2h) |
| + | c = (R-s-(B÷2h)+1)÷A |
| + | |
| + | よーし!これで求められるぞ! ばんざーい!ばんざーい! |
| + | |
| + | ・・・・''ダメじゃん''・・・・ orz |
| + | |
| + | あー。むずかしく考えすぎたかもしれん。こうかな??? |
| + | |
| + | ;まず、hを求める |
| + | h = R ÷ (B ÷ 2) (余りは破棄) |
| + | |
| + | ・・・うん。これでよさそうだ。 |
| + | で、これでhが求められると。 |
| + | |
| + | ;セクタも求める |
| + | s = R mod A + 1 |
| + | |
| + | ・・・おぉ? これでいいのかな??? |
| + | |
| + | そうするってーと、シリンダはこうかな? |
| + | |
| + | ;シリンダを求める |
| + | Q = B ÷ A ÷ 2 ;先に一面のシリンダ数を求める |
| + | c = R ÷ A - (Q * h) |
| + | |
| + | うん!これでよさそうだぞ! |
| + | |
| + | では、これらを実際のアセンブラにするってーと・・・・ |
| + | |
| + | まずはh。 |
| + | ;h = R ÷ (B ÷ 2) (余りは破棄)だから、 |
| + | |
| + | PUSH AX ;AXには論理セクタ値が入っている。一旦バックアップ。 |
| + | MOV AX,2880 ;まず、AXに総セクタ数を入れる。 |
| + | MOV DX,2 ;DXレジスタに2を入れる |
| + | DIV DX ;AX ÷ DX を実行し、商をAXへ。余りをDXへ。 |
| + | MOV DX,AX ;AXレジスタの値をDXにコピー |
| + | POP AX ;AXレジスタを元に戻す(論理セクタ値をセット) |
| + | PUSH AX ;もう一回バックアップ。 |
| + | DIV DX ;これで、AXにはhの値が入っているはず。 |
| + | |
| + | ふむ・・・一旦DOSのDEBUGで確認してみよう。 |
| + | |
| + | ・・・あっれえええええ??? 割り算(DIV)がどうしてもうまくいかないぞ?? |
| + | |
| + | なになに??? 「÷2」などは shr を使うのが早いと? じゃあこうか。 |
| + | |
| + | PUSH AX ;AXには論理セクタ値が入っている。一旦バックアップ。 |
| + | MOV AX,2880 ;まず、AXに総セクタ数を入れる。 |
| + | SHR AX,1 ;AX ÷ 2 を実行ししたのと同じ結果になる。 |
| + | |
| + | MOV DX,AX ;AXレジスタの値をDXにコピー |
| + | POP AX ;AXレジスタを元に戻す(論理セクタ値をセット) |
| + | PUSH AX ;もう一回バックアップ。 |
| + | DIV DX ;これで、AXにはhの値が入っているはず。 |
| + | |
| + | うーーーむ・・・・ 16bit同士の割り算ってどうやったらいいんだ・・・ |
| + | |
| + | まいったなぁ。やっぱどっかメモリを使わないといけないのかなぁ。メモリは遅いからできれば使いたくない。・・・うーん。遅いっていえばそもそもDIV命令も評判がよくないなぁ。なんかうまい工夫ないもんかな。 |
| + | |
| + | こういう時って、CXやBX使っちゃいけないのかなぁ・・・・。例えば |
| + | |
| + | |
| + | PUSH CX ;CXレジスタの値をバックアップ |
| + | PUSH AX ;AXには論理セクタ値が入っている。一旦バックアップ。 |
| + | MOV AX,2880 ;まず、AXに総セクタ数を入れる。 |
| + | SHR AX,1 ;AX ÷ 2 を実行ししたのと同じ結果になる。 |
| + | MOV CX,AX ;AXの内容をCXにコピー |
| + | |
| + | POP AX ;AXの値を論理セクタに戻す |
| + | PUSH AX ;もう一回バックアップ |
| + | MOV DX,0 ;これでDX:AXの値は32bitながら論理セクタ値となるはず。 |
| + | |
| + | DIV CX ;これで、AXにはhの値が入っているはず。 |
| + | |
| + | MOV DX,AX ;AXの値(h)をDXレジスタに移す |
| + | |
| + | POP AX ;AXを初期値に戻す(論理セクター値) |
| + | POP CX ;CXを初期値に戻す |
| + | |
| + | ;これで全て元通りで、かつ、DXにはhがセットされているはず。 |
| + | |
| + | うーむ。とりあえずは成功かな??? |
| + | |
| + | そんなわけで、[[こんなコード>IPL研究/コード1]]を妄想してみる。 |
| + | |
| + | debugコマンドで検証してみないと・・・ |
| + | |
| + | くそ!だめだ。割り算がメタメタだ。たぶん桁のせいだろう・・・ |
| + | |
| + | シリンダやセクタは同時に計算できないだろうか。 |
| + | |
| + | そんなわけで、[[こういうコード>IPL研究/コード2]]コードを書いてみた。 |
| + | |
| + | キタ━━━━━━(゚∀゚)━━━━━━!!!!!!!! |
| + | |
| + | たぶん、これで大丈夫なはずだぞ!!!!! |
| + | |
| + | |
| + | ''がーーーん!'' |
| + | 大勘違いしてたかも!?????。スタックって、メモリなの?????? |
| + | それじゃ、どこかに適当にメモリを確保して書き込んだって同じじゃんかよ!!! |
| + | |
| + | 課題としては、「DIV命令は遅いので最小で」と、「メモリも遅いので出来るだけレジスタ内で完結」だなこりゃ。 |
| + | |
| + | コード2を見直そう・・・・・ orz |
| + | |
| + | そんなわけで[[こんなコード>IPL研究/コード3]]になった。スタックの使用を最小限にして、割り算も減らした。OK。あとは、実際に繰り返しを行うプログラムを書いた時に、一回一回割り算をしなくてもいいように調整すると。 |
| + | |
| + | |
| + | **どこに読み込むか?(格納メモリ番地) [#j27da20b] |
| + | 上記のオベンキョでセクタの指定はできるようになったけど、読んだセクタはどこに格納したらいいでしょう??? |
| + | |
| + | まずはメモリマップを読んでお勉強 |
| + | |
| + | K氏のheboOSのIPLでは、読み込みは 0x800:0x100からスタートしている。このアドレスはなんだろう? (たぶんMS-DOSの実行ファイルとの互換だろうが・・・) |
| + | |
| + | えーーーーっと・・・・・ |
| + | |
| + | 8086時には、セグメント:オフセットという指定でアドレスを指定できるんだけど、本来は0hからいくらでもアドレスは増やせるはず。(たぶんプロテクトモードなんかではそうなんだろう。 |
| + | |
| + | セグメントの指定は5桁まで。で、オフセットは4桁まで。そう考えると、8086時には |
| + | |
| + | 0h 〜 FFFFFh までの空間がアクセル可能と。(5桁だから) |
| + | |
| + | 0h 〜 FFFFF までのアドレスにアクセルできる。で、この中に勝手に使っていい部分とそうでない部分があるはず。 |
| + | |
| + | heboOSで読み込んでいるアドレスは、0x800:0100 ってことは、 |
| + | |
| + | 00800 + 0100 = 900番地 |
| + | |
| + | からは、勝手に使っていい・・・・のかな? いやたぶんそんなことはないはず。0〜900番地の間はどうなっているんだろう??? |
| + | |
| + | |
| + | |
| + | |
| + | |
| + | |
| + | |
| + | |
| + | |
| + | |
| + | |
| + | |
| + | |
| + | |
| + | |
| + | |
| + | |
| + | |
| + | |
| + | |
| + | |
| + | |
| + | |
| + | |
| + | |
| + | |
| + | |
| + | |
| + | ~ |
| + | ~ |
| + | ~ |
| + | ~ |
| + | ~ |
| + | ~ |
| + | ~ |
| + | ~ |
| + | |
| + | |
| + | |
| + | |
| + | ---- |
| + | |
| + | ~ |
| + | ~ |
| + | ~ |
| + | ~ |
| + | ~ |
| + | ~ |
| + | ~ |
| + | ~ |
| + | |
| + | |
| + | |
| + | |
| + | |
| + | |
| + | |
| + | |
| + | |
| | | |
| IPLはかならずセクター0なのでいいのですが、FAT領域は何番から何番なのでしょう。 | | IPLはかならずセクター0なのでいいのですが、FAT領域は何番から何番なのでしょう。 |