home ホーム search 検索 -  login ログイン  | reload edit datainfo version cmd icon diff delete  | help ヘルプ

Assembler/なぜx86ではMBRが"0x7C00"にロードされるのか?(完全版)

Assembler/なぜx86ではMBRが"0x7C00"にロードされるのか?(完全版)

Assembler / なぜx86ではMBRが"0x7C00"にロードされるのか?(完全版)
id: 614 所有者: msakamoto-sf    作成日: 2010-03-16 12:36:57
カテゴリ: Assembler 

English Version: " Why BIOS loads MBR into 0x7C00 in x86 ? "

Assembler/なぜx86ではMBRが"0x7C00"にロードされるのか?で調査が不十分だった点を補った完全版です。

対象読者
x86アーキテクチャとアセンブラの基礎知識があり、MBRからのOSのロードに興味がある人("割り込みベクタ"や"INT xxx"と言われても「???」とならない人)。

自分がx86アーキテクチャの、特にOSのブートする仕組みやプロテクトモードを学習した時、MBR(Master Boot Record)で最初の一歩を踏み出した。MBRはフロッピーディスク(FDD)やHDDの最初の1セクタ:512バイトブロックである。OSをブートする機械語のプログラムと、ディスクの論理パーティション情報が格納されている。Interlの80x86系列のCPUを採用しているPCは、電源投入後、まずBIOSのPOST(Power On Self Test)が行われ、周辺機器が認識された後、MBRを読み込んでOSのブート処理を開始する。

BIOSはROMチップに書き込まれている為通常は変更出来ない(EEPROMなど、書き換え可能なROMを使っており、BIOSアップデートなどの特殊なソフトウェアを使うことで書き換えることができる)。一方のMBRはFDD/HDDの最初の1セクタということで、プログラマ自身が書き換えることが可能である。
2010年現在、Bochs, QEMU, VirtualBox, VMwareなどの仮想環境を手軽に活用できる環境が整っている。
そうした仮想環境を利用すれば、仮想環境用のディスクイメージファイルのMBR部分に好きな機械語を書き込むだけで、手軽に機械語の実験を行うことが可能になる。ホストOS側の現実の物理HDDのMBRを書き換えてOSが立ち上がらなくなる心配も無い。

多くの書籍やWebの記事では次のような流れでMBRのコードが実行される、と説明している。

  1. BIOSがPOST(Power On Self Test)を実行
  2. POST後、BIOSはFDD or HDDのMBRを0x7C00にロードし、そこから処理を続行する。(もちろんCD/DVDからブートすることも出来るが、本記事ではFDD/HDDからのブートを扱う。)
  3. MBRにはOSのカーネルをロードし、実行する為の機械語が格納されており、OSのブートが始まる。

MBRから直接OSのカーネルをロードするのではなく、一つ or 二つ程度の中間ローダを介する場合もある。MBRの機械語コード、およびMBRがロード&実行する中間ローダはOS固有の場合(MicrosoftのNTLDR)もあれば、複数のOSに対応したオープンソースソフトウェアの場合(LILO, GRUB)もある。

ところで、実際にMBRを弄った人は、次の疑問を抱いたことはないだろうか?

「"0x7C00"って、どこの仕様書で決められているんだろうか?」

x86CPUについて学んでいくと、独特の"メモリアドレスのマジックナンバー"がいくつかあることに気づく。

64KBの壁
もちろん、「セグメント16bit:オフセット16bit」の組み合わせによる、1セグメント64KBの制限値である。
0x3FF

これは割り込みベクタ用のメモリ空間で、

4バイトの割り込みハンドラのアドレス x 256個の割り込みベクタ
= 1024バイト
= 0x400 バイト

で、x86の仕様として割り込みベクタはアドレス0番地から始まる為、一つ分ずらした値となる。

0xFFFFF(1MB)
リアルモードでの最大アドレス値。これは80186以前はアドレスバスが20本、20bit = 指定可能な最大値が0xFFFFFだったことに基づく。
0xFFFF0
IBM PC(5150)の誕生に深く関わる Intel 8086/8088 が、電源投入の初期化後に最初に処理を開始するアドレス。

・・・ところが、"0x7C00" の意味については、CPU関連を調べても何も分からない。ブートローダやMBR関連の情報を調べても、単に0x7C00にロードされる、と書かれているだけで、なぜこの値なのか?誰が決めたのか?についての情報は皆無にみえる。

