x86アーキテクチャ

64Kバイトの壁と640Kバイトの壁

64Kバイトの壁は16ビットの計算機の宿命で連続したメモリアドレスへの アクセスが2の16乗しか許されないために作られます。 しかし、x86にはセグメントレジスタがあるため、8086では最大2の20乗 までのアドレッシングが可能です。つまり、1Mバイトです。この1Mバイトの 空間をどのように使うかはOSに依存します。例えばMS-DOSでは最初の 640Kバイトをメモリに、それ以外を画面表示のためのハードウエアや BIOSのROMなどに使いました。ですから、640Kバイト以上のメモリを 搭載しても、8086/MS-DOSでは640Kバイトを越えたメモリを 利用することはできません。これが640Kバイトの壁です。

プロテクトモードとリアルモード

リアルモードとは仮想記憶を利用しないモードでプロテクトモードとは 仮想記憶を利用するモードです。この切り替えはシステムレジスタ群に あるコントロールレジスタ0(CR0)の最下位ビット(ビット0)で行なわれます。 このビットが0の場合は、リアルモードで、1の場合がプロテクトモードです。

x86のアドレス変換

リアルモードでのセグメントアドレスとプロテクトモードでのそれは 異なって来ます。ここではプロテクトモード、つまり仮想記憶を利用した システムでのx86のセグメント機構の役割について説明しながら、 アドレス変換機構について解説します。 マシン語命令が示すアドレスから実際の物理メモリのアドレスへ到達 するためには2つの変換パスを通ります。 始めにはセグメントアドレスとオフセットアドレスからリニアアドレスを 作るパスです。次にリニアアドレスから物理アドレスを作るのはメモリ マネージメントユニット(MMU)と呼ばれる変換機構です。

セグメントアドレスは多少複雑なメカニズムで作られますので、 順を追って説明します。 セグメントレジスタにはいくつかあります。CSはコードセグメント レジスタで、マシン語命令を読み込む時に使われるものです。DSは データセグメントレジスタで、マシン語命令に記述されたメモリアクセス を行なう時に使われる物です。SSはスタックセグメントレジスタで、 マシン語のpushやpopの実行、ベースポインタ(BP)の操作に使われる ものです。オフセットアドレスはマシン語命令の中に含まれる アドレス値で、直接アドレッシングやベース相対アドレッシングなどの アドレッシングに現われるものです。
セグメントレジスタにはセレクタ値が含まれています。 この値はセグメントディスクリプタテーブルの中にある セグメントディスクリプタを示します。セグメントディスクリプタには セグメントベースが記述されていますので、それをセグメント アドレスとします。ディスクリプタテーブルには目的により グローバルディスクリプタテーブル、ローカルディスクリプタテーブルが ありります。グローバルディスクリプタテーブルは システムに1つですが、ローカルディスクリプタテーブルはタスク毎に 持ちます。そして、これらのテーブルの位置を指し示しているのは GDTRと LDTRです。これらのテーブルをどのように使うかはOSによって 違って来ます。

割り込み

STIとCLI

EFLAGSレジスタのビット9はCPUへの割り込みを許可/禁止するフラグです。 このフラグをオン/オフ(1/0)するのはマシン語命令STI(Set Interrupt Enable Flag)とCLI(Clear Interrupt Flag)です。このフラグはハードウエア割り込みを 制御するためのもので、例外割り込みには有効ではありません。

ハードウエア割り込みに限らず例外割り込みもその割り込み要因に 対応した割り込み番号が割り付けられています。リアルモードにおいては メモリ上に固定的にゼロ番地から1Kバイト分、割り込み番号と割り込みルーチンを 対応させる割り込みベクターテーブルがアサインされています。しかし、 プロテクトモードにおいては割り込みベクターテーブルの代わりに 割り込みディスクリプターテーブルを使います。IDTRはそのテーブルを 示しています。このレジスタの値はリアルモードでは0に固定されていましたが、 プロテクトモードではOSが値を自由に設定することができます。 割り込みディスクリプターテーブルの大きさは256個分です。

