1.
段寄存器
CS(代码段),代码段是一个存储器区域,这里保存微处理器使用的代码(程序和过程)。代码段寄存器定义了存放代码的存储器段的起始地址。在实模式下工作时,它定义一个64KB存储器段的起始地址;在保护模式下工作时,它选择一个描述代码存储器起始地址和长度的描述符。对于8086~80286,代码段限制为64KB;80386和更高级型号的微处理器在保护模式下操作时,代码段限制为4GB。
DS(数据段),数据段也是一段存储区域,容纳程序使用的大部分数据。可以通过偏移地址或者其他含有偏移地址的寄存器的内容寻址数据段里的数据。长度限制和代码段一样。
ES(附加段),是一个附加的数据段,为某些串指令存放目的数据。
SS(堆栈段),堆栈段定义为堆栈的存储区。由堆栈段和堆栈指针寄存器确定堆栈段内当前的入口地址。BP寄存器也可以寻址堆栈段内的数据。
FS和GS,是在80386,80486,Pentium~Pentium4微处理中增加的段寄存器,以便访问这两个附加的存储器段。
2.实模式存储器寻址
第一个1MB存储器称为实模式存储器(real memory)或常规内存(conventional memory)。
16位段+偏移寻址组合
段 偏移 主要用途
CS IP 指令地址
SS SP或BP 堆栈地址
DS BX,DI,SI,8位或16位数 数据地址
ES 串指令的DI 串目标地址
默认的32位段+偏移寻址组合
段 偏移 主要用途
CS EIP 指令地址
SS ESP或EBP 堆栈地址
DS EAX,EBX,ECX,EDX,EDI,ESI,8位或16位数 数据指针
ES 串指令的EDI 串目标地址
FS 无默认 一般地址
GS 无默认 一般地址
3.保护模式存储器寻址
在保护模式下,当寻址扩展内存里的数据和程序时,仍然使用偏移地址访问位于存储器段内的信息。两者的区别是,保护模式下的段地址不再像实模式那样由段寄存器提供。在原来存放段地址的段寄存器里含有一个选择子(selector),用于选择描述符表内的一个描述符。描述符(descriptor)描述存储器段的位置、长度和访问权限。由于段寄存器和偏移地址仍然用于访问存储器,所以保护模式指令和实模式指令是完全相同的。两种模式的区别是微处理器访问存储器段时对段寄存器的解释不通。在80386及更高档型号的微处理器中,两种模式的另一个差别是:在保护模式下可以用32位数取代16位数。32位偏移地址允许微处理器访问长达4GB的段内的数据。
装在段寄存器里的选择子从两个描述符表之一选择8192个描述符中的一个。段寄存器仍然选择一个存储器段,但不再像实模式那样直接选择而是间接选择。
段寄存器可以访问两个描述符表,一个包括全局描述符,另一个包括局部描述符。全局描述符(global descriptor)包含适用于所有程序的段定义,而局部描述符(local descriptor)通常用于唯一的应用程序。每个描述符表包含8192个描述符。每个描述符长8个字节。
描述符的基地址(base address)部分指示存储器段的起始位置。80386采用32位基地址,允许段起始于4GB存储器的任何地方。
段界限(segment limit)包含该段中最大的偏移地址。80386的界限是20位。80386访问长度为1B-1MB之间或者4KB-4GB的存储器段。
G位,即颗粒度(granularity bit)。如果G=0,说明段的界限为00000H-FFFFFH(从0到1MB)。如果G=1,界限值要乘以4KB,界限为00000XXXH-FFFFFXXXH。G=1,则允许段的长度为4KB-4GB,以4KB为单位。
AV位指示段有效(AV=1)或者段无效(AV=0)。
D位指示在保护模式或者实模式下80386指令如何访问寄存器和存储器数据。若D=0,则指令与8086微处理器兼容,是16位指令。这意味着指令在默认方式下用16位偏移地址和16位寄存器,这种方式通常称为16位指令方式。若D=1,则指令是32位的。在默认情况下,32位指令模式家丁所有的偏移地址和寄存器都为32位。
访问权限字节(access rights byte)控制着对保护模式中存储器段的访问。这个字节描述了段在系统中怎样起作用。访问权限字节始终全面地控制着段。如果是数据段,则指定起增长方向。入关段的增长超出了界限,则微处理器的程序就被中断,并给出一般保护错误。用户可以说明一个数据段是否为可写或写保护。
描述符是通过段寄存器从描述符表中选择的。
上图是80386微处理器在保护模式操作期间的段寄存器内容。段寄存器包括13位的选择子字段、表选择(TI)位和请求优先级字段。13位的选择子可以从描述符表的8192个描述符中选择一个。TI位选择全局描述符表(TI=0)或者局部描述符表(TI=1)。请求优先级(RPL)请求存储器段的访问优先级。最高优先级00,最低优先级11。
程序不可见寄存器
在保护模式中,每个段寄存器含有一个程序不可见区域。这些寄存器的程序不可见区域通常称为高速缓冲存储器(cache),因为它也是存储信息的小存储器。这些高速缓冲存储器与微处理器中的一级或二级高速缓冲存储器不能混淆。每当段寄存器中的数发生改变时,基地址、界限和访问权限就装入段寄存器的程序不可见区域。当一个新的段号被放入段寄存器里时,微处理器就访问一个描述符表,并把描述符装入该段寄存器的程序不可见高速缓冲存储器区域内。这个描述符一直保存在此处,并在访问存储器段时使用,直到段号再次发生变化。这就允许微处理器重复访问一个存储器段时,不必每次都去查询描述符表,因此称为高速缓冲存储器。
GDTR(Global Descriptor Table Register,全局描述符表寄存器)和IDTR(Interrupt Descriptor Table Register, 中断描述符表寄存器)包含描述符表的基地址及其界限。因为描述符表的最大长度为64KB,所以每个表的界限为16位。当工作于保护模式时,全局描述符表和它的界限装入GDTR。在使用保护模式前,必须初始化中断描述符表和IDTR。
局部描述符表的位置是从全局描述符表中选择的。为了寻址局部描述符表,建立了一个全局描述符。为了访问局部描述符表,将选择子装入LDTR(Local Descriptor Table Register,局部描述符表寄存器),如同在段寄存器装入选择子一样。这个选择子访问全局描述符表,并且将局部描述符表的基地址、界限和访问权限装入LDTR的高速缓冲存储器。
TR(Task Register,任务寄存器)包含一个选择子,该选择子用于访问一个确定任务的描述符。任务通常就是进程或者应用程序。进程或者应用程序的描述符存储在全局描述符表中,因此可通过优先级控制对它的访问。任务寄存器允许在约17μs内完成上下文或任务的切换。任务切换机制允许微处理器在足够短的时间内实现任务之间的切换,也允许多任务系统以简单而规则的方式,从一个任务切换到另一个任务。
分页机制
80386及更高档型号的微处理器的分页机制允许为任何线性地址分配物理存储器地址。线性地址(linear address)定义为由程序产生的地址。通过内存分页机制,线性地址透明地转换为物理地址,这样就能使需要在特定地址上运行的应用程序通过分页机制重定位,还可以将存储器放在“根本不存在的”存储区域,例如由EMM386.EXE提供的上位内存块。
EMM386.EXE程序以4KB块为单位,把扩展内存重新分配到视频BIOS和系统BIOS ROM之间的系统内存区,作为上位内存块。没有分页机制,就不可能使用这个内存区。
微处理中控制寄存器的内容控制着分页单位。控制寄存器CR0到CR3存在于80386中,从Pentium开始,又增加了一个名为CR4的控制寄存器,控制着由Pentium及更高档的微处理器提供的对基本结构的扩展。其特性之一就是,CR4的第4位置位,允许一个4MB的页。
CR0最左一位PG置1时,就选择分页。如果PG被清零,则程序产生的线性地址就是用于寻址存储器的物理地址。如果PG位置1,则线性地址通过分页机制转换为物理地址。在实模式和保护模式下分页机制都可以工作。
CR3的内容包括页目录基地址以及PCD和PWT位。PCD和PWT位控制微处理器PCD和PWT引脚的操作。如果PCD置位,则PCD引脚在非分页总线周期变为逻辑1,这就允许外部硬件控制二级高速缓冲存储器(二级高速缓冲存储器是一个外部的高速存储器,他是微处理器和主存DRAM之间的缓冲器)。PWT位也再非分页总线周期出现在PWT引脚上,用于控制系统中的写直达高速缓冲存储器。页目录基地址用于为页转换部件寻址页目录。注意,因为是在内部增添了000H,这个地址将寻址存储系统中以4KB为边界的页目录。页目录包含1024个目录项,每项长4字节。每个页目录项寻址一个包含1024项的页表。
由软件生产的线性地址分为3个部分,分别用于寻址页目录项(page directory entry)、页表项(page table entry)和页偏移地址(page offset address)。下图是线性地址和它的分页结构:
注意,最左边10位怎样寻址页目录中的一项。对应线性地址00000000H-003FFFFFH,访问页目录的第一项。每一个页目录项代表存储系统的一个4MB区域。页目录的内容选择由随后的10位线性地址(bit21-bit12)所指示的页表。这意味着地址00000000H到00000FFFH将选择页目录项0和页表项0。注意,这是个4KB的区域。线性地址的偏移部分(bit11-bit0)选择4KB页内的一个字节。在上图中,如果页表项0包含地址00100000H,则与线性地址00000000H-00000FFFH对应的物理地址为00100000H-00100FFFH。也就是说,当程序寻址00000000H-00000FFFH之间的地址时,微处理器实际上是寻址00100000H-00100FFFH之间的地址。
汇编笔记(6)Linux内核引导分析(MOVW指令)
MOVW:将DS:SI的内容送至ES:DI,是复制过去,原来的代码还在。很多书用了“移”这个字,实际上是复制过去。
Linux内核中引导部分一开始有这样一段代码:
45 entry start ! 告知连接程序,程序从start
标号开始执行。
46 start:
47 mov ax,#BOOTSEG ! 将ds 段寄存器置为0×7C0;
48 mov ds,ax
49 mov ax,#INITSEG ! 将es 段寄存器置为0×9000;
50 mov es,ax
51 mov cx,#256 ! 移动计数值=256 字;
52 sub si,si ! 源地址 ds:si = 0×07C0:0×0000
53 sub di,di ! 目的地址 es:di =
0×9000:0×0000
54 rep ! 重复执行,直到cx = 0
55 movw ! 移动1 个字;
56 jmpi go,INITSEG ! 间接跳转。这里INITSEG 指出跳转到的段地址。
57 go: mov ax,cs ! 将ds、es 和ss 都置成移动后代码所在的段处(0×9000)。
! 47–56 行作用是将自身(bootsect)从目前段位置0×07c0(31k)
! 移动到0×9000(576k)处,共256 字(512 字节),然后跳转到
! 移动后代码的go 标号处,也即本程序的下一语句处。
注意,在55行执行完毕之后,0×7c00之后的512字节应当与0×9000之后的512字节一模一样。
然后看56行,这里的go是段内偏移,也就是0×39(十进制的57),而INITSEG=0×9000,所以执行这条语句是跳到0×9000:0×39也就是复制过去的第57行,这样代码就相当于在一个程序里继续执行了。