「レガシーブート」タグアーカイブ

Hyfaxのこと(6)

x86リアルモードで動くモニタプログラムのHyfaxを作るお話の6回目です。今回は予告通りFATクラスタチェーンの追跡を取り込みたいと思います。

クラスタチェーン

FATはクラスタという単位で管理されています。クラスタは標準で4セクタ=0x800Byte=2048Byteになります。ここで、

  • もしすべてのファイルサイズが2014Byte未満だったら
    この場合は管理が簡単で、ディレクトリ管理領域にあるファイル名と開始LBAだけあれば内容にアクセスできます。
  • もしファイルが1回書いたら消せないとしたら
    この場合もファイル名と開始LBAがあればファイルにアクセスできます。

ところがわたしたちのやりたいことは、ファイルのサイズが自由に変えられて、削除追加ができることなんですね。例えば2048ByteのファイルAとB、4096ByteのファイルCがあったとします。ファイルA、Bと追加した後ファイルAを削除し、ファイルCを追加すると木のことを考えてみます。

ファイルAとファイルBを空きディスクに追加します。

すると感じになります。

次にファイルAを削除するとこう。

ここにファイルCを追加すると、空きエリアをできるだけ少なくするように(言い換えると効率的にディスクを使うために)ファイルCを分割して格納することになります。

結果としてディスクにはこのように格納されることになります。

このように異なるサイズのファイルを削除追加できるようにすると、場合によってファイルが分割され泣き別れになって格納されることになるわけです。このような場合、ファイルの先頭とサイズだけわかっていても全体を扱うことができません。ファイルが分割されたときに、その先がどこにあるかを記録しておく必要があります。これがFATです。FATでは特定のクラスタがどのクラスタにつながっているかが記録されています。開始クラスタから終端クラスタまでの単方向リストです。
あまりいい例ではないのですが、何度か見たディスクイメージのダンプを覗いてみます。

ディレクトリ領域は0x110800から始まります。最初のエントリはボリュームラベルです。『MONITOR BIN』のエントリは0x110820から始まり、開始クラスタが0x0002(エンジ色の部分)サイズが0x000008c5Byte(水色の部分)だとわかります。0x08c5=2245なので1クラスタを少し超えています。
次にFAT1を見ます。FATは2バイト=1エントリで、ゼロオリジンなので0x0002クラスタに対応する箇所は0x100804(エンジ色の部分)になります。そして1クラスタに入りきらないので次の空きクラスタに行くのですが、この例では隣が開いていたみたいでクラスタ0x0003に続いています。クラスタ0x0003ですべて格納できますのでクラスタ0x0003に続きはなく0xffff(EOF)で終了しています。
『MONITOR BIN」の次のファイルは『APP BIN』なのですが、同様に0x0004クラスタと0x0005クラスタの2クラスタ使用して格納されていることがわかります。

このような構造になっているのですが、使い方はそれほど複雑ではなく、ファイルの内容を読みに行くときにクラスタが終端ならばそこで終了、先があるなら先を読む。これを終端になるまで繰り返す。
言葉にしてしまえばこれだけなんです。

実装

コードにするとこんな感じになります。
前回のfind_dir_ent:から先を修正したものになります。

ね?めんどくさいでしょ?

さてキリがいいところで今日はここまでです。実行結果は前回、前々回と変わらないので載せません。でも『結果が同じことを確認する』ことができるだけで、言い換えれば『ゴールが明確になる』だけで、かなり気持ち的に楽になります。

次回は『MONITOR BIN』に機能を追加しようかなとか思ってますが、どうなるかわかりません。未来のわたしに聞いてください♪

Hyfaxのこと(5)

x86リアルモードで動くモニタプログラムのHyfaxを作るお話の5回目です。前回ではBOOT1.BINが起動するところまでを作成し確認しました。今回はBOOT1.BINにディレクトリ検索機能とFATチェーン追跡機能を追加してBOOT1.BINを完成させたいのですが、果たしてうまく事が運ぶかどうか。不安でいっぱいです。

BOOT1.BINの機能

BOOT1.BINはVBRに置かれ、MBRにあるboot0.binからメモリに読み込まれ実行されます。そしてFAT16ファイルシステム内にあるMONITOR.BINを検索して読み込み、さらにこれを実行します。MBRは完全固定で、VBRは半分ほど固定でディスク内に配置されるので、読み込む際に考慮する点は多くなく、というかとても少なく、ある意味LBA固定で作成することもできます。MONITOR.BINもLBAを固定で済ますこともできないわけじゃないです。というか、前回それをやりました。が、ご存じのようにファイル内にはいくつもファイルを置くことができますし、並び順も固定ではありません。なのでFAT16ファイルシステムを使うのであれば(言い換えれば使うときに便利にするためには)、手順を踏まなければいけません。

