目錄
檢測點10.2 ——考察通過位移進行轉移的call指令“call 標號”
檢測點10.3——考察轉移地址在指令中的call指令“call far ptr 標號”
檢測點10.4——考察轉移地址在寄存器中的call指令“call 16位reg”
檢測點10.5——考察轉移地址在內存中的call指令“call word ptr 內存單元地址” & “call dword ptr 內存單元地址”
檢測點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:
- 作用:使用棧頂元素修改ip地址
- 轉移類型:屬於段內轉移中的近轉移——依據位移進行轉移的指令。
- 修改範圍:能修改的字長是2byte(ip的字長,即16bit)可以到達的轉移目的地址共有2^16個(65536個)地址,其位移範是-32768~32767(補碼錶示)
- 使用該指令不需要任何標號,直接使用ret即可實現將ip改爲當前棧頂元素,同時將該棧頂元素pop出棧,相當於進行下面兩步操作:
- (IP)=((ss)* 16 +(sp))【將棧頂元素“賦值”給ip】
- (sp)=(sp)+2 【修改指針所指內存單元表示棧頂元素出棧】
retf:
- 作用:先使用棧頂元素修改ip地址,再使用下一個棧頂元素修改cs地址
- 轉移類型:屬於段間轉移的遠轉移——依據地址進行轉移的指令。
- 使用該指令不需要任何標號,直接使用retf即可實現cs:ip的修改,同時相應地pop出棧頂元素,相當於進行下面四步操作:
- (IP)=((ss)* 16 +(sp)) 【將棧頂元素“賦值”給ip】
- (sp)=(sp)+2 【修改指針所指內存單元表示棧頂元素出棧】
- (CS)=((ss)* 16 +(sp))【將棧頂元素“賦值”給ip】
- (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 標號”指令
- 指令作用:將當前IP壓棧後,然後跳轉到標號處執行標號處的指令。
- “當前IP”:也可以稱爲“call 標號”指令的下一條指令的第一個字節的ip地址,具體原因可以複習cs:ip的機制
- 轉移範圍:屬於段內轉移的近轉移,是依據位移進行的轉移。位移範圍是-32768~32767。
- 位移 = 標號處的地址 - call指令下一條指令的第一個字節的地址。(是16bit,用補碼錶示)
- 位移在編譯過程中算出。
- 指令執行時CPU的操作如下
- (sp)=(sp)- 2 【壓棧前的預備操作】
- ((ss)* 16+(sp))=(IP)【壓棧】
- (IP)=(IP)+16位位移
- jmp near ptr 標號 【轉移】
- 也相當於執行下面的操作
- push IP
- 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知識點小結
- 指令作用:將call指令的下一條指令的cs:ip按一定順序壓入棧,然後跳轉到標號處執行指令。指令的作用簡稱爲——“壓棧+跳轉”
- “call far ptr 標號”是一個轉移地址在機器指令中的call指令
- 壓棧:的順序是——“先壓cs入棧後壓cs入棧”
-
跳轉:屬於段間遠轉移
-
指令執行時cpu進行如下操作
-
(sp)=(sp)- 2
-
((ss)* 16 +(sp))=(CS)
-
(sp)=(sp)- 2
-
((ss)* 16 +(sp))=(IP)
-
(cs)=標號所在的段地址
-
(ip)=標號所在的偏移地址
-
-
指令的效果也可以相當於執行如下操作
-
push CS
-
push IP
-
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知識點小結
-
指令作用:將call的下一條指令的第一個字節的ip壓入棧並跳轉到寄存器指定的ip地址執行指令。作用也可以簡稱爲——“壓棧+跳轉”
-
壓棧:特點是將call的下一條指令的第一個字節的ip壓入棧
-
跳轉:特點是屬於段內轉移,cs不變,只修改ip,並且(ip)=(ax)因此也叫作轉移地址在寄存器中的call指令
-
指令執行時,cpu進行如下操作
-
(sp)=(sp)- 2
-
((ss)* 16 + (sp))=(IP)
-
(ip)=(16位reg)
-
-
也可以把指令的效果等價於下面的操作
-
push IP
-
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)涉及知識點小結
- 因爲涉及到內存地址單元,需要使用word ptr指定字長
- 指令作用:將call的下一條指令的第一個字節的ip壓入棧,並且跳轉到ip=(內存單元地址)的指令處執行指令
- 壓棧:只將call的下一條指令的第一個字節的ip壓入棧,不對cs壓棧。可理解爲段內轉移(短/近)
- 跳轉:特點是cs不改變,只修改ip爲(內存單元地址)
- 執行指令的時候CPU執行的是
- (sp)=(sp)- 2
- ((ss)* 16 +(sp))=(IP)
- (IP)=(內存單元地址)
- 也可以把指令效果等價於以下操作
- push IP
- 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單步執行到上圖
- 黃色邊框和箭頭:通過mov指令在數據段(datasg)的開頭處填入數據從低到高分別是標號s的偏移地址和代碼段地址cs
- 紅色邊框和箭頭:
- “call dword ptr 內存單元地址”被翻譯成了“call far 內存單元地址”可知這個指令的跳轉屬於段間遠轉移。
- 該指令的壓棧功能是把call指令的下一條指令的第一個字節的cs:ip按“先壓cs入棧後壓ip入棧”的順序壓入棧
- 藍色邊框和箭頭:此處是將(內存單元地址)作爲跳轉的目的地址,也就是說目的地址在內存單元中。
觀察上圖黃色邊框,可知nop指令只佔據一個字節單元,由此也可以直接推斷本例紅色邊框執行的結果
ax=001Ah-0019h=0001h
bx=076Bh-076Bh=0000h
檢測點10.5(2)的答案:
(2)此題推論可知ax=0001h bx=000h(可以畫出棧段的模型,助於理解)
檢測點10.5(1)涉及知識點小結
- 因爲涉及到內存地址單元,需要使用dword ptr指定字長爲雙字型數據
- 指令作用:將call的下一條指令的第一個字節的cs:ip按一定順序壓入棧,並且跳轉到cs:ip=(內存單元地址)的指令處執行指令
- 壓棧:把call指令的下一條指令的第一個字節的cs:ip按“先壓cs入棧後壓ip入棧”的順序壓入棧
- 跳轉:特點是cs:ip都改變爲(內存單元地址)。屬於遠轉移
- 執行指令的時候CPU執行的是
- (sp)=(sp)- 2
- ((ss)* 16 +(sp))=(CS)
- (sp)=(sp)- 2
- ((ss)* 16 +(sp))=(IP)
- (IP)=(內存單元地址指定的內存單元的低位的2個字節)
- (CS)=(內存單元地址指定的內存單元的高位的2個字節)
- 也可以把效果等價於以下操作
- push CS
- push IP
- jmp dword ptr 內存單元地址
call指令的小結
類型 | 指令 | 跳轉範圍 | 共同點 |
---|---|---|---|
依據位移進行轉移 | call 標號 | 段內—短轉移、近轉移 |
①功能都可以簡稱爲: 壓棧+轉移
②壓棧:壓棧對象都是當前 的ip或cs:ip
|
轉移目的地址在寄存器中 | call 16位reg | 段內—短轉移、近轉移 | |
轉移目的地址在內存單元中 | call word ptr 內存單元地址 | 段內—短轉移、近轉移 | |
call dword ptr 內存單元地址 | 段間—遠轉移 | ||
轉移目的地址在指令中 | call far ptr 標號 | 段間—遠轉移 |