匯編語(yǔ)言之轉(zhuǎn)移指令和原理

1、引言

可以修改IP,或同時(shí)修改CS和IP的指令統(tǒng)稱為轉(zhuǎn)移指令。概括地講,轉(zhuǎn)移指令就是可以控制CPU執(zhí)行內(nèi)存

中某處代碼的指令。

8086CPU的轉(zhuǎn)移行為有以下幾類:

1. 同時(shí)修改CS和IP時(shí),稱為段間轉(zhuǎn)移,比如:jmp 100:2a7。

2. 只修改IP時(shí),稱為段內(nèi)轉(zhuǎn)移,比如:jmp ax。

由于轉(zhuǎn)移指令對(duì)IP的修改范圍不同,段內(nèi)轉(zhuǎn)移又分為“短轉(zhuǎn)移”和“近轉(zhuǎn)移”。

3. 段內(nèi)短轉(zhuǎn)移IP的修改范圍為-128~127。

4. 段內(nèi)近轉(zhuǎn)移IP的修改范圍為-32768~32767。

8086CPU的轉(zhuǎn)移指令分為以下幾類:

1. 無(wú)條件轉(zhuǎn)移指令(比如:jmp)

2. 條件轉(zhuǎn)移指令

3. 循環(huán)指令

4. 過(guò)程

5. 中斷

這些轉(zhuǎn)移指令轉(zhuǎn)移的前提條件可能不同,但轉(zhuǎn)移的基本原理是相同的,我們?cè)谶@一章主要通過(guò)深入學(xué)習(xí)無(wú)條件

轉(zhuǎn)移指令jmp來(lái)理解CPU執(zhí)行轉(zhuǎn)移指令的基本原理。
2、 jmp指令

Jmp為無(wú)條件轉(zhuǎn)移指令,可以只修改IP,也可以同時(shí)修改CS和IP。

Jmp指令要給出兩種信息:

1. 轉(zhuǎn)移的目的地址。

2. 轉(zhuǎn)移的距離(段間轉(zhuǎn)移、段內(nèi)短轉(zhuǎn)移、段內(nèi)近轉(zhuǎn)移)。

不同的給出目的地址的方法,和不同的轉(zhuǎn)移位置,對(duì)應(yīng)有不同格式的jmp指令,下面的幾節(jié)內(nèi)容中,我們以

給出目的地址的不同方法為主線,講解jmp指令的主要應(yīng)用格式和CPU執(zhí)行轉(zhuǎn)移指令的基本原理。
3、 依據(jù)位移進(jìn)行轉(zhuǎn)移的jmp指令

Jmp short 標(biāo)號(hào)(轉(zhuǎn)到標(biāo)號(hào)處執(zhí)行指令)。

這種格式的jmp指令,實(shí)現(xiàn)的是段內(nèi)短轉(zhuǎn)移,它對(duì)IP的修改范圍為-128~127,也就是說(shuō),它向前轉(zhuǎn)移時(shí)可以

最多越過(guò)128個(gè)字節(jié),向后轉(zhuǎn)移可以最多越過(guò)127個(gè)字節(jié)。Jmp指令中的“short”符號(hào),說(shuō)明指令進(jìn)行的是短

轉(zhuǎn)移,jmp指令中的“標(biāo)號(hào)”是代碼段中的標(biāo)號(hào),指明了指令要轉(zhuǎn)移的目的地,轉(zhuǎn)移指令執(zhí)行結(jié)束后,CS:IP應(yīng)該

指向標(biāo)號(hào)處的指令。

請(qǐng)看下面一段代碼:

Mov ax, 0

Jmp short s

Add ax, 1

  S:add ax, 2

最下面那條指令中的S就是標(biāo)號(hào),jmp short s指令執(zhí)行后,CS:IP指向s:add ax, 2,上面那條指令add ax,

1已被跳過(guò),沒(méi)有被CPU執(zhí)行。

在“jmp short 標(biāo)號(hào)”指令所對(duì)應(yīng)的機(jī)器碼中,不包含轉(zhuǎn)移的目的地址,而包含的是轉(zhuǎn)移的位移,這個(gè)位移是

