「PC 移植記」の第2回は、2nd boot の読み込み処理です。
jmpi を実行してから 2nd boot に処理を渡すまでの、1st boot の処理は、次
のようになります。
mov ax, #0x7000
mov es, ax
mov ss, ax
mov ds, ax
mov sp, #2048
mov bx, #message
call print
call read2ndboot ! 2nd boot の読み込み
mov ax, #seg_2ndboot ! 2nd boot の呼び出し
push ax
mov ax, #off_2ndboot
push ax
reti
最初の一連の mov 命令はセグメントレジスタおよびスタックポインタの初期
化を行っています。スタックのサイズとしては、2048 - 512 = 1536 バイトを
指定しています。
その後で、起動メッセージを表示し、2nd boot を FD から読み込みます。
最後に 2nd boot を呼び出して 1stboot は終了します。
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
ここで、BOOT フロッピィの中身の説明をします。
BOOT フロッピィは、大きく 1st boot (IPL が読み込む)部分と、2nd boot の
部分とに分かれています。
1st boot は、FD の最初のセクタ (512 バイト)に入っています。
2nd boot は、大きく 4 つの部分に分かれています。
(1) 2nd boot 情報部分 (512 バイト)
1st boot のすぐ次のセクタに、2nd boot の大きさが入ります。
(2) GDT 情報 (8192 バイト)
32 bit モードで動かす時に必要な、GDT の情報が入ります。
2nd boot が 32 ビットモード (80386 のネイティブモード) で動くため
に必要な情報です。
(3) ページ情報 (20480 バイト)
32 bit の仮想記憶機能を使う時に必要な仮想メモリページの情報が入り
ます。
仮想ページモードで動く時に使用します。B-Free OS 本体を実行する時、
この情報を使用します。
(4) 2nd boot のプログラム部分
2nd boot の実行部分が入ります。
1st boot は、2nd boot を読み込んだあと、このプログラム部分の最初の
アドレスにジャンプします。この最初のアドレスが、2nd boot のエント
リ関数になっています。
このうち、(2) (3) (4) の情報がメモリ中に連続して読み込み、2nd boot に
引き渡します。(1) の 2nd boot の情報は、1st boot だけが使用します。
また、1st boot は、(2) から (4) までの情報は区別せず、そのままメモリ中に
読み込みます。
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
2nd boot プログラムを読み込むための処理は、read2ndboot 関数で行います。
と、read2ndboot 関数の説明の前に FD からデータを読み込むための BIOS コー
ルの説明を。
o FD のリセット
BIOS コール int 0x13
BIOS コード AH 0x00
パラメータ DH ドライブ番号
o FD からデータを読み込む
BIOS コール int 0x13
BIOS コード AH 0x02
パラメータ AL 読み込むセクタ数
CL シリンダ番号
DH ヘッダ番号
DL セクタ番号
ES:BX バッファアドレス
機能:ES:BX で表すアドレスに FD からデータを読み込む。
この BIOS を使ってデータを読み込みますが、そのための下受けとして
readdisk という関数を定義しています。
フロッピィディスクのどこのセクタを読み取るかを指定するための変数として、
cylinder と sector を定義しています。cylinder は、シリンダ番号とヘッダ
番号を組み合わせたものです。
シリンダ番号 = cylinder / 2
ヘッダ番号 = cylinder % 2
の関係になっています。また、読み込むアドレスを seg_addr (セグメント)
と off_addr (オフセット) という 2つ の変数にセットします。
readdisk を呼び出すときには、この 4 つの変数に値をセットします。
readdisk:
push ax
push bx
push cx
push dx
xor dl, dl ! ドライブ番号
movb al, cylinder
shr al, #1
movb ch, al ! シリンダ番号のセット
movb al, cylinder
and al, #0x01
movb dh, al ! ヘッド番号のセット
movb cl, sector ! セクタ番号のセット
mov ax, seg_addr ! 読み込む場所のセグメント値
mov es, ax
mov bx, off_addr ! 読み込む場所のオフセット値
movb al, #0x01
movb ah, #0x02
int 0x13 ! BIOS の呼び出し
jc fatal
pop dx
pop cx
pop bx
pop ax
ret
seg_addr: .word 0 ! バッファのセグメント
off_addr: .word 0 ! バッファのオフセット
cylinder: .byte 0 ! シリンダ番号(この中には、ヘッド番号も含まれる)
sector: .byte 0 ! セクタ番号
2nd boot の読み込み処理 (read2ndboot で実行します) は、次のように行います。
(1) 2nd boot の情報(サイズ)を読み取る。
2nd boot のサイズは、0x7800:0x0000 のアドレスに読み込みます。
(アドレスは、seg_size_info と off_size_info という定数で定義してい
ます)
(2) (1) で読み込んだサイズの情報に従って、GDT 情報 + ページ情報 +
2nd boot 本体 を読み取ります。
2nd boot 本体は、0x0100:0x0000 というアドレス(リニアアドレスでいうと
0x1000 になります) に読み込みます。
このアドレスは、seg_loadpoint と off_loadpoint という定数で定義し
ています。
(2) での読み込み自体は、単にループで回しているだけです。C 言語で表現すると
次のようになります。
int sector, cylinder;
2ndboot 本体の読み込み ()
{
int ax; /* 読み込むセクタの残り数 */
printf ("loading 2nd boot...\n");
seg_addr = seg_size_info; /* 2nd boot のサイズ(セクタ数)の */
off_addr = off_size_info; /* 読み取り */
sector = 1;
cylinder = 0;
readdisk ();
sector = 3;
cylinder = 0;
seg_addr = seg_loadpoint;
off_addr = off_loadpoint;
ax = off_size_info; /* 2nd boot のサイズ(セクタ数) */
while (ax != 0)
{
if (sector == 19) /* セクタは、1 から 18 まで */
{
cylinder++;
sector = 1;
}
readdisk ();
seg_addr += 0x0020; /* 512 バイト増加 */
ax--;
sector++;
}
printf ("loaded 2nd boot...");
}
PC9801 版では、セクタ数は 1 から 15 まででしたが、IBM-PC 互換機では
1.4 MB フロッピィなので、セクタ数は 1 から 18 までとなります。
最終的にできあがった read2ndboot は次のようになります。
read2ndboot:
! 2nd BOOT の情報をロード
mov bx, #boot2_message
call print
mov seg_addr, #seg_size_info
mov off_addr, #off_size_info
movb sector, *info_sector
movb cylinder, *info_cylinder
call readdisk
! 読み取った情報を使ってFDから2nd BOOT本体を読み取る
movb sector, #3
movb cylinder, #0
mov seg_addr, #seg_loadpoint
mov off_addr, #off_loadpoint
mov ax, #seg_size_info
mov es, ax
seg es
mov ax, off_size_info
load_loop:
cmp ax, #0
jz loop_done
cmpb sector, #19 ! 最後のセクタ?
jne l2
mov sector, #1 ! セクタ番号を1に戻す
incb cylinder
l2:
call readdisk
add seg_addr, #0x0020 ! 512 bytes/sector
dec ax
incb sector
jmp load_loop
loop_done:
mov bx, #read_done_message
call print
ret
--- B-Free プロジェクト実行中! 詳細はこの WWW へ -> (http://www.st.rim.or.jp/~isoyama/b-free)内藤隆一 (ggc00661@niftyserve.or.jp/night@bfree.rim.or.jp)