原文:http://blog.csdn.net/sunacmer/article/details/5310280
弄了几天了,终于弄好了,呵呵,也得到了不少东西。从保护模式到是模式的转换一定要注意的有(转):
1、程序开始时在实模式下要有自己的堆栈段,进入保护模式前先暂存ss及sp的值至某内存处,以便从保护模式返回实模式后恢复到原先的堆栈。
2、返回实模式前需把各段寄存器设置为规范段,包括SS也要设置
3、返回实模式前必须在16位段返回,不能在32位段里返回实模式。
4、返回实模式前的段必须定义在GDT中。
5、在32位段下操作有关寄存器时,注意32位寄存器和16位寄存器的差别,类似于SP寄存器,应用使用ESP寄存器。
6、在使用定义在LDT中的段时,一定要使用LLDT先加载LDT缓冲寄存器(LDTR)。
7、用于返回实模式的代码段的段界限必须为0FFFFH,不能为实际长度,否则要么在跳转到实模式的时候出错要么在跳转到实模式后执行int
21H出错。(个人推测原因如下:80286开始为每个段寄存器增加了段描述符高速缓冲寄存器,而这些缓冲寄存器对于程序员是不可见的,且在实模式下是不
能修改的,要想改变这些高速缓冲寄存器的值必须通过在保护模式下修改相应的段寄存器的选择子来实现,这也是为什么在返回实模式之前必须把DS、ES、
FS、GS、SS设置为规范段的原因。但是CS是一个特殊的段,不能通过常规方式修改,只能通过段间跳转修改,而一旦跳到实模式后又不能修改了,所以就要
求在返回实模式的段的段描述符必须符合实模式下的要求。实模式下的段长度是0FFFFH,也就是64K,如果用于返回实模式的段的段界限不是
0FFFFH,会导致返回实模式后实模式下的CS段的高速缓冲寄存器的段长度还是保护模式时的段界限值,这是不正确的,所以要求用于返回实模式的段的段界
限必须是0FFFFH。)
ps:所谓规范段是指实模式下的标准段属性,一般段界限为0FFFFH,段属性为可读写。
自己的认识是从保护模式到实模式转换的过程为从某个保护模式下的函数中跳到16位下,在16位模式下要做的工作是设置各个段寄存器和设置CR0,
(此处需要特别特别注意的是段寄存器中的选择子必须是要定义在GDT中的,而且其段界限必须为0xffff,否则仍然可以跳回实模式,但是会有段界限异
常),然后再跳到最开始引导的段中关A20和开中断(sti),之所以还要跳回最开始的引导段,是因为需要用jmp设置cs。
1.f和g段寄存器可以不设为Normal
2.Normal段界限必须为0xffff 不然仍可以返回,但会有段界限异常
3.code16所在的段界限可以不为0xffff
4.某个函数->16位关CR0&&设置段寄存器->跳到最开始引导的段关A20、开中断(之所以跳到开始引导的段是因为
需要用jmp语句设置cs为段0)
5.代码中将16位的代码放在了文件的最后,原来是放在DISPLAY前的,是因为碰到了如下奇怪的情况在生成bin文件后,jmp指令和下面的
mov
ax,VIDEOSELECTOR指令混淆了,CPU执行时仍然是按照32位的指令执行的,所以将jmp解释为32位的,而编译的文件中却是16位的,因
此会误将mov的机器指令当做jmp指令中的操作数。
6.如5中所说,在源文件中将代码声明为16位([BITS 16])后,生成的机器码为16位的,而在CPU执行时却仍按32位指令执行,暂时还不知道是怎么回事,有时间再看看。
代码中需要注意的是在引导段中的这一句
mov [RETURN_JMP+3],ax
这一句的作用是将jmp指令中的段字节设为0。在16位模式下,段间jmp指令的格式为
offset segment
ea byte1 byte2 byte3 byte4
在32位模式下段间jmp指令的格式为
0eah offset segment
byte1 byte2 byte3 byte4 byte5 byte6
- org 0x7c00
- jmp MAIN
- ;数据结构
- GDT:
- DEFAULT_SEG:
- dw 0,0,0,0
- CODE32_SEG:
- dw 0x07ff
- dw 0x0000
- dw 0x9a00
- dw 0x00cf
- DATA_SEG:
- dw 0x07ff
- dw 0x0000
- dw 0x9200
- dw 0x00cf
- TEST_DATA_SEG:
- dw 0x07ff
- dw 0x0000
- dw 0x9250;三字节的基址
- dw 0x00cf
- VIDEO_SEG:;0x20
- dw 0x07ff
- dw 0x8000
- dw 0x920B
- dw 0x00cf
- REAL_SEG:;0x28
- dw 0xffff
- dw 0x0000
- dw 0x9200
- dw 0x00cf
- CODE16_SEG:
- dw 0x07ff
- dw 0x0000
- dw 0x9a00
- dw 0x00cf
- CALL_GATE_SEG:
- dw 0x0000
- dw CODE_SELECTOR
- dw 0x8c00
- dw 0x0000
- GDT_END:
- GDTR:
- dw GDT_END-GDT-1
- dw GDT,0
- DEFAULT_SELECTOR equ DEFAULT_SEG-GDT
- CODE_SELECTOR equ CODE32_SEG-GDT
- DATA_SELECTOR equ DATA_SEG-GDT
- TEST_DATA_SELECTOR equ TEST_DATA_SEG-GDT
- VIDEO_SELECTOR equ VIDEO_SEG-GDT
- REAL_SEG_SELECTOR equ REAL_SEG-GDT
- CODE16_SELECTOR equ CODE16_SEG-GDT
- CALL_GATE equ CALL_GATE_SEG-GDT
- [BITS 16]
- MAIN:
- mov ax,cs
- mov ds,ax
- mov gs,ax
- mov [RETURN_JMP+3],ax
- ;init code32
- xor eax,eax
- mov ax,cs
- shl eax,4
- add eax,DISPLAY
- mov word [CODE32_SEG+2],ax
- shr eax,16
- mov byte [CODE32_SEG+4],al
- mov byte [CODE32_SEG+7],ah
- ;init code16
- xor eax,eax
- mov ax,cs
- shl eax,4
- add eax,BACK_TO_REAL
- mov word [CODE16_SEG+2],ax
- shr eax,16
- mov byte [CODE16_SEG+4],al
- mov byte [CODE16_SEG+7],ah
- lgdt [GDTR]
- cli
- ;a20
- in al,0x92
- or al,00000010b
- out 0x92,al
- ;cr0
- mov eax,cr0
- or eax,00000001b
- mov cr0,eax
- call CALL_GATE:0
- REAL_ENTRY: ;00007cb8
- mov ax,cs
- mov ds,ax
- mov es,ax
- mov ss,ax
- ;关A20
- ;in al,0x92
- ;and al,11111101b
- ;out 0x92,al
- ;开中断
- sti
- jmp $
- [BITS 32]
- DISPLAY:
- mov ax,VIDEO_SELECTOR
- mov gs,ax
- mov ah,0x0c
- mov al,'P'
- mov edi,(80*0+0)*2
- mov [gs:edi],ax
- ;jmp $
- jmp CODE16_SELECTOR:0
- ;放在CODE16_SEG中
- ;还在CODE16中,CS=0x0030
- ;ALIGN 32
- [BITS 16] ;指令是16位的,但CPU在32位模式下,取指时按32位取
- BACK_TO_REAL:
- mov ax,REAL_SEG_SELECTOR ;mov eax,0xd88e0028
- mov ds,ax
- mov ds,ax
- mov es,ax
- ;mov fs,ax
- mov gs,ax
- mov ss,ax
- mov eax,cr0
- and al,11111110b;!!!!!!!!! 段界限异常
- mov cr0,eax
- RETURN_JMP:
- jmp 0:REAL_ENTRY;jmp far b866:00007cb8
- times 510-($-$) db 0
- dw 0xaa55
阅读(1016) | 评论(0) | 转发(0) |