はじめに
本稿は、Intel Core2Duo世代のPC(ベアメタル)に、Windows 95 / 98/ Me / 2000のマルチブート環境を構築するための手順である。
Windows 9xやWindows 2000をベアメタル環境で稼働させる意義は、単に旧世代OSを起動させることではない。当時のハードウェアとOSが不可分に構成していた計算機環境を、物理的実体を伴って再現することにその本質がある。
仮想環境では、CPU、チップセット、BIOS、各種バス、割り込み、DMA、グラフィック、サウンド等の多くが抽象化ないしエミュレーションされる。その過程で、実機固有の電気的特性、タイミング、デバイス間の相互作用は不可避的に捨象される。したがって、実デバイス用ドライバ、ハードウェアアクセラレーション、割り込みやタイミングに依存するソフトウェアを、当時と同一の条件下で検証することは原理的に困難である。
とりわけこの時代のOSは、今日のOSと比較してハードウェアへの依存度が著しく高かった。
デバイスの認識、ドライバの選択、リソースの割り当て、バス上での挙動、さらには機器間の互換性や相性に至るまで、そのすべてが計算機システムを構成する不可分の要素であった。
ゆえに仮想環境が「旧OSを現代の計算機上で稼働させるための互換基盤」であるならば、ベアメタル環境は「当時の計算機システムを物理層から再構築し、その挙動を検証可能な状態で動態保存するための基盤」である。
利便性や実用性ではなく、実機性、再現性、技術的検証可能性、そして歴史的資料としての真正性を追求する立場から、レガシーOSをベアメタル環境へ導入・維持するための技術的指針を示す。
環境詳細
環境構築のために用意した端末(富士通製法人向けデスクトップPC)の主な仕様は下表の通り。
| 項目 | 仕様 |
|---|---|
| 機種 | FUJITSU FMV-D5270 |
| CPU | Intel Core 2 Duo E8400 |
| メモリ | PC2-6400 512MB |
| グラフィック | PCI接続 nVidia GeForce FX 5200 |
| サウンド | PCI接続 Creative SoundBlaster Live! CT4670 |
| 磁気ドライブ | SATA接続 120GB HDD、3.5インチFDD |
| 光学ドライブ | IDE/PATA接続 CD-ROM |
- FMV-D5270のBIOSには、AHCI / IDE / Compatible / Legacyなどの切り替え設定はない。
- Windows 9x系OSをそのままインストールした場合、高速CPU、SATAコントローラ等の非互換問題により起動に失敗する(インストール時の対策が必要)。
- 大容量メモリ(1GB)を搭載した場合、Windows 95のインストール中に「メモリ不足のため Windows を初期化できません」エラーが発生する(HIMEMXによる回避不可)。
本手順により実現できること
- Windows 95 / 98 / Me / 2000 のマルチブート
- 上記OSの通常モードでの正常起動
- Intel ICH7 SATA 上のSATA HDDからのOS起動、32bitディスクアクセス
- IDE/PATA接続CD-ROMの正常認識および動作
- 512MB RAM環境でのOS動作
必要なもの
- Windows 95 / 98 / Me / 2000 インストール用ディスク(推奨:新規インストールパッケージ)
- Windows 95 / 98 / Me / 2000 セットアップ用起動ディスク(必須:Windows 98 / 2000 用ディスク)
- PowerQuest社 PartitionMagic 7.0 および BootMagic 7.0
- FIX95CPU
- PTCHSATA(PTCHSATA.EXE, SATA.INF)
- Y-E DATA 内蔵USB FDD用ドライバ(Windows 98インストール後に必要)
- FD媒体(3枚以上)
事前準備
Windows 95 / 98 / Me / 2000 の完全分離環境を前提とするため、OS のインストール作業以前にPartitionMagic によりHDD(MBR)のパーティションを以下の通り構成する。
| パーティション | OS | 状態 |
|---|---|---|
| P1 | Windows 95 | Active / Visible |
| P2 | Windows 98 | Hidden |
| P3 | Windows Me | Hidden |
| P4 | Windows 2000 | Hidden |
- 重要:インストール対象のパーティション(領域)のみを Visible にする。
- Windows95初版をインストールする場合、P1のファイルシステムはFATとする。
Windows 95インストール手順
ドライブレターは A:FDD, C:HDD, E:CD-ROM の前提とし、以下の手順で行う。
- Windows 95用P1をActive / Visibleに設定し、他OS用パーティションはHiddenに設定する。
- Windows 95用セットアップ起動用FDで起動する(DOS上でCD-ROMを使用できる状態にする)。
- Windows 95用P1が未フォーマットであれば、「FORMAT C:」でフォーマットする。
- Windows 95インストール用CDから「E:\SETUP /P I」でOSのインストーラーを起動する。
「/P I」オプションを省略すると以降のPnP検出処理時にブルーバックエラーとなる。 - 初回の再起動でWindows 95を起動させない(最重要ポイント)。
Windows 95セットアップがファイルコピーを終えて「コンピュータを再起動します」メッセージ以降の初回再起動で、Windows 95を起動させてはならない。 - FIX95CPUをFDから起動する。
必ずWindows 95 GUIを見る前(初回起動時)にFIX95CPUを適用する。
FIX95CPUの適用完了後もWindows 95を通常起動させない。 - Windows 95用セットアップ起動用FD(またはDOS起動可能なFD)からDOSを起動する。
- DOSで以下のコマンドを実行する(任意)。
DIR C:\WINDOWS\SYSTEM\IOSUBSYS\ESDI_506.PDR
ESDI_506.PDRが存在を確認できればOKと判断。 - PTCHSATA.EXEを実行する。
実行後に「Patch Completed」が表示されることを確認する。 - PTCHSATAのSATA.INFをWindowsのINFフォルダへコピーする。
COPY A:\SATA\SATA.INF C:\WINDOWS\INF\SATA.INF
コピー完了後は「Ctrl + Alt + Del」でDOSを終了。 - Windows 95セットアップを再開し、ハードウェア検出が進むことを確認する。
手順10でコピーしたSATA.INFにより、ICH7 SATA 8086:27C0は「Standard Dual PCI SATA Controller」として自動で引き当てられるため手動適用は不要(不可)。
手動適用が必要になった時点でインストールは失敗しているため、手順2からのやり直しが必要。 - Windows 95セットアップを最後まで完了させる。
- ビデオ、サウンド、LAN、USB、チップセットINFなどを順次インストールする。
Windows 98インストール手順
ドライブレターは A:FDD, C:HDD, E:CD-ROM の前提とし、以下の手順で行う。
Windows 95インストール手順中の「FIX95CPU」インストールを除き、手順は同様。
- Windows 98用P2をActive / Visibleに設定し、他OS用パーティションはHiddenに設定する。
- Windows 98用セットアップ起動用FDで起動する(DOS上でCD-ROMを使用できる状態にする)。
- Windows 98用P2が未フォーマットであれば、「FORMAT C:」でフォーマットする。
- Windows 98インストール用CDから「E:\SETUP /P I」でOSのインストーラーを起動する。
「/P I」オプション省略時の挙動は未検証のためWindows 95インストール手順に倣う。 - 最初の再起動でWindows 98を起動させない(最重要ポイント)。
Windows 98セットアップがファイルコピーを終えて「コンピュータを再起動します」メッセージ以降の初回再起動で、Windows 98を起動させてはならない。 - Windows 98用セットアップ起動用FD(またはDOS起動可能なFD)からDOSを起動する。
- DOSで以下のコマンドを実行する(任意)。
DIR C:\WINDOWS\SYSTEM\IOSUBSYS\ESDI_506.PDR
ESDI_506.PDRが存在を確認できればOKと判断。 - PTCHSATA.EXEを実行する。
実行後に「Patch Completed」が表示されることを確認する。 - PTCHSATAのSATA.INFをWindowsのINFフォルダへコピーする。
COPY A:\SATA\SATA.INF C:\WINDOWS\INF\SATA.INF
コピー完了後は「Ctrl + Alt + Del」でDOSを終了。 - Windows 98セットアップを再開し、ハードウェア検出が進むことを確認する。
手順10でコピーしたSATA.INFにより、ICH7 SATA 8086:27C0は「Standard Dual PCI SATA Controller」として自動で引き当てられるため手動適用は不要(不可)。
手動適用が必要になった時点でインストールは失敗しているため、手順2からのやり直しが必要。 - Windows 98セットアップを最後まで完了させる。
- Y-E DATA 内蔵USB FDD (VID_057B&PID_0000)用のドライバをインストールする。
デバイスマネージャより「USB Floppy Drive」の「ドライバの更新」を行う。
ドライバの更新前はFDDを認識済だがディスク読取が行えない状態であるため、あらかじめDOSでCドライブ直下等にドライバファイル群を配置しておく必要がある点に注意。 - ビデオ、サウンド、LAN、USB、チップセットINFなどを順次インストールする。
FMV-D5270における自動電源OFF制約
FMV-D5270のPhoenix BIOSは、標準の APM Installation Check(INT 15h / AX=5300h)に応答しないため、Windows 95標準のAPM経由での自動電源OFFができない。そのため、OS終了後にACPI S5を直接発行する補助プログラム等が必要となる。
以下の表は、D5270固有のS5シーケンスを確定するために調査したACPI関連値。
| 項目 | 値 |
|---|---|
| SMI_CMD | 0x00B2 |
| ACPI_ENABLE | 0xF0 |
| ACPI_DISABLE | 0xF1 |
| PM1a_CNT_BLK | 0x1004 |
| PM1b_CNT_BLK | 0x0000 |
| PM1_CNT_LEN | 2 |
| _S5._SLP_TYPa | 7 |
| _S5._SLP_TYPb | 7 |
| S5 request bits | (7 << 10) | SLP_EN = 0x3C00 |
上記仕様を踏まえ、PWOFFHLP.COM(FMV-D5270上のWindows 95からACPI S5による完全電源断を安全に実行するための、16bitリアルモードDOS用COMプログラム)を作成した。
本プログラムの仕様およびソースコードは以下の通り。
- SMSWによるCR0.PE確認でProtected/V86モードを排除、INT 2Fh AX=1600hでWindowsが完全に停止していることを確認する。
- Windows 95のカスタムMS-DOSモードでは、退避されたCONFIG.WOS/AUTOEXEC.WOSを検出し、現在のCONFIG.SYS/AUTOEXEC.BATを.APPへ保存した後、.WOSから通常起動用構成を復元する。
- 片方の.WOS欠落、リネーム失敗等の異常時にはS5を禁止し、可能な範囲でロールバックする。
- 復元完了後はINT 21h AH=0Dhでディスクバッファをflushする。
- 電源制御ではD5270固有のSMI_CMD=0xB2へ0xF0を出力してACPIを有効化し、PM1a_CNT=0x1004の既存ビットを保持しつつ、SLP_TYP=7とSLP_ENを設定してS5へ移行する。
/*
* PWOFFHLP.COM
* FUJITSU FMV-D5270 ACPI S5 power-off helper for Windows 9x
*
* Target:
* FUJITSU FMV-D5270
*
* Verified ACPI values obtained from the actual machine:
* SMI_CMD = 0x00B2
* ACPI_ENABLE = 0xF0
* ACPI_DISABLE = 0xF1
* PM1a_CNT_BLK = 0x1004
* PM1b_CNT_BLK = 0x0000
* PM1_CNT_LEN = 2
* _S5_.SLP_TYPa = 7
* _S5_.SLP_TYPb = 7
*
* This program is intentionally machine-specific.
*
* Safety design:
* - Requires the explicit "/GO" switch.
* - Refuses protected/V86 mode.
* - Refuses to run while Windows enhanced mode is active.
* - Requests ACPI ownership only when SCI_EN is clear.
* - Aborts if SCI_EN cannot be obtained.
* - Preserves unrelated PM1a_CNT bits.
* - Safely restores Windows 95/98 custom MS-DOS-mode .WOS boot files.
* - Flushes DOS disk buffers before S5.
* - Restores PM1a_CNT and ACPI ownership if S5 unexpectedly returns.
*
* IMPORTANT:
* This utility should be launched only after Windows has cleanly
* transitioned to real-mode DOS, e.g. through a Windows 95/98
* "MS-DOS mode" shortcut/PIF. It must not be run inside a normal
* Windows DOS box.
*/
.code16
.intel_syntax noprefix
.global _start
_start:
push cs
pop ds
/* ------------------------------------------------------------
* Safety lock: require "/GO" in the DOS command tail.
* This prevents accidental double-click / mistyped execution.
* ------------------------------------------------------------ */
mov si, 0x81
xor cx, cx
mov cl, byte ptr ds:[0x80]
find_go:
cmp cx, 3
jb err_need_go
cmp byte ptr [si], '/'
jne next_char
mov al, byte ptr [si+1]
and al, 0xDF
cmp al, 'G'
jne next_char
mov al, byte ptr [si+2]
and al, 0xDF
cmp al, 'O'
je have_go
next_char:
inc si
dec cx
jmp find_go
have_go:
/* ------------------------------------------------------------
* Require true real mode.
* SMSW bit 0 is CR0.PE. If set, we are in protected/V86 mode.
* EMM386/V86 must therefore be disabled.
* ------------------------------------------------------------ */
smsw ax
test ax, 1
jnz err_not_real
/* ------------------------------------------------------------
* Refuse execution if Windows enhanced mode is still active.
* INT 2Fh AX=1600h returns AL=00h when Windows is not running.
* AL=80h is also accepted for the legacy XMS-only case.
* ------------------------------------------------------------ */
mov ax, 0x1600
int 0x2F
cmp al, 0x00
je windows_not_running
cmp al, 0x80
je windows_not_running
jmp err_windows_running
windows_not_running:
push cs
pop ds
/* ------------------------------------------------------------
* Windows 95/98 custom MS-DOS-mode cleanup.
*
* When CONFIG.WOS and AUTOEXEC.WOS both exist, Windows has saved
* the normal boot configuration while the PIF's temporary DOS-mode
* CONFIG.SYS/AUTOEXEC.BAT are active.
*
* Reproduce only the file-restoration part of WIN.COM /WX:
*
* CONFIG.SYS -> CONFIG.APP
* AUTOEXEC.BAT -> AUTOEXEC.APP
* CONFIG.WOS -> CONFIG.SYS
* AUTOEXEC.WOS -> AUTOEXEC.BAT
*
* We deliberately do NOT reboot. After a successful restoration
* we flush DOS disk buffers and continue to the proven ACPI S5
* power-off path.
*
* Safety rules:
* - If neither .WOS file exists, leave files untouched and
* continue with the original S5 behavior.
* - If only one .WOS file exists, abort before changing anything.
* - If CONFIG.APP or AUTOEXEC.APP already exists, abort rather
* than overwrite potentially useful recovery data.
* - Any rename failure triggers rollback of completed renames.
* ------------------------------------------------------------ */
call restore_windows_boot_files
jc err_restore_failed
/* Flush DOS file/disk buffers after any successful restoration. */
mov ah, 0x0D
int 0x21
/* Read the current PM1a control register. */
mov dx, 0x1004
in ax, dx
mov word ptr [pm1_initial], ax
/* ------------------------------------------------------------
* SCI_EN (bit 0) indicates that ACPI mode is active.
* If it is clear, ask firmware to enable ACPI by writing the
* machine-specific ACPI_ENABLE value (F0h) to SMI_CMD (B2h).
* ------------------------------------------------------------ */
test ax, 0x0001
jnz acpi_ready
mov dx, 0x00B2
mov al, 0xF0
out dx, al
mov byte ptr [enabled_by_us], 1
/* Wait for firmware to set SCI_EN, but never wait forever. */
mov cx, 0xFFFF
poll_sci:
mov dx, 0x1004
in ax, dx
test ax, 0x0001
jnz acpi_ready
loop poll_sci
jmp err_sci_timeout
acpi_ready:
/* ------------------------------------------------------------
* Re-read PM1a_CNT immediately before building the S5 request.
* Preserve every unrelated bit.
*
* PM1_CNT layout:
* SLP_TYP = bits 12:10
* SLP_EN = bit 13
*
* D5270 _S5_ returned SLP_TYPa = 7.
* 7 << 10 = 0x1C00
* SLP_EN = 0x2000
* Combined S5 request bits = 0x3C00
* ------------------------------------------------------------ */
mov dx, 0x1004
in ax, dx
mov word ptr [pm1_before], ax
and ax, 0xC3FF
or ax, 0x3C00
mov word ptr [pm1_target], ax
/* SCI_EN must still be set before issuing S5. */
test ax, 0x0001
jz err_sci_lost
/* ------------------------------------------------------------
* Issue ACPI S5.
* On the verified D5270, power is removed immediately here.
* ------------------------------------------------------------ */
cli
mov dx, 0x1004
mov ax, word ptr [pm1_target]
out dx, ax
sti
/* ------------------------------------------------------------
* If firmware does not cut power and execution continues,
* restore the original PM1a_CNT state.
* ------------------------------------------------------------ */
mov cx, 0xFFFF
delay1:
nop
loop delay1
mov dx, 0x1004
mov ax, word ptr [pm1_before]
out dx, ax
/* Return ACPI ownership only if this program enabled it. */
cmp byte ptr [enabled_by_us], 0
je err_s5_returned
mov dx, 0x00B2
mov al, 0xF1
out dx, al
jmp err_s5_returned
/* ------------------------------------------------------------
* Windows 95/98 MS-DOS-mode file restoration helpers
* ------------------------------------------------------------ */
/*
* file_exists
* DS:DX -> ASCIIZ path
* CF=0 file exists
* CF=1 file does not exist / cannot be opened
*
* Opens read-only, then closes immediately.
*/
file_exists:
push ax
push bx
mov ax, 0x3D00
int 0x21
jc file_exists_no
mov bx, ax
mov ah, 0x3E
int 0x21
clc
pop bx
pop ax
ret
file_exists_no:
stc
pop bx
pop ax
ret
/*
* rename_file
* DS:DX -> old ASCIIZ path
* ES:DI -> new ASCIIZ path
* Returns DOS CF/error status.
*/
rename_file:
mov ah, 0x56
int 0x21
ret
/*
* restore_windows_boot_files
*
* Return:
* CF=0 success, including the case where no .WOS pair exists.
* CF=1 unsafe/incomplete state or a rename failure.
*/
restore_windows_boot_files:
push ax
push bx
push cx
push dx
push si
push di
push es
mov byte ptr [restore_stage], 0
/* Check CONFIG.WOS. */
mov dx, offset path_config_wos
call file_exists
jc no_config_wos
mov byte ptr [have_config_wos], 1
jmp check_auto_wos
no_config_wos:
mov byte ptr [have_config_wos], 0
check_auto_wos:
mov dx, offset path_auto_wos
call file_exists
jc no_auto_wos
mov byte ptr [have_auto_wos], 1
jmp evaluate_wos
no_auto_wos:
mov byte ptr [have_auto_wos], 0
evaluate_wos:
mov al, byte ptr [have_config_wos]
add al, byte ptr [have_auto_wos]
/* Neither exists: not the custom .WOS mode. Leave untouched. */
cmp al, 0
je restore_ok
/* Exactly one exists: inconsistent; never touch the filesystem. */
cmp al, 2
jne restore_fail
/* Refuse to overwrite pre-existing recovery .APP files. */
mov dx, offset path_config_app
call file_exists
jnc restore_fail
mov dx, offset path_auto_app
call file_exists
jnc restore_fail
/* Both current temporary files must exist. */
mov dx, offset path_config_sys
call file_exists
jc restore_fail
mov dx, offset path_auto_bat
call file_exists
jc restore_fail
push ds
pop es
/* 1) CONFIG.SYS -> CONFIG.APP */
mov dx, offset path_config_sys
mov di, offset path_config_app
call rename_file
jc restore_fail
mov byte ptr [restore_stage], 1
/* 2) AUTOEXEC.BAT -> AUTOEXEC.APP */
mov dx, offset path_auto_bat
mov di, offset path_auto_app
call rename_file
jc rollback_stage1
mov byte ptr [restore_stage], 2
/* 3) CONFIG.WOS -> CONFIG.SYS */
mov dx, offset path_config_wos
mov di, offset path_config_sys
call rename_file
jc rollback_stage2
mov byte ptr [restore_stage], 3
/* 4) AUTOEXEC.WOS -> AUTOEXEC.BAT */
mov dx, offset path_auto_wos
mov di, offset path_auto_bat
call rename_file
jc rollback_stage3
mov byte ptr [restore_stage], 4
/* Force DOS to commit directory/FAT updates before S5. */
mov ah, 0x0D
int 0x21
mov byte ptr [restore_done], 1
jmp restore_ok
rollback_stage3:
/*
* CONFIG.WOS has already become CONFIG.SYS.
* Put it back first, then restore both temporary originals.
*/
mov dx, offset path_config_sys
mov di, offset path_config_wos
call rename_file
mov dx, offset path_auto_app
mov di, offset path_auto_bat
call rename_file
mov dx, offset path_config_app
mov di, offset path_config_sys
call rename_file
mov ah, 0x0D
int 0x21
jmp restore_fail
rollback_stage2:
/* Restore AUTOEXEC.APP and CONFIG.APP to their original names. */
mov dx, offset path_auto_app
mov di, offset path_auto_bat
call rename_file
rollback_stage1:
mov dx, offset path_config_app
mov di, offset path_config_sys
call rename_file
mov ah, 0x0D
int 0x21
restore_fail:
stc
jmp restore_exit
restore_ok:
clc
restore_exit:
pop es
pop di
pop si
pop dx
pop cx
pop bx
pop ax
ret
/* ------------------------------------------------------------
* Error handling
* ------------------------------------------------------------ */
err_need_go:
push cs
pop ds
mov dx, offset msg_need_go
call puts
mov ax, 0x4C10
int 0x21
err_not_real:
push cs
pop ds
mov dx, offset msg_not_real
call puts
mov ax, 0x4C11
int 0x21
err_windows_running:
push cs
pop ds
mov dx, offset msg_win
call puts
mov ax, 0x4C12
int 0x21
err_sci_timeout:
push cs
pop ds
cmp byte ptr [enabled_by_us], 0
je sci_timeout_msg
mov dx, 0x00B2
mov al, 0xF1
out dx, al
sci_timeout_msg:
mov dx, offset msg_sci_timeout
call puts
mov ax, 0x4C13
int 0x21
err_sci_lost:
push cs
pop ds
cmp byte ptr [enabled_by_us], 0
je sci_lost_msg
mov dx, 0x00B2
mov al, 0xF1
out dx, al
sci_lost_msg:
mov dx, offset msg_sci_lost
call puts
mov ax, 0x4C14
int 0x21
err_restore_failed:
push cs
pop ds
mov dx, offset msg_restore_failed
call puts
mov ax, 0x4C16
int 0x21
err_s5_returned:
push cs
pop ds
mov dx, offset msg_returned
call puts
mov ax, 0x4C15
int 0x21
/* ------------------------------------------------------------
* DOS output helper
* ------------------------------------------------------------ */
puts:
mov ah, 0x09
int 0x21
ret
/* ------------------------------------------------------------
* Local state
* ------------------------------------------------------------ */
enabled_by_us: .byte 0
pm1_initial: .word 0
pm1_before: .word 0
pm1_target: .word 0
/* Windows 95/98 custom MS-DOS-mode restoration state. */
have_config_wos: .byte 0
have_auto_wos: .byte 0
restore_stage: .byte 0
restore_done: .byte 0
/* Absolute DOS paths used by the /WX-compatible restoration. */
path_config_sys: .asciz "C:\\CONFIG.SYS"
path_auto_bat: .asciz "C:\\AUTOEXEC.BAT"
path_config_wos: .asciz "C:\\CONFIG.WOS"
path_auto_wos: .asciz "C:\\AUTOEXEC.WOS"
path_config_app: .asciz "C:\\CONFIG.APP"
path_auto_app: .asciz "C:\\AUTOEXEC.APP"
/* ------------------------------------------------------------
* Messages
* ------------------------------------------------------------ */
msg_need_go:
.ascii "PWOFFHLP: safety lock. Run only as PWOFFHLP /GO\r\n$"
msg_not_real:
.ascii "PWOFFHLP: ERROR - true real-mode DOS required (EMM386/V86 off).\r\n$"
msg_win:
.ascii "PWOFFHLP: ERROR - Windows is still running. Refusing power-off.\r\n$"
msg_sci_timeout:
.ascii "PWOFFHLP: ERROR - ACPI ownership request failed. No S5 issued.\r\n$"
msg_sci_lost:
.ascii "PWOFFHLP: ERROR - SCI_EN not set. No S5 issued.\r\n$"
msg_restore_failed:
.ascii "PWOFFHLP: ERROR - MS-DOS-mode boot-file restore failed; NO S5 issued.\r\n$"
msg_returned:
.ascii "PWOFFHLP: ERROR - S5 returned without power-off; state restored.\r\n$"


コメント