质量最好的微型车:从头开始编写操作系统(5) 第4章:引导加载器2 - xiaoxiaoyaya的专栏 - ...

来源:百度文库 编辑:九乡新闻网 时间:2024/07/07 14:20:24
  从头开始编写操作系统(5) 第4章:引导加载器2 收藏
译自:http://www.brokenthorn.com/Resources/OSDev4.html
第4 章:引导加载器2
by Mike, 2009 本系列文章旨在向您展示并说明如何从头开发一个操作系统。 介绍 欢迎! 在前一章里我们做了不少。我们了解了当我们按下电源按钮时到底发生了什么,也知道了BIOS 是怎样引导的。我们也了解了BIOS INT 0x19 ,会检查引导标志 (0xAA55) ,并且,如果找到,会将引导加载器加载到0x7C00 。 我们也开发了一个简单的引导加载器,并且我们也有了完成整个构建过程的经验。 在本章中,我们会扩展引导加载器,本章内容有: 处理器模式
中断——打印文字及其他
段: 偏移 寻址
注意:到目前为止,我们的引导加载器具有整个系统的控制权。这意味着所有的一切都依赖于我们的代码,所有的都听我们的!我们将会有更多的代码。 从现在起,事情会变得复杂起来。为了确保本系列文章有一个坚实的基础,从这章开始会有一个可供下载的演示文件。这样有助于加深对概念的理解。别担心——我会详细解释每一部分的。 准备好了吗? 处理器的模式 好,好……我们在哪见过这个条目呢?想想……每一章都有! 是的,我们已经说了很多了。理解不同模式的区别是很重的,为什么呢? 在前两章里,我们讨论了为什么x86 系列引导时是16 位模式。。我们希望开发32 位操作系统,所以我们需要将处理器从16 位切换到32 位。 不只有两个模式,让我们一个一个来看。 实模式Real Mode 如你所知,x86 处理器引导时处在一个16 位环境。是什么模式呢?(提示:是实模式吗?)……Okay ,它是。 到底什么是实模式?实模式是…… 使用段:偏移的内存模式
1 MB 内存
16 位偏移
没有虚存 ,没有内存保护.
其中一些很简单,其他则需要一些解释。一个要注意的是,上面所有的东西都直接或间接与内存有关。 让我们仔细看看。 段:偏移内存模式:历史 让我们 Back In Time™ ,看看第2 章,内存的概念和操作系统的使用要追溯到20 世纪50 年代。当时的计算机不是个人计算机而是大型机。 记得吗,当时的计算机大且笨重,既没有高级的操作系统,也没有高级的计算机。 随着计算机的快速发展,需求随之来临。当计算机是8 位的时候,想要16 位的。当是16 位是,微软正在考虑32 位。当32 为来临的时候。64 位已是主流(Okay, 最后一条不是真的),但是128 位已在途中了。 最主要的问题是计算机工业发展的太快。 当Intel 设计8086 处理器时,处理器使用16 位寄存器,访问64KB 内存。问题是,很多软件想要更多的内存。 8086 和8088 同时设计。8088 作为Intel 的“下一代”处理器,它预期花费更长的时间。为了应付其他公司的威胁Intel 想要更快的开发并发售一款处理器—— 8086 ——来占有市场,直到8088 上市。 问题是,在8088 上市之前,软件想要超过64KB 的内存,而Intel 的处理器8086 受到了已经开发了16 位处理器的竞争者们的威胁。Intel 需要一个好点子。 8086 的设计者提出一个办法,这个办法使得8086 处在16 位但可以访问超过1MB 的内存。他们同意了,Intel 也批准了。 段:偏移的策略诞生了。 为了理解段:偏移策略,我们先看看段和偏移。 段Segments 段就是整体的一部分。在这里,段就是内存的一节。Yep ——太简单了。 想像一下把内存分节,这些节就是段。 x86 系列使用4 个基本的寄存器来保存这些段的起始地址。它们就像基地址(base address ) 一样——提供段的起点。 通常,一个段64KB 长,并可以自由变化。 段代表内存的一节。这样如果段的基地址为0 ,那么这段节表示0 到64KB 之间的这一段。 这些寄存器是 CS, DS, ES, 和 SS 。这些寄存器存储了段的基地址,在了解了这个模式的地址之后我们会再看看这些寄存器。 偏移Offsets 偏移是加到基地址上的一个数。如,如果基地址是3 : Offset = base number (3) + the offset number
Offset 2 则 3+2 = 5
Offset 4 则 3+4 = 7 Okay, 这和我们有什么关系?好的,在段:偏移的寻址方式中,为了得到实际地址,我们用偏移地址加上基地址(记得吗,段代表基地址)。 很简单?现在把他们合起来。 段:偏移寻址 在段:偏移寻址中我们简单的把偏移地址加上基地址,但是在前一节里,我提到了段地址在实模式中是16 位 。这意味着我们要把段地址乘16 ,公式如下: 实际地址 = ( 段地址 * 16( 十进制)) + 偏移 关于它,到这儿就完了。 段:偏移的表示 段:偏移的寻址方式使用冒号分隔,如: 07C0:0000     < 07C0 是段地址,0 是偏移 使用公式,我们可以把它转化为实际地址0x7C00 :                 实际地址= 基地址 * 段长(16) + 偏移                        07C0:0000 = 07C0 * 16 ( 十进制) + 0                                   = 07C00 + 0 = 0x7C00 段:偏移的问题 段:偏移太特殊了。通过改变段和偏移的值,你会发现不同的段:偏移对表示同一个实际地址。为什么?因为他们表示同一个内存位置! 比如,下面所有的地址都表示我们在0x7C00 的一定加载器。   0007:7B90   0008:7B80   0009:7B70   000A:7B60   000B:7B50   000C:7B40       0047:7790   0048:7780   0049:7770   004A:7760   004B:7750   004C:7740       0077:7490   0078:7480   0079:7470   007A:7460   007B:7450   007C:7440       01FF:5C10   0200:5C00   0201:5BF0   0202:5BE0   0203:5BD0   0204:5BC0       07BB:0050   07BC:0040   07BD:0030   07BE:0020   07BF:0010   07C0:0000 这仅仅是一部分,事实上有4,096 种不同的组合来指向相同的内存位置——内存中的每一字节! 如果我们有两个小于 64KB 的段地址会发生什么呢?段长(偏移)是16 位的,并且段地址仅表示基地址,这导致了段覆盖 :  考虑上面的一层覆盖了下面的一层。这将导致问题。 这意味着,在实模式,你可以使用超过4,000 不同的方式来访问内存中的每一字节,由于段覆盖的存在,将有在你不知情的情况下发生数据损坏这。使得实模式没有内存保护 。 x86 使用的段寄存器列在下面: CS (Code Segment) – 保存代码段基地址
DS (Data Segment) – 保存数据段基地址
ES (Extra Segment) – 可以保存任意的段
SS (Stack Segment) – 保存栈段基地址
Wow, 实模式有很多问题,有没有改进呢? 保护模式Protected Mode 保护模式(PMode) 这个词我们常见,这里会多说一点。PMode 使用描述符表(Descriptor Tables )来描述内存分布,以达到内存保护的目的。 PMode 是32 位的处理器模式,允许使用32 位寄存器,访问超过4GB 内存 。相比于实模式是一个很大的进步。 我们会使用PMode 。是的,你可能想问,Windows 是一个PMode 操作系统? 设置PMode 并理解它是如何工作的,有些困难,我们会在后面详细说明。 非实模式Unreal Mode 非实模式"Unreal Mode" 表示有像保护模式一样的地址空间(4GB )的实模式。 要进入非实模式,简单的将处理器切换到PMode ,当加载新的描述符(Descriptor ) 后有可以回来。 描述符表(Descriptor Tables ) 可能 很难理解,我会在讨论保护模式时详加说明。 虚拟8086 模式 虚拟8086 模式(v86 Mode) 是保护模式下模拟16 位实模式的模式。 你可能认为这有些奇怪。v86 会很有用。所有的BIOS 中断只在实模式下有效 !v86 模式提供了一种在保护模式下使用BIOS 中断的能力。详见后文。 处理器模式切换 在这里我不会包含切换处理器模式的代码。相反,我们解释一下重要的概念。 只有两个实际存在的模式——实模式和保护模式。 换言之 其他的模式非实模式和v86 模式来源于实模式和保护模式。 非实模式是实模式,但有保护模式的地址系统。虚拟8086 模式是保护模式,但使用实模式执行16 位代码。 如你所见,v86 和非实模式都基于实模式和保护模式。因此,如果不了解保护模式,就很难理解这些模式。 我们很快会详细阐述保护模式、非实模式和v86 模式,别着急? 对于保护模式有些重要的事情: 没有中断是有效的。你需要编写自己的。使用任何的软硬件中断会导致三重错误。
一旦进入保护模式,一点儿错误就会导致三重错误。小心点儿。
PMode 需要描述符表, 比如 GDT, LDT, 和IDT 等.
PMode 可以访问4GB 内存,有内存保护。
段:偏移寻址方式与线性寻址方式一同使用
使用32 位的寄存器。
我们会在后文详细讨论保护模式。 扩展引导加载器 Wow, 我们做了很多,不是吗?我们了解了关于保护模式、非实模式和虚拟8086 模式的基本理论。我们对实模式做了很多的讨论,为什么呢?因为计算机引导时为了向后兼容处在一个16 位环境,这个16 位环境是实模式。 所以,当我们的引导加载器运行的时候是实模式,等等!这说明我们能使用BIOS 中断,对吗?是的,包括视频中断以及任何其他的直接来源于硬件的中断。 有用的程序段和BIOS 中断 OEM 参数块(OEM Parameter Block ) OEM 参数块保存有Windows MBR 和引导记录信息。它的基本作用是描述磁盘的文件系统,我们在讨论文件系统是再看这个表。可我们又不能跳过它。 因为这可以修复Windows 的“为格式化”错误。 现在,尽可能简单的考虑这张表。我会在文件系统那一节里详细说明它。 这是包含了这张表的引导加载器: ;********************************************* ;       Boot1.asm ;              - A Simple Bootloader ; ;       Operating Systems Development Tutorial ;*********************************************   bits    16                                                    ; 我们在实模式   org            0x7c00                                        ; BIOS 加载到 0x7c00   start:          jmp loader                                   ; 跳过OEM 块   ;*************************************************; ;       OEM Parameter block ;*************************************************;   TIMES 0Bh-$+start DB 0   bpbBytesPerSector:     DW 512 bpbSectorsPerCluster: DB 1 bpbReservedSectors:    DW 1 bpbNumberOfFATs:            DB 2 bpbRootEntries:             DW 224 bpbTotalSectors:            DW 2880 bpbMedia:                   DB 0xF0 bpbSectorsPerFAT:           DW 9 bpbSectorsPerTrack:    DW 18 bpbHeadsPerCylinder:   DW 2 bpbHiddenSectors:           DD 0 bpbTotalSectorsBig:     DD 0 bsDriveNumber:          DB 0 bsUnused:                   DB 0 bsExtBootSignature:    DB 0x29 bsSerialNumber:         DD 0xa0a1a2a3 bsVolumeLabel:          DB "MOS FLOPPY " bsFileSystem:          DB "FAT12   "   ;*************************************************; ;       引导加载器入口点 ;*************************************************;   loader:           cli                    ; 禁止中断         hlt                    ; 系统停机         times 510 - ($-$$) db 0               ; 我们要有512 字节,用0 填充   dw 0xAA55                      ; 引导标志 打印文字 – 中断 0x10 的0x0E 功能 您可以使用INT 0x10 视频中断。基本的中断是可以使用的。 INT 0x10 – 视频打印机输出 AH = 0x0E
AL = 待写字符
BH – 页号 ( 应为 0)
BL = 前景色 ( 只在图形模式有效)
例如:         xor     bx, bx         ; 一种快速使BX 清零的方法         mov     ah, 0x0e         mov     al, 'A'         int     0x10 我们会在屏幕上显示一个'A' 。 打印字符串 - 中断 0x10 的0x0E 功能 使用相同的中断,我们可以简单的打印0 结尾的字符串: msg     db      "Welcome to My Operating System!", 0   ;*************************************** ;       Prints a string ;       DS=>SI: 0 terminated string ;***************************************   Print:                        lodsb                        or                     al, al         ; al=当前字符                        jz                     PrintDone      ; 到达null终止
                       mov                    ah,     0eh    ; 下一个字符                        int                    10h                        jmp                    Print PrintDone:                        ret   ;*************************************************; ;       Bootloader Entry Point ;*************************************************;   loader:   ; Error Fix 1 ------------------------------------------           xor     ax, ax         ; 使段地址为 0         mov     ds, ax         ; 因为ORG 0x7c00. 所有的地址都基于它         mov     es, ax         ; 数据段一样                                ; 代码段,em ,没有           mov     si, msg         call    Print           cli                    ; 禁止中断         hlt                    ; 停机         times 510 - ($-$$) db 0               ; 我们需要512 字节,用0 填充   dw 0xAA55                      ; 引导标志 获取内存大小 简单: INT 0x12 – 获取内存大小
