彙編語言王爽 第十章檢測點上機實驗

目錄

 

檢測點10.1 ——考察ret和retf指令

檢測點10.1知識點小結

檢測點10.2 ——考察通過位移進行轉移的call指令“call 標號”

檢測點10.2知識點小結

檢測點10.3——考察轉移地址在指令中的call指令“call far ptr 標號”

檢測點10.3知識點小結

檢測點10.4——考察轉移地址在寄存器中的call指令“call 16位reg” 

檢測點10.4知識點小結

檢測點10.5——考察轉移地址在內存中的call指令“call word ptr 內存單元地址”  & “call dword ptr 內存單元地址” 

檢測點10.5(1)涉及知識點小結

檢測點10.5(1)涉及知識點小結

call指令的小結



檢測點10.1 ——考察ret和retf指令

 

assume cs:codesg,ss:stacksg
stacksg segment
    db 16 dup (0)
stacksg ends

codesg segment
start:    mov ax,stacksg
        mov ss,ax        ;初始化棧段
        mov sp,16     
     
        mov ax,1000h     ;高位是代碼段地址,
        push ax          ;先將段地址push入棧
        
        mov ax,0000H     ;低位是ip,
        push ax          ;ip放棧頂,故在cs入棧後ip才入棧
        
        retf    
        ;retf指令將棧頂的兩個字型數據pop出棧作爲cs:ip
        ;pop的先後順序是先ip後cs
        ;因此棧內應該是棧頂填ip,而cs填在ip下一個“單元”
        ;此單元字長爲一個字:因爲cs和ip都是4位16進制數表示,即4*4=16bit=2byte=1個字
        
        mov ax,4ch
        int 21h
codesg ends 
end start


調試過程如下,結果如下

  • 上圖用-u命令先查看代碼是否正確裝入內存 

 

  • 使用-t命令單步運行,在push ax運行之前,利用-d 076A:0 f命令查看段名爲stacksg的棧段數據

 

  • 繼續利-t命令單步運行程序,直到在兩次push指令都執行結束,
  • 而retf未執行前,利用-d 076A:0 f命令查看段名爲stacksg的棧段數據,可知兩個push指令正確修改了棧段數據

 

  • 接下來單步執行retf後,發現cs:ip如預期的那樣被修改了,實現了內存從1000:0000處開始執行命令。

檢測點10.1的答案:

mov ax,1000h

mov ax,0000h

檢測點10.1知識點小結

ret:

  1. 作用:使用棧頂元素修改ip地址
  2. 轉移類型:屬於段內轉移中的近轉移——依據位移進行轉移的指令。
  3. 修改範圍:能修改的字長是2byte(ip的字長,即16bit)可以到達的轉移目的地址共有2^16個(65536個)地址,其位移範是-32768~32767(補碼錶示)
  4. 使用該指令不需要任何標號,直接使用ret即可實現將ip改爲當前棧頂元素,同時將該棧頂元素pop出棧,相當於進行下面兩步操作:
    1. (IP)=((ss)* 16 +(sp))【將棧頂元素“賦值”給ip】
    2. (sp)=(sp)+2                       【修改指針所指內存單元表示棧頂元素出棧】

retf:

  1. 作用:先使用棧頂元素修改ip地址,再使用下一個棧頂元素修改cs地址
  2. 轉移類型:屬於段間轉移的遠轉移——依據地址進行轉移的指令。
  3. 使用該指令不需要任何標號,直接使用retf即可實現cs:ip的修改,同時相應地pop出棧頂元素,相當於進行下面四步操作:
    1. (IP)=((ss)* 16 +(sp))  【將棧頂元素“賦值”給ip】
    2. (sp)=(sp)+2                         【修改指針所指內存單元表示棧頂元素出棧】
    3. (CS)=((ss)* 16 +(sp))【將棧頂元素“賦值”給ip】
  4. (sp)=(sp)+2                         【修改指針所指內存單元表示棧頂元素出棧】

     

