【我所認知的BIOS】—The Big Real Mode

news/2024/11/16 18:38:21/

【我所認知的BIOS—>The Big Real Mode

LightSeed     

2009-6-23

前面两篇,我们对实模式和保护模式做了比较详细地探讨,说了那么多其实都是为这篇文章服务的。因为在BIOS POST的过程中经常要用到1M以上的内存。但是BIOS本身的code却又都是在实模式中运行的。当然我们可以进入保护模式去访问内存,然后再切会实模式。如此反复我们是可以做到的,但是这样必定是比较麻烦的。有没有一种模式,我们在实模式下就可以直接访问到4G的内存呢?答案是肯定的。它的名字就叫做“Big Real Mode”。

1Big Real Mode原理

在上一篇文章的far jmp说明段中,我详细说明了一下CPU取段寄存器的值(在保护模式下应该叫做取段选择子的值),然后会改变相应的hidden部分(高速缓冲)。倘若再有程序显示地修改某个段寄存器(段选择子),那么也随之会更新相应的hidden部分。反之,CPU只会从高速缓冲中去取段值(段基地址)。所以当我们在保护模式下加载了FSGS两个段选择子后,只要不再显示修改FSGS,那么返回实模式后用FSesi的形式就访问到1M以上的内存了。其实原理还是用FS已经在保护模式下就加载好了的hidden部分(FS的段基地址)+esi=线性地址来访问的。

2、程序实例

;-------------这段代码从<80X86汇编语言程序设计教程>修改而来-------

;-------------只是一个比较简单的big real mode设置程序-------------

;-------------原理还是进入保护模式显示修改FSGS返回实模式--------

;-------------切忌,本程序编译连接后生成的exe文件在纯DOS--------

;-------------才能够执行LGDT这个命令,才能顺利进入保护模式--------

 

;-------------宏定义区域开始--------------------------------------

;16位偏移的段间直接转移指令的宏定义

JUMP macro selector,offsetv

   db 0eah                 ;操作码 jmp

   dw offsetv              ;16位偏移

   dw selector            ;段值(real mode)或者选择子(protect mode)

   endm

 

 

;字符显示宏指令的定义

ECHOCH macro ascii

   mov ah, 2          ;选功能号

   mov dl, ascii           ;填将要显示的ASCII码给DL

   int 21h                  ;调用DOS中断来显示ASCII

   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         ;存在的只执行代码段属性值

   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 <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                ;乘数为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

   ;--------设置完成,只要不修改fsgs的值,那么返回real mode后,仍然可以用fsgs来访问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]

   inc esi

   mov    ds:[di],al

   inc di                ;存到buffer中去

   push   ax                ;保存ax

   shr al,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               ;显示完了否?

   jnz Nextline            ;bp = 0显示完了

 

   mov    ax,4c00h           ;结束

   int 21h

;---------------实模式下代码段定义结束--------------------------

 

;---------------子程式定义开始----------------------------------

 

;***************************************************************

;子程序名 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

   ret

toASCII1:

   add    al,30h

   ret

     

toASCII       endp

 

;***************************************************************

;打开a20地址线

;***************************************************************

EnableA20       proc

                push    ax

                in   al,92h

                or   al,00000010b

                out 92h,al

                pop ax

                ret

 EnableA20      endp

;***************************************************************

;关闭a20地址线

;***************************************************************

DisableA20      proc

                push    ax

                in   al,92h

                and al,11111101b

                out 92h,al

                pop ax

                ret

DisableA20      endp

;---------------子程式定义结束----------------------------------

 

cseg ends

;---------------实模式下代码段定义结束--------------------------

 

   end start                ;指明程序入口

 

对于这个程序,我要说三点。

①这个程序其实我是把上一章的那个实例修改了一下,就成了可以用FSGS两个段寄存器(其实准确地说应该叫做段选择子)来访问高于1M的内存了。但是我做了很多的简化。比如说我没有在进入实模式前加载CS,只单单加载了GDT(因为GDT是保护模式下必须要用的)。然而,不改变CS是因为我们在保护模式下也还想继续执行实模式下的那三句指令

mov    ax,Seg4G_sel

mov   fs,ax

    mov   gs,ax

