源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
; ==========================================
; pm.asm
; 编译方法:nasm pm.asm -o pm.bin
; ==========================================
%include    "pm.inc"    ; 常量, 宏, 以及一些说明
org 07c00h
    jmp LABEL_BEGIN
[SECTION .gdt]
; GDT
;                                                                         段基址,        段界限     ,                              属性
LABEL_GDT:                        Descriptor          0,                0,                                         0                  ; 空描述符
LABEL_DESC_CODE32: Descriptor           0,             SegCode32Len - 1,    DA_C + DA_32   ; 非一致代码段
LABEL_DESC_VIDEO:    Descriptor    0B8000h,     0ffffh,                              DA_DRW        ; 显存首地址
; GDT 结束
GdtLen      equ $ - LABEL_GDT   ; GDT长度
GdtPtr      dw  GdtLen - 1  ; GDT界限
                    dd  0       ; GDT基地址
; GDT 选择子
SelectorCode32      equ LABEL_DESC_CODE32   - LABEL_GDT
SelectorVideo       equ LABEL_DESC_VIDEO    - LABEL_GDT
; END of [SECTION .gdt]
[SECTION .s16]
[BITS   16]
LABEL_BEGIN:
    mov ax, cs
    mov ds, ax
    mov es, ax
    mov ss, ax
    mov sp, 0100h
    ; 初始化 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
    ; 为加载 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 eax, cr0
    or  eax, 1
    mov cr0, eax
    ; 真正进入保护模式
    jmp dword SelectorCode32:0  ; 执行这一句会把 SelectorCode32 装入 cs,
                    ; 并跳转到 SelectorCode32:0  处
; END of [SECTION .s16]
[SECTION .s32]; 32 位代码段. 由实模式跳入.
[BITS   32]
LABEL_SEG_CODE32:
    mov ax, SelectorVideo
    mov gs, ax          ; 视频段选择子(目的)
    mov ecx, 28
    mov edi, 0
    mov bx, BootMessage
.show:
    mov ah, 0Ch         ; 0000: 黑底    1100: 红字
    mov al, [bx]
    mov [gs:edi], ax
    inc edi
    inc edi
    inc bx
    loop .show
    ; 到此停止
    jmp $
BootMessage:        db  "Joey, I'm in protected mode!"
SegCode32Len    equ $ - LABEL_SEG_CODE32
; END of [SECTION .s32]

运行结果如下:

源码解析:

1.首先程序跳转至LABEL_BEGIN处,jmp LABEL_BEGIN。将ds、es、ss段寄存器全部初始化为当前代码段。

2.初始化32位代码段描述符

在实模式下,也就是8086的16位的CPU的寻址方式是段x16+偏移,而在保护模式下,段寄存器值变成了一个索引,这个索引指向段描述符,也就是GDT中对应的那个描述符,描述符中包含了最终的基地址。

38-41行表示将当前程序代码段左移4位也就是x16,再加上LABEL_SEG_CODE32的偏移地址,最终形成LABEL_SEG_CODE32的物理地址,42-45表示把此物理地址加载到段描述符对应的段基址位置,段描述符格式如下图所示

段基地址0-15位也就是LABEL_DESC_CODE32 + 2地址处,将16位的ax传入,第43行向右移动16位表示已经传入的16位消除,然后将剩余的两个8位高低寄存器值传入对应的段基地址处。

Descriptor是在pm.inc中定义的宏,如下图所示,表示的就是段描述符的格式:

3.加载GDTR,GDRT的结构图如下所示

GDTR是唯一的一个指向段描述符表的寄存器,48-55行就是把段描述符表的基地址加载到GDTR寄存器当中。48行清除eax值,49-52是把LABEL_GDT的物理地址加载到20行的GdtPtr的双字处,也就是GDT基地址。55行把GdtPtr地址的内容加载到GDTR,双字节dw对应的是16位界限,双字dd对应的是32位基地址。

