进入保护模式(三)——《x86汇编语言:从实模式到保护模式》读书笔记17

(十)保护模式下的栈

76         ;如下用简单的示例来帮助阐述32位保护模式下的堆栈操做 
77         mov cx,00000000000_11_000B         ;加载堆栈段选择子
78         mov ss,cx
79         mov esp,0x7c00

第77~79行用来初始保护模式下的栈。栈段描述符是GDT中第3个(从0开始数)描述符,这个描述符的线性基地址是0x0000_0000,段界限是0x0000_7a00,粒度是字节,B=1,属于可读可写、向下扩展的数据段。学习

我在博文数据段描述符和代码段描述符(一)——《x86汇编语言:从实模式到保护模式》读书笔记10中已经说过,spa

对于向上扩展的段(E=0),逻辑地址中的偏移值范围能够从0到(界限值*粒度);.net

对于向下扩展的段(E=1),逻辑地址中的偏移范围能够从(界限值*粒度)到0xFFFF(当B=0时)或者0xFFFF_FFFF(当B=1时)。指针

因此,对于描述符中的栈段,偏移范围是0x0000_7a00~0xffff_ffff. 仔细琢磨一下,这和咱们的想法不是那么一致,由于代码79行,令ESP的初值是0x7c00,也就是说,咱们本打算定义一个偏移范围是0x0000_7a00~0x0000_7c00的栈段。code

由于线性基地址是0x0000_0000,也就是说描述符定义的栈段,实际能够访问的物理空间是0x0000_7a00~0xffff_ffff,可是咱们却但愿这个栈能够访问的物理空间是0x0000_7a00~0x0000_7c00。示意图(根据原书的图11-14改编而成)以下图所示。虽然这个栈不完美,可是不用担忧,咱们会在后面的学习中用更好的方法来建立栈。眨眼blog

New0003栈

(十一)验证32位下的栈操做

隐式的栈操做(如push、pop、call、ret、iret等)涉及两个段,一个是指令所在的代码段,一个是栈段。以前的博文咱们说过,对于可执行代码段,字符串

D=1:默认是32位地址和32位或8位的操做数get

D=0:默认是16位地址和16位或8位的操做数it

注意:指令前缀0x66能够用来选择非默认值的操做数大小;前缀0x67能够用来选择非默认值的地址大小class

对于栈段,

B=1:栈指针使用ESP

B=0:栈指针使用SP

就本文的实验代码,其代码段描述符的D位是1,其栈段描述符的B位也是1. 因此,当进行隐式的栈操做时,默认是32位操做数(好比压栈的时候,压入的是双字),且用ESP进行操做。

因此,下面的代码就用来验证这个事实。

81         mov ebp,esp                        ;保存堆栈指针 
82         push byte '.'                      ;压入当即数(字节)
83         
84         sub ebp,4
85         cmp ebp,esp                        ;判断压入当即数时,ESP是否减4 
86         jnz ghalt                          
87         pop eax
88         mov [0x1e],al                      ;显示句点 
89      
90  ghalt:     
91         hlt                                ;已经禁止中断,将不会被唤醒

在阅读这段代码的时候,我多少有点怀疑:书上说的究竟是不是真的?我想经过实践来检验:探究一下PUSH指令在16位模式和32位模式下的执行规律。通过一番折腾,终于有告终果。请参考个人博文  16位模式/32位模式下PUSH指令探究——《x86汇编语言:从实模式到保护模式》读书笔记16

第81行,复制esp的值给ebp;第82行,压入一个字节(byte关键字不能省略);理论上,把ebp的值减去4后(第84行),应该和此时esp的值相等。为了证实这一点,第85行比较ebp和esp的值,若是不相等,就跳转到91行执行停机指令;若是相等,就把字符“.”显示在以前的字符串后面。

OK,第11章的内容已经学习完了。最后咱们看一下代码的运行结果吧,结果就是在屏幕的左上角显示“Protect mode OK.”

执行结果

(完)

相关文章
相关标签/搜索