从实模式到保护模式的跳转指令是怎么取到的?

liaozhicai 2011-09-04 10:59:00
好多类似的代码:
 lgdt GdtPtr //加载 GDTR
cli //关中断
//打开地址线A20
//准备切换到保护模式
mov eax, cr0
or eax, 1
mov cr0, eax
//真正进入保护模式,根据GDR中注册的信息,跳转到段基址0x7d00,偏移量为0的地方(pm32.c中main函数内存地址)
jmp dword SelectorSode32:0x0


执行mov cr0, eax命令之后,已经是保护模式了,此时,按实模式的解释方式cs:ip即cs*16+ip-->jmp dword SelectorSode32:0x0
但这时已经是保护模式了,此时的cs:ip为什么还是指向jmp命令呢?如果不是,那就根本执行不了jmp命令,也就乱了。


我的疑问:

mov cr0,eax是在实模式下执行的,当把这个命令取到指令缓冲寄存器的时候,ip下移,这时cs:ip指向jmp命令,接着执行
mov cr0,eax命令,此后,取指令的模式已经变了,已经从cs的投影寄存器取信息了,为什么还是取到jmp命令?
...全文
256 10 打赏 收藏 转发到动态 举报
写回复
用AI写文章
10 条回复
切换为时间正序
请发表友善的回复…
发表回复
liaozhicai 2011-09-04
  • 打赏
  • 举报
回复
[Quote=引用 2 楼 mydo 的回复:]
保护模式下实模式寄存器的内容做了升级
[/Quote]

能更详细一点吗?谢谢。

是不是这样:
保护模式下,计算线性地址,实际取用的是cs的描述符高速缓冲寄存器的基址与eip的和,那么实模式下是不是也利用了这个高速缓冲寄存器,或者说这个高速缓冲寄存器的基址部分,在实模式下置为cs*16,这样的话,执行mov cr0,eax之后,虽然处于保护模式了,但cs寄存器没有更新,高速缓冲寄存器的内容也没有更新,从高速缓冲寄存器取的值与实模式下用cs*16取的值是一样的,这样的话,还是指向jmp命令。
liaozhicai 2011-09-04
  • 打赏
  • 举报
回复
[Quote=引用 1 楼 haojiahuo50401 的回复:]
执行mov cr0,eax命令后,进入保护模式,此时cs称为段选择子,从cs指向的段描述符中所取到的段基址再加上eip中的段偏移,正好是jmp所在的内存地址。
[/Quote]

也就是说执行mov cr0,eax命令前后的cs值既要满足cs*16+ip指向jmp命令,同时又要满足将cs值按段选择子解释索引到GDT的表项[k],[k].base+eip也要指向jmp命令?

但实际上,没有这样构造啊?

;程序名:t10-1.asm
;功能:演示实方式和保护方式切换
;16位偏移的段间直接转移指令的宏定义
JUMP macro selector,offsetv
db 0eah ;操作码
dw offsetv ;16位偏移
dw selector ;段值或者选择子
endm
;字符显示宏指令的定义
ECHOCH macro ascii
mov ah,2
mov dl,ascii
int 21h
endm
;存储段描述符结构类型的定义
DESCRIPTOR struc
LIMITL dw 0 ;段界限(0-15)
BASEL dw 0 ;段基地址(0-15)
BASEM db 0 ;段基地址(16-23)
ATTRIBUTES dw 0 ;段属性
BASEH db 0 ;段基地址(24-31)
DESCRIPTOR ends
;伪描述符结构类型的定义
PDESC struc
LIMIT dw 0 ;16界限
BASE dd 0 ;基地址
PDESC ends
;常量定义
ATDW = 92h ;存在的可读写数据段属性值
ATCE = 98h ;存在的只执行代码段属性值
;____________________________________________________
;
.386P
;_______________________________________________________
;数据段
dseg segment use16 ;16位段
GDT label,byte ;全局描述表GDT
DUMMY DESCRIPTOR <> ;空描述符
CODE DESCRIPTOR <0ffffh,,,ATCE,>
CODE_SEL = CODE - GDT ;代码段描述符的选择子
GDTLEN = $ - GDT
DATAS DESCRIPTOR <0ffffh,0h,11h,ATDW,0>
DATAS_SEL=DATAS - GDT ;源数据段描述符的选择子
DATAD DESCRIPTOR <0ffffh,,,ATDW,>
DATAD_SEL= DATAD - GDT ;目标数据段描述符的选择子
GDTLEN = $ - GDT
;
VGDTR PDESC<GDTLEN-1,> ;伪描述符
;
BUFFERLEN = 256 ;缓冲区字节长度
BUFFER db BUFFERLEN dup(0) ;缓冲区
dseg ends
;--------------------------------------------------------
;代码段
cseg segment use16 ;16位段
assume cs:cseg, ds:dseg
start:
mov ax,dseg
mov ds,ax
;准备要加载到GDTR的伪描述符
mov bx,16
mul bx ;计算并设置GDT基地址
add ax,offset GDT ;界限已在定义时设置妥当
adc dx,0
mov word ptr VGDTR.BASE,ax
mov word ptr VGDTR.BASE+2,dx
;设置代码段描述符
mov ax,cs
mul bx
mov CODE.BASEL,ax ;代码段开始偏移为0
mov CODE.BASEM,dl ;代码段界限已在定义时设置妥当
mov CODE.BASEH,dh
;设置目标数据段描述符
mov ax,ds
mul bx ;计算并设置目标数据段基地址
add ax,offset BUFFER
adc dx,0
mov DATAD.BASEL,ax
mov DATAD.BASEM,dl
mov DATAD.BASEH,dh
;加载GDTR
LGDT qword ptr VGDTR
;
cli ;关中断
call ENABLEA20 ;打开地址线A20
;切换到保护方式
mov eax,cr0
or eax,1
mov cr0,eax
;清指令预取队列,并真正进入保护方式
JUMP <CODE_SEL>,<offset VIRTUAL>
;
VIRTUAL:;现在开始在保护方式下
mov ax,DATAS_SEL
mov ds,ax ;加载源数据段描述符
mov ax,DATAD_SEL
mov es,ax ;加载目标数据段描述符
cld
xor si,si ;设置指针初值
xor di,si
mov cx,BUFFERLEN/4 ;设置4字节为单位的缓冲区长度
repz movsd ;传送
;切换回实方式
mov eax,cr0
and eax,0fffffffeh
mov cr0,eax
;清指令预取队列,进入实方式
JUMP <seg REAL>,<offset REAL>
;
REAL: ;现在又回到实方式
call DISABLEA20 ;关闭地址线A20
sti ;开中断
;
mov ax,dseg ;重置数据段寄存器
mov ds,ax
mov si,offset BUFFER
cld ;显示缓冲区内容
mov bp,BUFFERLEN/16
NEXTLINE:
mov cx,16
NEXTCH:
lodsb
push ax
shr al,4
call TOASCII
ECHOCH al
pop ax
call TOASCII
ECHOCH al
ECHOCH ''
loop NEXTCH
ECHOCH 0dh
ECHOCH 0ah
dec bp
jnz NEXTLINE
;
mov ax,4c00h ;结束
int 21h
TOASCII proc
;把al低4位的十六进制数转换成对应的ASCII,保存在al
TOASCII endp
;
EA20 proc
;打开地址线A20,见下面的说明
EA20 endp
;
DA20 proc
;关闭地址线A20,见下面的说明
DA20 endp
cseg ends
end start
大熊猫侯佩 2011-09-04
  • 打赏
  • 举报
