前文说到我们由实模式进入保护模式之前会对生成GDT并且设置GDTR,现在就是在保护模式下利用他们访问内存的时候了。
文章目录
当前的GDT和GDTR结构
这是上篇中我们设置的GDT,它有四段,现在我们的目标是在屏幕上打印字符串,所以要操作的就是显存所在的段,第三段下标索引是2.
段选择子
8086下访问内存需要给CS,DS,ES,SS写入段地址,然后左移四位加上偏移地址直接访问物理内存。保护模式下,段寄存器扩展了两个FS和GS,改叫段选择器,我们向其中写入的是段选择子。段选择器的位数和8086段寄存器一样是16位,但是它还有一个不可见的描述符告诉缓存区,其中存储的是段的线性基地址,界限和属性,和我们前文的GDT中的结构相对应。
打印字符串
我们选择使用ds做为默认段,现在要做的就是修改ds中的值,顺便一提可以修改段寄存的指令有mov,pop,jmp,jmp far,call far,iret,retf等。
mov cx,0000000000010_0_00B ;加载数据段选择子(0x10)
;表示索引是2,特权级是0,存在于GDT中而不是LDT
mov ds,cx
处理器得到该指令后执行顺序是这样的:
- 将段选择子中的索引*8做为偏移地址
- 偏移地址和GDTR中线性基地址相加
- 判断是否超过段界限
- 找到对应GDTR加载到段选择器后面的不可见的描述符缓冲区
接下来就是直接利用段选择器加上我们自定义的偏移地址就可以操作内存,这时候不在去每次访问GDT而是直接利用描述符缓冲区中的内容。当需要将ds指向别处的时候,在用可以改变段选择器的指令修改段选择器,CPU会在描述符缓冲区加载新的内容。
mov byte [0x00],'P'
mov byte [0x02],'r'
mov byte [0x04],'o'
mov byte [0x06],'t'
mov byte [0x08],'e'
mov byte [0x0a],'c'
mov byte [0x0c],'t'
mov byte [0x0e],' '
mov byte [0x10],'m'
mov byte [0x12],'o'
mov byte [0x14],'d'
mov byte [0x16],'e'
mov byte [0x18],' '
mov byte [0x1a],'O'
mov byte [0x1c],'K'