前ぶりが長くなったが、本記事はMBRがロードされる "0x7C00" というアドレスの起源と、その意味について調べた結果をまとめている。

この記事単体で何かしら「使える」知識を増やすことは出来ないが、もしも読者のあなたが "0x7C00" について喉に魚の骨が刺さったままのような感触を覚えているのであれば、ぜひ一読をお奨めしたい。


IBM PC(5150) 誕生前後の流れの再確認

"0x7C00" が「いつ」現れたのかについて先に結論を言うと1981年8月に発表された IBM Personal Computer (PC) 5150の ROM BIOSで初めて現れた。

IBM PC 5150は16bit/8bit両対応のCPU Intel 8088 を使った、現在に連なるx86アーキテクチャのご先祖様に相当する。短い開発期間と、それまでのIBMのやり方と異なるオープンアーキテクチャの採用など、歴史の転換点にふさわしくその誕生はドラマティックな数々のエピソードに彩られている。
ここでIBM Personal Computer(PC), モデル番号5150誕生前夜の歴史について簡単に確認しておく。

Intel 4004 に始まる汎用CPUは、8bitの Intel 8080(1974年) でマイクロコンピュータ市場を席巻した。MITS Altair8800(1975年) にも搭載された。Digital Research社のOS、CP/M は1974-1975年前後に誕生したとされているが、8bitマイコン市場の盛り上がりと共に、CP/Mは互換CPUやほかのマイコンシステムへの移植が進んでいった。

当時既にメインフレーム市場で巨人となったIBMも、その流れを無視していたわけではない。
1975年には "5100 Portable Computer" を発表、1978年には "IBM 5110 Computing System"、1979年には "5520 Administrative System"、1980年2月には "5120 Computer System" と、机の上に載るサイズのコンピュータシステムを発表している。しかしいずれもビジネスオフィス、あるいは科学技術計算用途に使われており、マイコン市場には食い込めていない。

1980年、ドン・エストリッジをリーダーとする12人のチームは、1年という制約期間でIBM初の "Personal Computer" を開発し、マイコン市場に挑戦する事になる。この時に採用されたのが 8bit/16bit両対応の Intel 8088である。
ソフトウェアでさしあたり必要なのはプログラミング言語とOSで、プログラミング言語はMicrosoft社の開発したBASICをROMに搭載することになった。OSについては、一度Digital Research社のCP/Mを採用しようと商談を進めたようだがうまくまとまらず、こちらも最終的にMicrosoft社に依頼することになった。当時マイコン市場で人気のCP/Mに対応したソフトを動かしたいということで、CP/M互換OSの開発を依頼した。

この辺は有名な話になるが、当時のMicrosoft社にはOSを開発した経験が無かった。どうしたかというと、Seatle Computer Products(SCP)のTim Patersonが開発した8086対応のCP/M互換OS、"86-DOS"(初期には"QDOS : Quick and Dirty Operating System"と呼ばれていた)を買い取り、それをベースにIBM PC 5150用のCP/M互換OSを開発した。

1979年当時は"S-100"というバスを使ったマイコンがAltair8800やIMSAI 8080で主に使われており、SCPもS-100バスに挿せる形で8086CPUボードとそのOSを提供しようとしていた。OSについては当初はDigital Research社により8086に対応したCP/Mが提供されるのを期待していたらしい。
1979年の11月には、SCPによる8086CPUボードとスタンドアロンで動作するBASICが出荷されたが、中々CP/Mの8086対応が進まないため、自分達で互換OSを開発することを1980年の4月に決定。
1980年の8月にはQDOS 0.10をリリースし、1980年の年末には "86-DOS" ver 0.3 をリリース、同時にMicrosoft社に提供している。
これとほぼ同時期に、Digital Research社も8086対応のCP/M-86をリリース。
1981年の7月にはMicrosoftによりDOSの全権利が買い取られ、1981年の8月、86-DOSと「非常によく似たOS」であるPC-DOSがIBM PC 5150とともに発表される事になる。

86-DOS → PC-DOS + ROM BIOS

2010年現在のPCでは、マザーボード上のROMチップにBIOSが搭載され、OSはHDDなど外部記憶ディスクに保存されるのが常識となっている。この棲み分けは、まさしく IBM PC 5150 が原点となっている。

というのも、MS-DOSの原型にあたる86-DOS(とCP/M)ではBIOS相当の機能もOSの一部に含まれていた。

