+ -
当前位置:首页 → 问答吧 → 从实模式到保护模式的跳转指令是怎么取到的?

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

时间:2011-09-04

来源:互联网

好多类似的代码:  
 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命令?

作者: liaozhicai   发布时间: 2011-09-04

执行mov cr0,eax命令后,进入保护模式,此时cs称为段选择子,从cs指向的段描述符中所取到的段基址再加上eip中的段偏移,正好是jmp所在的内存地址。

作者: haojiahuo50401   发布时间: 2011-09-04

保护模式下实模式寄存器的内容做了升级

作者: mydo   发布时间: 2011-09-04

引用 1 楼 haojiahuo50401 的回复:
执行mov cr0,eax命令后,进入保护模式,此时cs称为段选择子,从cs指向的段描述符中所取到的段基址再加上eip中的段偏移,正好是jmp所在的内存地址。


也就是说执行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

作者: liaozhicai   发布时间: 2011-09-04

引用 2 楼 mydo 的回复:
保护模式下实模式寄存器的内容做了升级


能更详细一点吗?谢谢。

是不是这样:
保护模式下,计算线性地址,实际取用的是cs的描述符高速缓冲寄存器的基址与eip的和,那么实模式下是不是也利用了这个高速缓冲寄存器,或者说这个高速缓冲寄存器的基址部分,在实模式下置为cs*16,这样的话,执行mov cr0,eax之后,虽然处于保护模式了,但cs寄存器没有更新,高速缓冲寄存器的内容也没有更新,从高速缓冲寄存器取的值与实模式下用cs*16取的值是一样的,这样的话,还是指向jmp命令。

作者: liaozhicai   发布时间: 2011-09-04

楼主说;
执行mov cr0,eax之后,虽然处于保护模式了,但cs寄存器没有更新,高速缓冲寄存器的内容也没有更新,从高速缓冲寄存器取的值与实模式下用cs*16取的值是一样的,这样的话,还是指向jmp命令。

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

作者: haojiahuo50401   发布时间: 2011-09-04

我第一次回答问题时有些想当然了,没考虑到这个高速缓冲寄存器,惭愧……

作者: haojiahuo50401   发布时间: 2011-09-04

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

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

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

作者: WJN92   发布时间: 2011-09-04

引用 7 楼 wjn92 的回复:

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

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

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


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

打漏了几个字...

作者: WJN92   发布时间: 2011-09-04