檢測點10.2 ——考察通過位移進行轉移的call指令“call 標號”

assume cs:codesg

codesg segment
start:	mov ax,0
		call s
		inc ax
	s:	pop ax
	
		mov ax,4ch
		int 21h

codesg ends 

end start

 

實驗步驟、實驗結果如下

觀察黃色邊框內容,可知“call 標號”命令在編譯過程中標號會被替換成標號的ip地址

觀察藍色邊框內容,發現機器碼內不包含標號的ip地址,可知“call 標號”是根據位移進行轉移的指令

通過上圖,可以驗證“call 標號”的作用——簡稱爲“壓棧+跳轉”

壓棧:即將這條call指令的下一條指令的第一個字節的ip地址壓棧(這個ip地址在本例中爲0006,如下圖)

跳轉:跳轉到標號處的指令(標號在編譯中被譯爲標號的ip地址,本例中爲0007)

本例驗證了“call 標號”指令的功能

檢測點10.2答案:

ax的數值爲0006h(0006也正確)

檢測點10.2知識點小結

“call 標號”指令

  1. 指令作用:將當前IP壓棧後,然後跳轉到標號處執行標號處的指令。
  2. “當前IP”:也可以稱爲“call 標號”指令的下一條指令的第一個字節的ip地址,具體原因可以複習cs:ip的機制
  3. 轉移範圍:屬於段內轉移的近轉移,是依據位移進行的轉移。位移範圍是-32768~32767。
  4. 位移 = 標號處的地址 - call指令下一條指令的第一個字節的地址。(是16bit,用補碼錶示)
  5. 位移在編譯過程中算出。
  6. 指令執行時CPU的操作如下
    1. (sp)=(sp)- 2                      【壓棧前的預備操作】
    2. ((ss)* 16+(sp))=(IP)【壓棧】
    3. (IP)=(IP)+16位位移
    4. jmp near ptr 標號                       【轉移】
  7. 也相當於執行下面的操作
    1. push IP
    2. jmp near ptr 標號

檢測點10.3——考察轉移地址在指令中的call指令“call far ptr 標號”

assume cs:codesg,ss:stacksg

stacksg segment
	db 16 dup (0)
stacksg ends

codesg segment
start:	mov ax,stacksg 		;不能直接將數據直接傳入段寄存器:書本p49
		mov ss,ax			;要藉助一個通用寄存器(cs只能使用轉移指令)
		mov sp,16			;將棧頂指針指向棧底,初始化棧段

		mov ax,0
		call far ptr s		
		inc ax
	s:	pop ax
		add ax,ax
		pop bx
		add ax,bx
	
		mov ax,4ch
		int 21h

codesg ends 

end start

 實驗步驟和結果如下

觀察上圖上方兩個紅方框:

①源碼中的call指令中的標號在編譯過程中被翻譯成了標號的cs:ip地址

②call指令的機器指令中包含標號的cs:ip地址

此處驗證了“call far ptr 標號”指令的轉移地址在指令中的,而不是依據位移轉移的。

使用-g 076B:0008執行完棧段初始化的代碼、

驗證了“call far ptr 標號”指令的作用——“壓棧+跳轉”

壓棧(紅色方框和箭頭):將call指令的下一條指令的cs:ip壓棧

轉移(黃色方框和箭頭):轉移到從標號處開始執行指令(本例中的轉移地址爲076B:0011)

接下來將棧段的數據pop到ax和bx並做相應的加法

觀察ax可知先pop出來的是0010,即剛纔call指令壓入棧的ip地址

觀察bx可知後pop出來的 是007B,即剛纔call指令壓入棧的cs地址

由此驗證了壓棧的順序是:“先壓cs入棧後壓ip入棧”

檢測點10.3的答案:

實驗後,按原理推斷ax的值爲1010h(1000h+10h)

