MS-DOS 6.22 on Hyper-V
CPU の仕組みについて書かれた本の中でも、「はじめて読む 486」 はかなり有名な名著と言えるはずです。
途中で挫折していたので、最初からちゃんと読んでいたところ、やっぱり実機でサンプル コードを試したくなります。ちょっと前にブート コードの勉強をしていたときは、仮想ディスク (VHD) のマスター ブート レコード (MBR) をエディタで書き換えて、 Hyper-V の仮想マシンをその VHD から起動して動かすというけっこう面倒な方法を取っていました。
もっと楽に 16 ビット リアルモードで遊べる環境を作っておこう、ということで、MS-DOS を Hyper-V 仮想マシンにインストールしてみました。何も MS-DOS まで遡らなくても、Windows 9x や ME の DOS モードを使えばいいのですが、まあそこは好奇心ということで。どうでもいい昔話をすると、初めてプログラミングというものに触れたのが中一のときに学校にあった PC-9801 の N88-BASIC で、その後すぐに Windows 95 (年から考えると 98 だったかも) が導入されて Visual Basic/Visual C++ で遊び始めたので、Windows 以前の MS-DOS は全然触ったことがありません。そういえば当時の起動フロッピー ディスクはまだ手元にあるかも・・。
閑話休題、MS-DOS ですが、インストーラーは MSDN サブスクリプションに MS-DOS 6.0 と 6.22 が含まれていたのでそれを使います。MSDN を持っていない人は、”MS-DOS 6.22 download” とかのキーワードでググると、見つかるはずなので自己責任でどうぞ。
手順は以下の通り。6.22 はアップグレード用で、新規インストールはできないので、6.0 を入れてから 6.22 にアップグレードします。
- 適当な Windows マシンで MS-DOS 起動ディスクを作る
- MSDN からインストールしたファイルを仮想フロッピー、もしくは VHD にコピー
- 起動ディスクから DOS を起動
- MS-DOS 6.0 をフロッピーにインストール
- MS-DOS 6.0 を起動
- MS-DOS 6.22 を VHD にインストール
MSDN でダウンロードできるインストーラー en_msdos60.exe や en_msdos622.exe は自己解凍書庫で、解凍すると MS-DOS アプリの setup.exe が出てきます。これを使うためにはそもそも DOS を起動しないといけません。ということで、まずは DOS の起動ディスクを作らないといけません。
まずは空の仮想フロッピー ファイル (.vfd) を作ります。普通は PowerShell の New-Vfd コマンドレットを使うところですが、このコマンドレットは規定サイズの空ファイルを作っているだけなので、fsutil でも代用できます。サイズの 1474560 = 1440 * 1024 は、2HD フロッピーのバイト数です。
D:\MSWORK\dos> fsutil file createnew floppy.vfd 1474560
File D:\MSWORK\dos\floppy.vfd is created
これを仮想マシンにマウントし、エクスプローラーからフォーマットの画面を起動すると、なんと Windows 8.1 でも “Create an MS-DOS startup disk” のチェックボックスが使えます。よく廃止されないものです。ちなみに FORMAT コマンドにはそれに該当するようなオプションは見当たりません。GUI でしかできないんでしょうかね。
次に仮想マシンを作ります。RAM は 32MB、HDD は 1GB にしておきます。仮想 BIOS の設定で、Floppy を一番上に移動させます。こんな感じです。
先ほど作った起動ディスクをマウントして起動すると、無事 DOS が起動します。バージョンは Windows Millenium [Version 4.90.3000] だそうです。すぐには使わないので、仮想マシンはシャットダウンします。shutdown コマンドなんておものは存在せず、いきなり Turn off で問題ありません。
さて次に、DOS インストーラーの準備ですが、MS-DOS 6.0、6.22 共にインストール ファイルの合計サイズが 1.44MB を超えるので、VFD ではなく VHD で作ります。MS-DOS 6.22 の方は、親切にも複数のディスク イメージ ファイル (.img) をそれぞれフロッピーに書き込むためのスクリプトが用意されていますが、ディスクの入れ替えが面倒なだけなので使いません。
New-VHD コマンドレットなどで VHD を作って、MBR ディスクとして初期化し、FAT でフォーマットします。NTFS だと読めないので注意です。インストーラーのファイルは、おそらくドライブのルート ディレクトリ直下に入れておかないと動かないはずなので、それぞれのインストール用 VHD を作らないといけません。
作った VHD を仮想マシンにマウントし、再度起動ディスクから DOS を起動します。構成はこんな感じ。
1GB の VHDX はまだフォーマットされていないので認識されず、C: がインストーラーのドライブになります。したがって、C:\SETUP と実行します。
なぜか認識できないパーティションがあるとか言われます。よく分からないのでそのまま続行します。
MS-DOS 6.0 のインストーラーにはハード ディスクをフォーマットする機能が含まれず、空のハードディスクが見つからなかったため、フロッピーにインストールすることになります。仮にフォーマット済みの VHD をマウントして VHD にインストールしようとしても、有効な DOS が見つかりませんでした、というエラーが出てインストールできないので、最初の MS-DOS はフロッピーにインストールする必要があります。とりあえずこの画面ではそのまま Enter キーを押します。
この画面も Enter で続行します。
フロッピーを入れろと言われるので、ここで新たに VFD ファイルを作ってマウントしてから Enter キーを押します。
フロッピーの種類を聞かれるので、1.44MB を選びます。
無事終わりました。Enter キーを押してインストーラーを終了します。
元の DOS 画面でエラーが出ますが、おそらく起動ディスクを抜いてしまったためなので、気にせず仮想マシンを再起動します。
無事、MS-DOS 6.00 が起動しました。
次に MS-DOS 6.22 のインストールに移ります。手順はほとんど同じです。今度はハードディスクにインストールしたいので、インストール先の VHD を予め FAT でフォーマットしておきます。
今度は C: が空ディスク、D: がインストーラーなので D:\SETUP と実行します。
また未フォーマットのパーティションが検出されます。そんなのどこにあるんだ・・・気にせず続行します。
今度は最小構成ではなく、通常の構成でハードディスクにインストールしてくれそうです。ここも Enter を押します。
アンインストール ディスクというものを作る必要があるようです。今でいうリカバリ ディスクでしょうか。ここではそのまま Enter を押します。いちいちラベルを貼る猶予を与えてくれる親切設計。
インストール設定の確認です。C:\DOS にインストールされるようです。これぞ C:\WINDOWS の前身!
オプションのソフトウェアについて聞かれます。この当時のアンチ ウィルスって一体。Undelete ってのも謎。ごみ箱的な機能・・?とりあえず全部インストールしておきます。
いよいよ開始です。
フロッピーを入れ替える時間です。
サイズは 1.44MB です。
まさかのインストール エラー。ISK って何のことか分からず、わりと為す術がない。最初からのステップを 2 回繰り返しましたが、このエラーは変わらず。同じファイルを別の Windows 8.1 上の Hyper-V で試したら特にエラーは出ないので、Windows Server 2012 の Hyper-V との相性が悪いとかだったりして。詳細は不明です。
一応インストールは終わりました。フロッピーを抜いて Enter を押します。
Enter を押して再起動します。
起動には問題ないようです。HIMEM ってやつが結構遅い。
次に、どこからともなく入手してきた QuickC with Quick Assembler 2.51 をインストールします。ディスクが 10 枚もあって面倒くさい・・。
インストール後、何やら重要そうなことが書かれている画面が出てくるのでキャプチャーしておく。
インストールが終わったら、先ほどキャプチャーした画面の記述にしたがって環境変数の設定。現在の C:\AUTOEXEC.BAT はこんな感じ。
@ECHO OFF
PROMPT $p$g
PATH C:\DOS
SET TEMP=C:\DOS
これを、C:\QC25\BIN\NEW-VARS.BAT を参考にして、以下のように変更。MOUSE コマンドを実行してマウス ドライバーを有効にすると、高確率で OS がハングするので、マウスは諦めます。
@ECHO OFF
PROMPT $p$g
SET PATH=C:\QC25\BIN;C:\QC25\TUTORIAL;C:\DOS
SET LIB=C:\QC25\LIB
SET INCLUDE=C:\QC25\INCLUDE
SET TEMP=C:\DOS
あと config.sys。懐かしすぎる。C:\QC25\BIN\NEW-CONF.SYS には FILES=20, BUFFERS=10 と書かれていましたが、FILES は既に 30 になっていたので、BUFFERS を多めの 20 にしておくことに。
DEVICE=C:\DOS\SETVER.EXE
DEVICE=C:\DOS\HIMEM.SYS
DOS=HIGH
FILES=30
SHELL=C:\DOS\COMMAND.COM C:\DOS\ /p
BUFFERS=20
再起動して、早速 QuickC を起動。
これが 20 年前の IDE 。。というか画面ちっさ。Hyper-V のせいだけど。
ただ普通に Hello World を書いても面白くないので、以下のような 3 つのファイルを書いて NMAKE でビルド。
まずは C のソース。これは普通です。
//
// 00.C
//
#include <stdio.h>
extern int GetVer();
void main(int argc, char **argv)
{
printf("Hello, MS-DOS %d!", GetVer());
exit(0);
}
次にアセンブリ言語。サンプルからコピペしただけです。int 21 でバージョンが返ってくるみたいです。
;
; UTILS.ASM
;
.MODEL small, c
.CODE
;* GetVer - Gets DOS version.
;*
;* Shows: DOS Function - 30h (Get MS-DOS Version Number)
;*
;* Params: None
;*
;* Return: Short integer of form (M*100)+m, where M is major
;* version number and m is minor version, or 0 if
;* DOS version earlier than 2.0
GetVer PROC
mov ah, 30h ; DOS Function 30h
int 21h ; Get MS-DOS Version Number
cmp al, 0 ; DOS version 2.0 or later?
jne @F ; Yes? Continue
sub ax, ax ; No? Set AX = 0
jmp SHORT exit ; and exit
@@: sub ch, ch ; Zero CH and move minor
mov cl, ah ; version number into CX
mov bl, 100
mul bl ; Multiply major by 10
add ax, cx ; Add minor to major*10
exit: ret ; Return result in AX
GetVer ENDP
END
最後が MAKEFILE。サンプルのファイルを元にいろいろと修正しましたが、リンカの設定のところが意味不明・・。
PROJ =TEST
CC =qcl
AS =qcl
CFLAGS=/Od /Gi$(PROJ).mdt /DNDEBUG /Zi /Zr
AFLAGS=/Zi /Cx /P1
LFLAGS =/NOI /INCR /CO
.asm.obj: ; $(AS) $(AFLAGS) -c $*.asm
all: $(PROJ).EXE
utils.obj: utils.asm
00.obj: 00.c
$(PROJ).EXE: utils.obj 00.obj
echo >NUL @<<$(PROJ).crf
utils.obj +
00.obj
$(PROJ).EXE
NUL.MAP
<<
ilink -a -e "qlink $(LFLAGS) @$(PROJ).crf" $(PROJ)
ファイルを作ったら NMAKE するだけです。
おお、MS-DOS のバージョンが出た!これでリアルモードと仲良くできそう。
しかしファイルの編集がやりにくすぎる・・・SSH みたいなリモート接続はできないのだろうか。そもそも Hyper-V の仮想 NIC に対応している TCP/IP のドライバーなんてなさそう。