"WindowsOS内部のアーキテクチャのすべて"の第1章~第3章の要約、読書メモとなります。
本記事では以降、"WindowsOS内部のアーキテクチャのすべて" を "Windows OS Internal Architecture" として "WOIA" と略します。
1969年、日本のビジコンという会社の電卓用カスタムLSIの開発以来を受け、米Intelは汎用LSIの開発を開始。2年後の1971年、Intelは4004という型番で完成させた。他にROM(4001),RAM(4002),I/O(4003)を含む、一連のファミリーとして販売された。
1971年の時点ではIBMのSystem/360やOS/360、後継のSystem/370, OS/370が存在し、UNIXも既に産声を上げていた。ベル研究所ではKen ThompsonとDennis Ritchieらが 1970年 にはPDP-11/20上で動作するUNIXを開発していた。当時はアセンブラで開発されていたが、1973年にはC言語に移植されることになる。
4004のスペック:
アドレスバス | 12bit/8bit |
汎用レジスタ | 4bit |
外部データバス | 4bit |
内部データバス | 4bit |
※当時はまだレジスタに名前は付いておらず、4bit x 16個のレジスタに対して番号で指定する「インデックス・レジスタ」だった。
※"WOIA"ではアドレスバス8bitとしているが、正確にはプログラムROM(4001)のアドレス指定は12bit, RAM(4002)のアドレス指定は8bitとして分かれていた。ROMのアドレス指定はA3(4bit) + A2(4bit) + A1(4bit)で12bitだった。
プログラムを格納するROM(4001)は8bit x 256。つまりアドレス範囲は丁度0h-FFhで8bit。つまり4001一つ分が、A2(4bit) + A1(4bit)で指定出来る範囲になる。アドレスバスは12bitなので4KBまで使える。つまりA3(4bit)で、最大16個の4001を切り替えることができた。
当時のジャンプ命令には、A3まで指定するジャンプと、A3を指定しない(A2 + A1の8bit指定)、同じ4001内でのジャンプ命令とで分かれていた。
RAM(4002)のアドレス指定は8bitだったが、先頭4bitはバンク切り替えなどで使われていた。
4004のアドレスバスを何bitと解釈するかは資料やWebサイトによって異なる。そもそも、まだアドレスバスという概念自体が確立していない時代のCPUである。次の資料では"4bit"という表記になっている。
当時はまだアドレス専用のバスが存在していなかった為、4bitの内部データバスを使って 4bit x 2 or 4bit x 3 として読み取っていた。このため、データバス = アドレスバスと解釈すれば「アドレスバス = 4bit」という考え方も可能である。
また、当時はスタック領域がCPU内部に存在していた。4004では12bitのプログラムカウンタ + 3レベルの12bitスタックポインタで、計48bitの領域がCPU(4004)内部に確保されていた。
8bitCPUとして広まった8080について見てみる。
8080 に入る前に、1972年発表の 8008 を見ておく。
8008のスペック:
アドレスバス | 14bit/8bit |
汎用レジスタ | 8bit |
外部データバス | 8bit |
内部データバス | 8bit |
8008の時点では、まだアドレスバスとデータバスは区別されていない。アドレス自体は 14bit (=16KB) まで指定できたが、データバスは8bitだったため、8bitを二回読み込み、先頭2bitを捨てて14bitとしていた。
汎用レジスタはこの時に A, B, C, D, E, H, L の7つが名付けられた。このうち、HとLレジスタはメモリアドレスの指定に使われた。実際にHとLが関係する命令を見てみる。
Lr1r2 | 11 DDD SSS | 汎用レジスタSSSの値を汎用レジスタDDDに転送する。 |
LrM | 11 DDD 111 | メモリアドレス"M"の値を汎用レジスタDDDに転送する。 |
LMr | 11 111 SSS | 汎用レジスタSSSの値をメモリアドレス"M"に転送する。 |
SSS : Source Index Register DDD : Destination Index Register A(000), B(001), C(010), D(011), E(100), H(101), L(110)
例えば
LrM : 11 010 111
の場合は
汎用レジスタ H と L で指定されたアドレスの値を、汎用レジスタC(010)に転送する。
となる。
ここでHとLが以下の値になっていた場合は、Hの上位2bitが捨てられ、Hの6bit + Lの8bitで14bitのアドレス213Fhになる。
H: 1110 0001 : 71h L: 0011 1111 : 3Fh → 1110 0001 0011 1111 → 10 0001 0011 1111 (Hの上位2bitが落とされる) → アドレス:213Fh
4004の時と同様、14bitのプログラムカウンタ + 3レベルの14bitスタックポインタで、計52bitの領域がCPU(8008)内部に確保されていた。
8008は日本の精工舎から依頼を受けて、科学技術計算用のLSIとして開発された。4004と比べてCPUのクロックもあまり変わらず、周辺チップの開発も怠った為、後の8080のようにマイコン市場を作り上げるようなことは無かった。
8080は1974年発表の8bitCPU。発表年については資料によりばらつきがあり、おおよそ1974年であるが、一部 1973年12月 と表記している資料もある(intel 8080 Microcomputer Systems User's Manual)。
8080のスペック:
アドレスバス | 16bit |
汎用レジスタ | 8bit |
外部データバス | 8bit |
内部データバス | 8bit |
8080になり、"アドレスバス"がデータバスとは独立して登場し、16bit = 64KB までのメモリが扱えるようになった。
汎用レジスタは A, B, C, D, E, H, L, W, Z が導入されている。プログラムカウンタも16bitになっている。
なお8080のGND配線の設計ミスを修正した 8080Aという製品が発表されている。こちらの方がヒットしたため、"8080"と呼ぶ時に8080Aも含める場合もある。本記事もそれに従う。8080Aを設計した技術者が、後に独立し、ザイログ社を設立して互換CPU "Z80" を発表している。
"Control Program/Monitor", 後に "Control Program for Microcomputers" の略称となる。1973-1974年に Digital Research の Gary Kildall が開発したOS。CP/Mは後に幾つかのCPUに移植されることになり、8080用は"CP/M-80"と呼ばれるようになる。
CP/Mには"ASM.COM"というアセンブラが添付されているが、初代のCP/M自体はKildall自身が作成した"Programming Language for Microcomputers"(PL/M)で開発された。
アドレス100hから始まる ".COM" 実行ファイルフォーマットは、少なくとも初代CP/Mまで遡る事が出来る。
8080/Z80で盛り上がったマイコン市場で、CP/Mもまた大いに広まった。CP/M上で動作するソフトウェアが開発され、Microsoftもまだこの時点ではCP/M上で動作するプログラミング言語(BASIC, COBOL, FORTRAN)の一ベンダでしかなかった。
MS-DOSの前身(DOS-86)の前身となったCP/Mの構造を俯瞰していく。
CP/M-80が動作している状態のメモリレイアウトは次のようになっている。
+------------------------------+ FFFFh | [BIOS] | | Basic Input/Output System | | (Hadrware Drivers for BDOS) | +------------------------------+ F200h | [BDOS] | | Basic Disk Operating System | | (Operating System Functions) | +------------------------------+ E400h | [CCP] | | Console Command Processor | | (Command Line Interpreter) | +------------------------------+ DC00h | [TPA] | | Transient Program Area | | (Free Memory for Programs) | +------------------------------+ 0100h | CP/M System Area | | (System Buffers, Parameters) | +------------------------------+ 0000h
"Basic Disk Operating System" の名前が示すとおり、CP/Mは基本的に磁気ディスクを使うOSだった。
bootには別途bootloaderを格納したROMが必要で、CPUは最初はそのROMの0000hから起動して周辺機器を初期化、続いて磁気ディスクの先頭を読み込み、上記で示した各CP/MのシステムをRAM上に展開していく。展開が終了するとRAM上の0000hの命令をフェッチするようになっており、この時点でF200hへのJMP命令、つまりBIOSへのJMP命令が格納されていた。これによりBIOSに制御が移り、CP/MのOSの世界へ切り替わる。
bootloaderを格納したROMが無い場合は、スイッチなどを操作して直接bootloaderに相当する機械語を打ち込んでCP/Mをロードさせていたようだ。
なお、"BIOS"/"BDOS"/"CCP"についてだが相当する物理的なファイルは存在しない。CP/Mをロードする過程で磁気ディスクから読み取ったシステムデータをメモリに展開する際に、機能上の理由でこの3種類のモジュールに分類しているだけである。
上の図では64KB全て搭載されている前提でアドレスを載せているが、実際のマシンでは32KBや48KBなど異なる場合があった。
CP/Mのドキュメントにはそれぞれのメモリレイアウトに応じて調整可能なbootloaderのアセンブラコードが掲載されており、自分のマシンとそれを見比べつつ、手動でbootloaderを組み上げていったものと思われる。
+----------------------------+<- 0100h | File Buffer |<- 0080h - 00FFh +----------------------------+ | FCB: | | Default File Control Block |<- 0060h - +----------------------------+ | Unused (in CP/M 2.2) |<- 0050h - +----------------------------+ | BIOS Work Area |<- 0040h - +----------------------------+ | Restart Vector 7 |<- 0038h - 003Fh +----------------------------+ | Restart Vector 6 |<- 0030h - 0037h | Restart Vector 5 |<- 0028h - 002Fh | Restart Vector 4 |<- 0020h - 0027h | Restart Vector 3 |<- 0018h - 001Fh | Restart Vector 2 |<- 0010h - 0017h | Restart Vector 1 |<- 0008h - 000Fh +----------------------------+ | "JMP E400h" |<- 0005h - 0007h +----------------------------+ | Current Default Drive |<- 0004h +----------------------------+ | IO Byte |<- 0003h +----------------------------+ | "JMP F200h" |<- 0000h - 0002h +----------------------------+
最初の "JMP F200h" 命令はCPU初期化などで"0000h"の命令がフェッチされた場合に、BIOSコードを実行してメモリをリセットするためにある。
次のIO Byte, Current Default Drive についての説明は省略する。
0005hの "JMP E400h" 命令はBDOSコードの先頭へJMPする命令になり、これがOSの機能を呼び出す「システムコール」(DOSでは"ファンクションコール"とも呼ばれる)となる。
ユーザープログラムでは次のような手順で、0005hの "JMP E400h" 命令を通してOSの機能を呼ぶことが出来る。
例:"A"という文字を表示したい時
"Restart Vector 1 - 7"については、8080の割り込み機能と併せて簡単に紹介する。
8080における割り込みはハードウェアからのイベントによるハードウェア割り込みしか存在しなかった。
仕組みとしては、割り込み要求を出した装置がISR(Interrupt Service Routine)をコールする命令を返し、それをフェッチして実行することで割り込みハンドラを実行する仕組みになっていた。
この「ISRをコールする命令」が"RST"(ReSTart)命令で、実行する割り込みハンドラを8個選択出来るようになっていた。
"RST"(ReSTart) : 11 AAA 1111
この"AAA"の3bit、8つのISRを指すことが出来る。
ISRのアドレスは、アドレス0000hから始まる8バイトのブロック単位で指定可能になっていた。
つまり、割り込み要求を出した装置が
11 000 1111
を返すと、
0000h - 0007h
に格納されたアドレスに飛ぶ。
8バイト毎に8つなので、以下のアドレスに飛び先、つまりISRのアドレスが格納出来る。
0000h - 0007h, 0008h - 000Fh, 0010h - 0017h, 0018h - 001Fh, 0020h - 0027h, 0028h - 002Fh, 0030h - 0037h, 0038h - 003Fh,
このうち最初の 0000h - 0007h はCP/Mに割り込み以外の用途に使われ、最後の一つ0038hはデバッガ用に予約されていた。当時のマシンには「デバッグ」ボタンが存在し、ユーザーがこのボタンを押すとハードウェアで割り込みがかかり、
RST: 11 111 1111
が返されてデバッガが動作するようになっていたらしい。
残り6つについては、少なくとも初期のCP/Mでは使っていない。ユーザプログラム側で任意のISRアドレスを指定することが可能だった。
ちなみに8008の段階では割り込み機能は搭載されていない。8080と、同1974年リリースの4004強化版である"4040"において、Intel CPUで割り込み機能が搭載された。8080互換であるZ80は、さらに独自の割り込みモードを備え、8080互換(モード0)の他に2つの割り込みモードを使えるように拡張されていた。
OSが提供するサービス(ファンクション)を割り込み機能(あるいは "CALL 0005h" のような特定のゲートウェイ)を通じて呼ぶ仕組みは、x86のOSの歴史ではCP/Mで初登場となる。
+------------------------------+ FFFFh | [BIOS] | +------------------------------+ F200h | [BDOS] (3)|<-----------+ +------------------------------+ E400h | | [CCP] | | +------------------------------+ DC00h | | [TPA] (1)|--------+ | +------------------------------+ 0100h | | | CALL 0005h, (2)+-<|<-------+ | | ResetVector 1 - 7 +->|>-----------+ +------------------------------+ 0000h
このようにユーザプログラムとOSの機能を分けた理由としては、当時はまだ細かいメモリレイアウトやハードウェアの仕様が共通化されて居らず、マイコン・ミニコン毎にROMやRAM、周辺機器の仕様が異なっており、それを全てユーザプログラムでカバーするのが不可能だったからであると考えられる。
OSの基本機能や周辺機器の差異はBDOS + BIOSが吸収することにより、ユーザプログラムの負担を低減出来る。ユーザプログラムは周辺機器の詳細を考えずに、単にOSのサービスを呼ぶだけでよい。
もちろんユーザプログラム自身が直接ハードウェアを制御したい場合もあり得るが、この構成にすることでハードウェア周りの処理の多くをOS側に委譲出来たのは確かである。
そしてこの割り込みを使った処理の分離が、その後のMS-DOSやWindowsまで連綿と引き継がれていくことになる。
8086(1978年)のスペック:
アドレスバス | 20bit |
汎用レジスタ | 16bit |
外部データバス | 16bit |
内部データバス | 16bit |
8088(1979年)のスペック:
アドレスバス | 20bit |
汎用レジスタ | 16bit |
外部データバス | 8bit |
内部データバス | 16bit |
8086/8088のレジスタ構成、割り込み機能、想定するメモリレイアウトの順に簡単に紹介する。
8086/8088のレジスタ群(全て16bit, "xH", "xL"は上位/下位の8bitでアクセスする時のレジスタ名)
+---------+ +---------+ | AH | AL |=AX : ACCUMULATOR | S P | STACK POINTER +---------+ +---------+ | BH | BL |=BX : BASE | B P | BASE POINTER +---------+ +---------+ | CH | CL |=CX : COUNT | S I | SOURCE INDEX +---------+ +---------+ | DH | DL |=DX : DATA | D I | DESTINATION INDEX +---------+ +---------+ +---------+ +---------+ | C S | CODE SEGMENT | I P | INSTRUCTION POINTER +---------+ +---------+ | D S | DATA SEGMENT | FLAG | STATUS FLAGS +---------+ +---------+ | S S | STACK SEGMENT +---------+ | E S | EXTRA SEGMENT +---------+
アドレスバスが20bitになったにも関わらず、IPやSPなどアドレスを指示するレジスタは16bitのままである。これは8086/8088で導入された「セグメント:オフセット」形式のアドレス換算により20bitに変換出来る。
8086/8088では、メモリアドレスを16bitのセグメントと、16bitのオフセットを使って指定する。20bitへの変換には、セグメントアドレスを4bit左にずらした上で、オフセットアドレスを加算する。
セグメント:1234h オフセット:5678h → 12340h + 5678h -------- 179B8h :これが物理メモリアドレスになる。
SPやIPなどはこのオフセットを指定する事になる。一方のセグメントを指定するのが、CS/DS/SS/ESとなる。種類を分け、命令やスタック領域用にセグメントを分けることが出来るようになっている。
CS : IP → 次にフェッチする機械語の物理メモリアドレス SS : SP → スタックポインタの物理メモリアドレス
DSやESはその他の機械語命令で使われる。
一つのセグメントで指定可能な範囲はオフセットアドレス16bitの範囲内。64KBの壁はこの時誕生した。
8080では割り込み要求を出した装置が"RST"(ReSTart)命令を返し、それをフェッチすることでISR(Interrupt Service Routine)を実行していた。また、RST命令ではISRを0-7の番号(3bit)で指定するようになっていて、物理アドレス0000hから8バイト毎に実際のISRのアドレスを格納する、間接的な指定方式を採っていた。
また、8080ではハードウェアによる割り込みのみ対応していた。
8086/8088になって、"INT"命令によるソフトウェア割り込みが可能になった。ISRの指定方式は8080を継承している。
ソフトウェア割り込みの場合は、8bitで指定出来る割り込み番号を4倍した値をメモリアドレスとして、そこに格納されている値をISRのアドレスとしてジャンプする。
ハードウェア割り込みの場合は、割り込みコントローラの返す割り込み番号を同様に4倍し、ソフトウェア割り込みと同様にISRのアドレスを割り出してジャンプする。
例:ハードウェア or ソフトウェア割り込みで割り込み番号が3だった時 3 x 4 = 12 → 0000Ch のアドレスに格納されている値を読み出し、そこにジャンプする。
一つの割り込み番号に対して4バイト確保されており、2バイトで分かれてCS, IPレジスタにセットされることでISRにジャンプしている。
資料によっては、割り込みの発生源に応じて割り込みの呼称を変えている。
割り込み番号 00h - 1Fh まではIntelによる予約となっているが、初期は守られておらず、一部OS固有の割り込みハンドラとして使われた。例えばIBM PC 5150のROM BIOSではROM BASICをロードする機能が INT 18h に割り当てられていたが、当然これはROM BASICを搭載したマシン専用であり、現在は使われていないか、別の機能が割り当てられたりしている。
8086/8088がCPUとして想定するメモリレイアウトは次のようになっている。
+--------------------+ | RESET BOOTSTRAP | | PROGRAM JUMP | FFFF0h - FFFFFh +--------------------+ | | | | | | +--------------------+ | INTERRUPT POINTER | | FOR TYPE 255 | 3FCh - 3FFh +--------------------+ | (...) | +--------------------+ | INTERRUPT POINTER | | FOR TYPE 1 | 4h - 7h +--------------------+ | INTERRUPT POINTER | | FOR TYPE 0 | 0h - 3h +--------------------+
先頭から400h(1KB)まではこのように割り込み番号に応じたISRのアドレステーブル(割り込みベクタ)として予約されている。
"RESET BOOTSTRAP PROGRAM JUMP"の16バイトだが、ここは8086/8088が電源リセットの直後、最初に命令をフェッチするアドレスである。これはMS-DOSやIBM PC 5150 ROM BIOSのメモリレイアウトに関わってくる。
割り込み番号:
86-DOS, IBM PC 5150, MS-DOSの流れについては Assembler/なぜx86ではMBRが"0x7C00"にロードされるのか?(完全版) 参照。
640KBの壁については、ROM BIOSの配置やビデオRAMなどの配置の影響で、コンベンショナルメモリが0 - 640KBに制限されたことで生じた。
コンベンショナルメモリについては 技術/歴史/DOS時代のメモリ管理(EMS,XMS周辺) 参照。
MS-DOSのカーネル構成やメモリレイアウトについて簡単に紹介する。
Assembler/なぜx86ではMBRが"0x7C00"にロードされるのか?(完全版) より転載。
ROM BIOSのPOST(Power On Self Test)処理が完了し、BIOS内部でINT 19hが実行され、磁気ディスクの最初のセクタ(MBR)が0x7C00に読み込まれた状態。
+--------------------- 0x0 | Interrupts vectors +--------------------- 0x400 | BIOS data area +--------------------- 0x5?? | OS load area +--------------------- 0x7C00 | Boot sector +--------------------- 0x7E00 | Boot data/stack +--------------------- 0x7FFF | (not used) +--------------------- (...)
ちなみに、0xFFFF0など物理アドレスの最後尾にはROM BIOS領域が割り当てられている。
この時点で、BIOSの提供する割り込みハンドラについては既に 0h - 3FFh の割り込みベクタにISRのアドレスを登録済になっている。
INT 20h/INT 21hなど、MS-DOSの提供する割り込みハンドラは、この後、MS-DOSのシステムがロードされる時に登録される。
MBRからロードされたbootloaderによりシステムがロードされ、 IO.SYS, MSDOS.SYS, COMMAND.COM, およびデバイスドライバが下図のようにメモリ上に展開される。
+-------------------+ | VRAM/ROM BIOS領域 | +-------------------+ A0000h (640KB) | ユーザー領域 | +-------------------+ | COMMAND.COM | +-------------------+ | デバイスドライバ/ | | システム作業領域 | +-------------------+ | MSDOS.SYS | +-------------------+ | IO.SYS | +-------------------+ 5??h | BIOSデータ領域 | +-------------------+ 400h | 割り込みベクタ | +-------------------+ 0h
"MSDOS.SYS", "IO.SYS"は隠しファイルになっている。この二つは、実はDOSのバージョンやOEM先によって名前が違う。
System | BIOS Extension | DOS Resident |
---|---|---|
MS-DOS | IO.SYS | MSDOS.SYS |
PC-DOS | IBMBIO.COM | IBMDOS.COM |
Compaq DOS | IBMBIO.COM | IBMDOS.COM |
DR DOS before 6.0 | DRBIOS.SYS | DRDOS.SYS |
DR DOS 6.0 | IBMBIO.COM | IBMDOS.COM |
また"5??h"のアドレスについては、以下の資料では"5FFh"と明記されている。
CP/Mのモジュールと対比させると分かりやすい。
CP/M | MS-DOS | 機能 |
BDOS | MSDOS.SYS | OSカーネル |
BIOS | IO.SYS | ハードウェア制御 |
CCP | COMMAND.COM | ユーザインターフェイス |
TPA | ユーザー領域 | ユーザープログラムがロードされる |
CP/Mの時と同様、OSの提供する機能を利用するには一度ゲートウェイを通してカーネル(CP/MならBDOS)を呼ぶ必要がある。
CP/Mでは"CALL 0005h"がその役目を負ったが、MS-DOSの場合は"INT"命令によるソフトウェア割り込みで実現している。
MS-DOSでは、主に20h以降のソフトウェア割り込み番号をMSDOS.SYS内のサービスルーチンに割り当てている。
一つのサービスルーチン内では、AHレジスタに設定される「ファンクション番号」で更に機能を細分化している。
例えばコンソールへ文字を直接出力するには、INT 21h, ファンクション番号は06hとなる。
例:'A'を表示 MOV AH, 06h # ファンクション番号 06h MOV DL, 41h # 出力したい文字 INT 21h # ソフトウェア割り込みによるシステムコール
このように、ユーザープログラムが直接MSDOS.SYSの機能を使うのではなく、INT命令によるシステムコールを介して使うようになっている。
NG : [ユーザープログラム] <-----> [MSDOS.SYS] OK : [ユーザープログラム] <---> "INT xx" <---> [MSDOS.SYS]
周辺機器を扱う場合など、IO.SYSが関連してくる場合も同様である。ユーザープログラムが直接IO.SYSの機能を使うのではなく、INT命令によるシステムコール、つまりMSDOS.SYSを介するようになっている。
NG : [ユーザープログラム] <-----> [IO.SYS] OK : [ユーザープログラム] <---> "INT xx" <---> [MSDOS.SYS] <--+ | 周辺機器 (<---> [デバイスドライバ]) <---> [IO.SYS] <--+
MS-DOSのメモリレイアウト:
システムコール:
1985年、Windows 1.0が発表される。マーケティング上はWindows 1.0だが、実際はWindows 1.01というバージョンになっていた。
「本当の」Windows 1.0は1983年の時点でアナウンスされていたが、動作段階には至っていなかったらしい。IBMに見せたが、IBMの興味を引くことなく終わってしまったという説もある。1983年説を採用すれば、実際はWindows 1.01だったというバージョン番号にも納得が行く。
ともあれ、多くの、Microsoft公式サイトでも、Windows 1.0 は1985年発表とされている。
もっとも、Microsoft公式サイトにおいても1983年から開発が始まっていたことは確認出来る。(1983年の段階で動作出来たのか、IBMに見せたのかは確証が得られなかった)
参考資料: 技術/歴史/DOS時代,Windows初期のCPUとPC , "Windowsシリーズ"の参考URL参照。
Windows 1.0 はDOS上で動くGUIマネージャであり、リアルモードで動作するプログラムだった。OSというよりはGUIフロントエンドと呼んだ方がふさわしい。
DOS上で動作するリアルモードプログラムである以上、1セグメント64KBの制限および640KBの壁の制限を受けてしまう。
1984年時点でハードウェアタイプのEMSが利用可能だったが、OSとして公式サポートするまでには至っていなかった。
個人的にWindows 1.0で注目しておきたいポイントは、実行ファイルのフォーマットの一つ、"new executable"(NE)フォーマットが初登場したのがWindows 1.0である点。拡張子はMS-DOSと同様に".EXE"だったが、Windowsのみが取り扱えるヘッダー構造になっていたらしい。
1987年、Windows 2.0が発表される。
同年、Lotus, Intel, Microsoft, それに ATSを加えて EMS 4.0 が策定されており、Windows 2.0でサポートされた。
この時点では8086/8088を搭載したマシンも多く、その場合はハードウェアタイプのExpanded Memoryを利用出来た。(プロテクトモードの1MB超えのメモリ領域を使ったソフトウェアエミュレーションタイプのEMSを利用するには後述の80286が必要だったが、80286自体は1982年に既に発表されている。1987年の時点で "IBM PC AT" や "IBM PC XT 286" など80286が搭載されたマシンも出回っていた。)
他にも、Windows1.0ではタイル上に配置されたWindowが、WIndows2.0になって新しいWindowが古いWindowの上に重なるようになり、より「デスクトップ」のメタファーに近づいた。
Windows 2.0はEMS4.0に対応したとはいえ、未だにDOS上で動作するリアルモードのプログラムであった。とはいえ、ルックスの改良やExcelやPageMakerなどのキラーアプリケーションの出現によりある程度の成功を収めている。