檢測點10.3知識點小結

  1. 指令作用:將call指令的下一條指令的cs:ip按一定順序壓入棧,然後跳轉到標號處執行指令。指令的作用簡稱爲——“壓棧+跳轉”
  2. “call far ptr 標號”是一個轉移地址在機器指令中的call指令
  3. 壓棧:的順序是——“先壓cs入棧後壓cs入棧”
  4. 跳轉:屬於段間遠轉移

  5. 指令執行時cpu進行如下操作

    1. (sp)=(sp)- 2

    2. ((ss)* 16 +(sp))=(CS)

    3. (sp)=(sp)- 2

    4. ((ss)* 16 +(sp))=(IP)

    5. (cs)=標號所在的段地址

    6. (ip)=標號所在的偏移地址

  6. 指令的效果也可以相當於執行如下操作

    1. push CS

    2. push IP

    3. jmp far ptr 標號


檢測點10.4——考察轉移地址在寄存器中的call指令“call 16位reg” 

assume cs:codesg
codesg segment
start:	mov ax,6    
		call ax		
		inc ax
	s:	mov bp,sp            
		add ax,[bp+00]              ;[bp]相當於[ss:bp]
		
		mov ax,4ch
		int 21h
codesg ends 
end start


 

實驗步驟和結果如下 

圖中按箭頭分析,可知“call 16位reg” 的作用也是“壓棧+跳轉”

壓棧和跳轉的特點都是隻修改ip而不修改cs,因此驗證了:

壓棧只壓ip入棧,跳轉範圍屬於段內轉移。

而且在call的機器碼中並沒有轉移的目的地址,因此叫做“轉移地址在寄存器中的call指令”

繼續執行指令,涉及到bp的知識

本例中,指令add將內存地址爲(ss:bp+00)與(ax)相加並賦予ax

結果ax從0006h變成了000Bh

由此也可以驗證內存地址爲ss:sp的內容爲0005h

即被壓入棧的:是call指令的下一條指令的第一個字節的ip

檢測點10.4的答案:

按照“call 16位reg”的原理可推斷出ax的數值是000bh

檢測點10.4知識點小結

  1. 指令作用:將call的下一條指令的第一個字節的ip壓入棧並跳轉到寄存器指定的ip地址執行指令。作用也可以簡稱爲——“壓棧+跳轉”

  2. 壓棧:特點是將call的下一條指令的第一個字節的ip壓入棧

  3. 跳轉:特點是屬於段內轉移,cs不變,只修改ip,並且(ip)=(ax)因此也叫作轉移地址在寄存器中的call指令

  4. 指令執行時,cpu進行如下操作

    1. (sp)=(sp)- 2

    2. ((ss)* 16 + (sp))=(IP)

    3. (ip)=(16位reg)

  5. 也可以把指令的效果等價於下面的操作

    1. push IP

    2. jmp 16位reg


檢測點10.5——考察轉移地址在內存中的call指令“call word ptr 內存單元地址”  & “call dword ptr 內存單元地址” 

第(1)小題的源碼如下

assume cs:codesg,ss:stacksg

stacksg segment
	dw 8 dup (0)
stacksg ends

codesg segment
start:	mov ax,stacksg 		;不能直接將數據直接傳入段寄存器:書本p49
		mov ss,ax			;要藉助一個通用寄存器(cs只能使用轉移指令)
		mov sp,16			;將棧頂指針指向棧底,初始化棧段
		mov ds,ax
		mov ax,0
		call word ptr ds:[0eh]
		inc ax
		inc ax
		inc ax
		mov ax,4ch
		int 21h

codesg ends 

end start


 

(1)此題無法使用debug驗證,推論可知ax=3h(可以畫出棧段的模型,助於理解)

檢測點10.5(1)涉及知識點小結

  1. 因爲涉及到內存地址單元,需要使用word ptr指定字長
  2. 指令作用:將call的下一條指令的第一個字節的ip壓入棧,並且跳轉到ip=(內存單元地址)的指令處執行指令
  3. 壓棧:只將call的下一條指令的第一個字節的ip壓入棧,不對cs壓棧。可理解爲段內轉移(短/近)
  4. 跳轉:特點是cs不改變,只修改ip爲(內存單元地址)
  5. 執行指令的時候CPU執行的是
    1. (sp)=(sp)- 2
    2. ((ss)* 16 +(sp))=(IP)
    3. (IP)=(內存單元地址)
  6. 也可以把指令效果等價於以下操作
    1. push IP
    2. jmp word ptr 內存單元地址