4.关中断和打开A20

保护模式下中断处理机制和实模式不同,所以先关闭中断,指令为cli。打开A20为历史原因防止偏移超出最大值时回滚。58-63行

5.切换到保护模式

只要把寄存器cr0的第0位置为1就行了。66-68行

6.真正进入保护模式的代码段

jmp dword SelectorCode32:0,这一行是真正进入保护模式的代码,SelectorCode32是个段选择子,段选择子的作用是找到对应的段描述符从而找到对应的段基地址。段选择子的格式如下图所示

从位3开始表示为段描述符表的索引,这句代码执行完之后就会把描述符LABEL_DESC_CODE32中的段基址也就是LABEL_SEG_CODE32加载到cs。

dword的作用是因为当前还是16位的代码,如果没有dword,SelectorCode32:0的偏移地址如果超出16位那么只会截取16位。

到此已经完全进入32位保护模式的代码了。

7.显示字符

接下来就会跳转到LABEL_SEG_CODE32处运行指令了,80-81行把段选择子SelectorVideo传入段寄存器gs中,也就是描述符LABEL_DESC_VIDEO的段基地址0B8000h,这就是显存的基地址。84行edi是显存的偏移地址,87-88行是字符及字符属性,89行为最终显示字符。

8.描述符属性

代码段的属性是DA_C + DA_32,根据pm.inc中的定义,DA_C是98h

对应的二进制为10011000。根据第2条的格式,DA_C其实是上面的第8-15位,第15位P是1表明这个段在内存中存在,S位是1表明这个段是代码段或数据段,TYPE为1000也就是8表明这个段是只执行的代码段,TYPE格式如下图所示

DA_32是4000h,

4000h为100000000000000,表明是从上面的位8开始计算的后面15位也就是D/B位为1,表明这个段是32位的代码段。

操作系统开发系列—2.进入32位保护模式相关推荐

  1. x86汇编语言从实模式百度云_Intel x86 CPU 32位保护模式杂谈之任务切换 上

    目录: 什么是任务 任务由什么组成 任务门描述符是什么东东?有了TSS描述符为什么要有任务门描述符? 参考文献 什么是任务 任务(task)是处理器可以分配.执行.挂起的工作单位,笔者认为和我们操作系 ...

  2. 《操作系统真象还原》第4章 保护模式入门 ing... 持续更新

    目录 文章目录 目录 概述 初见保护模式 代码 32push.S 全局描述符表 段描述符 GDT.LDT及选择子 GDT 选择子正式介绍 LDT 打开A20地址线 保护模式的开关,CR0寄存器的 PE ...

  3. 文末赠书《GD32 MCU原理及固件库开发指南》5本 | 国产MCU中GD32系列有望成为未来32位MCU的主流

    学习优秀博文([guo产MCU移植]手把手教你使用RT-Thread制作GD32系列BSP)有感 一篇优秀的博文是什么样的?它有什么规律可循吗?优秀的guo产32位单片机处理器是否真的能成功替换掉st ...

  4. 软硬件兼容STM32F103系列的国产32位MCU

    介绍一款软硬件兼容STM32F103系列的国产32位MCU,此款32位MCU采用高性能ARM®Cortex®-M3 32位RISC内核,216兆赫兹频率运行,内置256KB的闪存和高达96KB的SRA ...

  5. 30天自制操作系统:第三天 进入32位模式并导入C语言

    今天的内容稍稍有点多,一起看看吧 1.制作真正的IPL 到昨天为止,讲到的启动区虽然也称为IPL(Initial Program Loader,启动程序装载器),但它实质上并没有装载任何程序. 小节中 ...

  6. 32位java 最大内存_【答疑系列】为什么32位系统只支持最大4G内存?

    这个问题一直都有同学问到,算是提问次数最多的问题之一了. 32位是什么 现在主流的操作系统都是64位的,早期存在32位操作系统,相信大家也都有所听闻,其实,在更早之前,还有16位.8位的,这里就不展开 ...

  7. BPI-Bit 开发板带有Xtensa 32位LX6双核处理器的嵌入式系统的ESP 32

    BPI:bit(也称为BPI-bit,作为webduino:bit)是一个带有Xtensa 32位LX6双核处理器的嵌入式系统的ESP 32.支持Webduino.Arduino.微python以及S ...

  8. 操作系统真象还原第5章:保护模式进阶,向内核进阶

    前言 由于涉及到马上要搞实习的事情,搞得我十分的浮躁,自己也是频繁失眠,想来还是自己太过懒了,没控制住自己,自己也在这一个多月没搞好,尤其是本来想花几天时间来写一个高性能服务器,也把游双大佬的linu ...

  9. 操作系统真象还原——第5章 从保护模式到内核

    目录 前言 5.1 获取物理内存容量 5.1.1 学习Linux获取内存的方法 5.1.2 实战内存容量检测 5.2 内存分页 为什么需要分页? 一级页表 二级页表 如何设计一个页表 分页机制的代码实 ...

