第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开始)

保护模式输出字符串

原文地址:https://www.cnblogs.com/Dua677/p/9665059.html

时间: 2024-10-12 00:41:50

第12课 - 实模式到保护模式(下)的相关文章

第十课 实模式到保护模式 上

远古时期的程序开发是直接操作物理内存的,CPU指令的操作数直接使用实地址(实际内存地址),程序员拥有绝对的权利,可以随意访问内存的任意一个地址,可以说是指哪打哪. 使用绝对的物理地址会带来很多问题,例如: 1.难以重定位,程序每次都需要同样地址的内存来运行. 2.给多道程序设计带来了障碍,也就是说,不管内存多大,但凡一个字节被其他程序占用都无法执行. 为了解决以上的问题,英特尔开发出了8086 CPU处理器,其引入了如下特性: 1.地址线宽度为20位,可访问1M内存空间 2.引入[段地址:偏移地

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

实模式和保护模式区别及寻址方式 转载请注明出处:http://blog.csdn.NET/rosetta 64KB-4GB-64TB? 我记得大学的汇编课程.组成原理课里老师讲过实模式和保护模式的区别,在很多书本上也有谈及,无奈本人理解和感悟能力实在太差,在很长一段时间里都没真正的明白它们的内含,更别说为什么实模式下最大寻址空间为1MB?段的最大长度不超过64KB?而保护模式下为啥最大寻址能力就变成了64TB?每个段最大也达4GB? 更甚者分段和分页这两个高深的概念像我这种菜鸟怎么也理解不了啊!

Linux-0.11源代码阅读二 实模式到保护模式

bootsect部分已经执行完成,程序也跳转到setup部分: start: ! ok, the read went well so we get current cursor position and save it for ! posterity. mov ax,#INITSEG ! this is done in bootsect already, but... mov ds,ax mov ah,#0x03 ! read cursor pos xor bh,bh int 0x10 ! sa

存储器的保护(一)——《x86汇编语言:从实模式到保护模式》读书笔记18

本文是原书第12章的学习笔记. 说句题外话,这篇博文是补写的,因为让我误删了,可恶的是CSDN的回收站里找不到! 好吧,那就再写一遍,我有坚强的意志.司马迁曰:“文王拘而演<周易>:仲尼厄而作<春秋>:屈原放逐,乃赋<离骚>:左丘失明,厥有<国语>:孙子膑脚,<兵法>修列:不韦迁蜀,世传<吕览>……”好了,不煽情了,进入正题. 第12章的代码如下. 1 ;代码清单12-1 2 ;文件名:c12_mbr.asm 3 ;文件说明:硬盘主引

存储器的保护(三)——《x86汇编语言:从实模式到保护模式》读书笔记20

存储器的保护(三) 改动本章代码清单,使之能够检測1MB以上的内存空间(从地址0x0010_0000開始,不考虑快速缓存的影响).要求:对内存的读写按双字的长度进行.并在检測的同一时候显示已检測的内存数量.建议对每一个双字单元用两个花码0x55AA55AA和0xAA55AA55进行检測. 上面的文字选自原书第12章的习题1. 这篇博文就讨论一下这道题.由于是初学,我不正确自己做太高的要求.仅仅要实现功能就可以. 代码清单 ;文件说明:第12章习题-1 ;创建日期:2016-3-7 ;------

《80X86汇编语言程序设计教程》十 实模式与保护模式的切换实例

1.  再次声明,需要纯DOS系统才能看到满意测试效果.内容是演示实模式与保护模式切换实例,实现功能是16进制显示从110000H开始的256个字节的值 2.  源代码如下: 1 ;功能:演示实模式与保护模式的切换,16进制显示从110000H开始的256个字节的值 2 ;16位偏移的段间直接转移指令的宏定义,这是一个JMP指令到所描述的地址 3 4 JUMP macro selector,offsetv 5 db 0eah ;操作码 6 dw offsetv ;16位偏移 7 dw selec

【自制操作系统04】从实模式到保护模式

通过前三章的努力,我们成功将控制权转交给了 loader.asm 这个程序.具体说就是 bios 通过加载并跳转到 0x7c00(IMB大叔们定的) 把控制权转交给了我们操作系统的第一个汇编程序 mbr.asm,然后 mbr.asm 里做的事就是通过加载 loader 程序并跳转到 0x900(这个是我们自己定的)把控制权转交给了 loader.asm 程序,目前这个程序里还只是向屏幕输出一行字符串"loader",今天我们就将扩展它.并且今天我们要做的事,是操作系统中的第一个精彩之处

程序的加载和执行(六)——《x86汇编语言:从实模式到保护模式》读书笔记26

程序的加载和执行(六)--<x86汇编语言:从实模式到保护模式>读书笔记26 通过本文能学到什么? NASM的条件汇编 用NASM编译的时候,通过命令行选项定义宏 Makefile的条件语句 在make命令行中覆盖Makefile中的变量值 第13章习题解答 复习如何构造栈段描述符 我们接着上篇博文说. 在我修改后的文件中,用到了条件汇编. 比如: %ifdef DEBUG put_core_salt: ;打印内核的符号 ... ... put_usr_salt: ;打印用户的符号 ... .

Linux进程5——实模式和保护模式

早期的Inter芯片只支持1MB内存,采用实模式,采用16bit地址.后来随着技术进步,出现可以访问更多内存的 保护模式芯片,采用32bit地址.为了保持对前面芯片的兼容,Inter支持这两种模式.当芯片启动时,默认处于实模式, 然后OS控制进入保护模式. 实模式和保护模式的最大区别: 实模式下,程序地址为真实的物理地址,可以访问任意地址空间,这样不同进程可能访问到其它进程程序,造成 严重错误. 保护模式下,程序地址为虚拟地址,然后由OS系统管理内存访问权限,这样每个进程只能访问分配给自己的物理

程序的载入和运行(五)——《x86汇编语言:从实模式到保护模式》读书笔记25

程序的载入和运行(五)--<x86汇编语言:从实模式到保护模式>读书笔记25 前面几篇博文最终把代码分析完了.这篇就来说说代码的编译.运行和调试. 1.代码的编译及写入镜像文件 之前我们都是在命令行输入命令进行编译和写入.源文件少的时候还不认为麻烦,当源文件多了,就会认为特别麻烦.有没有简单的方法呢? 当然有,就是用make工具. 1.1.什么是make工具 make是一个命令工具,它解释Makefile中的指令.在Makefile文件里描写叙述了整个project全部文件的编译顺序.编译规则