多分合ってないぞ!!!。いま一生懸命勉強中だ! (T∀T))間違ってるとこみつけたら、板にツッコミ入れよう!)
・・・ふっ・・・ 所詮笑われてナンボじゃい!。 (T∀T)
16bit(8086)のセグメント
セグメントとはなんじゃらほい?
8086は16bitですね。内部には沢山のレジスタがありますが、ほぼどれも最大16bitです。
CPUがメモリにアクセスする場合、当然メモリの番地を指定しなくてはなりませんが、そのためのレジスタも16bitです。
ん?おかしいですね?。メモリの番地を指定するためのレジスタも16bit。(16進数で4桁です。)そうなると・・・
0x0000〜0xffff
です。最大0xffffということは、十進数に直すと・・・65,535・・・え? 最大64KB???
そうです。CPUの素直な仕組みで考えると、16bitのCPUは、最大64KBしかメモリをアクセスすることができません。・・・しかし、8086は最大1MBを活用することができるのです。いったいどうやって???
8086CPUは大きなメモリ空間を64KBごとに区切り、各区切りを別の16bitのレジスタで指定することによって、事実上20bit(16進数で5桁)でメモリ番地を指定することができます。この64KBの区切りをセグメントと言います。
メモリ番地を20ビットで表現。これなら、0x00000〜0xFFFFF。つまり、1,048,575バイト。1MBとなる。
具体的には、「セグメントアドレス」+「オフセット値」でアドレスを表現する。
セグメントアドレスもやはり16ビットでしか表現できない。で、それ+何番地ずれているかを表現する。
たとえば、物理的に0x21000番地のメモリにアクセスしたいとする。その場合、レジスタはみな16ビットではあるが、
セグメントアドレスに:0x2000 (セグメントベースという) オフセットアドレスに:0x1000 (オフセット値という)
とセットすると、
0x2000x16 = 0x20000 + 0x1000 -------------------- 0x21000
こう解釈して内部回路に信号を送り、0x21000番地のメモリにアクセスしてくれる。
図にする・・・
物理アドレス 0x00000 ┌──────┐ │セグメント │ │ (64kb) │ ┌─── ここを読みたい! 0x10000 ├──────┤ │ │セグメント │ │ 0x21000とかの指定はできない。 │ │ │ (だってレジスタが16bitだもん) 0x20000 ├──────┤ │ │セグメント │ │ そこで、セグメントレジスタにセグメント │ │←─┘ のベース番地を指定する。(頭4桁だけ) 0x30000 ├──────┤ │セグメント │ 0x2000:0x1000 (セグメントベース:アドレス) │ │ これで0x21000にアクセスしてくれる 0x40000 ├──────┤ : │セグメント │ : : : : : : 0xE0000 ├──────┤ │セグメント │ │ │ 0xF0000 ├──────┤ │セグメント │ │ │ 0xFFFFF └──────┘ (1MB)
※少々乱暴な考え方をすれば、この16bit(8086時)のセグメントはとても変則的で無茶な方法といえるかも。整合性や理屈で考えれば、16bitのまま無理に1MBを使えるようにするより、20bitのCPUを開発するというのが本当といえないか。(もちろんあくまでも後付のお評論家的な話ってことで。)
32bit(i386)のセグメント
セグメントは元来は、上記の通り16bitでなんとか1MBのメモリを使うための変則的手法だったが、32bitになると使用法や意味合い・存在意義が変化する。
32bit状態になると、BXレジスタ(主にアドレスを指定する)も32bit化してEBXレジスタに変化する。32bitなのだから、0x00000000〜0xffffffffまで。すなわち4GBまで番地を直接指定できる。普通に考えればもうセグメントなど必要ではない。
しかし、32bit状態(i386)のCPUはマルチタスク(いくつものプログラムが平行して動作する)を意識している。そのため、この「セグメント」というメモリ分割管理の仕組みをうまく別の用途に転用することにしたようだ。
まず、GDTという、メモリ分割場所の表を用意する。この表はプログラム(主にOS)で設定する必要がある。GDT表はこんな感じになる。
セグメント番号 | セグメントの大きさ | セグメントの開始番地 | セグメントの属性 |
0 | 1MB | 0x30000000 | 書き込み禁止 |
1 | 32MB | 0xA0000000 | OS専用 |
2 | 7MB | 0x43880000 | 実行禁止 |
3 | 512KB | 0xBA500000 | OS専用,書き込み禁止 |
4 | 2MB | 0x90000000 | 特になし |
: | |||
8,191 | 2MB | 0xCF8600000 | 書き込み禁止 |
※逆に言えば、GDTを設定しておかないとセグメントが使えない
この状態で、セグメントレジスタに「4」を入れ、EBXレジスタに0x00001111を入れてアクセスしたとする。するとCPUは、
- えーっと。セグメント番号は4かぁ。
- じゃ、GDTの4番を見に行こう。ええと、開始番地は0x90000000だね。
- 大きさは2MBだから、EBXの値も問題ないね。
- 属性も特に問題はないか。
- ええと、そうすると、0x90000000+0x00001111だから・・・
- そうか。物理的な0x90001111番地にアクセスすればいいのか!
と、こう判断して動作を実行する。
特にアプリケーションプログラムなどでは、作る段階では
- とにかくこのプログラムは、0x00000000番地に読み込まれてスタートするのだ!
という想定で作ればよい。OS側が適切にセグメントをセット&選択してくれるので、上記のような物理アドレスの位置をプログラムが意識しなくてもちゃんと動作してくれる。