セグメント1つを表すには以下の情報が必要
・セグメントの大きさ ・セグメントがどの番地から始まるか ・セグメントの属性情報
CPUでは上記の情報を8バイトであらわす。しかしセグメントレジスタは16ビットである。
そこでセグメントレジスタにはセグメントセレクタを格納しておいて、実際の情報はメモリ上に設置する。
セグメントレジスタは仕様上13ビットしか使用できないので、8192個のセグメントを作成することができる。
したがって8192*8バイト=64KBのメモリが必要となる。このスペースをGDT(Global Description Table)と呼ぶ。
CPUのGTDR(レジスタ)にGDTの先頭番地と有効設定個数をセットする。
割込みが発生するとCPUは現在実行中の処理を中断し、あとで再開可能となるように準備をして、割り込みの種類に応じてあらかじめ設定してある関数を実行する。
IDTは割込みと関数の対応表のようなもの。harib02iはGDTとIDTを初期化するプログラムである。
メモリ1番地に1バイト(=8ビット)格納することができるので、0x00270000~0x0027ffffffを利用することにする。
同様にIDTは0x0026f800~0x0026ffffを利用することにする。理由はメモリマップ上で誰も使ってないから。
次のループでセグメントのリミット(セグメントのバイト数-1)、ベース(番地)、アクセス権属性を0にセットする。
セグメント1番と2番をセットする。そしてGTDRに情報をセットする。IDTについてもほぼ同様の動作をする。
まずはベース番地(セグメントか格納されるメモリアドレス)から
sd->base_low = base & 0xffff ← baseの下位16ビットをbase_lowに格納 sd->base_mid = (base >> 16) & 0xff ← baseの中位8ビット(17~24ビット)をbase_midに格納 sd->base_high = (base >> 24) & 0xff ← baseの上位8ビット(25~36ビット)をbase_highに格納
3つに分断するのは286CPUとの互換性確保のため。
次にリミットについて
sd->limit_low = limit & 0xffff ← limitの下位16ビットをlimit_lowに格納 sd->limit_high = ((limit >> 16) & 0x0f)|((ar >> 8) & 0xf0) ← limitの17~20ビットをlimit_highの下位4ビットに格納。arの上位4ビットをlimit_highの上位4ビットに格納
リミットは20ビットで表現。通常これだと1MBまで表現できないように思われるが、Gビットというフラグをセットすることにより、リミットをページ単位に換算することが出来る。 1ページ4KBであるので、4KB×1M=4GBのリミットを表現することが出来る。またlimit_highの上位4ビットにはセグメント属性が書き込まれるので注意
最後にセグメントのアクセス属性について、xxxx0000xxxxxxxxの16ビットで表現。上位4ビットは拡張アクセス権。GD00として使用される。GにはGビット、Dには286との互換性についての情報を入れる。
下位8ビットのアクセス属性の代表例は以下の通り
00000000(0x00):未使用 10010010(0x92):システム専用。読み書き可能。実行不可。 10011010(0x9a):システム専用。読み込み、実行可能。書き込み不可。 11110010(0xf2):アプリケーション専用。読み書き可能。実行不可。 11111010(0xfa):アプリケーション専用。読み込み、実行可能。書き込み不可。
CPUにはOSなどのプログラムを動かしているシステムモードと、アプリケーションを動かしているアプリケーションモードが存在する。
判断基準は実行しているプログラムが置いてあるセグメントの属性を見て判断する。
GDTRをセットする番地は0x00270000。最初はESP+4にリミットがESP+8に番地が入っている。
問題は64ビット分の情報をどうやって48ビットのレジスタに格納するか。
16ビットレジスタであるAXにESP+4を2バイト分読み込む。
ESP+6にAXをMOVすればESP+6から6バイト分がGTDRにセットする内容となる。厳密には下位16ビットがリミットで残り32ビットが番地となる。
実際にはLGDTでGDTRにセットする。