第(2)小題的源碼如下

assume cs:codesg

datasg segment
	dw 8 dup (0)
datasg ends

codesg segment
start:	mov ax,datasg 		;不能直接將數據直接傳入段寄存器:書本p49
		mov ss,ax			;要藉助一個通用寄存器(cs只能使用轉移指令)
		mov sp,16			;將棧頂指針指向棧底,初始化棧段
		mov word ptr ss:[0],offset s
		mov ss:[2],cs
		call dword ptr ss:[0] 
		nop
	s:	mov ax,offset s
		sub ax,ss:[0ch]		;寄存器指定了內存單元字長
		mov bx,cs
		sub bx,ss:[0eh]		;十六進制不能以字母開頭
		mov ax,4ch
		int 21h

codesg ends 

end start

 

實驗步驟和結果如下

使用debug單步執行到上圖

  1. 黃色邊框和箭頭:通過mov指令在數據段(datasg)的開頭處填入數據從低到高分別是標號s的偏移地址和代碼段地址cs
  2. 紅色邊框和箭頭:
    1. “call dword ptr 內存單元地址”被翻譯成了“call far 內存單元地址”可知這個指令的跳轉屬於段間遠轉移。
    2. 該指令的壓棧功能是把call指令的下一條指令的第一個字節的cs:ip按“先壓cs入棧後壓ip入棧”的順序壓入棧
  3. 藍色邊框和箭頭:此處是將(內存單元地址)作爲跳轉的目的地址,也就是說目的地址在內存單元中。

觀察上圖黃色邊框,可知nop指令只佔據一個字節單元,由此也可以直接推斷本例紅色邊框執行的結果

ax=001Ah-0019h=0001h

bx=076Bh-076Bh=0000h

檢測點10.5(2)的答案:

(2)此題推論可知ax=0001h  bx=000h(可以畫出棧段的模型,助於理解)

檢測點10.5(1)涉及知識點小結

  1. 因爲涉及到內存地址單元,需要使用dword ptr指定字長爲雙字型數據
  2. 指令作用:將call的下一條指令的第一個字節的cs:ip按一定順序壓入棧,並且跳轉到cs:ip=(內存單元地址)的指令處執行指令
  3. 壓棧:把call指令的下一條指令的第一個字節的cs:ip按“先壓cs入棧後壓ip入棧”的順序壓入棧
  4. 跳轉:特點是cs:ip都改變爲(內存單元地址)。屬於遠轉移
  5. 執行指令的時候CPU執行的是
    1. (sp)=(sp)- 2
    2. ((ss)* 16 +(sp))=(CS)
    3. (sp)=(sp)- 2
    4. ((ss)* 16 +(sp))=(IP)
    5. (IP)=(內存單元地址指定的內存單元的低位的2個字節)
    6. (CS)=(內存單元地址指定的內存單元的高位的2個字節)
  6. 也可以把效果等價於以下操作
    1. push CS
    2. push IP
    3. jmp dword ptr 內存單元地址

call指令的小結

類型 指令 跳轉範圍 共同點
依據位移進行轉移 call 標號 段內—短轉移、近轉移

①功能都可以簡稱爲:

壓棧+轉移

 

②壓棧:壓棧對象都是當前

的ip或cs:ip

 

轉移目的地址在寄存器中 call 16位reg 段內—短轉移、近轉移
轉移目的地址在內存單元中 call word ptr 內存單元地址 段內—短轉移、近轉移
call dword ptr 內存單元地址 段間—遠轉移
轉移目的地址在指令中 call far ptr 標號 段間—遠轉移

 

 

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