彙編語言(王爽)第十章

第十章

call和ret都是轉移指令,都修改IP或同時修改CS和IP,用於子程序的設計

10.1 ret和retf

ret指令用棧中的數據,修改IP的內容,從而實現近轉移

retf指令用棧中的數據,修改CS和IP的內容,實現遠轉移

執行ret時:

1、(IP)=((ss)*16+(sp))

2、(sp)=(sp)+2

用匯編語法解釋,相當於pop IP

執行retf時:

1、(IP)=((ss)*16+(sp))

2、(sp)=(sp)+2

3、(CS)=((ss)*16+(sp))

2、(sp)=(sp)+2

用匯編語法解釋,相當於pop IP、pop CS

下面的程序中,ret指令執行後,(IP)=0,CS:IP指向代碼段的第一條指令

assume cs:code

stack segment
	db 16 dup(0)
stack ends

code segment
			mov ax,4c00h
			int 21h
	
	start:	mov ax,stack
			mov ss,ax
			mov sp,16
			
			mov ax,0
			push ax
			mov bx,0
			
			ret
code ends
end start

下面的程序中,retf指令執行後,CS:IP指向代碼段的第一條

assume cs:code

stack segment
	db 16 dup(0)
stack ends

code segment
			mov ax,4c00h
			int 21h
	
	start:	mov ax,stack
			mov ss,ax
			mov sp,16
			
			mov ax,0
			push cs
			push ax
			mov bx,0
			
			retf
code ends
end start

10.2 call指令

CPU指向call指令時,分兩步

1、將當前的IP或CS和IP壓入棧中

2、轉移

call指令不能實現短轉移,除此之外,call指令轉移的方法與jmp指令原理相同

10.3 依據位移進行轉移的call指令

call 標號:將當前IP壓入棧後,轉移到標號處執行指令

執行過程:

1、(sp)=(sp)-2

​ ((ss)*16+(sp))=(IP)

2、(IP)=(IP)+16位位移

16位位移由編譯器算出=標號處地址-call指令後的第一個字節的地址

範圍-32768~32767

用匯編語言解釋push IP

​ jmp near ptr 標號

下面程序執行後,ax中的數值爲多少?

內存地址    機器碼        彙編指令  

1000:0     b8 00 00     mov ax,0    

1000:3     e8 01 00     call s      

1000:6     40           inc ax

1000:7     58           s:pop ax       

CPU從內存中取第二條指令以後,IP指向下一條指令,(IP)=6,隨後執行call指令,IP的值入棧,IP執行標號處的指令,執行後(ax)=6,由於還沒遇到ret指令,所以ax中最終的數值是6

10.4 轉移的目的地址在指令中的call指令

call far ptr 標號 實現的是段間轉移

用匯編語言解釋push CS

​ push IP

​ jmp far ptr 標號

10.5 轉移地址在寄存器中的call指令

指令格式 call 16位reg

用匯編語法解釋push IP

​ jmp 16位reg

下面的程序執行後,ax中的數值爲多少

內存地址    機器碼        彙編指令  

1000:0     b8 06 00     mov ax,6    

1000:3     ff d0	    call ax    

1000:5     40           inc ax

1000:6     	            mov bp,sp

						add ax,[bp]

CPU從內存中取完call指令後,IP指向下一條指令,(IP)=5,執行call指令,(IP)的值入棧,跳轉,在執行add指令時,因爲用bp表示偏移地址時,默認段寄存器爲ss,因此就是(ax)+之前壓入棧中IP的值,最終(ax)=0bH

10.6 轉移地址在內存中的call指令

有兩種格式

第一種:call word ptr 內存單元地址

用匯編語言解釋push IP

​ jmp word ptr 內存單元地址

執行下面指令後,(IP)=0123H,(sp)=0EH

mov sp,10h
mov ax,0123h
mov ds:[0],ax
call word ptr ds:[0]

第二種:call dword ptr 內存單元地址

用匯編語言解釋push CS

​ push IP

​ jmp dword ptr 內存單元地址

10.7 call和ret的配合使用

一個具有一定功能的程序段,我們稱爲子程序,在需要的時候,用call指令轉去執行,ret返回

框架

