←前ページへ :  INDEX :  次章へ→


1-2.セグメント ( Segment )

  8086,80186,(V30),80286のアドレスバスは20bitです。アドレスバスとは、電話番号の桁数のようなものです。10桁の電話番号では210すなわち10,000,000,000個の電話を管理できることになります。20bitですと220すなわち10進数では1,048,575(1メガ)まで管理できることになります。 16進数で表すと0x00000から0xFFFFFまでで、5桁となります。しかし、データバスやレジスタは16bitであり、16進数で4桁しか覚えられません。電話番号をメモしなければならないのに、メモ用紙に8桁しか書くスペースがなく、残りの2桁が書けないのと同じ状態です。そこでメモ用紙を2枚使用することになります。しかし2枚目に足りない2桁をメモすると、8桁メモできるスペースがあるのに残りの2桁しかメモしないのはもったいないです。

  そこで例えば、03-3123-4567をメモする場合に、1枚目に03-3123-45とメモし、2枚目に 0000-0067とメモし、これを2桁ずらして足します。そうすると03-3123-4567となります。 03-0000-00、3123-4567とメモしても同じです。「0」で埋めなくても、03-1111-11と 2012-3467としても同じです。

0 3 - 3 1 2 3 - 4 5
+) 0 0 0 0 - 0 0 6 7

  0 3 - 3 1 2 3 - 4 5 6 7
0 3 - 0 0 0 0 - 0 0
+) 3 1 2 3 - 4 5 6 7

  0 3 - 3 1 2 3 - 4 5 6 7
0 3 - 1 1 1 1 - 1 1
+) 2 0 1 2 - 3 4 6 7

  0 3 - 3 1 2 3 - 4 5 6 7

  これをアドレスについても適用させます。12345hというアドレスを16bitレジスタ 2つで表すのに、1234hと0005hと記憶し、これを1桁ずらして足すことで、12345hと表すことができます。これを「1234h:0005h」と表記します。「1000h:2345h」や「1111h:1235h」も12345hを表します。

1 2 3 4
+) 0 0 0 5

  1 2 3 4 5
1 0 0 0 0
+) 2 3 4 5

  1 2 3 4 5
1 1 1 1
+) 1 2 3 5

  1 2 3 4 5

  1234h:0005hと表したとき、上の4桁「1234h」をセグメント、下の4桁「0005h」をオフセットと呼びます。

 


CPUとメモリ間のハードウェア インターフェースは、どのコンピュータもほとんど同じようなものです。 アドレス線はCPUからメモリへと接続されていて、CPUがアドレスをバスにのせると、メモリはその位置に格納された値を読み出したり、CPUからのデータを書き込んだりします。

デジタル コンピュータは、本来2進処理なので、n個のアドレス線を持つシステムは、2n個のメモリを参照できます。 この場合、ハードウェアはリニア方式で動作し、2nで組合せ可能な各アドレスに対して、個別のメモリが対応していることになります。

ほとんどのコンピュータはリニア メモリ モデルを持っています。 このメモリ モデルでは、0番地から2n-1番地までの連続した領域をアクセスできます。 理論的には、アプリケーションは0番地から次のバイト、そしてシステムの最後のバイトまで連続して読むことが可能です。 このモデルはハードウェア インターフェースと一致した考え方です。

8086ファミリには、このメモリ モデルとは異なるプログラム用のメモリ モデルがあります。 これらのプロセッサはセグメント化されたメモリ モデルを持っています。 プログラムにとって、アドレス空間はセグメント単位に分割されており、セグメント中に含まれるデータのみアクセスが可能となります。 各セグメント内でのアドレス指定はリニア方式で、プログラムは0番地(1バイト目)から1番地(2バイト目)、2番地(3バイト目)というように連続してアクセスできます。 このアドレス指定はセグメントの開始位置に対応していますが、ソフトウェア上のアドレスの0番地に対応するハードウェア アドレスはプログラマからは隠されています。

メモリ管理では、この方法が自然です。 プログラムは通常、コードとデータのセグメントに分割されます。 プログラムは1つのコード セグメントと1つのデータ セグメント、あるいは複数のコード セグメントと複数のデータ セグメントで作成することができます。 マルチタスク環境では、セグメンテーションはプロセスごとに独立しています。 あるプログラムは自分のコードとデータしか扱うことができないので、他のプログラムのコードやデータを不法に変更ができません。 下図で、複数のセグメントがメモリ内に混在するマルチセグメントの構成例を示します。

ハードウェア アドレス セグメント アドレス
プログラム1 コード ?
← 1つのセグメント
0
プログラム2 データ ?
← 1つのセグメント
0
プログラム2 コード ?
← 1つのセグメント
0
プログラム1 データ ?
← 1つのセグメント
0
?
00000 ?

80286までは4つ、80386以降は、6つのセグメント レジスタを持っています。 これらレジスタ内の値は、プログラムがアクセスできるメモリ セグメントを決定します。 CSレジスタは、プログラムのコードが入るセグメントを示し、CALL命令JMP命令は、現在のコード セグメントを暗黙的に参照します。 DSレジスタはプログラムのデータ領域を示します。 例えば、命令 M0V AL,[0]は、データ セグメントの最初に位置するバイト(オフセット0)の内容をALレジスタにコピーします。

SSレジスタが示すスタック セグメントは、通常(必要なければ)、データ セグメントと同じようにデータを扱うセグメントです。 PUSH命令POP命令系の命令はスタック セグメントにデータを保存したり、そこから読み出したりします。

ESレジスタと追加された2つのレジスタ(FS, GS)は、FORTRANプログラムのC0MMON変数のような、プログラムがあまり頻繁に必要としない捕助的なデータのセグメントを指示します。 データ セグメント レジスタを参照してアクセスする命令には、特別なプレフィックスをつけることができます。 例えばMOV命令で補助データ セグメント(例ではES)の1つから最初の1バイトの内容を取得するためには、M0V AL,ES:[0]と書きます。 また、コードセグメントから最初の1バイトの内容を取得するには、M0V AL,CS:[0]と書きます。

80386以前の8086ファミリでも、セグメント化メモリを扱っていましたが、これらのプロセッサはセグメント サイズが64Kバイトまでの制限があったので不便でした。 80386以降では、1セグメントが4Gバイト(ギガバイト)まで可能です。

場合によっては、リニア メモリ モデル(8086ファミリではフラット モデルと呼ばれる)を使用することもできます。 つまり、1つの大きなコード セグメントと1つの大きなデータ セグメントを作成し、すべてのプログラムでCSやDSを同じ値にします。 これはリニア アドレス マシン上で走っていたプログラムを移植する場合の一般的な方法です。


←前ページへ :  INDEX :  次章へ→