匯編基礎(chǔ)教程段的定義應(yīng)用詳解
段
將一段內(nèi)存定義為一個段,用一個段地址指示段,用偏移地址訪問段內(nèi)的單元
種類
代碼段
定義
對于8086PC機,在編程時,可以根據(jù)需要,將一組內(nèi)存單元定義為一個段。
可以將長度為 N( N≤64KB )的一組代碼,存在一組地址連續(xù)、起始地址為 16的倍數(shù)的內(nèi)存單元中,這段內(nèi)存是用來存放代碼的,從而定義了一個代碼段。
例如
這段長度為 10 字節(jié)的字節(jié)的指令,存在從123B0H~123B9H的一組內(nèi)存單元中,我們就可以認(rèn)為,123B0H~123B9H這段內(nèi)存單元是用來存放代碼的 ,是一個代碼段 ,它的段地址為123BH,長度為10字節(jié)。
如何使得代碼段中的指令被執(zhí)行
- CPU 只認(rèn)被 CS:IP 指向的內(nèi)存單元中的內(nèi)容為指令。
- 所以要將CS:IP指向所定義的代碼段中的第一條指令的首地址。
- CS = 123BH,IP = 0000H。
數(shù)據(jù)段
定義
我們可以將一組長度為N(N≤64K)、地址連續(xù)、起始地址為16的倍數(shù)的內(nèi)存單元當(dāng)作專門存儲數(shù)據(jù)的內(nèi)存空間,從而定義了一個數(shù)據(jù)段。
比如我們用123B0H~123B9H這段空間來存放數(shù)據(jù):
- 段地址:123BH
- 長度:10字節(jié)
將一段內(nèi)存當(dāng)作數(shù)據(jù)段,是我們在編程時的一種安排,我們可以在具體操作的時候 ,用 ds 存放數(shù)據(jù)段的段地址,再根據(jù)需要,用相關(guān)指令訪問數(shù)據(jù)段中的具體單元。
DS和[address]
我們要讀取10000H單元的內(nèi)容
CPU要讀取一個內(nèi)存單元的時候,必須先給出這個內(nèi)存單元的地址
- 在8086PC中,內(nèi)存地址由段地址和偏移地址組成。
- 8086CPU中有一個 DS寄存器,通常用來存放要訪問的數(shù)據(jù)的段地址。
將10000H(1000:0)中的數(shù)據(jù)讀到al中。
mov bx,1000H
mov ds,bx
mov al,[0]
mov 指令可以將一個內(nèi)存單元中的內(nèi)容送入一個寄存器。
mov指令的格式:
- mov 寄存器名,內(nèi)存單元地址
- “[…]”表示一個內(nèi)存單元, “[…]”中的0表示內(nèi)存單元的偏移地址。
如何用mov指令從10000H中讀取數(shù)據(jù)?
- 10000H表示為1000:0(段地址:偏移地址)
- 將段地址1000H放入ds
- 用mov al,[0]完成傳送(mov指令中的[]說明操作對象是一個內(nèi)存單元,[]中的0說明這個內(nèi)存單元的偏移地址是0,它的段地址默認(rèn)放在ds中)
如何把1000H送入ds?
- 8086CPU不支持將數(shù)據(jù)直接送入段寄存器的操作,ds是一個段寄存器。(硬件設(shè)計的問題)
- mov ds,1000H 是非法的。
- 數(shù)據(jù)>>一般的寄存器>>段寄存器
例如
我們將123B0H~123BAH的內(nèi)存單元定義為數(shù)據(jù)段,我們現(xiàn)在要累加這個數(shù)據(jù)段中的前3個單元中的數(shù)據(jù),
代碼如下:
棧段
棧
棧空間當(dāng)然也是內(nèi)存空間一部分,它只是一段可以以一種特殊方式進(jìn)行訪問的內(nèi)存空間。
操作方式
棧有兩個基本的操作
入棧:將一個新的元素放到棧頂
出棧:從棧頂取出一個元素
棧頂?shù)脑乜偸亲詈笕霔#枰鰲r,又最先被從棧中取出
棧的操作規(guī)則:LIFO(Last In First Out,后進(jìn)先出)
8086CPU的入棧和出棧操作都是以字為單位進(jìn)行的。
注意:字型數(shù)據(jù)用兩個單元存放,高地址單元放高 8 位,低地址單元放低8 位。
8086CPU提供入棧和出棧指令
SS:SP
8086CPU中,有兩個寄存器:
- 段寄存器SS 存放棧頂?shù)亩蔚刂?/li>
- 寄存器SP 存放棧頂?shù)钠频刂?/li>
任意時刻,SS:SP指向棧頂元素。
SS和SP只記錄了棧頂?shù)牡刂?,依靠SS和SP可以保證在入棧和出棧時找到棧頂。
棧頂超界的問題
種類
- 當(dāng)棧滿的時候再使用push指令入棧,
- ??盏臅r候再使用pop指令出棧,
8086CPU不保證對棧的操作不會超界。
8086CPU 只知道棧頂在何處(由SS:SP指示),而不知道讀者安排的棧空間有多大。
8086CPU的工作機理,只考慮當(dāng)前的情況:
- 當(dāng)前棧頂在何處;
- 當(dāng)前要執(zhí)行的指令是哪一條。
解決辦法
- 要根據(jù)可能用到的最大棧空間,來安排棧的大小,防止入棧的數(shù)據(jù)太多而導(dǎo)致的超界
- 執(zhí)行出棧操作的時候也要注意,以防??盏臅r候繼續(xù)出棧而導(dǎo)致的超界。
push、pop指令
定義
push和pop指令是可以在寄存器和內(nèi)存之間傳送數(shù)據(jù)的。
PUSH(入棧)
push ax:將寄存器ax中的數(shù)據(jù)送入棧中;
執(zhí)行過程 push ax
(1)SP=SP–2;
(2)將ax中的內(nèi)容送入SS:SP指向的內(nèi)存單元處,SS:SP此時指向新棧頂。
圖示
入棧時,棧頂從高地址向低地址方向增長
POP(出棧)
pop ax :從棧頂取出數(shù)據(jù)送入ax。執(zhí)行過程 (1)將SS:SP指向的內(nèi)存單元處的數(shù)據(jù)送入ax中;(2)SP = SP+2,SS:SP指向當(dāng)前棧頂下面的單元,以當(dāng)前棧頂下面的單元為新的棧頂。
圖示
注意:
出棧后,SS:SP指向新的棧頂 1000EH,pop操作前的棧頂元素,1000CH 處的2266H 依然存在 ,但是,它已不在棧中。
當(dāng)再次執(zhí)行push等入棧指令后,SS:SP移至1000CH,并在里面寫入新的數(shù)據(jù),它將被覆蓋。
格式
(1)
push 寄存器:將一個寄存器中的數(shù)據(jù)入棧
pop 寄存器:出棧,用一個寄存器接收出棧的數(shù)據(jù)
例如:push axpop bx
(2)
push 段寄存器:將一個段寄存器中的數(shù)據(jù)入棧 pop
段寄存器:出棧,用一個段寄存器接收出棧的數(shù)據(jù)
例如:push dspop es
(3)
push 內(nèi)存單元:將一個內(nèi)存單元處的字入棧(棧操作都是以字為單位)
pop 內(nèi)存單元:出棧,用一個內(nèi)存字單元接收出棧的數(shù)據(jù)
例如:push [0] pop [2]
指令執(zhí)行時 ,CPU 要知道內(nèi)存單元的地址,可以在 push、pop 指令中給出內(nèi)存單元的偏移地址,段地址在指令執(zhí)行時,CPU從ds中取得。
注意
與mov指令不同的是,push和pop指令訪問的內(nèi)存單元的地址不是在指令中給出的,而是由SS:SP指出的。
CPU執(zhí)行mov指令只需一步操作,就是傳送,而執(zhí)行push、pop指令卻需要兩步操作
執(zhí)行push時:先改變SP,后向SS:SP處傳送。
執(zhí)行pop時: 先讀取SS:SP處的數(shù)據(jù),后改變SP。
push、pop 等棧操作指令,修改的只是SP。也就是說,棧頂?shù)淖兓秶畲鬄椋?~FFFFH。
提供:SS、SP指示棧頂;改變SP后寫內(nèi)存的入棧指令;讀內(nèi)存后改變SP的出棧指令。
棧段定義
- 可以根據(jù)需要 ,將一組內(nèi)存單元定義為一個段
- 比如我們將10010H~1001FH 這段長度為 16 字節(jié)的內(nèi)存空間當(dāng)作棧來用,以棧的方式進(jìn)行訪問。
- 我們可以將長度為 N(N ≤64K )的一組地址連續(xù)、起始地址為16的倍數(shù)的內(nèi)存單元,當(dāng)作棧來用,從而定義了一個棧段
- 這段空間就可以成為棧段,段地址為1000H,大小為16字節(jié)
- 如何使的如push、pop 等棧操作指令訪問我們定義的棧段
- 將SS:SP指向我們定義的棧段。
思考
我們將10000H~1FFFFH這段空間當(dāng)作棧段 ,SS=1000H ,??臻g大小為64KB ,棧最底部的字單元地址為1000:FFFE。
任意時刻,SS:SP指向棧頂,當(dāng)棧中只有一個元素的時候,SS=1000H,SP=FFFEH。
棧為空,就相當(dāng)于棧中唯一的元素出棧,出棧后,SP=SP+2。
SP原來為FFFEH,加2后SP=0,所以,當(dāng)棧為空的時候,SS=1000H,SP=0。
任意時刻,SS:SP指向棧頂元素,當(dāng)棧為空的時候 ,棧中沒有元素 ,也就不存在棧頂元素,所以SS:SP只能指向棧的最底部單元下面的單元 ,該單元的偏移地址為棧最底部的字單元的偏移地址+2 ,棧最底部字單元的地址為1000:FFFE,所以??諘r,SP=0000H。
訪問
對于數(shù)據(jù)段,將它的段地址放在 DS中,用mov、add、sub等訪問內(nèi)存單元的指令時,CPU就將我們定義的數(shù)據(jù)段中的內(nèi)容當(dāng)作數(shù)據(jù)段來訪問;
對于代碼段,將它的段地址放在 CS中,將段中第一條指令的偏移地址放在IP中,這樣CPU就將執(zhí)行我們定義的代碼段中的指令;
對于棧段,將它的段地址放在SS中,將棧頂單元的偏移地置放在 SP 中,這樣CPU在需要進(jìn)行棧操作的時候,比如執(zhí)行 push、pop 指令等,就將我們定義的棧段當(dāng)作棧空間來用。
可見,不管我們?nèi)绾伟才?,CPU 將內(nèi)存中的某段內(nèi)存當(dāng)作代碼 ,是因為CS:IP指向了那里;CPU將某段內(nèi)存當(dāng)作棧 ,是因為 SS:IP 指向了那里
比如我們將10000H~1001FH安排為代碼段,并在里面存儲如下代碼:
設(shè)置CS=1000H,IP=0,這段代碼將得到執(zhí)行。
可以看到,在這段代碼中,我們又將10000H~1001FH 安排為棧段和數(shù)據(jù)段。
10000H~1001FH這段內(nèi)存,既是代碼段,又是棧段和數(shù)據(jù)段。
一段內(nèi)存,可以既是代碼的存儲空間,又是數(shù)據(jù)的存儲空間,還可以是??臻g,也可以什么也不是。
關(guān)鍵在于CPU中寄存器的設(shè)置,即:CS、IP、SS、SP、DS的指向。
段前綴
在訪問內(nèi)存單元的指令中,用于顯式地指明內(nèi)存單元的段地址的“ds:”、“cs:”、“ss:”或“es:”
應(yīng)用
場景1
問題:將內(nèi)存ffff:0~ffff:b段元中的數(shù)據(jù)拷貝到 0:200~0:20b單元中。
分析
(1) 0:200~0:20b單元等同于0020:0~0020:b單元,它們描述的是同一段內(nèi)存空間
(2)拷貝的過程應(yīng)用循環(huán)實現(xiàn),簡要描述如下:
- 初始化:X=0 循
- 環(huán)12次
將ffff:X單元中的數(shù)據(jù)送入0020:X(需要用一個寄存器中轉(zhuǎn))
X=X+1
(3)在循環(huán)中,源單元ffff:X和目標(biāo)單元的0020:X的偏移地址X是變量。我們用bx來存放
(4)我們用將0:200~0:20b用0020:0~0020:b描述,就是為了使目標(biāo)單元的偏移地址和源始單元的偏移地址從同一數(shù)值0開始。
問題
因源單元ffff:X和目標(biāo)單元0020:X 相距大于64KB,在不同的64KB段里,程序中,每次循環(huán)要設(shè)置兩次ds。
這樣做是正確的,但是效率不高。
解決
我們可以使用兩個段寄存器分別存放源單元ffff:X和目標(biāo)單元0020:X的段地址,這樣就可以省略循環(huán)中需要重復(fù)做12次的設(shè)置ds的程序段。
改進(jìn)的程序中,使用 es 存放目標(biāo)空間0020:0~0020:b的段地址,用ds存放源空間ffff:0~ffff:b的段地址。
在訪問內(nèi)存單元的指令“mov es:[bx],al”中 ,顯式地用段前綴 “es:” 給出單元的段地址,這樣就不必在循環(huán)中重復(fù)設(shè)置ds
場景2
Debug和匯編編譯器Masm對指令的不同處理
Debug中
- mov ax,[0]
- 表示將ds:0處的數(shù)據(jù)送入al中。
在匯編源程序中,指令“mov ax,[0]”被編譯器當(dāng)作指令“mov ax,0”處理。
解決:利用段前綴顯性的指出段地址
以上就是匯編基礎(chǔ)教程段的定義應(yīng)用詳解的詳細(xì)內(nèi)容,更多關(guān)于匯編基礎(chǔ)教程段的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
匯編語言AND指令實現(xiàn)對兩個操作數(shù)進(jìn)行邏輯(按位)與操作
這篇文章主要介紹了匯編語言AND指令實現(xiàn)對兩個操作數(shù)進(jìn)行邏輯(按位)與操作,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-01-01