2004年04月06日
川俣晶の縁側IT都市伝説 total 10918 count

Intel 8086はメモリが64Kバイト単位で分割され扱いにくかった

Written By: 川俣 晶連絡先

 誰が言い出したのか、まことしやかに流布される謎の解釈。

 今回のIT都市伝説はこれだ!

8086はメモリが64Kバイト単位で分割され扱いにくかった §

 現在使用されているPentium系 x86 CPUの初代モデルとなる8086は、1Mバイトのメモリ空間を持っていましたが、メモリが64Kバイト単位で分割されていたため、扱いにくいものでした。これが解消されるのは32bit CPUとなった386以降で、アクセスできる範囲が64Kバイトから4Gバイトに拡大しました。

8086のメモリ空間は、分割されていない §

 8086のメモリ空間は1Mバイトの広さがあります。これは20bitのアドレスによってアクセス可能です。このメモリ空間は連続したものであって、分割などはされていません。たとえば、64Kバイトのメモリ空間を持つ8bitCPUのZ-80などを使用したパソコンなどでは、バンク切り替えによって、64Kバイトを越えるメモリを扱う場合がありますが、これはバンク切り替えの単位でメモリ空間が分割されていたと言えます。しかし、8086はそれとは全く性質が異なります。

 8086のメモリアクセスのアーキテクチャは、セグメントとオフセットにより表現されます。セグメントとオフセットはどちらも16bitの情報です。実際にアクセスされるアドレスは、セグメント×16+オフセットという式によって決定されます。通常は、セグメントを固定値として、オフセットを変化させてメモリにアクセスします。この場合、オフセットの値の範囲が0~65536しかあり得ないため、ここに64Kバイトの壁が発生します。しかし、セグメントにも任意の値を指定することができるため、この64Kバイトの範囲がメモリ空間のどの位置に来るかは、16バイト単位で自由に設定することができます。そのため、最初の64Kバイトと、次の64Kバイトは分割されておらず、この2つの64Kバイトにまたがる64Kバイトを使うこともできました。

 つまり、64Kバイトの壁がある、ということは言えても、64Kバイト単位で分割されていた、とは言えません。

64Kの壁も幻か? §

 更に、64Kの壁も絶対的なものではありません。

 既に、「通常は、セグメントを固定値として、オフセットを変化させてメモリにアクセス」と書きましたが、セグメントの値も変えてやれば、64Kバイトの壁はありません。実際、Microsoft C Compilerなどでサポートされるhugeモデルというものを使うと、64Kバイトの壁の無いプログラミングを容易に行うことができます。64Kバイトを越えるメモリを確保して、これを扱うことも簡単です。

 ただ、セグメントの値を変化させるコードは、大きく重くなるため、hugeモデルは本当に必要とされる場合以外には使われないようです。それだけでなく、それが必要とされている場合でも、上手く工夫して64Kバイト以下で収まるようにプログラミングすることも行われていたようです。貧弱なパソコンの性能を極限まで引き出すには、たとえ可能であっても回避するという行為に価値があります。

同時アクセス可能なメモリは64Kだけではない §

 もう1つだけ付記しておくと、性能を低下させずに8086が一度にアクセスできるメモリは64Kバイトに限定されません。

 セグメントの値は、セグメントレジスタという場所に格納してから使います。セグメントレジスタは演算の対象にできないため、この値を変えようと思ったら、他のレジスタに引き出してから演算して戻す必要があります。巨大データへのアクセスで性能が低下する理由は、そのためのコードと処理時間が大きいことによります。

 しかし、このセグメントレジスタは4本あります。つまり、64Kバイト×4=256Kバイトの範囲が、速度低下のペナルティを受けないでアクセスできるメモリサイズということになります。

 この4つとは、CS(プログラム実行用)、SS(スタック用)、DS(データ用)、ES(データ用その2)です。CSはプログラムを実行するためのものですが、データを置くことができないわけではありません。SSもスタック用として用途が決まっていますが、他のデータを置くことができないわけではありません。

 これらをフル活用して記述された8086用のプログラムは、そもそも64Kバイトしかメモリ空間が存在しない8bit CPUよりもはるかに強力なパワーを発揮することが可能だったと言えます。

 なお、多くの場合、SSとDSには同じ値を入れて使われます。(同じ値を入れない時に発生するのが、16bit版WindowsのDLLやWonderWitchプログラミングで起こるSS!=DS問題)。また、ESは固定値を入れず、随時値を設定して使うことが多いかもしれません。それらのことから、64Kバイト×4=256Kバイトは理想的な状態での理論値で、実際はそういう単純な計算では対応できないと言えます。

286のプロテクトモードでも64Kバイトの壁を越えられる §

 余談ですが、286のプロテクトモードでは、セグメントではなくセレクタが使われます。セグメントの場合は次の64Kバイトのセグメント値を知るために16進数の1000を足せば良かったのですが、セレクタ値はメモリを管理するテーブルの番号に過ぎないため、そういう単純な計算では得られないのです。

 しかし、これは逆に考えれば単純な計算で得られるようにセレクタ値を割り当てれば良いことです。実際、16bitプロテクトモードのWindowsなどでは、64Kバイトを超えるメモリを確保した場合、連続したセレクタの番号が割り当てられ、容易に64Kバイト以上のメモリを扱えるように配慮されています。(ついでに余談を書けば。こうやって確保されたメモリブロックに対して、386以上の場合は32bit命令を使って32bitオフセットでアクセスすると、そのまま64Kバイト以上のメモリブロックにアクセス可能でした)