編譯器根據(jù)匯編指令中的“標(biāo)號(hào)”計(jì)算出來(lái)的,具體的計(jì)算方法如下圖所示:

上圖中,標(biāo)號(hào)處的指令s0:inc bx的偏移地址為6,指令jmp s0后的第一個(gè)字節(jié)的偏移地址為3,位移量就是

6-3=3。

標(biāo)號(hào)處的指令s:inc ax的偏移地址為0,指令jmp s下的第一個(gè)字節(jié)的偏移地址為9,位移量就是0-9=﹣9。

“Jmp short 標(biāo)號(hào)”的功能為:IP=IP+8位位移:

1.8位位移=標(biāo)號(hào)處的地址-jmp指令后的第一個(gè)字節(jié)的地址。

2.short指明此處的位移為8位位移。

3.8位位移的范圍為﹣128~127,用補(bǔ)碼表示(本教程不講解補(bǔ)碼,若你想了解,請(qǐng)看相關(guān)書(shū)籍)。

4.8位位移由編譯程序在編譯時(shí)算出。

還有一種和“jmp short 標(biāo)號(hào)”功能相近的指令格式:“jmp near ptr 標(biāo)號(hào)”,它實(shí)現(xiàn)的是段內(nèi)近轉(zhuǎn)移。

“jmp near ptr 標(biāo)號(hào)”的功能為:IP=IP+16位位移。

1.16位位移=標(biāo)號(hào)處的地址-jmp指令后的第一個(gè)字節(jié)的地址。

2.near ptr指明此處的位移為16位位移,進(jìn)行的是段內(nèi)近轉(zhuǎn)移。

3.16位位移的范圍為﹣32768~32767,用補(bǔ)碼表示。

4.16位位移由編譯程序在編譯時(shí)算出。

style="text-align:center;">


上圖中,標(biāo)號(hào)處的指令s0:inc bx的偏移地址為6,指令jmp s0后的第一個(gè)字節(jié)的偏移地址為3,位移量就是
6-3=3。
標(biāo)號(hào)處的指令s:inc ax的偏移地址為0,指令jmp s下的第一個(gè)字節(jié)的偏移地址為9,位移量就是0-9=﹣9。
“Jmp short 標(biāo)號(hào)”的功能為:IP=IP+8位位移:
1.8位位移=標(biāo)號(hào)處的地址-jmp指令后的第一個(gè)字節(jié)的地址。
2.short指明此處的位移為8位位移。
3.8位位移的范圍為﹣128~127,用補(bǔ)碼表示(本教程不講解補(bǔ)碼,若你想了解,請(qǐng)看相關(guān)書(shū)籍)。
4.8位位移由編譯程序在編譯時(shí)算出。
還有一種和“jmp short 標(biāo)號(hào)”功能相近的指令格式:“jmp near ptr 標(biāo)號(hào)”,它實(shí)現(xiàn)的是段內(nèi)近轉(zhuǎn)移。
“jmp near ptr 標(biāo)號(hào)”的功能為:IP=IP+16位位移。
1.16位位移=標(biāo)號(hào)處的地址-jmp指令后的第一個(gè)字節(jié)的地址。
2.near ptr指明此處的位移為16位位移,進(jìn)行的是段內(nèi)近轉(zhuǎn)移。
3.16位位移的范圍為﹣32768~32767,用補(bǔ)碼表示。
4.16位位移由編譯程序在編譯時(shí)算出。
 
 
4、 轉(zhuǎn)移地址在指令中或寄存器中的jmp指令
“Jmp far ptr 標(biāo)號(hào)”實(shí)現(xiàn)的是段間轉(zhuǎn)移(又稱為遠(yuǎn)轉(zhuǎn)移),功能如下:
CS=標(biāo)號(hào)所在段的段地址,IP=標(biāo)號(hào)在段中的偏移地址;
“Far ptr”指明了指令用標(biāo)號(hào)的段地址和偏移地址修改CS和IP。
在“jmp far ptr 標(biāo)號(hào)”指令所對(duì)應(yīng)的機(jī)器碼中,包含轉(zhuǎn)移目的地的地址。
 
