【我所認知的BIOS】—>The Big Real ModeLightSeed 2009-6-23前面两篇,我们对实模式和保护模式做了比较详细地探讨,说了那么多其实都是为这篇文章服务的。因为在BIOS POST的过程中经常要用到1M以上的内存。但是BIOS本身的code却又都是在实模式中运行的。当然我们可以进入保护模式去访问内存,然后再切会实模式。如此反复我们是可以做到的,但是这样必定是比较麻烦的。有没有一种模式,我们在实模式下就可以直接访问到4G的内存呢?答案是肯定的。它的名字就叫做“Big Real Mode”。1、Big Real Mode原理在上一篇文章的far jmp说明段中,我详细说明了一下CPU取段寄存器的值(在保护模式下应该叫做取段选择子的值),然后会改变相应的hidden部分(高速缓冲)。倘若再有程序显示地修改某个段寄存器(段选择子),那么也随之会更新相应的hidden部分。反之,CPU只会从高速缓冲中去取段值(段基地址)。所以当我们在保护模式下加载了FS和GS两个段选择子后,只要不再显示修改FS和GS,那么返回实模式后用FS:esi的形式就访问到1M以上的内存了。其实原理还是用FS已经在保护模式下就加载好了的hidden部分(FS的段基地址)+esi=线性地址来访问的。2、程序实例;-------------这段代码从<80X86汇编语言程序设计教程>修改而来-------;-------------只是一个比较简单的big real mode设置程序-------------;-------------原理还是进入保护模式显示修改FS和GS返回实模式--------;-------------切忌,本程序编译连接后生成的exe文件在纯DOS下--------;-------------才能够执行LGDT这个命令,才能顺利进入保护模式-------- ;-------------宏定义区域开始--------------------------------------;16位偏移的段间直接转移指令的宏定义JUMPmacro selector,offsetv db 0eah ;操作码jmp dw offsetv ;16位偏移 dw selector ;段值(real mode下)或者选择子(protect mode下) endm ;字符显示宏指令的定义ECHOCHmacro ascii mov ah, 2 ;选功能号 mov dl, ascii ;填将要显示的ASCII码给DL int 21h ;调用DOS中断来显示ASCII码 endm;-------------宏定义区域结束------------------------------------- ;-------------结构体定义区域开始--------------------------------- ;存储段描述符结构类型的定义 DESCRIPTOR struc Limitl dw0 ;段界限(0~15) Basel dw0 ;段基地址(0~15) Basem db0 ;段基地址(16~23) Attributesdw0 ;段属性 Baseh db0 ;段基地址(24~31)DESCRIPTOR ENDS ;伪描述符结果类型的定义PDESC struc Limit dw0 ;16界限 Base dd0 ;基地址PDESC ENDS;-------------结构体定义区域结束--------------------------------- ;常量定义 ATDW= 92H ;存在的可读写数据段属性值 ATCE = 98H ;存在的只执行代码段属性值 AT4G = 0CF92H ; .386P ;--------------实模式下数据段定义开始----------------------------dseg segment use16 align 16 ;16位段 GDT label byte ;全局描述符表GDT标志 DUMMY DESCRIPTOR ??> ;空描述符 Seg4GSelector DESCRIPTOR <0FFFFH,0h,0h,AT4G,0>;FS&GS;的描述符 Seg4G_sel = Seg4GSelector - GDT GDTLEN = $ - GDT ;全局描述符表长度 ; VGDTR PDESC ;①伪描述符 ; 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 ;乘数为16,是为了在实模式中计算地址 mul bx ;计算并设置GDT基地址 add ax,offset GDT ;此时AX中为GDT在实模式中的地址,界限在已定义时设置妥当 adc dx,0 ;如果有进位那么ADC加上 mov word ptr VGDTR.Base,ax ;填入GDT的实际地址的低word到伪描述符结构体中 mov word ptr VGDTR.Base+2,dx ;填入GDT的实际地址的高word到伪描述符结构体中 ;加载GDTR DB 66H ; execute a 32 bit LGDT LGDT VGDTR ;命令不熟悉的话去查查 cli ;关中断 call EnableA20 ;打开地址线A20 ;切换到保护模式 mov eax,cr0 or eax,1 mov cr0,eax ;清指令预取队列,并真正进入保护方式 jmp VIRTUAL ;整个过程没有改变CS的值,那么存在于hidden部分的值也没有被改变 ;CPU取指令时不管在实模式还是保护模式下都取到的是实模式下的指令 ;这个jmp在这里只是起个形式而已,实质上没有做任何动作 VIRTUAL: ;--------设置可以访问4G的段选择子------- mov ax,Seg4G_sel ;准备要加载的段选择子(for 4G) mov fs,ax ;显示修改fs,使得fs在保护模式下重新加载 mov gs,ax ;--------设置完成,只要不修改fs和gs的值,那么返回real mode后,仍然可以用fs和gs来访问1M以上4G以下的内存 ;切回到实方式 mov eax,cr0 and eax,0fffffffeh mov cr0,eax ;清指令预取队列,进入实方式 jmp REAL ;整个过程没有改变CS的值,那么存在于hidden部分的值也没有被改变 ;CPU取指令时不管在实模式还是保护模式下都取到的是实模式下的指令 ;这个jmp在这里只是起个形式而已,实质上没有做任何动作 REAL: ;现在又回到实方式 sti ;开中断 mov esi,208each ;源数据pointer mov di,offset BUFFER cld ;显示缓冲区内容 mov bp,BUFFERLEN/16 ;bp作为显示的行数的计数器 Nextline: mov cx,16 ;每行只显示16*2个字符 NextCH: mov al,fs:[esi] incesi mov ds:[di],al incdi ;存到buffer中去 push ax ;保存ax shral,4 ;准备显示高4bit中的值 call ToASCII ;把al中的值转换成ASCII ECHOCH al ;显示之 pop ax ;回复ax call ToASCII ECHOCH al ECHOCH ' ' ;在字符于字符之间显示空格 loop NextCH ;处理下一个字符 ECHOCH 0dh ECHOCH 0ah ;显示这两个ASCII回车+换行 dec bp ;显示完了否? jnzNextline ;bp = 0显示完了 mov ax,4c00h ;结束 int21h;---------------实模式下代码段定义结束-------------------------- ;---------------子程式定义开始---------------------------------- ;***************************************************************;子程序名:HtoASC;功 能:十六进制数转换成 ASCII;入口参数:al=8位二进制数;出口参数:无;说 明:无;***************************************************************toASCII proc and al,0fh ;屏蔽al的高4 bits cmp al,9 ;compare 9 jbe toASCII1 ;小于9,直接+30H add al,37h ;否则,al+37H rettoASCII1: add al,30h ret toASCII endp ;***************************************************************;打开a20地址线;***************************************************************EnableA20 proc push ax in al,92h or al,00000010b out92h,al popax ret EnableA20 endp;***************************************************************;关闭a20地址线;***************************************************************DisableA20 proc push ax in al,92h andal,11111101b out92h,al popax retDisableA20 endp;---------------子程式定义结束---------------------------------- cseg ends;---------------实模式下代码段定义结束-------------------------- end start ;指明程序入口 对于这个程序,我要说三点。①这个程序其实我是把上一章的那个实例修改了一下,就成了可以用FS和GS两个段寄存器(其实准确地说应该叫做段选择子)来访问高于1M的内存了。但是我做了很多的简化。比如说我没有在进入实模式前加载CS,只单单加载了GDT(因为GDT是保护模式下必须要用的)。然而,不改变CS是因为我们在保护模式下也还想继续执行实模式下的那三句指令“mov ax,Seg4G_selmov fs,ax mov gs,ax”所以我只用了一个段跳跃(只是形式上看上去像是一个实际进入实模式的过程。其实连jmp这句都是可以省略的。留在这里只是为了让大家容易理解而已。)②大家肯定都会发现,当我返回实模式的时候没有关A20。这是因为我要在实模式下通过FS和GS去访问1M以上的空间,所以我必须要让A20一直都处于打开状态。③于是我们也可以很清楚地看到,看到big real mode其实是介于实模式和保护模式之间的一种模式。如图1 图1实模式,big real mode,保护模式的比较 至此关于实模式和保护模式的切换,以及big real mode的探讨就暂时告一段落了。如果有兴趣的话,还可以把上面的程序精简。(笔者:比如说去掉其中的量个短jmp,因为他们在程序没有任何的存在的意义,笔者把他们注掉后编译连接仍然是可以访问到4G内存的。)好像csdn论坛里面就有人摆擂台用最短的语句来实现big real mode。不过如果有人和我一章懒的话,其实直接用上一章实例加上一个描述符,再在保护模式下加上两句话就可是实现big real mode了。???>