來自:
弄了幾天了,終於弄好了,呵呵,也得到了不少東西。從保護模式到是模式的轉換一定要注意的有***:
1、程式開始時在實模式下要有自己的堆疊段,進入保護模式前先暫存ss及sp的值至某記憶體處,以便從保護模式返回實模式後恢復到原先的堆疊。
2、返回實模式前需把各段暫存器設定為規範段,包括ss也要設定
3、返回實模式前必須在16位段返回,不能在32位段裡返回實模式。
4、返回實模式前的段必須定義在gdt中。
5、在32位段下操作有關暫存器時,注意32位暫存器和16位暫存器的差別,類似於sp暫存器,應用使用esp暫存器。
6、在使用定義在ldt中的段時,一定要使用lldt先載入ldt緩衝暫存器(ldtr)。
7、用於返回實模式的**段的段界限必須為0ffffh,不能為實際長度,否則要麼在跳轉到實模式的時候出錯要麼在跳轉到實模式後執行int 21h出錯。(個人推測原因如下:80286開始為每個段暫存器增加了段描述符高速緩衝暫存器,而這些緩衝暫存器對於程式設計師是不可見的,且在實模式下是不能修改的,要想改變這些高速緩衝暫存器的值必須通過在保護模式下修改相應的段暫存器的選擇子來實現,這也是為什麼在返回實模式之前必須把ds、es、fs、gs、ss設定為規範段的原因。但是cs是乙個特殊的段,不能通過常規方式修改,只能通過段間跳轉修改,而一旦跳到實模式後又不能修改了,所以就要求在返回實模式的段的段描述符必須符合實模式下的要求。實模式下的段長度是0ffffh,也就是64k,如果用於返回實模式的段的段界限不是0ffffh,會導致返回實模式後實模式下的cs段的高速緩衝暫存器的段長度還是保護模式時的段界限值,這是不正確的,所以要求用於返回實模式的段的段界限必須是0ffffh。)
ps:所謂規範段是指實模式下的標準段屬性,一般段界限為0ffffh,段屬性為可讀寫。
自己的認識是從保護模式到實模式轉換的過程為從某個保護模式下的函式中跳到16位下,在16位模式下要做的工作是設定各個段暫存器和設定cr0,(此處需要特別特別注意的是段暫存器中的選擇子必須是要定義在gdt中的,而且其段界限必須為0xffff,否則仍然可以跳回實模式,但是會有段界限異常),然後再跳到最開始引導的段中關a20和開中斷(sti),之所以還要跳回最開始的引導段,是因為需要用jmp設定cs。
1.f和g段暫存器可以不設為normal
2.normal段界限必須為0xffff 不然仍可以返回,但會有段界限異常
3.code16所在的段界限可以不為0xffff
4.某個函式->16位關cr0&&設定段暫存器->跳到最開始引導的段關a20、開中斷(之所以跳到開始引導的段是因為
需要用jmp語句設定cs為段0)
5.**中將16位的**放在了檔案的最後,原來是放在display前的,是因為碰到了如下奇怪的情況在生成bin檔案後,jmp指令和下面的mov ax,videoselector指令混淆了,cpu執行時仍然是按照32位的指令執行的,所以將jmp解釋為32位的,而編譯的檔案中卻是16位的,因此會誤將mov的機器指令當做jmp指令中的運算元。
6.如5中所說,在原始檔中將**宣告為16位([bits 16])後,生成的機器碼為16位的,而在cpu執行時卻仍按32位指令執行,暫時還不知道是怎麼回事,有時間再看看。
**中需要注意的是在引導段中的這一句
mov [return_jmp+3],ax
這一句的作用是將jmp指令中的段位元組設為0。在16位模式下,段間jmp指令的格式為
offset segment
ea byte1 byte2 byte3 byte4
在32位模式下段間jmp指令的格式為
offset segment
ea byte1 byte2 byte3 byte4 byte5 byte6
[cpp]view plain
copy
org 0x7c00
jmp main
;資料結構
gdt:
default_seg:
dw 0,0,0,0
code32_seg:
dw 0x07ff
dw 0x0000
dw 0x9a00
dw 0x00cf
data_seg:
dw 0x07ff
dw 0x0000
dw 0x9200
dw 0x00cf
test_data_seg:
dw 0x07ff
dw 0x0000
dw 0x9250;三位元組的基址
dw 0x00cf
video_seg:;0x20
dw 0x07ff
dw 0x8000
dw 0x920b
dw 0x00cf
real_seg:;0x28
dw 0xffff
dw 0x0000
dw 0x9200
dw 0x00cf
code16_seg:
dw 0x07ff
dw 0x0000
dw 0x9a00
dw 0x00cf
call_gate_seg:
dw 0x0000
dw code_selector
dw 0x8c00
dw 0x0000
gdt_end:
gdtr:
dw gdt_end-gdt-1
dw gdt,0
default_selector equ default_seg-gdt
code_selector equ code32_seg-gdt
data_selector equ data_seg-gdt
test_data_selector equ test_data_seg-gdt
video_selector equ video_seg-gdt
real_seg_selector equ real_seg-gdt
code16_selector equ code16_seg-gdt
call_gate equ call_gate_seg-gdt
[bits 16]
main:
mov ax,cs
mov ds,ax
mov gs,ax
mov [return_jmp+3],ax
;init code32
xor eax,eax
mov ax,cs
shl eax,4
add eax,display
mov word [code32_seg+2],ax
shr eax,16
mov byte [code32_seg+4],al
mov byte [code32_seg+7],ah
;init code16
xor eax,eax
mov ax,cs
shl eax,4
add eax,back_to_real
mov word [code16_seg+2],ax
shr eax,16
mov byte [code16_seg+4],al
mov byte [code16_seg+7],ah
lgdt [gdtr]
cli
;a20
in al,0x92
or al,00000010b
out 0x92,al
;cr0
mov eax,cr0
or eax,00000001b
mov cr0,eax
call call_gate:0
real_entry:;00007cb8
mov ax,cs
mov ds,ax
mov es,ax
mov ss,ax
;關a20
;in al,0x92
;and al,11111101b
;out 0x92,al
;開中斷
sti
jmp $
[bits 32]
display:
mov ax,video_selector
mov gs,ax
mov ah,0x0c
mov al,'p'
mov edi,(80*0+0)*2
mov [gs:edi],ax
;jmp $
jmp code16_selector:0
;放在code16_seg中
;還在code16中,cs=0x0030
;align 32
[bits 16];指令是16位的,但cpu在32位模式下,取指時按32位取
back_to_real:
mov ax,real_seg_selector;mov eax,0xd88e0028
mov ds,ax
mov ds,ax
mov es,ax
;mov fs,ax
mov gs,ax
mov ss,ax
mov eax,cr0
and al,11111110b;!!!!!!!!! 段界限異常
mov cr0,eax
return_jmp:
jmp 0:real_entry;jmp far b866:00007cb8
times 510-($-$) db 0
dw 0xaa55
保護模式到實模式
弄了幾天了,終於弄好了,呵呵,也得到了不少東西。從保護模式到是模式的轉換一定要注意的有 1 程式開始時在實模式下要有自己的堆疊段,進入保護模式前先暫存ss及sp的值至某記憶體處,以便從保護模式返回實模式後恢復到原先的堆疊。2 返回實模式前需把各段暫存器設定為規範段,包括ss也要設定 3 返回實模式前...
實模式與保護模式
實模式與保護模式 1.實模式,又叫實位址模式,cpu完全按照8086的實際定址方法訪問從00000h fffffh 1mb大小 的位址範圍的記憶體,在這種模式下,cpu只能做單任務執行 定址公式為 實體地址 左移4位的段位址 偏移位址,即 實體地址是由16位的段位址和16位的段內偏移位址組成的。2....
實模式與保護模式
實模式 即實位址訪問模式 它是intel公司80286及以後的x86 80386,80486和80586等 相容處理器 cpu 的一種操作模式。實模式被特殊定義為20位位址記憶體可訪問空間上,這就意味著它的容量是2的20次冪 1m 的可訪問記憶體空間 物理記憶體和bios rom 軟體可通過這些位址...