轉(zhuǎn)移的目的地在寄存器中的jmp指令,指令格式為:
Jmp 16位通用寄存器。
功能:IP=16位通用寄存器
這種指令我們?cè)谇懊娴膬?nèi)容(參見(jiàn)2.6節(jié))中已經(jīng)講過(guò),這里就不再詳述。
 
 
5、轉(zhuǎn)移地址在內(nèi)存中的jmp指令
轉(zhuǎn)移地址在內(nèi)存中的jmp指令有兩種格式:
1. “jmp word ptr 內(nèi)存單元地址”(段內(nèi)轉(zhuǎn)移)。
功能:從內(nèi)存單元地址處開(kāi)始存放著一個(gè)字,是轉(zhuǎn)移的目的偏移地址。內(nèi)存單元地址可用尋址方式的任一格式
給出,比如,下面的指令:
Mov ax, 123H
Mov DS:[200], ax
Jmp word ptr DS:[200]
執(zhí)行后,IP=123H
又比如,下面的指令:
Mov ax, 123H
Mov [bx], ax
Jmp word ptr [bx]
執(zhí)行后,IP=123H
2. “jmp dword ptr 內(nèi)存單元地址”(段間轉(zhuǎn)移)。
功能:從內(nèi)存單元地址處開(kāi)始存放著兩個(gè)字,高地址處的字是轉(zhuǎn)移的目的段地址,低地址處的字是轉(zhuǎn)移的目的
偏移地址。
CS=內(nèi)存單元地址+2
IP=內(nèi)存單元地址
內(nèi)存單元地址可用尋址方式的任一格式給出。比如,下面的指令:
Mov ax, 123H
Mov DS:[200], ax
Mov word ptr DS:[202], 100
Jmp dword ptr DS:[200]
執(zhí)行后,CS=100H,IP=123H,CS:IP指向100:123。
又比如,下面的指令:
Mov ax, 123H
Mov [bx], ax
Mov word ptr [bx+2], 100
Jmp dword ptr [bx]
執(zhí)行后,CS=100H,IP=123H,CS:IP指向100:123。
在上面的指令中,我們接觸到了一個(gè)新的符號(hào)“dword”,它表示什么意思呢?前面我們已學(xué)過(guò),Byte表示字
節(jié),word表示字,dword則表示雙字。
 
6 、CALL指令
Call和ret指令都是轉(zhuǎn)移指令,它們都修改IP,或同時(shí)修改CS和IP,它們經(jīng)常被共同用來(lái)實(shí)現(xiàn)子程序的設(shè)
計(jì)。這兩節(jié),我們講解call和ret指令的原理。
CPU執(zhí)行call指令時(shí),進(jìn)行兩步操作:1.將當(dāng)前的IP或CS和IP壓入棧中。2.轉(zhuǎn)移。
Call指令不能實(shí)現(xiàn)短轉(zhuǎn)移,除此之外,call指令實(shí)現(xiàn)轉(zhuǎn)移的方法和jmp指令的原理相同,下面我們以給出轉(zhuǎn)
移目的地址的不同方法為主線,講解call指令的主要應(yīng)用格式。
1. 依據(jù)位移進(jìn)行轉(zhuǎn)移的call指令。
Call 標(biāo)號(hào)(將當(dāng)前的IP壓入棧后,轉(zhuǎn)到標(biāo)號(hào)處執(zhí)行指令)。指令執(zhí)行時(shí),它的功能相當(dāng)于:
Push IP
Jmp near ptr 標(biāo)號(hào)
2. 轉(zhuǎn)移地址在指令中的call指令。
Call far ptr 標(biāo)號(hào)(實(shí)現(xiàn)的是段間轉(zhuǎn)移)。
指令執(zhí)行時(shí),它的功能相當(dāng)于:
Push CS
Push IP
Jmp far ptr 標(biāo)號(hào)
3. 轉(zhuǎn)移地址在寄存器中的call指令。
指令格式:call 16位通用寄存器
指令執(zhí)行時(shí),它的功能相當(dāng)于:
Push IP
Jmp 16位通用寄存器
4. 轉(zhuǎn)移地址在內(nèi)存中的call指令。
這種call指令有兩種格式:
格式1:call word ptr 內(nèi)存單元地址
指令執(zhí)行時(shí),它的功能相當(dāng)于:
Push IP
Jmp word ptr 內(nèi)存單元地址
格式2:call dword ptr 內(nèi)存單元地址
指令執(zhí)行時(shí),它的功能相當(dāng)于:
Push CS
Push IP
Jmp dword ptr內(nèi)存單元地址
 
