一个操作系统的实现-2_保护模式1

1:实模式下内存寻址

段首地址*16+偏移量=物理地址(段寄存器左移四位+offset)

2:保护模式下寻址

1)段寄存器中存放段选择子Selector
2)GDTR(全局描述符表寄存器)中存放段描述符首地址
3)通过选择子与GDTR中首地址,找到对应的段描述符
4)段描述符中有段的物理首地址,就得到段在内存中的首地址
5)加上偏移量,就得到这个段中存放的数据的真正物理地址


3:程序说明

;宏定义Descriptor可看做是表示描述符的结构体,由3个参数构成,这些Descriptor组成全局描述符表GDT
%macro Descriptor 3        
dw %2 & 0FFFFh
dw %1 & 0FFFFh
db (%1>>16) & 0FFh
dw ((%2>>8) & 0F00h) | (%3 & 0F0FFh)
db (%1>>24) & 0FFh
%endmacro

DA_32 EQU 4000h
DA_C EQU 98h
DA_DRW EQU 92h

;org 07c00h  ;直接加载.bin文件时用07c00h地址(小于512bytes时)
org 0100h    ;大于512bytes时制作成.com文件,通过freedos来加载.com文件,得到运行的效果
jmp LABEL_BEGIN

[SECTION .gdt]
LABEL_GDT: Descriptor 0, 0, 0  ;描述符的三个参数分别表示段物理首地址,段界限,段属性,下面定义的三个描述符LABEL_GDT,CODE32和VIDEO构成描述符表
LABEL_DESC_CODE32: Descriptor 0,SegCode32Len - 1, DA_C + DA_32
LABEL_DESC_VIDEO: Descriptor 0B8000h, 0ffffh, DA_DRW

GdtLen  equ $ - LABEL_GDT  ;描述符表长度
GdtPtr dw GdtLen - 1        ;GdtPtr结构共48位,低16位为段界限,高32位为0,以后会重置,高32位的访问通过GdtPtr+2进行,高32位存放GDT的物理地址        
dd 0

SelectorCode32 equ LABEL_DESC_CODE32 - LABEL_GDT ;定义段选择子,即当前描述符相对全局描述符表的偏移量
SelectorVideo equ LABEL_DESC_VIDEO - LABEL_GDT

[SECTION .s16]
[BITS 16]            ;告诉编译器这是一个16位代码段,此段代码初始化所以段描述符表中的段物理首地址
LABEL_BEGIN:        ;程序的入口处
mov ax,cs            ;ds,es,ss与cs代码段基地址都一样
mov ds,ax
mov es,ax
mov ss,ax
mov sp,0100h

xor eax,eax            ;自己与自己异或,将eax清零
mov ax,cs            ;eax是32位寄存器,ax是eax的低16位
shl eax,4            ;左移四位,相当于乘以16,实模式下计算物理地址
add eax,LABEL_SEG_CODE32    ;加上段相对代码段的偏移地址,eax中得到32位的CODE32段的物理地址
mov word [LABEL_DESC_CODE32 + 2],ax    ;用实际物理地址填充LABEL_DESC_CODE32段描述符的段物理首地址字段,其中低16位分别放在2,3字节,高16位中的低8位放在第4字节,高16位中的高8位放在第7字节.
shr eax,16
mov byte [LABEL_DESC_CODE32 + 4],al
mov byte [LABEL_DESC_CODE32 + 7],ah

;得到段描述符表的物理地址,存放到GDTR寄存器中
xor eax,eax        
mov ax,ds        ;GDT的段地址为数据寄存器DS
shl eax,4        
add eax,LABEL_GDT    ;计算出实际物理地址
mov dword [GdtPtr + 2],eax    ;将实际物理地址直接放到GdtPtr+2中(和实际GDTR寄存器结构相同)

lgdt  [GdtPtr]            ;通过lgdt指令将GdtPtr中内容加载到GDTR寄存器

cli                ;关中断(实模式和保护模式下中断处理不同,故关中断防止出错)

in al,92h        ;打开A20地址线
or al,00000010b
out 92h,al

mov eax,cr0        ;将cr0寄存器的第0位(PE位,决定CPU运行于实模式还是保护模式)置为1
or eax,1
mov cr0,eax

jmp dword SelectorCode32:0    ;跳入code32执行,加dword告诉编译器这句代码要编译成32位代码

[SECTION .s32]
[BITS 32]

LABEL_SEG_CODE32:
mov ax,SelectorVideo        ;视频选择子,用于找到显存段的描述符
mov gs,ax
xor edi,edi
mov edi,(80*11 + 79)*2        ;屏幕的第11行,79列
mov ah,0Ch                    ;0000黑底,1100红字
mov al,'P'
mov [gs:edi],ax


jmp $

SegCode32Len equ $ - LABEL_SEG_CODE32



这里使用freeos加载运行,从bochs下载freedos压缩文件,解压,将其中的a.img放到工作目录下并更名位freedos.img,用bximage新建一个pm.img.
修改.bochsrc配置文件,floppya:.......freedos.img
                   floppyb:......pm.img
                   boot=a
运行bochs,freedos运行成功后格式化b盘;format b:
将0100h的编译版本用nasm编译生成pmtest1.com文件
在/mnt/下新建一个目录floppy,
mount -o loop pm.img  /mnt/floppy
cp ./pmtest1.com  /mnt/floppy
umount /mnt/floppy


在freedos中运行dir b:查看b盘内容,再运行b:\pmtest1.com即可完成此次实验

若采用第一个实验中的加载方式而不采用freedos方式,只需要将0100地址改为07c00,并且编译成.bin文件直接运行即可






进入保护模式的主要步骤总结:
1)准备GDT(包括设置各个段描述符内容)
2)用lgdt加载GDTR
3)关中断,打开A20地址线
4)置cr0的PE位
5)跳转,进入保护模式

你可能感兴趣的:(一个操作系统的实现)