BIOSの機能というのは、

  1. メモリやCPUのチェック、周辺機器の認識など現在のPOSTにあたる機能に加え、
  2. CP/M互換OSが使うソフトウェア割り込み処理も含まれる。

もちろんPOST処理は重要だが、それ以上にOSが使うソフトウェア割り込みも重要で、これにより画面上に文字や図形を描画したり、FDD/HDDにアクセスしてデータを読み出したり、ファイル処理やプログラムをメモリ上にロードしたりしていた。当時はそうした機能が全て"OS"の中に含まれていたことになる。86-DOSのベースとなったCP/Mもそのようになっていた。

これが OS と BIOS に分離された形式になり、以降の流れを決定づけた起点が IBM PC 5150 である。

閑話休題:0xFFFF0(Intel 8086/8088がハードリセット後に最初に実行するアドレス)

ここで、そもそもx86のCPU自身が、ハードリセット(電源投入)後電子回路のレベルで初期化され、最初に命令をフェッチするアドレスについて再確認しておきたい。

CPU CS IP 物理アドレス
8086/8088 F000H FFF0H FFFF0H
80286 F000H(※1) FFF0H FFFFF0H
80386以降 F000H(※2) FFF0H FFFFFFF0H

どの世代でも、当時のマシンに搭載されていた標準的な物理メモリを越えた場所を指している。
これは割り込みベクタの影響でアドレス0を使えない為と、最初の命令フェッチから始まる初期化プログラムをなるべくアドレスの高位に配置することで、一般的なプログラムの使えるアドレス空間を邪魔しないようにとの意図らしい。
BIOSが格納されたROMは、電子回路のレベルでこれら高位アドレスにマッピングされるよう調整されている。

IBM PC 5150 ではROM BIOSがFE000以降にロードされるようになっており、丁度 FFFF0H に、BIOSコードの先頭へJMPする機械語コードが配置されている。(後述)

※1 : 80286は24bitのアドレスバスを持つが、リアルモードでは20bitしか使われない。A20-A23はリセット後は1になっている。そのため、CSこそ"F000H"になっているが、もう4bit分全て1のデータが上位に隠れている。

(F)F0000 H(CS) (4bit左シフト)
    FFF0 H(IP)
--------------
 F FFFF0 H

となり、"FFFFF0H"が最初にフェッチされるアドレスとなる。

参考:

  • "M80C286 HIGH PERFORMANCE CHMOS MICROPROCESSOR WITH MEMORY MANAGEMENT AND PROTECTION"
    • "Memory Addressing", "Reserved Memory Locations" (p10)

※2 : 80386ではアドレスバスが32bitになるが、リアルモードでは20bitしか使われない。A20-A31はリセット後は1になっている。そのため、CSこそ"F000H"になっているが、もう12bit分全て1のデータが上位に隠れている。

(FFF)F0000 H(CS) (4bit左シフト)
      FFF0 H(IP)
--------------
 FFF FFFF0 H

参考:

  • "INTEL 80386 PROGRAMMER'S REFERENCE MANUAL 1986"
    • "10.1 Processor State After Reset" (p174)
    • "10.2.3 First Instructions" (p176)

86-DOS のブートプロセス(MBRを200Hにロード)

SCPは8086用CPUボードの他に"CPU Support Card"というボードも開発した。"CPU Support Card"にはデバッグ機能とbootstrapローダ機能を備えた、2KBの機械語プログラム("Monitor")が搭載されていた。
Monitorを使うと、マシン起動後はMonitorのコマンドプロンプトが最初に表示される。ここで"B"コマンドを実行することで、ディスクの先頭セクタをbootstrapとして 200H にロードし、bootstrapの先頭にジャンプして処理を続行する。

Monitorプログラムは8086のメモリ空間(1MB)の末尾2KBに位置するようになっている。
8086が電源投入後に最初にフェッチするアドレス FFFF0H には、Monitorプログラム上で次の機械語が配置されるようになっている。

JMP    0,0FF80H    ;Power-on jump to monitor

セグメント指定のFARジャンプで、

FF80:0000 → FF800 H

にジャンプすることになる。FF800HからはMonitor用のコードが始まる。

MON.ASM:

;Start of Monitor code

	ORG	0
	PUT	PUTBASE ; 無視してOK(恐らく当時のアセンブラ独特の疑似命令)

;One-time initialization

	UP ; 今のアセンブラでは"CLD"
	XOR	AX,AX
	MOV	SS,AX
	(...)