返回值: AX = 从0x0 开始的内存大小,以KB 计数。 例子: xor     ax, ax int     0x12 ; 现在 AX = BIOS 记录的系统内存大小,以KB 计数 Wow... 很难吗?事实上,在保护模式中,由于不能使用中断这变得很难。 注意:BIOS 返回的内存大小可能不准确!后面我们会看到其他的方法。 演示  ;********************************************* ;       Boot1.asm ;              - A Simple Bootloader ; ;       Operating Systems Development Tutorial ;*********************************************   bits    16                                                    ; 我们处在16 位实模式   org            0x7c00                                        ; BIOS 将我们加载到0x7c00   start:          jmp loader                                   ; 跳过 OEM 块   ;*************************************************; ;       OEM Parameter block ;*************************************************;   ; Error Fix 2 - Removing the ugly TIMES directive -------------------------------------   ;;      TIMES 0Bh-$+start DB 0                                ; OEM 参数块正好离我们加载的位置有3 字节 ,填充                                                              ; 3 字节 bpbOEM                 db "My OS   "                         ; 这8 字节是你的操作系统的名字   bpbBytesPerSector:     DW 512 bpbSectorsPerCluster: DB 1 bpbReservedSectors:    DW 1 bpbNumberOfFATs:            DB 2 bpbRootEntries:             DW 224 bpbTotalSectors:            DW 2880 bpbMedia:                   DB 0xF0 bpbSectorsPerFAT:           DW 9 bpbSectorsPerTrack:    DW 18 bpbHeadsPerCylinder:   DW 2 bpbHiddenSectors:           DD 0 bpbTotalSectorsBig:     DD 0 bsDriveNumber:          DB 0 bsUnused:                   DB 0 bsExtBootSignature:    DB 0x29 bsSerialNumber:         DD 0xa0a1a2a3 bsVolumeLabel:          DB "MOS FLOPPY " bsFileSystem:          DB "FAT12   "   msg     db      "Welcome to My Operating System!", 0          ; 待打印字符串   ;*************************************** ;       Prints a string ;       DS=>SI: 0 terminated string ;***************************************   Print:                        lodsb                                 ; 从SI 指明的字符串读下一字节到AL 中                        or                     al, al         ; AL=0?                        jz                     PrintDone      ; 是,打印结束                        mov                    ah,     0eh     ; 打印字符                        int                    10h                        jmp                    Print          ; 重复直到找到结束标志 PrintDone:                        ret                                   ; 完成,返回   ;*************************************************; ;       入口点 ;*************************************************;   loader:           xor     ax, ax         ; 段地址为0         mov     ds, ax         ; 因为ORG 0x7c00. 所有的地址都基于它   mov     es, ax         ; 数据段也一样                                        mov     si, msg                                       ; 我们要打印的信息         call    Print                                         ; 调用打印函数         xor     ax, ax                                        ; 清0ax         int     0x12                                          ; 取得内存大小           cli                                                   ; 禁止中断         hlt                                                   ; 停机         times 510 - ($-$$) db 0                                              ; 我们需要512 字节,用0 填充   dw 0xAA55                                                    ; 引导标志 总结 拍拍你的后背放松一下。 这一章很烦人。我试图找到一种好的,又不过分艰深的方法来解释段:偏移寻址方式和处理器模式,我想,我做到了。 我们谈论了不同的处理器模式——实模式、保护模式、非实模式和v86 。我们深入了解了实模式,因为这是我们开发引导加载器的模式。我们也了解了段:偏移的寻址方式,这对于那些DOS 程序员来说是一次复习,我们也讨论了BIOS 中断,并以一个完整的例子结束。 在下一章里,我们会深入了解那个丑陋的OEM 参数块,我们也会了解一下文件系统的基本知识,以及如何从磁盘加载文件。 下次见。  本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/xiaoxiaoyaya/archive/2011/04/13/6321709.aspx
从头开始编写操作系统(5) 第4章:引导加载器2 - xiaoxiaoyaya的专栏 - ... 从头开始编写操作系统(4) 第3章:引导加载器 - xiaoxiaoyaya的专栏 - C... 从头开始编写操作系统(6) 第5章:引导加载器3 - xiaoxiaoyaya的专栏 - ... 从头开始编写操作系统(2) 第1章:介绍 - xiaoxiaoyaya的专栏 - CSDN... 从头开始编写操作系统(3) 第2章:基本理论 - xiaoxiaoyaya的专栏 - CS... 使用VB6编写COM加载项(方法一:AddinInstance) 女人的美丽从头开始 Drupal专业开发指南 第2章 创建一个模块(1) - g089h515r806的专栏 - CSDNBlog 使用c++开发excel插件(第4章编写一个完整的xll) J2EE项目代码编写规范 - fbtdjs的专栏 - CSDN博客 微软操作系统最核心开发故事第4章:死胡同 Drupal专业开发指南 第2章 sdfsdf建一个模块(1) - g089h515r806的专栏 - CSDNBlog Eclipse 插件安装方法和插件加载失败解决办法 - TATA的专栏 - CSDN博客 怎样才能拍出好的摄影作品 (1)从头开始 (原创) Photoshop从头开始 数码摄影从头开始 健康从头开始 [转载]从头开始写毛爷爷的故事 从头开始锻炼意志力(1)–选择你的目标 编写超级可读代码的15个最佳实践 - 蒋宇捷的专栏 - CSDN博客 大智慧公式编写教程 - 董昊(昊子)的专栏 - CSDN博客 微软操作系统最核心开发故事第2章:代码之王 - CNET科技资讯网 各大网站所用的操作系统与Web服务器 - yzc的专栏 - CSDNBlog 教程带列表的FLV播放器(无限加载网络视频)