您的位置:首页 > 其它

操作系统实验三:实模式与保护模式间自由跳转

2010-01-24 02:54 288 查看
此次实验,是在 操作系统实验二:从实模式跳转到保护模式 基础上的进一步实验。实验实现的内容很简单,一开始进入实模式,然后跳转到保护模式,然后再跳转回实模式,然后再跳转到保护模式,如此不断循环。



从实模式调转到保护模式步骤:

1.准备好GDT

2.使用lgdt指令加载gdtr

3.打开地址线A20

4.设置cr0的PE位为1

5.使用cli指令关闭中断

6.根据GDT选择子跳转到32位代码段,此时进入保护模式





从保护模式转到实模式调步骤:

1.跳转到16位代码段

2.加载适合的GDT选择子到有关段寄存器

3.设置cr0的PE位为0

4.跳转到16位代码段,此时进入实模式

5.关闭地址线A20

6.使用sti指令打开中断



实验流程简要说明:

(pm16.c部分)

1.清屏

2.跳到第9步

3.加载适合的描述符选择子到有关段寄存器

4.设置cr0的PE位为0

5.一个跳转,真正回到实模式

6.关闭 A20 地址线

7.开中断

8.调用延时函数

9.显示字符串This is real model!

10.设置下一次显示字符串的位置

11.加载 GDTR

12.关中断

13.打开地址线A20

14.置cr0的PE位为1

15.跳转到32位代码段,进入保护模式

(pm32.c部分)

16.调用延时函数

17.显示字符串I am now in protect model^_^

18.保存最后的光标位置

19.跳转到(pm16.c)第3步



实验代码如下:

code:run.c(请参考 操作系统实验二:从实模式跳转到保护模式 中的run.c代码)



code:pm.h

]//文件:pm.h
//功能:pm16.c与pm32.c的公共头文件
//运行:run.exe自动会编译pm16.c与pm32.c然后生成img并调用Bochs运行此程序
//提示:请先用yc09编译run.c文件,生成run.exe程序  
//      之后修改pm16.c与pm32.c中代码,可直接运行run.exe查看效果 
//作者:miao
//时间:2010-1-24
//定义GDT属性
#define  DA_32         0x4000 //32位段
#define  DA_DRW        0x92   //存在的可读写数据段属性值
#define  DA_CR         0x9A   //存在的可执行可读代码段属性值
//GDT 选择子,根据pm16.c中的GDT界限设置偏移量值
#define SelectorNormal 8*1 //返回实模式需要用到的选择子
#define SelectorSode32 8*2 //指向32位段处
#define SelectorVideo  8*3 //指向显存首地址
#define SelectorSode16 8*4 //指向16位代码段
#define SelectorData32 8*5 //指向32位段处
typedef	unsigned int   t_32;  //4字节
typedef	unsigned short t_16;  //2字节
typedef	unsigned char  t_8;   //1字节
typedef	int            t_bool;//4字节
typedef unsigned int   t_port;//4字节
//存储段描述符/系统段描述符
struct DESCRIPTOR         //共 8 个字节
{
  t_16  limit_low;        //Limit 2字节
  t_16  base_low;         //Base  2字节
  t_8   base_mid;         //Base  1字节
  t_8   attr1;            //P(1) DPL(2) DT(1) TYPE(4)  1字节
  t_8   limit_high_attr2; //G(1) D(1) 0(1) ***L(1) LimitHigh(4) 1字节
  t_8   base_high;        //Base  1字节
};
#define Descriptor(bas,len,attr) { (len) & 0xffff, /
               (bas) & 0xffff, /
               ((bas)>>16)&0xff, /
               (attr) & 0xff, /
               (((attr)>>8) &0xf0) + (((len)>>16) & 0x0f), /
               ((bas) >> 24) & 0xff } /