オフセット(ORG)が0になっているが、これは(恐らく)アセンブラの都合とMonitor用のボードの電子回路的な都合によるものだろう。

この時点でのメモリ空間:

+---------------------- FFFFFH
|
+----------------------
| JMP 0,FF80H           FFFF0H
+----------------------
|
|
| Monitor コード        FF800H
+----------------------
|
|
+---------------------- 3FFH
|(割り込みベクタ)
+---------------------- 0H

Monitorのコマンドプロンプトで"B"コマンドを実行すると、FDDの先頭512バイトを200Hにロードする。

MON.ASM:

(...)
LOAD:	EQU	200H
(...)
COMTAB:
	DW	BOOT		;B
	DW	PERR		;C
(...)
BOOT:
	PUSH	DI
(以下、当時のディスクコントローラ毎に200Hにロードするコード)
;Successful read
	MOV	[CSSAVE],0
	MOV	[IPSAVE],LOAD
	POP	DI
	JMP	GO ; GOルーチン内でレジスタ調整後、CS:0000H,IP:200H(LOAD)へJMP

これにより読み込まれるbootstrapのアセンブラが、BOOT.ASMとして86-DOSに同梱されている。
BOOT.ASMを読んでみると、SS(Stack Segment)に0, SP(Stack Pointer)に200Hをセットしている。bootstrap実行中の一時的なスタック領域は200Hから始まることが分かる。

BOOT.ASM(bootstrap)を200Hへロードした後のメモリ空間:

+---------------------- FFFFFH
|
+----------------------
| JMP 0,FF80H           FFFF0H
+----------------------
|
|
| Monitor コード        FF800H
+----------------------
|
|
+---------------------- 3FFH これ以下↓が8086の割り込みベクタ
| BOOT.ASM
+---------------------- 200H これ以下↓がBOOT.ASMが使うスタック領域
|
+---------------------- 0H

200HにロードされたBOOT.ASM中では引き続きシステムをディスクからロードする処理が続く。BOOT.ASMではOSの基本部分を400H以降にロード後、400HにJMPする。ちょうど400Hから始まるOSの基本部分の一部が、DOSIO.ASMとして86-DOSに同梱されている。

BOOT.ASM:

	ORG	BOOTER
	(...)
	MOV	DI,LOAD   ; LOAD equ 400H
	(...)
READ:
	(...)
DONE:
	(...)
	JMP	0,SEG ; SEG equ 40H

最後のJMPはfarジャンプとなり、CS:40H, IP:0H → 400Hにジャンプする。

ちなみに、まだこの時点では割り込みベクタは初期化されていない。MON.ASMやBOOT.ASMを読んでみるとINT命令が全く使われていないことに驚く。

OSの基本部分を400Hへロードした後のメモリ空間:

+---------------------- FFFFFH
|
+----------------------
| JMP 0,FF80H           FFFF0H
+----------------------
|
|
| Monitor コード        FF800H
+----------------------
|
| DOSIO.ASM + その他OS
| の基本部分
+---------------------- 400H以上↑領域
+---------------------- 3FFH これ以下↓が8086の割り込みベクタ
| BOOT.ASM
+---------------------- 200H これ以下↓がBOOT.ASMが使うスタック領域
|
+---------------------- 0H

いよいよOSの初期化処理が始まる。400HにJMP後、SSは0にクリアされ、SPも400Hに変更される。BOOT.ASMはもう使わないので、直前までBOOT.ASMがロードされていた 200H - 3FFH のメモリ空間をOSの初期化処理用のスタック領域に使うことを意味する。
SS, SP調整後、この時点でロード済のメモリ 800H 以降のルーチンをCALLする。

DOSIO.ASM:

	(...)
DOSSEG:	EQU	80H
	ORG	0
	(...)
	JMP	INIT
	(...)
INIT:
	(...)
	XOR	AX,AX	; AXが0クリア
	MOV	SS,AX	; SSが0Hに。
	MOV	SP,400H		;Set stack just below I/O system
	(...)
	CALL	0,DOSSEG	; 80H:0Hのサブルーチン呼び出し
	MOV	DX,100H
	MOV	AH,26		;Set DMA address
	INT	21H
	(以下、COMMAND.COMの起動へ続く)

この 800H のルーチンCALLにより、INT20h以降のソフトウェア割り込みベクタが調整され、OSの提供するINT機能が使えるようになる。
800Hより戻ってきたら、引き続きCOMMAND.COMの起動に入り、OSのユーザに対するインターフェイスである、コマンドプロンプトを開始する。(COMMAND.COM起動時にはSS,SPも再調整される)

