第12课 - 实模式到保护模式(下)

Posted dua677

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了第12课 - 实模式到保护模式(下)相关的知识,希望对你有一定的参考价值。

不一般的jmp(s16->s32)

    在16位代码中,所有的立即数默认为16位
    从16位代码段跳转到32位代码段时,必须做强制转换
    技术分享图片技术分享图片

  技术分享图片

技术分享图片

深入保护模式:定义显存段

    为了显示数据,必须存在两大硬件:显卡 + 显示器
        显卡
            为显示器提供需要显示的数据
            控制显示器的模式和状态
        显示器
            将目标数据以可见的方式呈现在屏幕上

显存的概念和意义

    显卡拥有自己内部的数据存储器,简称显存

显卡的工作模式:文本模式&图形模式

    在不同的模式下,显卡对显存内容的解释是不同的
    可以使用专属指令或int 0x10中断改变显卡工作模式
    在文本模式下:
        显存的地址范围映射为:[0xB8000, 0xBFFFF]    // 第一课有介绍
        一屏幕可以显示25行,每行80个字符

显卡的文本模式原理

    技术分享图片技术分享图片 

文本模式下显示字符

    技术分享图片技术分享图片

小目标

    在保护模式下,打印指定内存中的字符串
        定义全局段(.gs),用于保护模式下的函数调用
        定义全局数据段(.dat),用于定义只读数据(D.T.OS!)
        利用对显存段的操作定义字符串打印函数(PrintString)

打印函数(PrintString)的设计

  技术分享图片