回复
保护模式下实模式寄存器的内容做了升级
canmeng 2011-09-04
  • 打赏
  • 举报
回复
执行mov cr0,eax命令后,进入保护模式,此时cs称为段选择子,从cs指向的段描述符中所取到的段基址再加上eip中的段偏移,正好是jmp所在的内存地址。
liaozhicai 2011-09-04
  • 打赏
  • 举报
回复
还有一个问题:
GDT的各个描述符表项中间可以插一些短结构吗?如下面这段代码,DUMMY、CODE是两个完整的表项,接着是两个16位的变量,再接着又是DATAS一个完整的表项。中间两个16位的变量CODE_SEL、GDTLEN共32位,不是8个字节,之后的DATAS表项就无法8字节对齐了,STRUCT有字节对齐吗?8字节对齐?


;数据段
dseg segment use16 ;16位段
GDT label,byte ;全局描述表GDT
DUMMY DESCRIPTOR <> ;空描述符
CODE DESCRIPTOR <0ffffh,,,ATCE,>
CODE_SEL = CODE - GDT ;代码段描述符的选择子
GDTLEN = $ - GDT
DATAS DESCRIPTOR <0ffffh,0h,11h,ATDW,0>
DATAS_SEL=DATAS - GDT ;源数据段描述符的选择子
DATAD DESCRIPTOR <0ffffh,,,ATDW,>
DATAD_SEL= DATAD - GDT ;目标数据段描述符的选择子
GDTLEN = $ - GDT
;
VGDTR PDESC<GDTLEN-1,> ;伪描述符
;
BUFFERLEN = 256 ;缓冲区字节长度
BUFFER db BUFFERLEN dup(0) ;缓冲区
dseg ends
WJN92 2011-09-04
  • 打赏
  • 举报
回复
[Quote=引用 7 楼 wjn92 的回复:]

我们的也有不可见的部分,也就是说
这个不可见的部分就是控制着CPU的操作

你可以查一下 16位使用32位寻址

或你也可以自己做个试验,进入了32位或,不更改寄存器,看看能不能在32位使用16位寻址(这个操作我已经试过的了,你也可以自己试试,呵呵)
[/Quote]

我们的寄存器也有不可见的部分,也就是说

打漏了几个字...
WJN92 2011-09-04
  • 打赏
  • 举报
回复
我们的也有不可见的部分,也就是说
这个不可见的部分就是控制着CPU的操作

你可以查一下 16位使用32位寻址

或你也可以自己做个试验,进入了32位或,不更改寄存器,看看能不能在32位使用16位寻址(这个操作我已经试过的了,你也可以自己试试,呵呵)
canmeng 2011-09-04
  • 打赏
  • 举报
回复
我第一次回答问题时有些想当然了,没考虑到这个高速缓冲寄存器,惭愧……
canmeng 2011-09-04
  • 打赏
  • 举报
回复
楼主说;
执行mov cr0,eax之后,虽然处于保护模式了,但cs寄存器没有更新,高速缓冲寄存器的内容也没有更新,从高速缓冲寄存器取的值与实模式下用cs*16取的值是一样的,这样的话,还是指向jmp命令。

不错,实模式下利用了这个高速缓冲寄存器。因为高速缓冲寄存器中保存代码段的段基址,如果cs不改变,那么高速缓冲寄存器中的代码段的段基址就不会变化。cpu每次取数据都从高速缓冲寄存器中取段基址,然后加上eip的内容,作为要执行的代码所在的内存地址,这样代码执行的效率会很高。楼主的上面的那句话说的很是简洁明了,学习了。

21,459

社区成员

发帖
与我相关
我的任务
社区描述
汇编语言(Assembly Language)是任何一种用于电子计算机、微处理器、微控制器或其他可编程器件的低级语言,亦称为符号语言。
社区管理员
  • 汇编语言
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