COMMAND.COM起動後のメモリ空間:

+---------------------- FFFFFH
|
+----------------------
| JMP 0,FF80H           FFFF0H
+----------------------
|
|
| Monitor コード        FF800H
+----------------------
|
| OS + COMMAND.COM
| + ユーザープログラム
|
+---------------------- 400H
+---------------------- 3FFH
|(割り込みベクタ)
+---------------------- 0H

以上が86-DOSのブートの流れである。

参考:

86-DOSがbootstrapを 200H にロードする理由 (From Tim Paterson)

86-DOS ではディスクの先頭に配置されているbootstrapを 200H にロードしている。
この "200H" という値は、8086の割り込みベクタ(0H - 3FFH)の内部である。
Tim Paterson によれば、CPUの割り込みベクタ内であり仕様的には「予約済」であることと、そのためにOS自身は0H-3FFH内には絶対にロードされない「安全地帯」であることが、200Hを選択した理由とのこと。

From Tim Paterson:

At SCP, I chose 0x200 because it was in the interrupt vector space (0 -
3FFH). This means it needed to be reserved and couldn't be in the way of
an OS, no matter where it wanted to load.

まとめると

  1. 0x0 - 0x3FF は8086の仕様上、割り込みベクタに使われていた。
  2. 0x400 以降に86-DOSのシステムがロードされる。
  3. 0x200 - 0x3FF の間の割り込みベクタは86-DOSの時点では特に使っていなかった。

以上の理由で 200H - 3FFH の間の隙間が使われた。ちなみに、100H - 200H の間も、レジスタの退避やMonitor/bootstrapなどの初期処理が使うスタック領域として利用されていたらしい。

IBM PC(5150) ROM BIOS INT19h

86-DOSやCP/Mでは、bootstrapのロードや割り込みベクタの調整と対応するハンドラなどが全て"OS"の中に含まれていた。
これが、IBM PC 5150において次のように分裂した。

  • ROM BIOS
    • IBMが開発
    • OSに関わらず最低限度必要な機能(bootstrapのロードや基本的な割り込みハンドラ、POST処理)
  • OS本体 + MBR
    • Microsoftが開発
    • bootstrap(MBR)やOS固有の初期化処理、割り込みハンドラ、COMMAND.COM

86-DOSやCP/Mが厳密にMS-DOSの祖先かというと議論が必要なようだが(例:メモリレイアウトの設計コンセプトが大幅に変更されている。86-DOSでは"640KBの壁"という制限は無かった。)、とにかく、IBMとMicrosoftの二社協同で分担したことが、現在に至るBIOSとOSの役割分担の起源であることは間違いないだろう。

IBM PC 5150 の ROM BIOS でMBRを 7C00H にロードするのは INT 19h というソフトウェア割り込みハンドラである。
これは ROM BIOS により割り込みベクタに登録されると共に、POST後に INT 命令で呼ばれる。
ちなみに5150ではROM BASICを起動する INT 18h というソフトウェア割り込みも存在し、INT 19hでMBRのロードに失敗した場合に INT 18h が呼ばれるようになっていた。

INT 19h までの流れ(MBRを7C00Hにロード)

5150のROM BIOSのアセンブラリストは、"IBM Personal Computer Technical Reference manual"に掲載されている。
実際にBIOSのアセンブラコードを追いつつ、INT 19hまでの流れを簡単に追ってみる。

line.6199 - :

VECTOR SEGMENT AT 0FFFFH
	JMP RESET
	DB '10/27/82' ; RELEASE MARKER
VECTOR ENDS

"SEGMENT" - "ENDS" はコードの配置を決定する擬似コード。セグメントが "0FFFFH" で先頭がJMP命令と言うことは、物理アドレス上は

FFFFH:0000H = FFFF0H : JMP RESET

ということになる。つまり、CPUのハードリセット直後にフェッチされる命令は"RESET"ラベルへのJMPとなる。
ちなみに"RELEASE MARKER"が1982年になっているが、入手出来たドキュメントのバージョンが1983年4月の改訂版なので、その影響だろう。
RESETラベルは308行に存在し、8088CPUのテストコードが始まる。以降、現在のPOSTに相当するCPU/メモリ/HWのチェックコードが続く。

line.306 - :

	ASSUME CS:CODE,DS:NOTHING,ES:NOTHING,SS:NOTHING
	ORG 0E05BH
