在上一篇中我們雖然成功進入了保護模式,但是並沒有體驗到保護模式帶給我們的便利。其實在保護模式下定址空間可以達到4GB,實模式下1MB的定址能力差得太遠了。那麼下麵,我們就把程式稍作修改,體驗一下它對超過1MB記憶體的訪問能力。 我們來試驗一下讀寫大地址記憶體。在前面程式的基礎上,新建一個段,這個段以5M ...
在上一篇中我們雖然成功進入了保護模式,但是並沒有體驗到保護模式帶給我們的便利。其實在保護模式下定址空間可以達到4GB,實模式下1MB的定址能力差得太遠了。那麼下麵,我們就把程式稍作修改,體驗一下它對超過1MB記憶體的訪問能力。
我們來試驗一下讀寫大地址記憶體。在前面程式的基礎上,新建一個段,這個段以5MB為基址,遠遠超出實模式下1MB的界限。我們先讀出開始處8位元組的內容,然後寫入一個字元串,再從中讀出8位元組,如下所示:
call TestRead call TestWrite call TestRead
如果讀寫成功的話,兩次讀出的內容應該是不同的,而且第二次讀出的內容應該是我們寫進的字元串。字元串是保存在數據段中的,也是新增加的。增加的兩個段如下所示:
LABEL_DESC_DATA: Descriptor 0, DataLen-1, DA_DRW ; Data LABEL_DESC_TEST: Descriptor 0500000h, 0ffffh, DA_DRW SelectorData equ LABEL_DESC_DATA - LABEL_GDT SelectorTest equ LABEL_DESC_TEST - LABEL_GDT [SECTION .data1] ; 數據段 ALIGN 32 [BITS 32] LABEL_DATA: BootMessage: db "Joey, I'm in protected mode!" OffsetPMMessage equ BootMessage - $$ ;表示字元串BootMessage相對於本節的開始處(LABEL_DATA)的偏移 StrTest: db "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 0 OffsetStrTest equ StrTest - $$ DataLen equ $ - LABEL_DATA ; END of [SECTION .data1]
段[SECTION .s32]這個段的開頭初始化了ds、es和gs,讓ds指向新增的數據段,es指向新增的5MB記憶體的段,gs指向顯存。如下所示:
LABEL_SEG_CODE32: mov ax, SelectorData mov ds, ax ; 數據段選擇子 mov ax, SelectorTest mov es, ax ; 測試段選擇子 mov ax, SelectorVideo mov gs, ax ; 視頻段選擇子(目的)
數據段的基址便是LABEL_DATA的物理地址。於是OffsetStrTest既是字元串相對於LABEL_DATA的偏移,也是其在數據段中的偏移。我們在保護模式下需要用到的正是這個偏移,而不再是實模式下的地址。section的一點妙用指的便是這裡的$$。
; 初始化數據段描述符 xor eax, eax mov ax, ds shl eax, 4 add eax, LABEL_DATA mov word [LABEL_DESC_DATA + 2], ax shr eax, 16 mov byte [LABEL_DESC_DATA + 4], al mov byte [LABEL_DESC_DATA + 7], ah
運行如下所示:
第一次執行call TestRead時顯示8個空字元,第二次執行call TestRead時顯示ABCDEFGH。因為TestRead的段寄存器指向的是5MB記憶體地址處的內容,一開始是沒有任何數據。
TestRead: xor esi, esi mov ecx, 8 .loop: mov al, [es:esi] call DispAL inc esi loop .loop ret
然後第190行向這個5MB記憶體地址處依次寫入8位元組數據。
【源碼及軟盤映像】