雑記帳
ここはhideyosiの雑記帳です。テケトーに書き散らしてるだけなので間違っていたりとは普通にしてます。信用度は相当低いことをあらかじめご了承を。またご覧のようにWikiを使ってますが、hideyosi意外は書き込めません。
2: 2005-07-21 (木) 19:05:26 ソース バックアップ No.2 を復元して編集 現: 2024-01-06 (土) 22:39:10 ソース 編集
Line 67: Line 67:
| 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を書くらしい|
Line 127: Line 127:
-予約領域 -予約領域
---おそらくなにか特別な用途に使うために用意されたものでしょう。ファイルとしては絶対にアクセスできない領域になるようです。算出は、「FATがどこから格納されているか」の数字で求めます。IPLはセクター1。で、この数字を例えば10にすると、FAT領域はセクター11からとなります。そうすると必然的に、セクター2〜セクター10が予約領域となり、通常は使うことができない特殊な領域となります。 +--おそらくなにか特別な用途に使うために用意されたものでしょう。ファイルとしては絶対にアクセスできない領域になるようです。算出は、「FATがどこから格納されているか」の数字で求めます。IPLはセクター0。で、この数字を例えば10にすると、FAT領域はセクター10からとなります。そうすると必然的に、セクター1〜セクター9が予約領域となり、通常は使うことができない特殊な領域となります。 
---上記の例では「1」ですね。つまり、FAT領域はセクター1の次から(セクター1から)ということになり、事実上予約領域は0となります。+--上記の例では「1」ですね。つまり、FAT領域はセクター0の次から(セクター1から)ということになり、事実上予約領域は0となります。
-FAT領域 -FAT領域
---ファイルの情報(データではない)が格納されます。ここは、「FATの長さ(1つ分の長さをセクタ数で)」の値から解ります。9です。つまり、上記のスタート地点(セクター2)から9個分のセクターを使うということになります。なので、「セクター1〜セクター9」ということになります。+--ファイルの情報(データではない)が格納されます。ここは、「FATの長さ(1つ分の長さをセクタ数で)」の値から解ります。9です。つまり、上記のスタート地点(セクター1)から9個分のセクターを使うということになります。なので、「セクター1〜セクター9」ということになります。
--さらに、「FATがいくつあるか」という情報も参照します。2ですね。もう一個は予備用らしいですが、ちゃんと領域を喰います。二つ目の予備FATも同じく9個なので、「セクター10〜セクター18」となります。 --さらに、「FATがいくつあるか」という情報も参照します。2ですね。もう一個は予備用らしいですが、ちゃんと領域を喰います。二つ目の予備FATも同じく9個なので、「セクター10〜セクター18」となります。
--結果、FAT領域は、「セクター1〜セクター18」となります。 --結果、FAT領域は、「セクター1〜セクター18」となります。
Line 165: Line 165:
*論理セクタと物理セクタ [#gcd8fb39] *論理セクタと物理セクタ [#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番地の間はどうなっているんだろう???
 +
 +
 +
 +
 +
 +
 +
 +
 +
 +
 +
 +
 +
 +
 +
 +
 +
 +
 +
 +
 +
 +
 +
 +
 +
 +
 +
 +
 +~
 +~
 +~
 +~
 +~
 +~
 +~
 +~
 +
 +
 +
 +
 +----
 +
 +~
 +~
 +~
 +~
 +~
 +~
 +~
 +~