RESET LABEL FAR
START:
	CLI			; DISABLE INTERRUPTS
	MOV AH,0D5H
	SAHF
	(...)

気になるのが、

ASSUME CS:CODE,...


ORG 0E05BH

の二行。なぜ初期化コードが中途半端な場所から始まるのか?
まず "CODE" については224行目で次のようにSEGMENT 0F000H が指定されている。

line. 224 - :

CODE SEGMENT AT 0F000H

その直後、56KB分データ領域(?)を確保している。
line. 225 - :

    DB 57344 DUP(?)   ; FILL LOWEST 56K
57344 = E000 H

となり、さらにE000Hから暫くはデータやストレージのR/Wテスト用サブルーチンが配置されており、結果としてRESETラベルは "0E05BH" から始まる。

RESETラベルから始まる初期処理に戻ると、1132行目からDISKETTE(FDD)のテストに入り、OKであればINT 19hを呼ぶ。
line. 1132 - :

;-----------------------------------------------------------------------;
;       DISKETTE ATTACHMENT TEST                                        ;
; DESCRIPTION                                                           ;
;       CHECK IF IPL DISKETTE DRIVE IS ATTACHED TO SYSTEM. IF ATTACHED, ;
;       VERIFY STATUS OF NEC FDC AFTER A RESET. ISSUE A RECAL AND SEEK  ;
;       CMD TO FDC AND CHECK STATUS. COMPLETE SYSTEM INITIALIZATION     ;
;       THEN PASS CONTROL TO THE BOOT LOADER PROGRAM.                   ;
;-----------------------------------------------------------------------;
F9:
	MOV AL, BYTE PTR EQUIP_FLAG
	(...)
(line. 1261)
;----- ENABLE NMI INTERRUPTS
	MOV AL,80H ; ENABLE NMI INTERRUPTS
	OUT 0A0H,AL
	CMP MFG_TST,1 ; MFG MODE?
	JE F21 ; LOAD BOOT_STRAP
	MOV DX,1
	CALL ERR_BEEP ; BEEP 1 SHORT TONE

F21:            ; LOAD_BOOT_STRAP:
	INT 19H     ; BOOTSTRAP

割り込みベクタはいつ初期化されたのか?E05Bから始まる初期処理の途中、595行目からBIOS用の割り込みベクタの初期化が行われている。line. 595 - :

;----- SET UP THE BIOS INTERRUPT VECTORS TO TEMP INTERRUPT
	MOV CX, 32          ; FILL ALL 32 INTERRUPTS
	SUB DI,DI           ; FIRST INTERRUPT LOCATION
D3:
	MOV AX,OFFSET D11   ; MOVE ADDR OF INTR PROC TO TBL
	STOSW
	MOV AX, CS          ; GET ADDR OF INTR PROC SEG
	STOSW
	LOOP D3             ; VECTBL0

;----- SET UP OTHER INTERRUPTS AS NECESSARY
	MOV NMI_PTR,OFFSET NMI_INT       ; NMI INTERRUPT
	MOV INT5_PTR,OFFSET PRINT_SCREEN ; PRINT SCREEN
	MOV BASIC_PTR+2,0F600H           ; SEGMENT FOR CASETTE BASIC

(すみません、この辺はきちんと読み切れてません。この直ぐ上の592行目で既に INT 3EH が呼ばれていたりするので、割り込みの種類毎に何段階かにわけて初期化しているのかもしれません。あるいは途中の分岐を読み飛ばしている、など。少なくとも INT 19h の読み出しまでに、割り込みベクタが初期化されていることは確かです。でないとINT 19hをそもそも呼べないので。)

INT 19h の割り込みハンドラは1493行目から始まる。
line. 1493 - :

;--- INT 19 ------------------------------------------------------
; BOOT STRAP LOADER                                              ;
;       IF A 5 1/4" DISKETTE DRIVE IS AVAILABLE ON THE SYSTEM,   ;
;       TRACK 0, SECTOR 1 IS READ INTO THE BOOT LOCATION         ;
;       (SEGMENT 0, OFFSET 7C00) AND CONTROL IS TRANSFERRED      ;
;       THERE.                                                   ;
;                                                                ;
;       IF THERE IS NO DISKETTE DRIVE, OR IF THERE IS A          ;
;       HARDWARE ERROR CONTROL IS TRANSFERRED TO THE RESIDENT    ;
;       BASIC ENTRY POINT.                                       ;
;                                                                ;
; IPL ASSUMPTIONS;                                               ;
;       8255 PORT 60H BIT 0 = 1 IF IPL FROM DISKETTE             ;
;-----------------------------------------------------------------
		ASSUME CS:CODE,DS:ABS0

