第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课 - 实模式到保护模式(下)的主要内容,如果未能解决你的问题,请参考以下文章