assume cs:code
code segment
	main:
		……
		call sub1
		……
		mov ax,4c00h
		int 21h
		
	sub1:
		……
		call sub2
		……
		ret
		
	sub2:
		……
		ret
code ends
end main

10.8 mul指令

mul是乘法指令,使用的注意點

1、兩個相乘的數,要麼都是8位,要麼都是16位。如果是8位,一個默認放在AL中,另一個放在8位reg或內存字節單元中;如果是16位,一個默認在AX中,另一個放在16位reg或內存字單元中

2、結果如果是8位乘法,結果默認放在AX中,如果是16位乘法,結果高位默認在DX中存放,低位在AX中放

格式:mul reg/內存單元

內存單元可以用不同的尋址方式給出,如

mul byte ptr ds:[0]
mul word ptr [bx+si+8]

計算100*10000,由於10000大於255,所以必須做16位乘法

mov ax,100
mov bx,10000
mul bx

10.9 模塊化程序設計

利用call和ret實現多個相互聯繫、功能獨立的子程序解決一個複雜的問題

10.10 參數和結果的傳遞問題

用寄存器來存儲參數和結構是最常用的方法

編程,計算data段中第一組數據的三次方,結果保留在後面一組dword單元中

assume cs:code
data segment
	dw 1,2,3,4,5,6,7,8
	dd 0,0,0,0,0,0,0,0
data ends

code segment
	start:	mov ax,data
			mov ds,ax
			mov si,0		; ds:si指向第一組word單元
			mov di,16		; ds:di指向第二組dword單元
			
			mov cx,8
		s:	mov bx,[si]
        	call cube
        	
        	mov [di],ax		; 存儲低位數據
        	mov [di].2.dx	; 存儲高位數據
        	
        	add si,2
        	add di,4
        	loop s
        	
        	mov ax,4c00h
        	int 21h
        	
     cube:	mov ax,bx
    	 	mul bx
    	 	mul bx
    	 	
 code ends
 end start

10.11 批量數據的傳遞

如果要傳入多個參數,寄存器的數量是不足的,這種時候,我們將批量數據放到內存中,然後將它們所在內存空間的首地址放在寄存器中,傳遞給需要的子程序,對於批量返回的結果,用同樣的方法

編程,將data段中的字符串轉化爲大寫

assume cs:code

data segment
	db 'conversation'
data ends

code segment
	start:	mov ax,data
			mov ds,ax
			mov si,0		; ds:si指向批量數據所在空間的首地址
			mov cx,12		; cx存放字符串的長度
			call captial
			
			mov ax,4c00h
			int 21h
			
  capital:	and byte ptr [si],11011111b		; 表明參與運算的數據大小
  			inc si
  			loop capital
  			ret
  			
 code ends
 end start

注意,除了用寄存器傳遞參數外,還可以用棧來傳遞參數

10.12 寄存器衝突的問題

編程,將一個全是字母,以0結尾的字符串轉化爲大寫

因爲0標誌着字符串結束,所以子程序不需要字符串的長度作爲參數,用jcxz來檢測0

但是這會出現一個問題:因爲可能有不止一個字符串,因此需要循環將字符串變爲大寫,而在子程序中,jczx是根據cx的值進行跳轉,因此子程序會改變cx的值,影響程序 的正確運行

在編寫子程序時,注意子程序不要使用會和主程序產生衝突的寄存器,儘量使用其他寄存器代替

如果都必須用到同一個寄存器,則在子程序開始前將所用到的寄存器的內容先保存起來,可以用棧來保存,還要注意出棧的順序

assume cs:code
data segment
	db 'word',0
	db 'unix',0
	db 'wind',0
	db 'good',0
data ends

code segment
	start:	mov ax,data
			mov ds,ax
			mov bx,0
			
			mov cx,4
		s:	mov si,bx
			call capital
			add bx,5
			loop s
			
			mov ax,4c00h
			int 21h
			
  capital:	push cx
  			push si
  			
   change:	mov cl,[si]
   			mov ch,0
   			jczx ok
   			and byte ptr [si],11011111b
   			inc si
   			jmp short change
   			
   	   ok:	pop si
   	   		pop cx
   	   		ret
 code ends
 end start

編寫子程序的標準框架

子程序開始:子程序中使用的寄存器入棧

​ 子程序內容

​ 子程序中使用的寄存器出棧

​ 返回

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章