MS-DOS互換の「魔改造OS」FreeDOSを、あろうことかArmコアのラズパイ4機上で定番CPUエミュレータQEMU使って動かしてます。いろいろ触っていくために、古の時代のMS-DOSのメモリ管理を思い出す必要に迫られました。今回は「面倒かった」記憶だけが残るMS-DOSのメモリ管理の復習ってことで。
※実機動作確認には以下を使用しております。
MS-DOSのメモリ管理、お惚け老人の茫漠とした記憶
DOSに詳しいお兄さんお姉さん方には常識のハズのコマケー話をおさらいしていきます。昔のことは忘れない、とか言いつつ、結構、コマケー記憶はあやふやだったりする老人です。それに、時代により、また機種によりめっちゃ例外などもあって一筋縄でくくれないのがMS-DOSの世界なんであります。それでもバッサリやってしまうとこんな感じかと。
左から、
-
- 8088/8086(V30)の初期MS-DOSのメモリマップ概要
- 8088/8086(V30)のメモリ不足が露呈、LIM(Lotus/Intel/Microsoft)規格 EMSが登場したころのメモリマップ概要
- 80386以降(一部は80286でもできたが)の後期MS-DOSのメモリマップ概要
初期のMS-DOS
元々8088/8086(2つのCPUの命令セットは同一)用のオペレーティングシステムとして開発されているので、8088/8086のアドレス空間1Mバイト(20ビットアドレス)の管理が前提となります。そのうち8086のハードウエア的に変えられない部分があります。
-
- RESETベクタ 0xFFFF0番地(インテル的にCS:IPとして書けば 0FFFFh:0000h)
- 割り込みベクタのシステム予約領域0x0000番地から0x007F番地
電源投入で、8088/8086は 0xFFFF0番地のメモリから最初の命令をフェッチに来ます。通常ここにはROM(現代的にはFlash)を置きます。多くの場合、このROMの中にはPOWER ON時のハードウエア初期化のための各種ルーチンおよびPOST(Power ON Self Test)などの機能や外部記憶から「ブートローダ」を読み込むための「最初の」ブートローダなどが置かれてます。ハードウエア入出力のためのサブルーチン集であるBIOSもここに置かれてます。また、最初期のマシンでは同じROMの中にBASICインタプリタなども入っていたかもしれません(MS-DOSには関係ない。)
上記のROMがまず走って、DRAMを初期化し0x00000番地からに置かれたRAMに「割り込みベクタ」をセットします。8086/8088の場合、0番地から0x3F番地までの64バイトにおかれる32個のベクトルは「システム(インテル)予約」ということで必ず対応の所定の割り込みハンドラ(例えばゼロ割り算割り込みとか)へのベクトルをセットせにゃなりません。なお、33番目以降256番目まではユーザー利用が可能な割り込みベクタです。MS-DOS自身や各種の常駐ソフトウエアが特定の割り込みを使っているので、実質0x3FF番地までは割り込みテーブルということで他の目的には使わないと思います。
さて一番古い時代のMS-DOSは、このベクトルテーブルの次から(フロッピイもしくはHDDから引きずり出した)MS-DOS本体、デバイス・ドライバ、MS-DOSのSHELLプログラムであるCOMMAND.COM、TSR(ターミネイト・ステイ・レジデント)プログラム類(TSRは実行するとメモリに常駐し何かサービスを提供してくれるようなプログラム)、がメモリを使っていきます。
残ったメインメモリのうち最大 0x9FFFF番地までが、実際にアプリケーションプログラムが使えるメモリです。この部分が0番地から数えて640Kバイト、後の時代になるとこの貴重なメモリ領域は「コンベンショナルメモリ」と呼ばれて珍重されるようになります。
0xA0000番地からBIOS-ROMの下端(システムによる)まではIOに割り当てられてました。各種のビデオボード、プリンタ、通信など用のカードが使える領域です。御本家IBMのVGAなどの「ディスプレイ・アダプタ」などのメモリの割付が標準となり、例えば 0xA0000番地から0xBFFFF番地あたりはVRAMに割り当てるといったスタイルが一般化した記憶です。ハードウエアが割付られてないアドレスは「空き」です。
メモリ不足が露呈したころのMS-DOS
当初640Kバイトのメモリ空間は巨大と思われてました。なにせ前世代の8ビット機は合計64Kバイト(RAMとして使えるのはその半分くらいが多かった)の空間だったので10倍になったからです。最初期の一番お安いIBM-PC(8088搭載)などDRAMは16Kバイトしか積んでなかったし。
しかし、MS-DOS上でビジネス・アプリを使うことが一般化すると640Kバイトなど使い切ってしまうものが出てきました。その代表が一世を風靡した表計算ソフト
Lotus 1-2-3
でした。そこで、Lotus、Intel、MicroSoftの3社が主導して決めたのが以下です。
LIM EMS規格
これは20本(1Mバイト分)しかアドレス線のない8088/8086に接続したメモリボード上に2Mバイトとか4Mバイトとか主記憶を多いに上回るDRAMを載せておいて、ちまちまと「バンクスイッチ」的なハードウエア操作を行って主記憶上に開いた「小さい窓」から大きなデータを操作できるようにしよう、という規格です。LIM EMS規格については別シリーズにて仕様書を参照してます。
この拡張メモリを
Expanded Memory
と称しました。後で似たお名前がでてくるので、混乱しないでね。Expandedです。なお、これを制御するデバイスドライバは、EMM.sys といったEMMというお名前(ただしEMM386と386がつくと、機能が多いに拡張されているので異なる存在)がついているのが一般的でした。なお、Expanded Memoryという場合
0xC0000番地からの64KバイトにEMSページフレーム
という領域を確保し、外部のDRAM上のデータは16Kバイト毎の小分けの「ページ」としてこのページフレームに4ページずつ割り当てる、というのがお作法でやんした。
386以降のMS-DOS
一部の機能は286でも使えたのですが、簡単のため80386以降のCPUを搭載したMS-DOS機に絞ります。286以降のプロテクト・モードを使えば8086/8088の1Mバイトの制限を打破できるのですが、プロテクト・モードへの出入りがメンドイこともあり、まず考え出されたのが、HIMEMという部分
0x100000番地から0x10FFEF番地
までの約64Kバイト部分の活用です。これは20本以上のアドレス線を持つ286、386においては、8086の互換のリアルモードでもセグメント・レジスタを操作することで1Mバイトの上の空間が「見えてしまう」ことを利用したものです。初期はこれを互換性を損なうものとみてA20アドレス線を無理やりOFFにしたりしていたのですが、「使えばいいじゃん」ということになって使うようになりましたな。知らんけど。
このHMA(など)を管理するドライバとしてHIMEM.SYSが登場します。
そしてさらに386以降のプロテクトモード機能を活用してさらに上のEMB、Extended Memoryを利用できるようにしてしまったのが EMM386.EXEです。多分、HIMEM.SYSとEMM386.EXEは共同でメモリ管理しているので2つ合わせてXMSドライバと言われることが多いでないかと?怪しい記憶デス。
このころになると以下のテクニックが定着
-
- LIM EMS規格対応のハードウエアメモリボードがなくても386の機能で上位のメモリを0xC0000からのEMSページフレームに割り当てて使う
- MS-DOSの大部分、command.comの大部分をHMAに追い出してコンベンショナルメモリを空ける
- VRAMやEMSページフレームに割り当てられていない「コマケー隙間」にメモリを割り当て UMB(アッパーメモリブロック?多分)と称する。そこにアドレスの下の方にいたデバイスドライバとかTSRとかを追い出してコンベンショナルメモリをさらに空ける
そして上位の空間に忽然とあらわれた「フラット(リニア)で巨大な」空間をDOSから使用するために、
DOSイクステンダ
と称されるプログラムが登場するのであります。
手元のFreeDOSのメモリ
以上で手元のFreeDOS上で走らせた MEM コマンドの以下出力を解釈できるかと。
上記みると、DOSのクセに130Mバイトほどのメモリを使ったプログラムを実行できるようです。まあ、64ビットArmコアの上でQEMUが作り出した幻影なんだけれども。走っているプログラムどもは気づかず走るんであります。知らんけど。