所以我只用了一个段跳跃(只是形式上看上去像是一个实际进入实模式的过程。其实连jmp这句都是可以省略的。留在这里只是为了让大家容易理解而已。)

②大家肯定都会发现,当我返回实模式的时候没有关A20。这是因为我要在实模式下通过FSGS去访问1M以上的空间,所以我必须要让A20一直都处于打开状态。

③于是我们也可以很清楚地看到,看到big real mode其实是介于实模式和保护模式之间的一种模式。如图1

 

1 实模式,big real mode,保护模式的比较

 

至此关于实模式和保护模式的切换,以及big real mode的探讨就暂时告一段落了。如果有兴趣的话,还可以把上面的程序精简。(笔者:比如说去掉其中的量个短jmp,因为他们在程序没有任何的存在的意义,笔者把他们注掉后编译连接仍然是可以访问到4G内存的。)好像csdn论坛里面就有人摆擂台用最短的语句来实现big real mode。不过如果有人和我一章懒的话,其实直接用上一章实例加上一个描述符,再在保护模式下加上两句话就可是实现big real mode了。


http://www.ppmy.cn/news/195395.html

相关文章

关于IBM 3550 3650 3850 在BIOS设置legacy模式

1、在Boot Manager中&#xff0c;选择add Boot Option&#xff0c;选择legacy only 2、在Go to Boot Manager > Change Boot Order中&#xff0c;把legacy only选项添加到第一位。 3、commit changes &#xff0c;保存

小白的proxmox ve(pve)打造AIO(all in boom)折腾日记 (四)硬件(显卡、硬盘、网卡)直通

硬件直通就是就是将自己主机的部分硬件直通到其中某一个虚拟机&#xff0c;其他虚拟机不能使用&#xff0c;这样可以有效的提升硬件使用性能。需要做的不算多&#xff0c;但是想要做好还是要下一定功夫&#xff0c;当时我也弄了很久&#xff0c;收藏夹里有很多文章&#xff0c;…

进入BIOS模式 史上最全

文章目录 一、在开机的瞬间反复按快捷键二、查看电脑是否有后门三、在电脑正常运行的情况下进入BIOS模式 三种方法&#xff1a;1、首先电脑关机–>在开机的瞬间反复按快捷键&#xff1b;2、查看电脑是否有后门&#xff08;电脑旁边的一个小孔&#xff09;&#xff1b;3、在电…

Leetcode 1037. 有效的回旋镖

给定一个数组 points ,其中 points[i] = [xi, yi] 表示 X-Y 平面上的一个点,如果这些点构成一个 回旋镖 则返回 true 。 回旋镖 定义为一组三个点,这些点 各不相同 且 不在一条直线上 。 示例 1: 输入:points = [[1,1],[2,3],[3,2]] 输出:true示例 2: 输入:points …

郑轻oj1037

#include<stdio.h> #include<string.h> #include<math.h> //给你一个简单的四则运算表达式&#xff0c;包含两个实数和一个运算符&#xff0c;请编程计算出结果 //表达式的格式为&#xff1a;s1 op s2&#xff0c; s1和s2是两个实数&#xff0c;op表示的是运…

蓝桥杯1037

求N的阶乘末尾存在多少个0。 输入格式&#xff1a; 输入存在多组测试数据&#xff0c;对于每组测试数据输入一个整数N(0<N<10^9) 输出格式&#xff1a; 对于每组测试数据&#xff0c;输出一行表示答案。 输入样式&#xff1a; 12 20 输出样式&#xff1a; 2 4 #include…

PAT甲级 1037(C++)

一开始设想&#xff0c;在排序后对coupon从两头逐渐向中间访问&#xff0c;以保证获益最大&#xff0c;后来发现这种想法有bug&#xff0c;原因如下&#xff1a; 若输入 5 -5 -4 -3 -2 -1 5 -5 1 2 3 4 则输出5&#xff0c;而不是25。 错误代码如下&#xff1a; #include&l…

1037: 四则运算 C语言

1037: 四则运算 时间限制: 1 Sec 内存限制: 30 MB 提交: 77176 解决: 23132 [状态] [讨论版] [提交] [命题人:admin] 题目描述 给你一个简单的四则运算表达式&#xff0c;包含两个实数和一个运算符&#xff0c;请编程计算出结果 输入 表达式的格式为&#xff1a;s1 op s2&#…