1. 程式人生 > >第12課 - 實模式到保護模式(下)

第12課 - 實模式到保護模式(下)

文本 選擇 字符串結束 使用 hello 移除 mode back fdt

不一般的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課 - 實模式到保護模式(下)