見出し画像

8086のセグメントレジスタとメモリモデル

さて一種の仮想アドレスでもある6809向けのMMUの話は以下に書きました。

6829 - 6809用MMU

いよいよ286のプロテクトモードに行くのかと思いきや、それに先立って8086のセグメントレジスタの使い方を復習しておきます。

8086 - 今も生き残るアーキテクチャ

8086には8080から引き継いだようなAX/BX/CX/DXという汎用16ビットレジスタとSP/BP/SI/DIといった16ビットのインデックスレジスタを持っています。メモリ空間は20ビットあるのに実行する命令アドレスを保持しているIPは16ビットしかありません。これでどうやって20ビットあるアドレス空間を指定していたのかと言うと、CS/DS/ES/SSと4つある16ビットのセグメントレジスタを併用するのです。

4つのセグメントレジスタは3つのカテゴリに分かれ、IPに対してはCSが、SIやDIに対してはDS、そしてBPやSPに対してはSSが組みとなり、2つの16ビットレジスタを組み合わせることで20ビットのアドレスを作ります。例えばCSに1000H、IPが100Hだとすると、CSを4ビット左にシフトした値にIPが足されて実行する命令を10100Hのアドレスから取り出すわけです。同様にDSに2000H、SIに200Hが入っていた場合にこのSIを使ったアドレスから値を読み込めば、その具体的なアドレスは20200Hの値になるわけです。

CS       : 1 0 0 0 -
IP        : - 0 1 0 0 
                 ↓
CS+IP : 1 0 1 0 0

16進数は1桁で4ビット分になる

このようにセグメントレジスタを使うことで、8ビット時代のコードと同じように16ビットのアドレス空間のように見せかけることが出来るわけです。小規模なプログラムであれば、セグメントレジスタを意識する必要はなく、16ビットを超えるようなメモリが必要なときだけ、適切にセグメントレジスタを操作して使えば充分なわけです。

8086では、プログラムがコードおよびデータに対して、それぞれどのようにセグメントレジスタを使うかに応じて6種類のメモリモデルが用意されています。つまりコードに対するアドレスもデータに対するアドレスも16ビットで済む場合(Small)とデータのみ32ビットが必要となる場合(Compact)、コードのみ32ビットが必要となる場合(Medium)、そして共に32ビットが必要な場合(Large)があるわけです。さらに8ビットと互換性のあるコードとデータが同じセグメントである場合(Tiny)と、ひとつのデータで16ビットを超えてしまう場合(Huge)です。

8ビットとの互換性が大切だった初期の頃はTinyであるとかSmallで間に合うので良かったのですが、だんだん扱うデータは大きくなりコードも肥大化していけば、この複雑なメモリモデルを使いこなす必要が増えてきます。このようにアドレスを扱う際に常にそれが16ビットなのか32ビットなのかを意識する必要があり、例えばC言語で記述する際には16ビットをnearポインタ、32ビットをfarポインタと呼び区別が必要でした。小規模のプログラムでは、だいたいnearポインタで間に合うことも多かったのですが、APIを呼び出すのであれば、そこだけはfarポインタが必要であったり、farポインタの配列を作ると、その配列へのポインタはnearポインタとなったりするので、言語上の表記だけではなく頭の中身までnearとfarでいつも混乱していました。また32ビットの値で20ビットのアドレスを指定しているわけですから、異なる32ビットの値で同じ20ビットを表す組み合わせが多くあり、ポインタの一致を正確に判断するには、20ビットの値に正規化して比較する必要があるなど、今、思い出しても寒くなるばかりです。

Intel 8086

セグメント方式

このように20ビットあるアドレス空間を単に16分割してバンク切り替えという形で扱うのではなく、12ビット分もオーバーラップさせた16ビットのセグメントレジスタを使う形にしたのかは明確な説明を見た記憶が無いのですが、セグメント無しでは固定的な絶対アドレスを使うので、これをセグメントレジスタの値を適切に制御することでメモリ空間の自由な場所に配置できるように使うことを考え、メモリ空間を無駄にしないためにはきめ細かなアドレス指定ができた方が良い(16バイト単位で指定できる)ので、可能な限りオーバーラップさせたのかもしれません。仮想アドレスを導入しなくても既存のバイナリをリロケータブルに出来るうまい仕組みを考えたのでしょうね。

リロケータブルとバイナリローダ - 絶対アドレスの扱い

もしこのままさらに多くのメモリ空間を扱えるようにした時にオーバーラップのビット数を変えて済まそうとしていたのか、既にプロテクトモードのアイデアを持っていたのかはわかりませんが、このセグメントレジスタを使うことで64K以上のメモリが扱えるようになりました。逆に言うとコードにせよデータにせよ64K単位のメモリ空間に分断されてしまうことには違いなく、より多くの連続した空間をアクセスするには非常に煩雑な処理になってしまいました。2バイトでアドレスを指定できるのはコードの肥大化を防ぐにはかなり効果的でバイト単位でのメモリアクセスが可能なこともあってx86アーキテクチャは、まだまだメモリが貴重な時代に決して悪くないものであったのですが、やはりどこかマイコン的で細かなテクニックがたくさん必要な世界でした。このあたりが68K系のCPUとは大きく違うところでしたね。UNIXな人たちがx86を嫌うのも無理はないと思います。

さて、これで286のプロテクトモードに進めそうかな。

ヘッダ画像は、以下のものを使わせていただきました。
https://commons.wikimedia.org/wiki/File:Register_8086.PNG
Daniel B - 投稿者自身による著作物, CC 表示-継承 3.0, https://commons.wikimedia.org/w/index.php?curid=431614による

#8086 #セグメントレジスタ #メモリモデル #near #far #リロケータブル

この記事が気に入ったらサポートをしてみませんか?