code:pm16.c

]//文件:pm16.c
//功能:设置GDT,在GDT中初始化32位代码段,然后切换到保护模式,跳转到32位代码段
//      当从32位代码段保护模式跳转回来后,切换到实模式,显示字符串,再切换到保护模式
//说明:16位部分引导程序放在镜像引导扇区的前半部分,0~255字节中,程序大小不能超过这个限制
//运行:run.exe自动会编译pm16.c与pm32.c然后生成img并调用Bochs运行此程序
//提示:请先用yc09编译run.c文件,生成run.exe程序  
//      之后修改pm16.c与pm32.c中代码,可直接运行run.exe查看效果
//      若在_BackToRealModel标签前添加或删除代码,会导致保护模式无法正确跳转回来,
//      此时需要反汇编pm16.c的代码,查找到_BackToRealModel所在地址,修改pm32.c中的跳转值
//作者:miao
//时间:2010-1-24

#define YCBIT 16     //告诉编译器,以16位格式编译程序
#define YCORG 0x7c00 //告诉编译器,在7c00处加载程序

#include "pm.h"

//GDT界限
DESCRIPTOR label_gdt[] = 
{
  //         段基址   段界限   属性
  Descriptor(0,       0,       0),
  Descriptor(0,       0xfffff, DA_DRW),         //Normal 描述符,返回实模式前要加载的
  Descriptor(0x7d00,  0xfffff, DA_CR | DA_32),  //32位代码段(pm32.c),可执行可读
  Descriptor(0xb8000, 0xffff,  DA_DRW),         //显存地址段,可读可写
  Descriptor(0x7c00,  0xfffff, DA_CR),          //16位代码段(pm16.c),可执行可读
  Descriptor(0x7d00,  0xfffff, DA_DRW | DA_32), //令32位代码段(pm32.c)的变量可以读写
};
#pragma pack(1)
struct GDT_PTR
{
  unsigned short size;
  void *addr;
};
#pragma pack()
GDT_PTR  GdtPtr = { sizeof(label_gdt), (char*)label_gdt}; //段界限,基地址

#define MsgLngth 24   //串长度
char  Msg[MsgLngth] = "This is real model!    ";

char   row = 0;   //行数
char   line = 0;  //列数
asm void Delay(); //延时函数

asm void main()
{
  mov   ax, cs
  mov   ds, ax
  mov   es, ax
  //清屏
  mov   ah, 06h       //屏幕初始化或上卷
  mov   aL, 00h       //AH = 6,  AL = 0h
  mov   bx, 1110h     //蓝色底色
  mov   cx, 0         //左上角: (0, 0)
  mov   dl, 4fh       //最后一列
  mov   dh, 1fh       //最后一行(右下角)
  int   10h           //显示中断
  jmp   _Start16      //跳过下面从保护模式切换到实模式的代码
//保护模式代码执行完后直接跳转到此处
_BackToRealModel:
  mov   ax, SelectorNormal  //加载适合的描述符选择子到有关段寄存器
  mov   ds, ax
  mov   es, ax
  mov   fs, ax
  mov   gs, ax
  mov   ss, ax
  //准备切换到实模式,清除cr0的PE位
  mov   eax, cr0
  and   al, 11111110b
  mov   cr0, eax
  jmp   0:_RealEntry  //这一跳,段地址被设置成正确的值,真正回到实模式
_RealEntry:
  mov   ax, cs
  mov   ds, ax
  mov   es, ax
  mov   ss, ax
  mov   sp, YCORG
  //关闭 A20 地址线
  in    al, 92h
  and   al, 11111101b
  out   92h, al
  sti                 //开中断
  call Delay          //调用延时函数
_Start16:
  mov   ax, &Msg
  mov   bp, ax        //es:bp=串地址
  mov   ah, 13h       //AH:13显示字符串
  mov   al, 1h        //AH = 13,  AL = 01h
  mov   bx, 0014h     //页号为0(BH = 0) 蓝底红字(BL = 14h)
  mov   cx, MsgLngth  //CX = 串长度
  mov   dl, line      //起始列
  mov   dh, row       //起始行
  int   10h           //显示中断
  add   line, MsgLngth  //下一次显示字符串是当前列的后MsgLngth - 3列
  cmp   line, 0x50 - MsgLngth  //判断列数是否过大
  jng   _LineNotEnd
  mov   line, 0       //列数过大,列数归零
  add   row, 1        //行数加一
  
  cmp   row, 24
  jge    _DeadLoop    //行数超过24行,跳转到一个死循环
_LineNotEnd:
  lgdt  GdtPtr        //加载 GDTR
  cli                 //关中断
  //打开地址线A20
  in    al, 92h
  or    al, 00000010b
  out   92h, al
  //准备切换到保护模式,置cr0的PE位为1
  mov   eax, cr0
  or    eax, 1
  mov   cr0, eax
  //真正进入保护模式
  jmp   dword SelectorSode32:0x0
//死循环
_DeadLoop:
  jmp _DeadLoop
}
//延时函数
asm void Delay()
{
  mov   ax, 0x0
_Loop1:
  inc   ax
  mov   cx, 0xFFFF
_Loop2:
  loop _Loop2
  cmp   ax, 0xFF
  jne   _Loop1
  ret
}