Linuxにおける割り込み番号の管理

Linuxにおける割り込み番号の管理は次のようです。 0x00から0x1FまではCPUが使用するため、リザーブされます。 ですから、利用可能な番号は0x20から0xFF(224)までです。

asm-i386/irq.h
#define NR_IRQS 224

linux/include/asm/hw_irq.h:
IDT vectors usable for external interrupt sources start at 0x20:
#define FIRST_EXTERNAL_VECTOR   0x20
#define SYSCALL_VECTOR          0x80

Vectors 0x20-0x2f are used for ISA interrupts.

Vectors 0xf0-0xfa are free (reserved for future Linux use).
#define SPURIOUS_APIC_VECTOR    0xff
#define ERROR_APIC_VECTOR       0xfe
#define INVALIDATE_TLB_VECTOR   0xfd
#define RESCHEDULE_VECTOR       0xfc
#define CALL_FUNCTION_VECTOR    0xfb

#define LOCAL_TIMER_VECTOR      0xef

extern int irq_vector[NR_IRQS];
80386以降のCPU
論理アドレス空間 64TB
物理アドレス空間 4GB
セグメントサイズ 4GB

486CPUのバス
アドレスバス	32 bit
データバス	32 bit
I/O バス	16 bit

汎用レジスタ群
				32 bit	16bit	8bit
アキュムレータ			EAX	AX	AH,AL
ベースレジスタ			EBX	BX	BH,BL
カウンタレジスタ		ECX	CX	CH,CL
				EDX	DX	DH,DL
ソースインデックス		ESI	SI
デスティネーションインデックス	EDI	DI
ベースポインタ			EBP	BP		
スタックポインタ		ESP	SP		
インストラクションポインタ	EPI	PI		
フラグレジスタ			EFLAGS	FLAGS
セグメントレジスタ
	コードセグメント		CS
	データセグメント		DS
	スタックセグメント		SS
	エクストラセグメント		ES
					FS
					GS
*インストラクションポインタはいわゆるプログラムカウンタのこと

システムレジスタ群
				 48 bit	32 bit	16 bit
システムアドレスレジスタ
グローバルディスクリプタテーブル GDTR
割り込みディスクリプタテーブル	 IDTR
						LDTR
						TR
コントロールレジスタ
					CR0
					CR1
					CR2
					CR3

GNUアセンブリ言語

表現規則の例題
movw %ax,%bx		%axの内容を%bxに代入
movw $123,%ax		十進数123を%axに代入
movb $-0xa1,%cx		16進数-a1を%cxに代入
movb $0b10111101,%dh	2進数10111101を%dhに代入
movb $'A',%dl		文字'A'を%dlに代入
movl $var,%esi		変数varのアドレス値を%esiに代入
movb var,%ah		変数varのメモリの内容を%ahに代入
movb (%ebx),var		%ebxが示すアドレスのメモリの内容を変数varに代入

x86のメモリアドレッシングモード
イミーディエートアドレッシング
	mov  $0x12, %al		16進数12を%alに代入
	addl $0x89af,%ebx	16進数89afを%ebxの内容に加算
				
直接アドレッシング
	movb ($0x12),%al	16進数12のアドレスの内容をを%alに代入
	movb 0x12,%al	 	上記と同じ
	movl var, %ebx		変数varの内容を%ebxに代入

間接アドレッシング
	movb (%bx),%al		%bxが示すアドレスの内容を%alに代入
	movb %cl,(%ebx)		%clの内容を%ebxが示すアドレスに代入

ベース相対アドレッシング
	movb array(%bx),%al	%bxの内容にarrayを加えた値をアドレスとし
				  その内容を%alに代入
        incw -10(%ebp)		%ebpの内容に十進数-10を加えた値を
				  アドレスとしてその内容をインクリメントする