汇编小贴士

    32位保护模式下的乘法操作mul
        被乘数放到AX寄存器
        乘数放到通用寄存器或内存单元(16位
        相乘的结果放到EAX寄存器中
    再论$$$
        $表示当前行相对于代码起始位置的偏移量
        $$表示当前代码节(section)的起始位置
        技术分享图片技术分享图片

小结

    实模式下可以使用32位寄存器和32位地址
    显存是显卡内部的存储单元,本质上与普通内存无差别
    显卡有两种工作模式:文本模式&图形模式
    文本模式下操作显存单元中的数据能够立即反映到显示器

代码

  1 // loader.asm
  2 // 打印字符P
  3 [section .gdt] ; 全局描述符表
  4 ; GDT definition
  5 ; 段基址 段界限 段属性
  6 GDT_ENTRY    : Descriptor 0,          0,                   0
  7 CODE32_DESC  : Descriptor 0,          Code32SegLen - 1,    DA_C + DA_32
  8 VIDEO_DESC   : Descriptor 0xB8000,    0x07FFF,             DA_DRWA + DA_32
  9 ; FDT end
 10 
 11 ; GDT Selector
 12 Code32Selector equ (0x0001 << 3) + SA_TIG + SA_RPL0 ; 0x0001==第二个选择子
 13 VideoSelector  equ (0x0002 << 3) + SA_TIG + SA_RPL0
 14 ; end of [section .gdt]
 15 
 16 [section .s32] ; 32位代码段
 17 [bits 32] ; 使用32位编译
 18 CODE32_SEGMENT: ; 32位代码段数据
 19 mov eax, 0
 20 mov ax, VideoSelector            ; 把视频段放到gs寄存器
 21 mov gs, ax
 22 mov edi, (80 * 12 + 37) * 2      ; 每行80个字符,每个字符占2字节(字符+属性);当前要放到12行37列
 23 mov ah, 0x0c                     ; 输出字符为红色
 24 mov al, P                      ; 输出字符P
 25 mov [gs:edi], ax                 ; 往gs寄存器指定位置写入显示信息
 26 jmp $
 27 
 28 // loader.asm
 29 // 打印字符串
 30 %include "inc.asm"
 31 
 32 org 0x9000
 33 
 34 jmp CODE16_SEGMENT
 35 
 36 [section .gdt]    ; 全局描述符表,部分段基址暂未知地址需使用时再调节
 37 ; GDT definition
 38 ;                                    段基址        段界限                    段属性
 39 GDT_ENTRY         :        Descriptor    0,            0,                        0
 40 CODE32_DESC       :        Descriptor    0,            Code32SegLen - 1,        DA_C    + DA_32
 41 VIDEO_DESC        :        Descriptor    0xB8000,      0x07FFF,                 DA_DRWA + DA_32
 42 DATA32_DESC       :        Descriptor    0,            Data32SegLen - 1,        DA_DR   + DA_32
 43 STACK_DESC        :        Descriptor    0,            TopOfStackInit,          DA_DRW  + DA_32
 44 ; GDT end
 45 
 46 GdtLen    equ    $ - GDT_ENTRY
 47 
 48 GdtPtr:                    ; 全局描述符表指针
 49         dw    GdtLen - 1   ; 偏移,记录描述符数量
 50         dd    0            ; 全局描述符起始地址,先初始化为0
 51 
 52 
 53 ; GDT Selector
 54 ;                        TI:描述符作用域1bit    RPL:请求权限级别2bit
 55 Code32Selector   equ (0x0001 << 3) + SA_TIG + SA_RPL0    ; 0x0001==第二个选择子
 56 VideoSelector    equ (0x0002 << 3) + SA_TIG + SA_RPL0
 57 Data32Selector   equ (0x0003 << 3) + SA_TIG + SA_RPL0
 58 StackSelector    equ (0x0004 << 3) + SA_TIG + SA_RPL0
 59 
 60 ; end of [section .gdt]
 61 
 62 TopOfStackInit equ 0x7c00
 63 
 64 [section .dat]                        ; 32位数据段
 65 [bits 32]
 66 DATA32_SEGMENT:
 67     DTOS           db    "D.T.OS!", 0            ; 注意添加字符串结束标记0
 68     DTOS_OFFSET          equ    DTOS - $$
 69     HELLO_WORLD    db    "Hello    World!", 0
 70     HELLO_WORLD_OFFSET   equ    HELLO_WORLD - $$
 71 
 72 Data32SegLen equ $ - DATA32_SEGMENT
 73 
 74 [section .s16]    ; 实模式代码段(16bit)
 75 [bits 16]         ; 使用16位编译
 76 CODE16_SEGMENT:
 77     mov ax, cs    ; 初始化相关寄存器
 78     mov ds, ax
 79     mov es, ax
 80     mov ss, ax
 81     mov sp, TopOfStackInit
 82 
 83     ; initialize GDT for 32 bits code segment
 84     mov esi, CODE32_SEGMENT
 85     mov edi, CODE32_DESC
 86 
 87     call InitDescItem
 88         
 89     mov esi, DATA32_SEGMENT
 90     mov edi, DATA32_DESC
 91 
 92     call InitDescItem
 93 
 94     ; initialize GDT pointer struct
 95     mov eax, 0                         ; 代码段地址左移4位
 96     mov ax, ds
 97     shl eax, 4
 98     add eax, GDT_ENTRY                 ; 代码段偏移地址==> 左移过后的代码段+全局描述符表入口地址偏移量
 99     mov dword [GdtPtr + 2], eax        ; 写入全局描述符表指针
100 
101     ; 1. load GDT
102     lgdt [GdtPtr]                      ; 加载全局描述符表
103 
104     ; 2. close interrupt
105     cli                                ; 关闭中断
106 
107     ; 3. open A20
108     in al, 0x92                        ; 通过0x92端口开启A20地址线开关
109     or al, 00000010b
110     out 0x92, al
111 
112     ; 4. enter protect mode
113     mov eax, cr0                       ; 设置cr0寄存器,进入保护模式
114     or eax, 0x01
115     mov cr0, eax
116 
117     ; 5. jump to 32 bits code
118     jmp dword Code32Selector : 0       ; 使用jmp跳转到32位代码段选择子的0偏移处
119 
120 
121 ; esi    --> code segment label
122 ; edi    --> descriptor label
123 InitDescItem:                         ; 初始化描述符项目
124     push eax
125     
126     mov eax, 0                        ; 代码段地址左移4位
127     mov ax, cs
128     shl eax, 4                        ; 实地址=段寄存器地址左移4位+偏移地址
129     add eax, esi
130     mov word [edi + 2], ax            ; 将段基址写入描述符2个字节(16位寄存器),低32位的16-31bit(偏移2字节)
131     shr eax, 16                       ; 移除eax实地址中已经写入段基址的2字节数据
132     mov byte [edi + 4], al            ; 将段基址写入描述符1个字节(8位寄存器),高32位的0-7bit(偏移4+0=4字节)
133     mov byte [edi + 7], ah            ; 将段基址写入描述符1个字节(8位寄存器),高32位的24-31bit(偏移4+3=7字节)
134 
135     pop eax
136     
137     ret
138 
139 
140 [section .s32]    ; 32位代码段
141 [bits 32]        ; 使用32位编译
142 CODE32_SEGMENT:    ; 32位代码段数据
143     mov ax, VideoSelector             ; 把视频段选择子放到gs全局段寄存器
144     mov gs, ax
145 
146     mov ax, StackSelector             ; 32位保护模式栈段
147     mov ss, ax
148 
149     mov ax, Data32Selector
150     mov ds, ax
151 
152     mov ebp, DTOS_OFFSET              ; 32位模式下用段内偏移地址
153     mov bx, 0x0C                      ; 黑底淡红字
154     mov dh, 12                        ; 指定行地址,注意行列都是从0开始
155     mov dl, 33                        ; 指定列地址
156 
157     call PrintString
158 
159     mov ebp, HELLO_WORLD_OFFSET       ; 32位保护模式下字符串偏移地址
160     mov bx, 0x0C
161     mov dh, 13
162     mov dl, 31
163 
164     call PrintString                  ; 32位保护模式打印字符串
165 
166 
167     jmp $
168 
169 ; ds:ebp    --> string address
170 ; bx        --> attribute
171 ; dx        --> dh : row, dl : col
172 PrintString:                          ; 打印字符串函数
173     push ebp
174     push eax
175     push edi
176     push cx
177     push dx
178 print:
179     mov cl, [ds:ebp]                  ; cl记录要打印的字符
180     cmp cl, 0                         ; 对比字符串结束符号
181     je end
182     mov eax, 80                       ; 每行字符数
183     mul dh                            ; 乘以行数
184     add al, dl                        ; 加上列数,最终计算出要显示的位置
185     shl eax, 1                        ; 左移乘以2,计算字节偏移
186     mov edi, eax                      ; 写入显示的偏移地址到edi
187     mov ah, bl                        ; 字符属性写入高位
188     mov al, cl                        ; 字符写入低位
189     mov [gs:edi], ax                  ; 写入显存对应地址
190     inc ebp                           ; 指向下一个字符
191     inc dl                            ; 指向屏幕的下一列
192     jmp print
193 
194 end:
195     pop dx
196     pop cx
197     pop edi
198     pop eax
199     pop ebp
200     ret
201 
202 Code32SegLen    equ    $ - CODE32_SEGMENT

输出

  在显示器的字符模式下12行37列打印字符P为淡红色(注意行列都从0开始)
      技术分享图片

 

技术分享图片
 
    保护模式输出字符串
    技术分享图片技术分享图片

 

以上是关于第12课 - 实模式到保护模式(下)的主要内容,如果未能解决你的问题,请参考以下文章

实模式和保护模式区别及寻址方式

13 从保护模式返回实模式

实模式和保护模式区别及寻址方式

OS实模式和保护模式区别及寻址方式

01保护模式

实模式与保护模式下的分段分页机制