;----- IPL HAS SUCCESSFUL
H4:
	JMP     BOOT_LOCN
	ORG     0E6F2H
BOOT_STRAP  PROC NEAR
	STI                            ; ENABLE INTERRUPTS
	(...)

;----- MUST LOAD SYSTEM FROM DISKETTE -- CX HAS RETRY COUNT
	MOV     CX,4                   ; SET RETRY COUNT
H1:                                ; IPL_SYSTEM
	PUSH    CX                     ; SAVE RETRY COUNT
	MOV     AH,0                   ; RESET THE DISKETTE SYSTEM
	INT     13H                    ; DISKETTE_IO
	JC      H2                     ; IF ERROR, TRY AGAIN
	MOV     AX,201H
	SUB     DX,DX
	MOV     ES,DX
	MOV     BX,OFFSET BOOT_LOCN
	MOV     CX,1                   ; SECTOR 1, TRACK 0
	INT     13H                    ; DISKETTE_IO
H2: POP     CX                     ; RECOVER RETRY COUNT
	JNC     H4                     ; CF SET BY UNSUCCESSFUL READ
	LOOP    H1                     ; DO IT FOR RETRY TIMES

;----- UNABLE TO IPL FROM THE DISKETTE
H3:                                ; CASSETE_JUMP:
	INT     18H                    ; USE INTERRUPT VECTOR TO GET TO BASIC
BOOT_STRAP  ENDP

"H4"ラベルがなぜ"BOOT_STRAP"の前に位置しているのかが謎だが、とにかく、"H2"ラベルで読み出し成功と判断されると"JNC H4"でH4にジャンプし、そのまま "BOOT_LOCN" にジャンプするようになっている。
"BOOT_LOCN"はROM BIOSのアセンブラリストの冒頭で7C00HにEQU(equate)されている。

line. 34 - :

;---------------------------------
;    8088 INTERRUPT LOCATIONS    ;
;---------------------------------
ABS0          SEGMENT   AT 0
STG_LOC0      LABEL     BYTE
              ORG       2*4
NMI_PTR       LABEL     WORD
              ORG       5*4
INT5_PTR      LABEL     WORD
(...)
              ORG       7C00H
BOOT_LOCN     LABEL     FAR
ABS0          ENDS

ここで、

JMP BOOT_LOCN

がセグメント内のNEARジャンプになってしまうのでは?と疑問に思った人もいるだろう。
ROM BIOSのアセンブラリストには、対応する機械語も掲載されている。このJMPに対応する機械語を見てみると、次のようになっている。

EA007C0000
=
E    A    00 7C        00 00
1110 1010 (offset L-H) (segment L-H)

よって、これはセグメント指定のFARジャンプであり、ジャンプ後は

CS:0000H, IP:7C00H

となる。よって問題なく、0000:7C00HにJMPできる。

IBM PC 5150 の ROM BIOS INT 19h が MBR を 7C00H(0x7C00) にロードする理由 (From Dr. David Bradley)

いよいよ 7C00H の謎が解かれる。
が、その前に 7C00H のそもそもの謎について再確認したい。

まず 7C00H というのは

32KB(8000H) - 1024B

である。先頭から32KB、その丁度1024バイト手前が、7C00Hである。まず一点目、この"32KB - 1024 = 7C00"というのがいかにも意味ありげであり、なぜこの場所にしたのか?というのが謎の一つめ。

謎の2つ目が、IBM PC 5150 発表時の最小メモリモデルが16KB(4000H) のRAMしか積んでいないこと。つまり、16KBのRAMでは当然7C00にMBRをロードすることは出来ない。よって16KBモデルではDOSを起動出来なかったのではないか?

まとめると:

  • "0x7C00" は 32KB - 1024Bに位置する。なぜこの位置なのか?
  • 5150の最小メモリモデルは16KBしかRAMを搭載していない。 → 16KBのモデルではOSを起動出来なかったのではないか?(0x7C00にアクセス出来ない)

この2点について、IBM PC 5150 の ROM BIOS の開発者、David Bradley氏にメールで質問してみた。同氏は"Ctrl-Alt-Delete"によるリセット機能を実装したことで知られている。