最新文章

  1. 【剑指offer-Java版】40数组中只出现一次的数字
  2. Linux wget 命令详解
  3. IBatis.net动态SQL语句
  4. vue进行判断使用class_vue如何判断dom的class
  5. linux下安装树梅派系统,优麒麟树莓派系统(优麒麟 for Raspberry Pi)的安装方法...
  6. 软件公司管理基本原则
  7. Flask爱家租房--订单支付(支付过程)
  8. 电脑很卡~~~~为什么???
  9. Visual Studio Code预览版Ver 0.3.0试用体验
  10. window java 环境_Windows配置java环境
  11. atitit 文件搜索 映象文件夹结构模式.docxAtitit 百度网盘 文件 与跨机器 文件 搜索 查询 检索 解决方案 最小化索引法 映象文件夹结构模式. 1. 生成文件夹 结构信息 1
  12. Java 序列化与反序列化详解
  13. Python中inplace参数
  14. overflow属性的用法
  15. c语言 字符串转换中文乱码,怎么将unicode转中文字符编码存在文本中
  16. 不管你学的是什么专业,你都应该多少懂些管理学的东西之【鳄鱼法则】【鲇鱼效应】...
  17. 基于STM32和hs1527、ev1527、rt1527、fp1527的无线接收解码程序
  18. windows使用cmd删除目录和文件(详细)
  19. 日本轮胎制造商纷纷提高轮胎价格
  20. 基于springboot+vue的幼儿园管理系统 elementui

热门文章

  1. Python第四章-字典
  2. 栈 - 关于出栈序列,判断合法的出栈序列
  3. 【Linux 内核 内存管理】RCU 机制 ④ ( RCU 模式下更新链表项 list_replace_rcu 函数 | 链表操作时使用 smp_wmb() 函数保证代码执行顺序 )
  4. 【开发环境】安装 Visual Studio Community 2013 开发环境 ( 下载 Visual Studio Community 2013 with Update 5 版本的安装包 )
  5. 【Google Play】管理目标受众群体 ( 加入“亲子同乐计划“ 由于政策原因 “更新被拒“ 后的处理 )
  6. 【OpenGL】二十二、OpenGL 光照效果 ( 模型准备 | 光照设置 | 启用光照 | 启用光源 | 设置光源位置 | 设置光照参数 | 设置环境光 | 设置反射材质 | 设置法线 )
  7. 【Android 安全】DEX 加密 ( 代理 Application 开发 | 加载 dex 文件 | 将系统的 dexElements 与 应用的 dexElements 合并 | 替换操作 )
  8. php中static和self的区别
  9. 对象及变量的并发访问一
  10. android中可以使用bitmap的平铺,镜像平铺等减小图片带来的apk过大的问题