8080拥有16根地址线(寄存器是16位,2的16次方=64K), 8086拥有20根地址线(2的20次方=1M=16×64K,但它的寄存器仍然是16位),为了兼容8080,Intel仍然让程序只使用1M里的64K字节段, 这叫内存的实模式。8088, 8086, 80286的寄存器都仍然是16位,它是使用两个16位寄存器来寻址20位,一个16位寄存器用来存放段地址,一个16位寄存器用来存放段偏移。段可以是在1M内任意以16为倍数的段地址开始,接着最大可达64K的界限。0001:0019=0001H段开始的第0019H个字节处,我们知道,一个段可能开始于实内存中所有1M字节里的任意一个16字节处,所以0001:0019等于0000:0029, 也等于0002:0009。
这这种实模式下,一个段只有64K大小,一个程序不够用,所以一般通过划分CS, DS, SS, ES等段的方式来扩大大小。
8088, 8086, 80286有4个用于存放段地址的段寄器,80386增加了2个。
CS(Code Segment):代码段,用于存放机器指令。
DS(Data Segment): 数据段,用于存放变量和其他数据。可能会有很多数据段,但CPU一次只能使用一个。
SS(Stack Segment): 堆栈段, 一个单独的程序只能有一个堆栈
ES(Extra Segment): 附加段寄存器(结合DS使用意味着可以同时访问两个数据段),用于指定内存中某一位置的备用段。
FS和GS:是ES的克隆,命令按ES往后的F,G排列,只存在于80386(寄存位为32位)及后来的x86 CPU中。
在32位世界中,通用寄存器分为三个一般类:16位通用寄存器、32位扩展寄存器和8位的半寄存器(实际上,16位和8位寄存器只是32位寄存器内部的一块区域而已)。
有8个16位通用寄存器:AX,BX,CX,DX,BP,SI,DI和SP用于存放16位的或更少位的值。在实模式下它可以是结合段寄存器这样指定一个完整的20位地址使用:
SS:SP
SS:BP
ES:DI
DS:SI
CS:BX
后来x86体系结构将其扩展为32位时,在原有的名称前加了前缀E(EAX, EBX, ECX, EDX, EBP, ESI, EDI和ESP, 低16位仍然可以用老式不加前缀E的叫法,但不幸的是,寄存器的高16位根本没有自己的名字)。
64位系统,在原有的名称前加了前缀R(RAX, RBX, RCX, RBP, RSI, RDI, RSP)。另外添加了8个全新的64位寄存器(R8到R15)。x86-64也增加了8个128位的SSE寄存器到IA-32的8个同类寄存器中,一共有16个SSE寄存器。
上面的4个通用寄存器(EAX, EBX, ECX, EDX)的低16位(AX, BX, CX, DX)又被划分为8位的半寄存器,高位后加H,低位后加L,如:在BX中有BH和BL两个半寄存器,依此类推。
16位中叫IP, 32位中叫EIP,存放当前代码段(一个程序可能包含多个代码段)中下一段即将执行的机器指令的偏移地址。CS和IP一起,保存下一条即将执行的指令的完整地址(在实模式下,CS与IP一起工作带来20位的地址,CS由操作系统设置,IP可以跟踪64K内存段;在保护模式下,32位系统的IP可以跟踪4G内存段)。IP寄存器是唯一不能读入与写出的寄存器,它只能使用跳转指令移动。
16位中叫FLAGS,32位中叫EFLAGS,每一位都有特殊的含义,也有单独的名字,如CF, DF, OF等。
实模式因为寻址只有64K不够用,所以有段的概念。但在32位系统中,可以寻址4G的内存空间,就不需要分段了。但传统的段寄存器依然存在,只是你不能读或写它们,完全交由操作系统来做。操作系统有一个虚拟地址空间可以非常大,但32位系统的物理寻址空间最大只能是4G,操作系统从虚拟地址空间中找一块4G的内存空间作为内存寻址,那么段寄存器就被操作系统设置为这个虚拟空间下的基址。所以说,Linux没有实模式“遗留问题”需要处理,自从1992年第一次出现以来,它就一直运行在保护模式下。只有BIOS需要运行在实模式下(Linux提供软中断80H去访问BIOS)。如果多个程序同时访问一块内存,可能造成混乱,像DOS是单任务程序无此问题,多个程序通过驱动访问(驱动能隔离程序到某块内存的访问)也无此问题,保护模式可以让多个程序在同一时刻运行。
x86-64定义了三种一般模式:实模式,保护模式和长模式。实模式是兼容模式兼容16位系统,保护模式也是一种兼容模式兼容32位系统,长模式。由于64位地址过于巨大(10亿GB),今天的x86-64 CPU一般只支持48位虚拟内存和40位物理内存地址(1000GB)。