ファイルシステムを使うからどこにMONITOR.BINを置いてもいいかというと、そうはいきません。ブートプログラムはサイズ制限が厳しいのでMONITOR.BINはルートディレクトリに置くことになります。BOOT1.BINはルートディレクトリ管理領域からMONITOR.BINの開始クラスタ番号とサイズを読み取り、メモリに読み込んで実行することになります。

ディスクイメージダンプ

ここでもう一度ディスクイメージのダンプを見てみます。

ルートディレクトリの管理領域は0x110800Byteから始まります。数字の羅列でなにがなにやらなんですが、その右側にあるASCII表示を見ると、『MONITOR BIN』『APP BIN』『APP1 BIN』……とどこかで見たような名前があります。そうファイル名です。ディレクトリエントリは1エントリ=32Byteでファイル名、属性、開始クラスタ番号、サイズ等を持っています。最大エントリ数であるROOT_ENT_CNT(0x0200)個並んでいる中から、お目当てのファイル名を探し出して開始クラスタ番号、サイズを取り出し使用するわけです。
ディレクトリエントリの構造はこんな感じ。この例だと『MONITOR BIN』の開始クラスタは0x0002、サイズは0x000008c5ですね。(注:リトルエンディアン)あ、FAT16前提なので上位クラスタ番号は使いません。

実装

前々回DIR_START_LBA、DATA_START_LBA、ROOT_ENT_CNTを保存しました。
①DIR_START_LBAから32Byteずつ取り出し
②先頭の11文字を照合して
③目当てのファイルだったら
④下位クラスタ番号とサイズを取り出し
⑤クラスタ番号からLBA値を算出し
⑥DATA_START_LBAから始まってその値にあたる部分からファイルサイズだけ読み込む

という実装になります。うむぅ。FATのクラスタチェーンの話は次回ですね。今回は連続して置かれているだろうことを信じてサイズをそのまま使いましょう。(今回の例ではクラスタが連続していないということはあり得ないんですけどね。)
検索処理をコードにするとこんな感じになります。
前回のコードのFILEのLBAを算出する部分とファイルサイズ・セクタ変換する部分をまるっと置き換えると動くはずです。

これを見て『なんをしてるかわかんな~い』と思ったあなた。悲観する必要はありません。書いたわたしが今そう思ってます。書いた時点では理解していても時間がたつと忘れます。ホント。コメント大事です。きっぱり。

実行結果

一応実行結果もあげておきますね。

まぁ、前回と変わらないんですけどねw
とはいえゴールが明確に示され、達成がはっきりわかるのはいいことです。うんうん。

さて、今回はここまでにします。次回こそラスボス感のある掛け算七の段こと、FATクラスタチェーンの追跡を実装しようと思います。
お疲れさまでした。

《2025/12/14 17:35:29》

Hyfaxのこと(3)

前回boot0.asmを作ったわけですが、次のboot1.asmを作るにあたって用意しておくと多少楽になって整理がしやすい数値群があります。今回はboot0.asm側であらかじめそういった数値を用意しておこうと思います。

用意するのは
BOOT_DRIVE
VBR_START_LBA
ROOT_ENT_CNT
FAT_START_LBA
DIR_START_LBA
CLUS_SIZ
DATA_START_LBA
です。

それぞれ少し説明しますね。

BOOT_DRIVE

Bootされたドライブの値です。boot0.asm実行時にdlレジスタに渡されてきます。boot0.asmでしか拾えない値なので、保存して使いまわします。

VBR_START_LBA

文字通りVBRが開始されるLBAです。これはMBRのパーテションテーブルの中にしかないので、これまた保存して使いまわします。

ROOT_ENT_CNT

ルートディレクトリの中にいくつエントリが入るかを示します。FAT16ではROOTディレクトリがサイズ固定なので、この数値を見て最大値を算出します。BPBと呼ばれる領域に設定されています。

FAT_START_LBA

文字通りFATが解されるLBAです。計算で出します。VBR開始後、リザーブされた領域を挟んで、そのあとからFATが始まります。

FAT_START_LBA = VBR_START_LBA + BPB_FAT16.RsvdSecCnt

となります。
余談ですが、MBRはMAX512Byte、パーテションテーブルを除くと446Byte「しか」取れません。必達です。ですがVBRの方はVBRの開始からFATの開始までの領域を使うことができます。VBR_START_LBA + BPB_FAT16.RsvdSecCnt分だけ余裕があるわけです。

DIR_START_LBA

ディレクトリ領域が開始されるLBAです。FATの後に位置し、計算で出します。FATは2個あるので、FAT開始LBAのあと、FAT領域を2つ分追加した後がこの値になります。