なお質問で「86-DOSは200Hにロードするが、なぜ変更したのか?」というのも含めたが、

I don't know anything about 86-DOS.

と回答している。つまり同氏は 86-DOS のコードは見ずに、つまり "86-DOSは200Hにbootstrapをロードする" 事実は知らないまま、ROM BIOSを開発した。

ではDavid Bradley氏からの回答で、まずは16KBモデルについての回答から。

It had to boot on a 32KB machine.
DOS 1.0 required a minimum of 32KB, so we weren't concerned about attempting a boot in 16KB.

DOS 1.0 は最小で32KBを必要としていた為、16KBのメモリモデルについては考慮しなかったとのこと。

続いていよいよ、「なぜ32KB-1024Bなのか?」に対する回答:

We wanted to leave as much room as possible for the OS
to load itself within the 32KB. The 808x Intel architecture
used up the first portion of the memory range for software
interrupts, and the BIOS data area was after it. So we put
the bootstrap load at 0x7C00 (32KB-1KB) to leave all the room
in between for the OS to load. The boot sector was 512 bytes,
and when it executes it'll need some room for data and a stack,
so that's the other 512 bytes. So the memory map looks like
this after INT 19H executes:

  1. OSの必要とする最小メモリ、32KBをなるべく空けておきたかった。
  2. アドレス0から始まる 0H - 3FFH までは割り込みベクタとして予約されているので使えない。
  3. なので、32KBの末尾にロードするようにした。
  4. MBR中のbootstrap自身が使うデータやスタック用に、もう512バイト確保した。
  5. よって、32KB - 512B(MBR) - 512B(データとスタック) で、7C00H とした。

同氏によれば、INT 19h実行後のメモリレイアウトは次のようになる:

+--------------------- 0x0
| Interrupts vectors
+--------------------- 0x400
| BIOS data area
+--------------------- 0x5??
| OS load area
+--------------------- 0x7C00
| Boot sector
+--------------------- 0x7E00
| Boot data/stack
+--------------------- 0x7FFF
| (not used)
+--------------------- (...)

以上が、四半世紀を越えて x86 IBM PC/AT互換機上のOSのbootstrapを支配してきた "0x7C00", "7C00H" の出自である。

参考資料

86-DOS関連PDF資料:

  • "8086 Monitor Instruction Manual"(MON 86 - V1.4)
  • "86-DOS(TM) User's Manual Version 0.3"
  • "86-DOS(TM) Programmer's Manual Version 0.3"
  • "86-DOS(TM) Instruction Manual Version ??"

IBM PC 5150 関連PDF資料:

  • "IBM Personal Computer Hardware Reference Library", "Technical Reference" (IBM Personal Computer Technical Reference manual)
  • "IBM Personal Computer XT Hardware Reference Library", "Technical Reference" (IBM Personal Computer XT Technical Reference manual)

Intel CPU データシートPDF:

  • "8086 16-BIT HMOS MICROPROCESSOR"
  • "M80C86/M80C86-2 16-BIT CHMOS MICROPROCESSOR"
  • "8088 8-BIT HMOS MICROPROCESSOR"

書籍:

以下、参考URL。

"Personal Computer"の歴史

Apple関連
NEC勢
CP/M関連
86-DOS, Tim Paterson 氏, 初期のMS-DOS(PC-DOS)関連

Tim Paterson 氏のサイト:

初期のMS-DOSの日本語資料
IBM PC 5150 前後の歴史について
  • PC Technical Reference Manual -- by Jalkanen Art & Science

その他IBM PC関連のWikipage:

David Bradley氏関連

BIOS, MBR, アセンブラ関連

  • Programming MS-DOS with Power
    • http://www.fysnet.net/index.htm
      • MS-DOSのプログラミングに関する、ひたすら「濃い」コンテンツ。
      • ROM BIOSのメモリレイアウトなど、機械語 - BIOS - DOSの境界線をひた走る。

Intel x86 の初期CPUのWikipedia

Special Thanks

下手くそな英語で突然質問してきたmsakamoto-sfに対して、親切に回答してくれた

Tim Peterson
David Bradley

両氏に対してSpecial Thanksです。



プレーンテキスト形式でダウンロード
現在のバージョン : 3
更新者: msakamoto-sf
更新日: 2010-05-13 11:07:47
md5:b5e0e32f3616548c666e6da829e20d6e
sha1:c3cea2065cb2055d0ba20515ea5bff10b7d9a878
コメント
コメントを投稿するにはログインして下さい。