code:pm32.c

]//文件:pm16.c
//功能:保护模式下32位代码段,功能为显示一个字符串,然后跳转到16位代码段(pm16)切换到实模式
//说明:32位部分引导程序放在镜像引导扇区的后半部分,256~509字节中,程序大小不能超过这个限制
//运行:run.exe自动会编译pm16.c与pm32.c然后生成img并调用Bochs运行此程序
//提示:请先用yc09编译run.c文件,生成run.exe程序  
//      之后修改pm16.c与pm32.c中代码,可直接运行run.exe查看效果 
//作者:miao
//时间:2010-1-24

#define YCBIT 32  //告诉编译器,以32位格式编译程序
#define YCORG 0x0 //必须定义一个值

#include "pm.h"
char  Msg1[] = "I am now in protect model^_^";
int   printPlace = ((80 * 1 + 0) * 2); //目的数据偏移。屏幕第1行, 第0列。

//延时函数
asm void Delay()
{
  mov   ecx, 0xFFFFFF
_Loop2:
  loop _Loop2
  ret
}

//32 位代码段. 由实模式跳入(可执行可读)
asm void main()
{
  mov   eax, SelectorVideo
  mov   gs, ax      //视频段选择子(目的)
  mov   eax, SelectorData32 //令32位代码段的变量(printPlace)可以读写
  mov   ds, ax

  call Delay        //调用延时函数

  //下面显示一个字符串(显示已经到达保护模式信息)
  mov   ah, 14h     //蓝底红字(AL = 14h)
  mov   esi, &Msg1  //源数据偏移
  mov   edi, printPlace //根据保存的光标位置,继续显示字符

//循环逐个将字符串输出
_DispStr:
  mov   al, cs:[esi]//因为可读,才能用cs指向当前段的Msg1字符串
  inc   esi
  cmp   al, '/0'    //判断是否字符串结束
  jz    _stop
  mov   gs:[edi], ax
  add   edi, 2
  jmp   _DispStr
_stop: //显示完毕

  add   edi, 2
  mov  printPlace, edi //保存最后的光标位置

  //通过反汇编,知道pm16.c的_BackToRealModel在内存的0x7c18处
  jmp   dword SelectorSode16:0x7c18 - 0x7c00
}






想要实验以上代码的朋友请注意:

1.到杨晓兵大大的博客上下载安装yc09,安装只需一分钟左右。

2.将操作系统实验二:从实模式跳转到保护模式 中的run.c代码,以及此次试验中的pm.h、pm16.c、pm32.c代码拷贝到某个实验用的文件夹内。

3.在YC09/example文件夹内找到bochs.exe、BIOS-bochs-latest、VGABIOS-elpin-2.40、x11-pc-us.map四个文件拷贝到试验用的文件夹内。

4.使用yc09编译运行run.c
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: