English Version: " Why BIOS loads MBR into 0x7C00 in x86 ? "
Assembler/なぜx86ではMBRが"0x7C00"にロードされるのか?で調査が不十分だった点を補った完全版です。
自分が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のコードが実行される、と説明している。
MBRから直接OSのカーネルをロードするのではなく、一つ or 二つ程度の中間ローダを介する場合もある。MBRの機械語コード、およびMBRがロード&実行する中間ローダはOS固有の場合(MicrosoftのNTLDR)もあれば、複数のOSに対応したオープンソースソフトウェアの場合(LILO, GRUB)もある。
ところで、実際にMBRを弄った人は、次の疑問を抱いたことはないだろうか?
「"0x7C00"って、どこの仕様書で決められているんだろうか?」
x86CPUについて学んでいくと、独特の"メモリアドレスのマジックナンバー"がいくつかあることに気づく。
これは割り込みベクタ用のメモリ空間で、
4バイトの割り込みハンドラのアドレス x 256個の割り込みベクタ = 1024バイト = 0x400 バイト
で、x86の仕様として割り込みベクタはアドレス0番地から始まる為、一つ分ずらした値となる。
・・・ところが、"0x7C00" の意味については、CPU関連を調べても何も分からない。ブートローダやMBR関連の情報を調べても、単に0x7C00にロードされる、と書かれているだけで、なぜこの値なのか?誰が決めたのか?についての情報は皆無にみえる。
前ぶりが長くなったが、本記事はMBRがロードされる "0x7C00" というアドレスの起源と、その意味について調べた結果をまとめている。
この記事単体で何かしら「使える」知識を増やすことは出来ないが、もしも読者のあなたが "0x7C00" について喉に魚の骨が刺さったままのような感触を覚えているのであれば、ぜひ一読をお奨めしたい。
"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とともに発表される事になる。
2010年現在のPCでは、マザーボード上のROMチップにBIOSが搭載され、OSはHDDなど外部記憶ディスクに保存されるのが常識となっている。この棲み分けは、まさしく IBM PC 5150 が原点となっている。
というのも、MS-DOSの原型にあたる86-DOS(とCP/M)ではBIOS相当の機能もOSの一部に含まれていた。
BIOSの機能というのは、
もちろんPOST処理は重要だが、それ以上にOSが使うソフトウェア割り込みも重要で、これにより画面上に文字や図形を描画したり、FDD/HDDにアクセスしてデータを読み出したり、ファイル処理やプログラムをメモリ上にロードしたりしていた。当時はそうした機能が全て"OS"の中に含まれていたことになる。86-DOSのベースとなったCP/Mもそのようになっていた。
これが OS と BIOS に分離された形式になり、以降の流れを決定づけた起点が IBM PC 5150 である。
ここで、そもそも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"が最初にフェッチされるアドレスとなる。
参考:
※2 : 80386ではアドレスバスが32bitになるが、リアルモードでは20bitしか使われない。A20-A31はリセット後は1になっている。そのため、CSこそ"F000H"になっているが、もう12bit分全て1のデータが上位に隠れている。
(FFF)F0000 H(CS) (4bit左シフト) FFF0 H(IP) -------------- FFF FFFF0 H
参考:
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 にロードしている。
この "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.
まとめると
以上の理由で 200H - 3FFH の間の隙間が使われた。ちなみに、100H - 200H の間も、レジスタの退避やMonitor/bootstrapなどの初期処理が使うスタック領域として利用されていたらしい。
86-DOSやCP/Mでは、bootstrapのロードや割り込みベクタの調整と対応するハンドラなどが全て"OS"の中に含まれていた。
これが、IBM PC 5150において次のように分裂した。
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 が呼ばれるようになっていた。
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できる。
いよいよ 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を起動出来なかったのではないか?
まとめると:
この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:
同氏によれば、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資料:
IBM PC 5150 関連PDF資料:
Intel CPU データシートPDF:
書籍:
以下、参考URL。
Tim Paterson 氏のサイト:
その他IBM PC関連のWikipage:
下手くそな英語で突然質問してきたmsakamoto-sfに対して、親切に回答してくれた
Tim Peterson David Bradley
両氏に対してSpecial Thanksです。