7、 子程序
Ret指令用棧中的數(shù)據(jù)修改IP的數(shù)值,從而實(shí)現(xiàn)近轉(zhuǎn)移。Ret指令執(zhí)行時(shí),進(jìn)行下面兩步操作:
1. IP=(SS×16+SP)中的數(shù)據(jù)
2. SP=SP+2
指令執(zhí)行時(shí),它的功能相當(dāng)于:pop IP
學(xué)習(xí)了call和ret指令,現(xiàn)在來(lái)看一下,如何將它們配合使用來(lái)實(shí)現(xiàn)子程序的機(jī)制。請(qǐng)看下面的一段代碼:
Mov ax, 1
Mov cx, 3
Call s
Mov bx, ax
Mov ax, 4c00H
Int 21H
S:add ax,ax
   Loop s
   Ret
   我們來(lái)分析一下CPU執(zhí)行這一段代碼的過(guò)程。
1. CPU執(zhí)行第一、第二條指令后,CS:IP指向call s。
2. CPU將call s指令的機(jī)器碼讀入,IP指向call s后的指令mov bx, ax。
3. 執(zhí)行call s指令,將當(dāng)前IP值(指令mov bx, ax的偏移地址)壓入棧中,并將IP的值改變?yōu)闃?biāo)號(hào)s處的
偏移地址。
4. CPU從標(biāo)號(hào)s處執(zhí)行指令,直至loop指令循環(huán)完畢。
5. CPU指向并執(zhí)行ret指令,從棧中彈出一個(gè)數(shù)據(jù)(即先前壓入棧中的指令mov bx, ax的偏移地址)送入
IP,則CS:IP指向指令mov bx, ax。
6. CPU執(zhí)行指令mov bx, ax,并向下繼續(xù)執(zhí)行,直到執(zhí)行int 21H后,程序結(jié)束。
上面第3、第5項(xiàng)是重點(diǎn),它揭示了子程序執(zhí)行完之后,如何讓CPU接著call指令向下執(zhí)行。什么是子程序?
具有一定功能的程序段,我們稱之為子程序。比如,上面的那一段代碼,s:add ax, bx到ret那3條指令就是一個(gè)
簡(jiǎn)單的子程序,它的功能是把a(bǔ)x中的數(shù)值累加3次,累加次數(shù)放在cx中,用循環(huán)指令loop實(shí)現(xiàn)累加。
在需要的時(shí)候,我們用call指令轉(zhuǎn)去執(zhí)行它,執(zhí)行完子程序后,要讓CPU接著call指令向下執(zhí)行,則需要用
到ret指令,call指令轉(zhuǎn)去執(zhí)行子程序之前,call指令后面的指令的地址將被存儲(chǔ)在棧中,在子程序的后面使用ret
指令,用棧中的數(shù)據(jù)設(shè)置IP的值,從而轉(zhuǎn)到call指令后面的代碼處繼續(xù)執(zhí)行。
    在上面那一段代碼中,int 21H和loop這兩條指令可能你看不懂,不過(guò)沒(méi)關(guān)系,不影響討論call和ret指令。
 

 






作者:chen.yu
深信服三年半工作經(jīng)驗(yàn),目前就職游戲廠商,希望能和大家交流和學(xué)習(xí),
微信公眾號(hào):編程入門到禿頭 或掃描下面二維碼
零基礎(chǔ)入門進(jìn)階人工智能(鏈接)