DIR_START_LBA = FAT_START_LBA + BPB_FAT16.FATSz16 * BPB_FAT16.NumFATs

CLUS_SIZ

クラスタの総バイト数です。標準では4セクタ=1クラスタ、512Byte=1セクタなので0x800Byteになるはずです。

CLUS_SIZ = BPB_FAT16.SecPerClus * BPB_FAT16.BytesPerSec

DATA_START_LBA

DIR_START_LBAにディレクトリエントリの分の領域を要した後がデータ開始LBAになります。

DATA_START_LBA = ROOT_ENT_CNT * 32 / BPB_FAT16.BytesPerSec

boot0.asmの修正

boot0.asmにデータ保存用のコードを入れていきます。主処理の間にチマチマ出てきてうっとうしいし見通しが悪くなるんですが、まぁ、しかたがないですね。で、修正したソースがこれ。

あと、定義ファイルも使用しています。これですね。

これでboot1.asmを作成する準備ができました。

次回こそはboot1.asmを公開したいと思います。

《2025/12/12 22:00:47》

Hyfaxのこと(2)

このテーマを進めるにあたって、どうしようかいろいろ考えたのですが、まずはディスクイメージを作るところから始めることにしました。
QEMUでの実行ではプログラムを指定というよりディスクイメージを指定しするので、これがないと何もできません。今回はFAT16の32MByteのハードディスクイメージを作成します。

作り方はこれ。

ddでファイルのガワを作って、パーティションテーブル作って、ブート可能にして、フォーマットしてます。
まんまですねw

プログラムの方なんですが、オレオレOSというかオレオレ環境なので、どんな構成でも構わないっちゃ構わないんですが、FAT16を選択してしまった以上、決まったお作法があります。

Fat構造だとまず最初にMBR(Master Boot Record)から512Byte 読み込んで実行します。そこでパーティションテーブルを確認して、最初にブート可能なパーティションのVBR(Volume Boot Record)の位置を取り出して、そこを読み込みます。
VBRでは実行するプログラムをファイルシステムから探して読み込みます。

というような、ちょっと面倒くさいお作法になります。
都合上、MBRに置くプログラムをboot0.bin、VBRに置くプログラムをboot1.bin、ファイルシステムから読み込むプログラムをmonitor.binとしています。
アイキャッチの図の通りです。

今回はboot0.binを作りたいと思います。
これは結構楽なんですよね。なにせMBRに置いておけばあとは勝手に読み込まれて実行されるので。
まずは「とにかく動けばいいや」というコードを書いてみます。実はVBRの位置は0x800セクタ、VBRの大きさはMAX4セクタとわかっているので、パラメータを揃えて実行するだけです。
こんな感じ?

確認用に読み込んだ内容の先頭16バイトを表示しています。実行結果はこんな感じ。

ディスクイメージをダンプするとこんな感じ。

セクタ0x800、バイトで0x100000の先頭はeb 3c 90。正しく実行されてますね。

さて、骨格ができたところで肉付けを。というかパーテションテーブルをきちんと見るように修正します。パーティションテーブル、4エントリをなめて当たりを探す処理を追加です。
一気に行っちゃいます。

事項結果は前と変わらないので省略。
最後にboot0.binをディスクイメージに書き込む例を挙げて終わりにします。
パーティションテーブルより前の446バイトを書き込んでいます。

お疲れさまでした。(主にわたしが疲れてるという……)

《2025/12/3 223:41:32》

Hyfaxのこと

これまでBIOSやらBootやらで遊んできたわけなんですが、何か機能とかが気になって遊ぼうとするたびに、boot、機能読み込み、実行とかすることになるわけです。
で、ブートの部分とか機能の読み込みの部分はほぼ同一だという。
同じものを使いまわしするので面倒くささはあまりないのですが、同じものを何度も使うならソースの再利用じゃなくてバイナリの再利用をしたいなぁ、とか思っちゃうんですね。ほとんど同じソースがあちこちにあると、心がゾワゾワするというかなんというか。プログラム作成者の性って奴かもしれません。
そういった流れで処理を見直すと、結局は3つのパートに集約されるなぁと。

①ブートする

②機能を読みこんで遷移する

③試したい機能

流れとしてはこんな感じ。流れ図にするならこうかな。

この形は言うまでもなくOSの基本機能なわけですが、OSというほどたいそうなものを作りたいわけじゃありません。いわばモニタプログラムといったところでしょうか。

よく使う形を一般化して何度も使いまわせるようにするわけです。OSなんておこがましいささやかなものですが、名前がないと不便なのと、名前があると愛着がわくので、仮にHyfaxと名付けました。

今回からしばらくは、このHyfax君を作っていきたいと思います。

大雑把な方針とか

