自己动手写操作系统( 五 )


图1 实模式的寻址
图2 保护模式下的寻址
图3 段描述俯的格式此外,还有一个中断描述符表(IDT) 。这些中断描述符会告诉处理器到那里可以找到中断处理程序 。和实模式一样,每一个中断都有一个入口,但是这些入口的格式却完全不同 。因为在切换到保护模式的过程中没有使用到IDT,所以在此就不多做介绍了 。进入保护模式80386有4个32位控制寄存器,名字分别为CR0、CR1、CR2和CR3 。CR1是保留在未来处理器中使用的,在80386中没有定义 。CR0包含系统的控制标志,用于控制处理器的操作模式和状态 。CR2和CR3是用于控制分页机制的 。在此,我们关注的是CR0寄存器的PE位控制,它负责实模式和保护模式之间的切换 。当PE=1时,说明处理器运行于保护模式之下,其采用的段机制和前面所述的相应内容对应 。如果PE=0,那么处理器就工作在实模式之下 。切换到保护模式,实际就是把PE位置为1 。为了把系统切换到保护模式,还要做一些其它的事情 。程序必须要对系统的段寄存器和控制寄存器进行初始化 。把PE位置1后,还要执行跳转指令 。过程简述如下:1.创建GDT表;2.通过置PE位为1进入保护模式;3.执行跳转以清除在实模式下读取的任何指令 。下面使用代码来实现这个切换过程 。需要的东西◆ 一张空白软盘◆ NASM编译器下面是整个程序的源代码:org 0x07c00; 起始地址是0000:7c00
jmp short begin_boot ; 跳过其它的数据,跳转到引导程序的开始处
bootmesg db "Our OS boot sector loading ......"
pm_mesg db "Switching to protected mode ...."
dw 512 ; 每一扇区的字节数
db 1 ; 每一簇的扇区数
dw 1 ; 保留的扇区号
db 2
dw 0x00e0
dw 0x0b40
db 0x0f0
dw 9
dw 18
dw 2 ; 读写扇区号
dw 0 ; 隐藏扇区号
print_mesg :
mov ah,0x13 ; 使用中断10h的功能13,在屏幕上写一个字符串
mov al,0x00 ; 决定调用函数后光标所处的位置
mov bx,0x0007 ; 设置显示属性
mov cx,0x20 ; 在此字符串长度为32
mov dx,0x0000 ; 光标的起始行和列
int 0x10 ; 调用BIOS的中断10h
ret ; 返回调用程序
get_key :
mov ah,0x00
int 0x16 ; Get_key使用中断16h的功能0,读取下一个字符
ret
clrscr :
mov ax,0x0600 ; 使用中断10h的功能6,实现卷屏,如果al=0则清屏
mov cx,0x0000 ; 清屏
mov dx,0x174f ; 卷屏至23,79
mov bh,0 ; 使用颜色0来填充
int 0x10 ; 调用10h中断
ret
begin_boot :
call clrscr ; 先清屏
mov bp,bootmesg ; 提供串地址
call print_mesg ; 输出信息
call get_key ; 等待用户按下任一键
bits 16
call clrscr ; 清屏
mov ax,0xb800 ; 使gs指向显示内存
mov gs,ax ; 在实模式下显示一个棕色的A
mov Word [gs:0],0x641 ; 显示
call get_key ; 调用Get_key等待用户按下任一键
mov bp,pm_mesg ; 设置串指针
call print_mesg ; 调用print_mesg子程序
call get_key ; 等待按键
call clrscr ; 清屏
cli ; 关中断
lgdt[gdtr] ; 加载GDT
mov eax,cr0
or al,0x01 ; 设置保护模式位
mov cr0,eax ; 将更改后的字送至控制寄存器中
jmp codesel:go_pm
bits 32
go_pm :
mov ax,datasel
mov ds,ax ; 初始化ds和es,使其指向数据段
mov es,ax
mov ax,videosel ; 初始化gs,使其指向显示内存
mov gs,ax
mov word [gs:0],0x741 ; 在保护模式下显示一个白色的字符A
spin : jmp spin ; 循环
bits 16
gdtr :
dw gdt_end-gdt-1 ; gdt的长度
dd gdt ; gdt的物理地址
gdt
nullsel equ $-gdt ; $指向当前位置,所以nullsel = 0h
gdt0 ; 空描述符
dd 0
dd 0 ; 所有的段描述符都是64位的
codesel equ $-gdt ; 这是8h也就是gdt的第二个描述符
code_gdt
dw 0x0ffff ; 段描述符的界限是4Gb
dw 0x0000
db 0x00
db 0x09a
db 0x0cf

推荐阅读