HN 日本語サマリー

← 一覧へ戻る
プログラミング

8086のセグメントメモリは良いアイデアだった

8086 Segmented Memory was a good idea (owl.billpg.com)

15 pointsby billpg7 コメント

要約

8086プロセッサのセグメントメモリ設計は、後世で嫌われたにもかかわらず、その登場時の文脈では非常に巧妙な解決策であったと主張しています。特に、既存の8080向け64KBコードを新しい1MBアドレス空間で実行可能にするための、後方互換性への配慮が強調されています。しかし、開発者がメモリをフラットな連続空間として扱おうとしたため、本来の意図とは異なる使われ方になり、将来的な拡張性を損ねたことが指摘されています。

全文翻訳

これはクリックベイト的なタイトルとしてどうでしょう。正気の人間が8086のセグメントメモリという怪物的なアーキテクチャを擁護するでしょうか? 私が8086アセンブリに触れ始めた頃には、PCはほとんど80286ベースになっていましたが、すべてDOSの下で動作していました。“普通の”マシンには標準の640KBのコンベンショナルメモリしかなく、どのアセンブリの教科書にもセグメンテーションを説明する忌まわしい章がありました。私は今でもその苦い経験を覚えています。ニアポインタ、ファーポインタ、そして悪名高い「どこでも」ポインタ。そんな私が、仮想的な1980年代のホームコンピューターを後知恵の利点を活かし、実際にシリコンを製造する負担なしに設計するプロジェクトHearthfireで、同様のアーキテクチャ上の決定を下している自分に気づいたとき、Intelがなぜそのような選択をしたのかをようやく理解しました。セグメンテーションは、たった一つの小さな詳細がすべてを台無しにしたことを除けば、未来のための強固な基盤となり得たのです。そして、皮肉にもその詳細とは、私たちでした。ソフトウェア開発者です。私たちがそれを壊したのです。「あなたに言いたいことはたくさんあるけれど、どう言えばいいのか分からない。」 8086のセグメントメモリとは?8086は1MBのメモリをアドレス指定できましたが、64KBが贅沢とされていた時代には途方もない量でした。そのためには20ビットアドレスが必要でした。しかし、Intelはプログラマに20ビットレジスタを与える代わりに、おなじみの16ビットレジスタを維持し、不足している4ビットはセグメントレジスタと呼ばれる別の16ビットレジスタセットから得られました。各メモリアクセスは、これらのセグメントレジスタのいずれかと16ビットオフセットを組み合わせて行われました。各セグメントは前のセグメントの16バイト後から始まりました。セグメントは大きく重複していました。同じ物理アドレスが、セグメントとオフセットの多くの異なる組み合わせで表現できました。1バイトを読み取るには、セグメント(開始点)とオフセット(その点からどれだけ進みたいか)をロードしました。CPU内部では、セグメントを左に4ビットシフトし、オフセットを加算しました。2つの16ビット値から20ビットアドレスを生成する。誰もがそれを嫌ったのも無理はありません。 後発が先発となるセグメンテーションを嘲笑するのは流行ですが、その本来の文脈では、むしろ巧妙でした。8086をx86系統の「最初の」チップと見なしたくなるのは、特に後継のすべてのチップがそのリアルモードDNAを受け継いでいるためです。しかし、話はそれよりも前から始まります。8080は1970年代半ばの主力チップでした。当時の主要OSであったCP/Mを動作させ、その16ビットアドレスバスはきっちり64KBの世界を提供していました。(Z80はもう少し有名な兄弟分でした。)ソフトウェアが成長するにつれて、その64KBの限界が問題となりました。顧客はより多くのメモリを望みましたが、既存のアセンブリコードも引き続き動作することを望んでいました。アセンブリは依然として人間が書く主要な言語であり、新しいCPUのためにアセンブリを再コンパイルするだけというわけにはいきません。新しいアーキテクチャに移行することは、本当にすべてを書き直すことを意味しました。この観点から見ると、Intelの提案はシンプルでした。「メモリを64KBのセグメントに分割しました。8080のコードをこれらのセグメントの1つにロードし、すべてのセグメントレジスタをそこに向けさせれば、以前とまったく同じように動作します。書き換えは不要、面倒なこともありません。」そしてある意味では、8086は先進的な設計に見えます。セグメントとオフセットを合わせると32ビットになり、4GBをアドレス指定するのに十分です。このチップには20個のアドレスピンしかありませんでしたが、次のチップはもっと多く持つことができました。セグメント間の重複を拡張すれば、すべてがうまくいくでしょう。理論的には、8086は64ビットアドレッシングが必要になる日まで、優雅にスケールアップできたはずです。ただし、一つだけ例外が… 先発が後発となる「セグメント」という名前は、Intelの意図を明らかにしています。私たちはメモリを連続した1MBの空間として扱うべきではありませんでした。私たちはそれを、それぞれが不透明なセレクタによって識別される多数の64KBブロックとして扱うべきでした。プログラムがOSにメモリを要求すると、OSはセグメント値を渡します。それをセグメントレジスタにロードし、オフセットを使用してその中でインデックスを指定します。64KB以上必要ですか?2つのブロックを割り当ててください。しかし、開発者は2つのブロックを望んでいませんでした。彼らはフラットなアドレス空間を望んでいました。セグメントが常に16バイト離れていることに人々が気づくと、正規化ポインタが登場しました。セグメントレジスタが20ビットアドレスの上位16ビットになり、オフセットが下位4ビットを提供しました。少し工夫すれば、メモリをほぼ連続したものとして扱うことができました。80286が登場する頃には、この慣行は定着していました。重複を16バイトから256バイトに変更すれば、すべてが壊れてしまったでしょう。そこでIntelは、286の高度な機能と24ビットアドレッシングのために新しいモードを追加し、古いコードはリアルモードに残されました。主流のソフトウェアが1MBの制限からようやく脱出できるようになったのは、80386とその仮想8086モードが登場してからでした。もし私たちがセグメントを実際のセグメントとして扱うことに合意していれば、8086アーキテクチャは何十年も存続したかもしれません。「愛しい人よ、ある夜、私が眠っていると、あなたを腕に抱いている夢を見ました。目覚めると、愛しい人よ、私は夢を見ていたと気づき、うなだれて泣きました。」 彼らはどうすべきだったのか?ここで私は、簡単な解決策はなかったことを認めます。後から考えると、私たちが必要としていたのは、セグメントを真のセレクタ、つまり算術的な意味を持たない不透明なハンドルとして扱うことでした。次のセグメントが16バイト先にあると仮定できない場合、セグメンテーションを意図通りに使用せざるを得ません。しかし、そのためにはセグメントごとのメタデータ、そのメタデータのストレージ、そしてそれを管理するハードウェアが必要でした。それも64KBがまだ「たくさん」と考えられていた時代にです。そして、たとえIntelがそのようなシステムを実装していたとしても、一人の賢い開発者が非公式のショートカットを発見するだけで、その挙動が次のチップの要件になってしまったでしょう。そのため、Hearthfireは8086スタイルのセグメンテーションを使用しません。しかし、それは貴重な教訓として残ります。 クレジット📸 「We Picked A Poppy」by 「A Guy Named Nyal」。(クリエイティブ・コモンズ)📸 「I Broke The Build」by Dirk Haun。(クリエイティブ・コモンズ)