まずは媒体からです。QEMUを使うのは確定です。なので一般的なものとしてFDDとHDDがあります。ですが今更FDDはないだろうというわけでHDDの一択。

ファイルフォーマットはFATなのは当然です。フロッピーでないのでFAT12は没。FAT32は64Bit 演算ができないと辛いのでこれも没。まことに中途半端ながらFAT16で実行できるようにしましょう。

モニタプログラムは0x0ffffまでを占有することにします。アプリエリアは0x10000~0xA0000までの576KByteとしましょう。

モニタプログラムからアプリはjmpで遷移することにします。アプリからはモニタの先頭にjmpすることで復帰することにします。

モニタプログラムはアプリのファイル名を使ってディスクをリードし、0x10000に展開した後で同アドレスに遷移するものとします。

完全なシングルタスク・シングルユーザですね。だってモニタですもの、高機能は望みません。

忘れてましたけど、リアルモード厳守で行きます。拡張レジスタとか使わない方向で。

今日はここまで

大雑把な方針が決まったところで今日はここまでです。やってみなくてはわかりませんが都合6回くらいになるんじゃないでしょうか。

わたしの『やる気スイッチ』と連動しているので、次回がいつになるかはわかりません。

《2025/11/28 13:54:22》

ブートで遊ぼっ!(2)

86系のPCをブートさせて遊んでみよう、というお話の2回目です。テキストは『OSを書く:初歩から一歩ずつ』です。
環境はAMD64+Windows10+VMware Workstation 17 Player+Debian 12.4です。
今回はMakefileを作るのと、余裕があれば文字を表示するところまでかな。届くかな?

ビルドプロセスを短縮する

Makefileを作ってビルド…というかアセンブルから実行までを簡単にしましょう、というお話。
『今時make?』という気もしなくはないんですが、アセンブラでboot周りのことをやるのなら『やっぱmakeよね!』とも思います。
やっていることというか、やりたいことは凄く単純で

と、2回コマンドを打っていたのを

で済まそうということ。
いちいちだらだらタイプしたくないし、ヒストリから拾うのも面倒だし、という怠け者の発想ですね。怠けるためにメンドクサイ仕込みをするとか、世の中よくあることです。
makeの使い方とかMakefileの書式とかを知りたい方は、ネットで調べてもらえば沢山記事があると思いますので、そちらに任せます。

テキスト通りMakefileというファイルを作成します。


実行してみます。



ん?
なにこれ?

って、どういうこと?

ちょっと調べてみたら、Makefileではコマンドの前にタブがないといけないみたいです。
何て言うか…『年寄りはこれだから困る』とか言いたくなってみたり?
気を取り直してMakefileを修正します。

2行目、5行目、8行目の先頭にタブを入れます。
で、改めて実行します。

ちゃんと動きました。めでたしめでたし。

ここで気力が尽きました。文字列の表示は次回だなぁ…。

《2024/4/19 11:14:24》

ブートで遊ぼっ!

x86系のPCをブートさせて遊んでみようと思います。
OSを作ろうとか、大それたことは言いません。技術力も知識も能力も全く足りないですから。
そもそもOSを作るのって、とっても

テキストは『OSを書く:初歩から一歩ずつ』です。ざっと目を通してみたんですが、それほどボリュームないし、解説もそれなりにあるし、これなら1日あれば確認できるかなと。チュートリアルだしねw

前提条件

テキストがDebianを前提にしているので、それに乗っかります。
今回のわたしの環境は

Intel Core i7
Windows10
VMwera Workstation 17 Plyer
Debian 12.4

です。Debianはこのために新規で入れました。ほとんど、まっさらです。

テキストで『nasm』『build-essential』『qemu』が必要だというのでインストールします。
Debianを起動してターミナルエミュレーターを実行し、以下を入力します。

む!

とか返ってきた。
どうしよう、どうする?

テキストは2016年とか2017年に書かれたものだし、今は2024年だし、8年もあれば生まれた子供も小学校3年生?になるし、パッケージ構成が変わっててもおかしくはないなぁ。
ともあれ、これでお終いにするってのは、あまりに情けなさすぎる。どうしたものか。

少し調べてみたら、色々な情報がありました。余計なものをインストールしても邪魔にならなければいいんだけれど、できるなら最小限の構成にしたいなぁ。

なので、これでどうだっ!

インストールは成功。
ではソースを書きましょう。
ファイル名は『boot.asm』です。

; boot.asm
hang:
jmp hang

times 510-($-$$) db 0

; This is a comment

db 0x55
db 0xAA

PlayWithBoot.1

コードの解説はテキストに任せて、書かれたとおりにオペレーションします。

問題ないですね。

動きました。こんな感じです。


これで前提条件が終了です。

なんか、思ったより先が長そう…

《2024/4/17 0:04:09》