4 寻址机制

前面,bochs加载代码清单1进入实模式,在显示屏上打印字符串,然后进入死循环。那么如何让实模式跳转到保护模式呢?那么就不得不说实模式和保护模式的寻址机制了。


1实模式寻址机制


由于实模式下只有20根地址线可用,它的寻址能力为:

1M =220* 8bit

由于每位地址指向一个字节8bit,所以后面我们乘于8bit。一个地址由段和偏移地址决定,计算公式如下:

物理地址(PhysicalAddress=段值(Segment* 16 +偏移(Offset

段乘于16表示将段左移4位,然后和偏移构成20位的地址。注意的是,在实模式中段值和偏移都为16位。


2保护模式寻址机制


保护模式有32根地址线可用,它的寻址能力为:

4G =232* 8bit

保护模式下一个地址也是由段和偏移地址构成,但是段中存储的不是地址,而是GDT(会LDT)的索引值。通过索引值找到GDT中的数据项(段描述符),段描述符中才实际存储段地址值。保护模式寻址机制如图1所示。

4 寻址机制_第1张图片

1保护模式寻址机制


3什么是GDT


既然从实模式进入保护模式涉及到GDT,那么就要了解GDT的结构。GDT实际就是容器,它存储的段描述符由8个字节构成。如图2所示

4 寻址机制_第2张图片

2GDT中段描述符结构


4编写GDT段描述符


;------------------------------------------------------------------------------------------------------

;

;描述符

;usage: Descriptor Base, Limit, Attr

; Base: dd

; Limit: dd (low 20 bits available)

; Attr: dw (lower 4 bits of higher byte are always 0)

%macroDescriptor3

dw %2& 0FFFFh ;段界限1

dw %1& 0FFFFh ;段基址1

db (%1>> 16) & 0FFh ;段基址2

dw ((%2>> 8) & 0F00h) | (%3 & 0F0FFh) ;属性1+段界限2+属性2

db (%1>> 24) & 0FFh ;段基址3

%endmacro;


宏有3个参数,在宏体中用%12%3%表示,1%32位基址,2%为少于20位的段界限,3%为少于4位的属性。


宏中对三个参数进行了拆分组合生成8字节的段描述符。


5将段描述符放入GDT


[SECTION.gdt]

;GDT

;段基址,段界限 ,属性

LABEL_GDT: Descriptor 0, 0, 0 ;空描述符

LABEL_DESC_CODE32:Descriptor 0,SegCode32Len- 1, DA_C + DA_32 ;非一致代码段

LABEL_DESC_VIDEO: Descriptor 0B8000h, 0ffffh, DA_DRW ;显存首地址

;GDT结束


gdt段中,存储了3个段描述符。分别时空描述符LABEL_GDT32位代码段描述符、视频段描述符。


[SECTION.s32]; 32位代码段.由实模式跳入.

[BITS 32]

LABEL_SEG_CODE32:

mov ax,SelectorVideo

mov gs,ax ;视频段选择子(目的)

mov edi,(80 * 11 + 79) * 2 ;屏幕第11,79列。

mov ah,0Ch ; 0000:黑底 1100:红字

mov al,'P'

mov [gs:edi],ax

;到此停止

jmp $

SegCode32Len equ $- LABEL_SEG_CODE32

;END of [SECTION .s32]


其中在32位代码段描述符中,SegCode32Len– 1表示32位代码段的长度(即段界限),减去1是应为从0开始计算。


;GDT选择子

SelectorCode32 equ LABEL_DESC_CODE32 -LABEL_GDT

SelectorVideo equ LABEL_DESC_VIDEO -LABEL_GDT

;END of [SECTION .gdt]


这里可以看出SelectorCode32SelectorVideo为索引值,表示在GDT中的位置。从这里也可以猜测空描述符的作用。


6设置段描述符


段寄存器存放的是GDT的索引值,但是段描述符中的32位代码段基址为0,视频段为0B8000h。很明显32为代码段的地址并不是0,显存的代码段为0B8000h。在实模式下设置32位代码段基址。


[SECTION.s16]

[BITS 16]

LABEL_BEGIN:

mov ax,cs

……

;初始化32位代码段描述符

xor eax,eax

mov ax,cs

shl eax,4

add eax,LABEL_SEG_CODE32

mov word[LABEL_DESC_CODE32 + 2], ax

shr eax,16

mov byte[LABEL_DESC_CODE32 + 4], al

mov byte[LABEL_DESC_CODE32 + 7], ah

……


这里将代码寄存器cs(存放执行代码段地址)左移4位(段*16),然后和32位代码段LABEL_SEG_CODE32在执行代码段中的地址(偏移地址)相加。得到物理地址存放到eax中。然后将eax拆分组合放到LABEL_DESC_CODE32描述符中段基址地方。


现在段描述符都初始化好了,可以加载gdtr了。


7加载gdtr


GDTR是一个长度为48bit的寄存器,内容为一个32位的基地址和一个16位的段限。其中32位的基址是指GDT在内存中的地址。


[SECTION.gdt]

……

LABEL_GDT: Descriptor 0, 0, 0 ;空描述符

……

;GDT结束


GdtLen equ $- LABEL_GDT ; GDT长度

GdtPtr dw GdtLen- 1 ; GDT界限

dd 0 ;GDT基地址;为加载GDTR作准备

……

xor eax,eax

mov ax,ds

shl eax,4

add eax,LABEL_GDT ; eax <- gdt基地址

mov dword[GdtPtr + 2], eax ; [GdtPtr + 2] <- gdt基地址

;加载GDTR

lgdt [GdtPtr];将GDT地址赋值给gdtr


代码将数据段赋值给ax,然后左移4位,加上GDT在程序中的偏移地址。这个值为GDT的物理地址。最后将这个地址赋值给dword[GdtPtr + 2]


完成这些之后,再进行:

  • 关闭实模式下中断

  • 打开A20

  • 设置cr0PE

  • 跳转进入保护模式

你可能感兴趣的:(4 寻址机制)