1. 程式人生 > >x86彙編--保護模式下的氣泡排序

x86彙編--保護模式下的氣泡排序

        其實這個程式碼本身(氣泡排序)沒有什麼意思,而有用的是怎麼從真實模式下進入保護模式,以及怎麼 使用段選擇子和段描述符。

;保護模式下的冒泡測試

	;設定堆疊
	mov eax , cs
	mov ss  , eax 
	mov esp , 0x7c00

	;設定下es為0x7c00
	mov eax , 0x7c00
	mov es  , eax  

	;設定ds為段描述符表起始地址
	mov eax , [es:program_gdt+0x2]
	xor edx , edx 
	mov ebx , 16
	div ebx 
	mov ds  , eax 
	mov ebx , edx 

	;設定全域性描述符表
	;0號插槽
	mov dword[ebx+0x00],0x00
	mov dword[ebx+0x04],0x00
	;1號描述符:程式碼段
	mov dword[ebx+0x08],0x7c0001ff  ;主引導扇區起始地址:0x7c00;段界限長度為:512位元組(段界限=長度-1)
	mov dword[ebx+0x0c],0x00409800  ;G=0 D=1:該段內指令偏移地址和運算元為32位(用eip);p=1:一般虛擬記憶體中使用;
							   ;DPL=00 最高特權:表示該程式碼段可以訪問任何許可權段內的資料;
							   ;s=1:表示程式碼段/資料段(堆疊段也是資料段)(若為0,則表示系統段)
							   ;x=1:表示該段只執行,不可讀和寫(程式碼段要能執行的,但預設是一定不能寫入的)
                                               ;【x:執行c特權程式碼間呼叫r讀a已訪問位】
	;2號描述符;程式碼段的別名
	mov dword[ebx+0x10],0x7c0001ff
	mov dword[ebx+0x14],0x00409a00  ;因為程式碼段預設是要能執行的,上面的程式碼段是不能讀取的,這裡設定個可以讀取的程式碼段別名
							   
	;3號描述符:資料段;因為程式碼段不能寫入,所以用資料段來寫入資料
	mov dword[ebx+0x18],0x0000ffff  ;資料段起始地址:0x0000;段界限長度為4G:fffff+1 = 100000 = 2^20=1MB,
					                ;因為G=0,則(2^20)*(4kb)=4G
	mov dword[ebx+0x1c],0x00cf9200  ;G=0,4kb為偏移量單位;w=1:表示該資料段能寫
					                ;(資料段不能執行,預設是可以讀的)【xewa】
  
	;4號描述符:堆疊段
	mov dword[ebx+0x20],0x00007a00  ;ss=0x00000 esp=0x7a00
	mov dword[ebx+0x24],0x00409600  ;ew=11:表示向下擴充套件,可寫

	;5號描述符:視訊記憶體段
	mov dword[ebx+0x28],0x8000ffff  ;視訊記憶體起始地址:0xb8000;0xb8000~0xbffff
	mov dword[ebx+0x2c],0x0040920b

	mov eax , (6*8-1)
	mov [es:program_gdt],eax
	lgdt [es:program_gdt]

	;開啟快速A20
	mov dx , 0x92
	in  al , dx 
	or  al , 0x2
	out dx , al 

	;設定進入保護模式
	cli
	mov eax , cr0
	or  eax , 0x1
	mov cr0 , eax 

	;清除流水線,進入保護模式
	jmp dword 0x0008:flush
		[bits 32]
flush:
	
	;設定堆疊
	mov eax , 0x0020
	mov ss  , eax 
	mov esp , 0x7c00
	
	;段暫存器檢查規則:ss:可讀可寫;cs:可執行;其他的段暫存器一定要可讀(否則檢查不通過);
	;程式碼段不能讀取,所以用程式碼段的別名段讀取要排序對的資料
	mov eax , 0x0010 ;可讀的程式碼段(即:可以用作程式碼段也可以用作其他段暫存器)
	mov fs  , eax 
	
	;用視訊記憶體段來顯示資料
	mov eax , 0x0028
	mov es  , eax 

	;開始顯示未排序的array
	mov esi , array
	mov edi , 0x00
	mov ecx , (separate - array)
	call print_string
	
	;列印下分割線
	mov esi , separate
	mov ecx , (program_gdt - separate)
	call print_string
	
	;開始氣泡排序
	mov eax , 0x0018
	mov ds  , eax 
	mov ebx  , array
	mov ecx , (separate - array -1)
	call Bubble_sort
	
	;列印排序號的array
	mov esi , array
	mov ecx , (separate - array)
	call print_string

halt:
	hlt		
	
;=============Bubble sort====================
;輸入引數:ds:ebx  ecx
Bubble_sort:	
	push esi
@1:
	xor esi , esi
	push ecx 
@2:
	mov ax , [0x7c00 + ebx+esi] 
	cmp al , ah 
	jg @3
	xchg al , ah 
	mov [0x7c00 + ebx+esi],ax
@3:
	inc esi
	loop @2

	pop ecx 
	loop @1
	
	pop esi
	ret
;Bubble_sort end 
	
;============print_string=====================
;輸入引數 fs:esi  es:edi ecx
;輸出 esi指向字元末尾,edi 指向下個要顯示的地址 
print_string: 
	push ecx 
	push fs
	push es
	
show_array:
	mov al , [fs:esi]
	mov [es:edi] , al
	add edi , 2
	inc esi
	loop show_array

	pop es 
	pop fs 
	pop ecx 
	ret
;print_string	

;=============data=====================
	array db '9102837564pzlaoskdmrnebq'
	separate db '   ====   '
	
	program_gdt dw 0x00
		    dd 0x00007e00

	times 510-($-$$) db 0x00
					 dw 0xaa55

  結果如下圖: