现在的位置: 首页 > 技术文章 > 操作系统 > 正文

《自己动手写操作系统》学习笔记(五)

2014年03月06日 操作系统 ⁄ 共 6773字 ⁄ 字号 《自己动手写操作系统》学习笔记(五)已关闭评论 ⁄ 阅读 2,818 次

《自己动手写操作系统》学习笔记目录(持续更新)  http://www.techbulo.com/832.html

实模式->保护模式->实模式

实模式->保护模式->实模式

实模式------>保护模式------>实模式

一、“实模式--保护模式--实模式”的转换过程

1、“实模式--保护模式”的跳转

(1)准备GDT

(2)用lgdt加载gdtr

(3)打开A20

(4)置cr0的PE位

(5)跳转,进入保护模式

2、“保护模式--实模式”的跳转 

(比实模式--->保护模式要复杂一些,因为在准备结束保护模式回到实模式之前,需要加载一个合适的段描述符选择子到有关段寄存器,以使对应段描述符告诉缓冲寄存器中含有合适的段界限和属性,而且我们不能从32位代码段返回实模式,只能从16位代码段中返回。这是因为无法实现从32位代码段返回时CS高速缓冲寄存器中的属性符合实模式的要求【实模式不能改变段属性】,至于为什么不能从32位的保护模式直接跳转到实模式,可以参见这篇文章 http://www.techbulo.com/860.html

(1)从保护模式下的32位代码段跳转到保护模式下16位代码段

(2)在16位代码段下初始化除了cs寄存器外的所有其他段寄存器(以实现更新段描述符告诉缓冲寄存器的目的)

(3)置cr0的末位为0

(4)实现跳转,返回到实模式

二、《自己动手写操作系统》第三章pmtest2.asm中具体实现

1、实模式--保护模式--实模式”的跳转过程

实模式--保护模式--实模式”的跳转过程

2、代码实现

(1)、pm.inc中的宏定义

在程序中,我们定义属性时,使用了pm.inc中的宏定义DA_DRW、DA_C、DA_32、DA_DRW、DA_DRWA。下面让我来向大家解释这些宏定义。


DA   : Descriptor Attribute
D    : 数据段
C    : 代码段
S    : 系统段
R    : 只读
RW   : 读写
A    : 已访问
-----------------------------------------------------------
DA_32               EQU 4000h       ; 32 位段
DA_DPL0          EQU   00h        ; DPL = 0
DA_DPL1          EQU   20h        ; DPL = 1
DA_DPL2          EQU   40h        ; DPL = 2
DA_DPL3          EQU   60h        ; DPL = 3
-----------------------------------------------------------
存储段描述符类型值说明
-----------------------------------------------------------
DA_DR                  EQU 90h  ; 存在的只读数据段类型值
DA_DRW              EQU 92h  ; 存在的可读写数据段属性值
DA_DRWA            EQU 93h  ; 存在的已访问可读写数据段类型值
DA_C                     EQU 98h  ; 存在的只执行代码段属性值
DA_CR                  EQU 9Ah  ; 存在的可执行可读代码段属性值
DA_CCO               EQU 9Ch  ; 存在的只执行一致代码段属性值
DA_CCOR            EQU 9Eh  ; 存在的可执行可读一致代码段属性值
-----------------------------------------------------------
系统段描述符类型值说明
-----------------------------------------------------------
DA_LDT             EQU   82h       ; 局部描述符表段类型值
DA_TaskGate    EQU   85h       ; 任务门类型值
DA_386TSS      EQU   89h       ; 可用 386 任务状态段类型值
DA_386CGate   EQU   8Ch       ; 386 调用门类型值
DA_386IGate     EQU   8Eh       ; 386 中断门类型值
DA_386TGate    EQU   8Fh       ; 386 陷阱门类型值

(2)pmtest2.ASM文件


;=====================================================
;pmtest2.asm
;编译方法:nasm pmtest2.asm -o pmtest2.com

%include "pm.inc" ;常量,宏,以及一些说明

org 0100h
 jmp LABEL_BEGIN

[SECTION .gdt]
;GDT
; 段基址 段界限 段属性
LABEL_GDT: Descriptor 0, 0, 0 ;空描述符
LABEL_DESC_NORMAL: Descriptor 0, 0ffffh, DA_DRW ;Normal描述符
LABEL_DESC_CODE32: Descriptor 0, SegCode32Len-1, DA_C + DA_32 ;非一致代码段,32
LABEL_DESC_CODE16: Descriptor 0, 0ffffh, DA_C ;非一致代码段,16
LABEL_DESC_DATA: Descriptor 0, DataLen-1, DA_DRW + DA_DPL1 ;Data
LABEL_DESC_STACK: Descriptor 0, TopOfStack, DA_DRWA + DA_32 ;Stack,32位
LABEL_DESC_TEST: Descriptor 0500000h, 0ffffh, DA_DRW ;测试代码段
LABEL_DESC_VIDEO: Descriptor 0B8000h, 0ffffh, DA_DRW ;显存首地址
;GDT 结束

GdtLen equ $ - LABEL_GDT ;GDT长度
GdtPtr dw GdtLen - 1 ;GDT界限
 dd 0 ;GDT基地址

;GDT选择子
SelectorNormal equ LABEL_DESC_NORMAL - LABEL_GDT
SelectorCode32 equ LABEL_DESC_CODE32 - LABEL_GDT
SelectorCode16 equ LABEL_DESC_CODE16 - LABEL_GDT
SelectorData equ LABEL_DESC_DATA - LABEL_GDT + SA_RPL3
SelectorStack equ LABEL_DESC_STACK - LABEL_GDT
SelectorTest equ LABEL_DESC_TEST - LABEL_GDT
SelectorVideo equ LABEL_DESC_VIDEO - LABEL_GDT
;END of [SECTION .gdt]

[SECTION .data1] ;数据段
ALIGN 32
[BITS 32]
LABEL_DATA:
SPValueInRealMode dw 0
;字符串
PMMessage: db "In Protect Mode now.", 0 ;进入保护模式后显示该字符串
OffsetPMMessage equ PMMessage - $

StrTest: db "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 0
OffsetStrTest equ StrTest - $

DataLen equ $ - LABEL_DATA
;END of [SECTION .data1]

;全局堆栈段
[SECTION .gs]
ALIGN 32
[BITS 32]
LABEL_STACK:
 times 512 db 0
TopOfStack equ $ - LABEL_STACK - 1
;END of [SECTION .gs]

[SECTION .s16]
[BITS 16]
LABEL_BEGIN:
 mov ax, cs
 mov ds, ax
 mov es, ax
 mov ss, ax
 mov sp, 0100h

mov [LABEL_GO_BACK_TO_REAL + 3], ax
 mov [SPValueInRealMode], sp

;初始化16位代码段描述符
 mov ax, cs
 movzx eax, ax
 shl eax, 4
 add eax, LABEL_SEG_CODE16
 mov word[LABEL_DESC_CODE16+2], ax
 shr eax, 16
 mov byte[LABEL_DESC_CODE16+4], al
 mov byte[LABEL_DESC_CODE16+7], ah

;初始化32位代码段描述符
 xor eax, eax
 mov ax, cs
 shl eax, 4
 add eax, LABEL_SEG_CODE32
 mov word[LABEL_DESC_CODE32+2], ax
 shr eax, 16
 mov byte[LABEL_DESC_CODE32+4], al
 mov byte[LABEL_DESC_CODE32+7], ah

;初始化数据段描述符
 xor eax, eax
 mov ax, ds
 shl eax, 4
 add eax, LABEL_DATA
 mov word[LABEL_DESC_DATA+2], ax
 shr eax, 16
 mov byte[LABEL_DESC_DATA+4], al
 mov byte[LABEL_DESC_DATA+7], ah

;初始化堆栈段描述符
 xor eax, eax
 mov ax, ds
 shl eax, 4
 add eax, LABEL_STACK
 mov word[LABEL_DESC_STACK+2], ax
 shr eax, 16
 mov byte[LABEL_DESC_STACK+4], al
 mov byte[LABEL_DESC_STACK+7], ah

;为加载GDTR作准备
 xor eax, eax
 mov ax, ds
 shl eax, 4
 add eax, LABEL_GDT ; eax <- gdt
 mov dword[GdtPtr+2], eax ; [GdtPtr+2] <- gdt 基地址

;加载GDTR
 lgdt [GdtPtr]

;关中断
 cli

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

;清屏操作(用以指定色彩)
 mov ah, 06H ;功能06H和07H
 mov ch, 00 ;功能描述:初始化屏幕或滚屏
 mov cl, 00 ;入口参数:AH=06H——向上滚屏,07H——向下滚屏
 mov dh, 24 ;AL=滚动行数(0——清窗口)
 mov dl, 79 ;BH=空白区域的缺省属性
 mov bh, 7 ;(CH、CL)=窗口的左上角位置(Y坐标,X坐标)
 mov al, 00 ;(DH、DL)=窗口的右下角位置(Y坐标,X坐标)
 int 10H ;出口参数:无

;准备切换到保护模式
 mov eax, cr0
 or eax, 1
 mov cr0, eax

;真正进入保护模式
 jmp dword SelectorCode32:0 ;执行这一句会把SelectorCode32装入cs,并跳转到SelectorCode32:0处

;......................................................................
;,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,

LABEL_REAL_ENTRY: ;从保护模式跳回到实模式就到了这里
 mov ax, cs
 mov ds, ax
 mov es, ax
 mov ss, ax

mov sp, [SPValueInRealMode]

in al, 92h ;关闭A20地址线
 and al, 11111101b
 out 92h, al

sti ;开中断

;回到DOS
 mov ax, 4C00h
 int 21h;
;END of [SECTION .s16]

[SECTION .s32] ;32位代码段,由实模式跳入
[BITS 32]

LABEL_SEG_CODE32:
 mov ax, SelectorData
 mov ds, ax ;数据段选择子
 mov ax, SelectorTest
 mov es, ax ;测试段选择子
 mov ax, SelectorVideo
 mov gs, ax ;视频段选择子

mov ax, SelectorStack
 mov ss, ax ;堆栈段选择子
 mov esp, TopOfStack

;下面显示一个字符串
 mov ah, 0Ch ;0000:黑底 1100:红字
 xor esi, esi
 xor edi, edi
 mov esi, OffsetPMMessage ;源数据偏移
 mov edi, (80*10+0)*2 ;目的数据偏移。屏幕第10行,第0列
 cld
.1:
 lodsb
 test al, al
 jz .2
 mov [gs:edi], ax ;将ax中的数据送入到显存
 add edi, 2
 jmp .1
.2: ;显示完毕

 call DispReturn

call TestRead
 call TestWrite
 Call TestRead

;到此停止
 jmp SelectorCode16:0
;---------------------------------------------------------------------------

TestRead:
 xor esi, esi
 mov ecx, 8
.loop:
 mov al, [es:esi]
 call DispAL
 inc esi
 loop .loop

call DispReturn

ret
;TestRead结束--------------------------------------------------------------

;--------------------------------------------------------------------------
TestWrite:
 push esi
 push edi
 xor esi, esi
 xor edi, edi
 mov esi, OffsetStrTest ;源数据偏移

.1:
 lodsb
 test al, al
 jz .2
 mov [es:edi], al
 inc edi
 jmp .1
.2:
 pop edi
 pop esi

ret

;TestWrite结束--------------------------------------------------------------

;---------------------------------------------------------------------------
;显示AL中的数字
;默认地:
; 数字已经存在AL中
; edi始终指向要显示的下一个字符的位置
;被改变的寄存器
; ax, edi
;---------------------------------------------------------------------------

DispAL:
 push ecx
 push edx

mov ah, 0Ch
 mov dl, al
 shr al, 4
 mov ecx, 2
.begin:
 and al, 01111b
 cmp al, 9
 ja .1
 add al, '0'
 jmp .2
.1:
 sub al, 0Ah
 add al, 'A'
.2:
 mov [gs:edi], ax
 add edi, 2

mov al, dl
 loop .begin
 add edi, 2

 pop edx
 pop ecx

ret
;DispAL结束-----------------------------------------------------------------

;---------------------------------------------------------------------------
DispReturn:
 push eax
 push ebx
 mov eax, edi
 mov bl, 160
 div bl
 and eax, 0FFh
 inc eax
 mov bl, 160
 mul bl
 mov edi, eax
 pop ebx
 pop eax

ret
;DispReturn结束--------------------------------------------------------------
SegCode32Len equ $ - LABEL_SEG_CODE32
; END of [SECTION .s32]

;16位代码段,由32位代码段跳入,跳出后到实模式
[SECTION .s16code]
ALIGN 32
[BITS 16]
LABEL_SEG_CODE16:
 ;跳回实模式:
 mov ax, SelectorNormal
 mov ds, ax
 mov es, ax
 mov fs, ax
 mov gs, ax
 mov ss, ax

mov eax, cr0
 and al, 11111110b
 mov cr0, eax

LABEL_GO_BACK_TO_REAL:
 jmp 0:LABEL_REAL_ENTRY ;段地址会在程序开始处被设置成正确的值

Code16Len equ $-LABEL_SEG_CODE16
;END of [SECTION .s16code]

×