匯編基礎教程段的定義應用詳解
段
將一段內存定義為一個段,用一個段地址指示段,用偏移地址訪問段內的單元
種類
代碼段
定義
對於8086PC機,在編程時,可以根據需要,將一組內存單元定義為一個段。
可以將長度為 N( N≤64KB )的一組代碼,存在一組地址連續、起始地址為 16的倍數的內存單元中,這段內存是用來存放代碼的,從而定義瞭一個代碼段。
例如
這段長度為 10 字節的字節的指令,存在從123B0H~123B9H的一組內存單元中,我們就可以認為,123B0H~123B9H這段內存單元是用來存放代碼的 ,是一個代碼段 ,它的段地址為123BH,長度為10字節。
如何使得代碼段中的指令被執行
- CPU 隻認被 CS:IP 指向的內存單元中的內容為指令。
- 所以要將CS:IP指向所定義的代碼段中的第一條指令的首地址。
- CS = 123BH,IP = 0000H。
數據段
定義
我們可以將一組長度為N(N≤64K)、地址連續、起始地址為16的倍數的內存單元當作專門存儲數據的內存空間,從而定義瞭一個數據段。
比如我們用123B0H~123B9H這段空間來存放數據:
- 段地址:123BH
- 長度:10字節
將一段內存當作數據段,是我們在編程時的一種安排,我們可以在具體操作的時候 ,用 ds 存放數據段的段地址,再根據需要,用相關指令訪問數據段中的具體單元。
DS和[address]
我們要讀取10000H單元的內容
CPU要讀取一個內存單元的時候,必須先給出這個內存單元的地址
- 在8086PC中,內存地址由段地址和偏移地址組成。
- 8086CPU中有一個 DS寄存器,通常用來存放要訪問的數據的段地址。
將10000H(1000:0)中的數據讀到al中。
mov bx,1000H
mov ds,bx
mov al,[0]
mov 指令可以將一個內存單元中的內容送入一個寄存器。
mov指令的格式:
- mov 寄存器名,內存單元地址
- “[…]”表示一個內存單元, “[…]”中的0表示內存單元的偏移地址。
如何用mov指令從10000H中讀取數據?
- 10000H表示為1000:0(段地址:偏移地址)
- 將段地址1000H放入ds
- 用mov al,[0]完成傳送(mov指令中的[]說明操作對象是一個內存單元,[]中的0說明這個內存單元的偏移地址是0,它的段地址默認放在ds中)
如何把1000H送入ds?
- 8086CPU不支持將數據直接送入段寄存器的操作,ds是一個段寄存器。(硬件設計的問題)
- mov ds,1000H 是非法的。
- 數據>>一般的寄存器>>段寄存器
例如
我們將123B0H~123BAH的內存單元定義為數據段,我們現在要累加這個數據段中的前3個單元中的數據,
代碼如下:
棧段
棧
棧空間當然也是內存空間一部分,它隻是一段可以以一種特殊方式進行訪問的內存空間。
操作方式
棧有兩個基本的操作
入棧:將一個新的元素放到棧頂
出棧:從棧頂取出一個元素
棧頂的元素總是最後入棧,需要出棧時,又最先被從棧中取出
棧的操作規則:LIFO(Last In First Out,後進先出)
8086CPU的入棧和出棧操作都是以字為單位進行的。
註意:字型數據用兩個單元存放,高地址單元放高 8 位,低地址單元放低8 位。
8086CPU提供入棧和出棧指令
SS:SP
8086CPU中,有兩個寄存器:
- 段寄存器SS 存放棧頂的段地址
- 寄存器SP 存放棧頂的偏移地址
任意時刻,SS:SP指向棧頂元素。
SS和SP隻記錄瞭棧頂的地址,依靠SS和SP可以保證在入棧和出棧時找到棧頂。
棧頂超界的問題
種類
- 當棧滿的時候再使用push指令入棧,
- 棧空的時候再使用pop指令出棧,
8086CPU不保證對棧的操作不會超界。
8086CPU 隻知道棧頂在何處(由SS:SP指示),而不知道讀者安排的棧空間有多大。
8086CPU的工作機理,隻考慮當前的情況:
- 當前棧頂在何處;
- 當前要執行的指令是哪一條。
解決辦法
- 要根據可能用到的最大棧空間,來安排棧的大小,防止入棧的數據太多而導致的超界
- 執行出棧操作的時候也要註意,以防棧空的時候繼續出棧而導致的超界。
push、pop指令
定義
push和pop指令是可以在寄存器和內存之間傳送數據的。
PUSH(入棧)
push ax:將寄存器ax中的數據送入棧中;
執行過程 push ax
(1)SP=SP–2;
(2)將ax中的內容送入SS:SP指向的內存單元處,SS:SP此時指向新棧頂。
圖示
入棧時,棧頂從高地址向低地址方向增長
POP(出棧)
pop ax :從棧頂取出數據送入ax。執行過程 (1)將SS:SP指向的內存單元處的數據送入ax中;(2)SP = SP+2,SS:SP指向當前棧頂下面的單元,以當前棧頂下面的單元為新的棧頂。
圖示
註意:
出棧後,SS:SP指向新的棧頂 1000EH,pop操作前的棧頂元素,1000CH 處的2266H 依然存在 ,但是,它已不在棧中。
當再次執行push等入棧指令後,SS:SP移至1000CH,並在裡面寫入新的數據,它將被覆蓋。
格式
(1)
push 寄存器:將一個寄存器中的數據入棧
pop 寄存器:出棧,用一個寄存器接收出棧的數據
例如:push axpop bx
(2)
push 段寄存器:將一個段寄存器中的數據入棧 pop
段寄存器:出棧,用一個段寄存器接收出棧的數據
例如:push dspop es
(3)
push 內存單元:將一個內存單元處的字入棧(棧操作都是以字為單位)
pop 內存單元:出棧,用一個內存字單元接收出棧的數據
例如:push [0] pop [2]
指令執行時 ,CPU 要知道內存單元的地址,可以在 push、pop 指令中給出內存單元的偏移地址,段地址在指令執行時,CPU從ds中取得。
註意
與mov指令不同的是,push和pop指令訪問的內存單元的地址不是在指令中給出的,而是由SS:SP指出的。
CPU執行mov指令隻需一步操作,就是傳送,而執行push、pop指令卻需要兩步操作
執行push時:先改變SP,後向SS:SP處傳送。
執行pop時: 先讀取SS:SP處的數據,後改變SP。
push、pop 等棧操作指令,修改的隻是SP。也就是說,棧頂的變化范圍最大為:0~FFFFH。
提供:SS、SP指示棧頂;改變SP後寫內存的入棧指令;讀內存後改變SP的出棧指令。
棧段定義
- 可以根據需要 ,將一組內存單元定義為一個段
- 比如我們將10010H~1001FH 這段長度為 16 字節的內存空間當作棧來用,以棧的方式進行訪問。
- 我們可以將長度為 N(N ≤64K )的一組地址連續、起始地址為16的倍數的內存單元,當作棧來用,從而定義瞭一個棧段
- 這段空間就可以成為棧段,段地址為1000H,大小為16字節
- 如何使的如push、pop 等棧操作指令訪問我們定義的棧段
- 將SS:SP指向我們定義的棧段。
思考
我們將10000H~1FFFFH這段空間當作棧段 ,SS=1000H ,棧空間大小為64KB ,棧最底部的字單元地址為1000:FFFE。
任意時刻,SS:SP指向棧頂,當棧中隻有一個元素的時候,SS=1000H,SP=FFFEH。
棧為空,就相當於棧中唯一的元素出棧,出棧後,SP=SP+2。
SP原來為FFFEH,加2後SP=0,所以,當棧為空的時候,SS=1000H,SP=0。
任意時刻,SS:SP指向棧頂元素,當棧為空的時候 ,棧中沒有元素 ,也就不存在棧頂元素,所以SS:SP隻能指向棧的最底部單元下面的單元 ,該單元的偏移地址為棧最底部的字單元的偏移地址+2 ,棧最底部字單元的地址為1000:FFFE,所以棧空時,SP=0000H。
訪問
對於數據段,將它的段地址放在 DS中,用mov、add、sub等訪問內存單元的指令時,CPU就將我們定義的數據段中的內容當作數據段來訪問;
對於代碼段,將它的段地址放在 CS中,將段中第一條指令的偏移地址放在IP中,這樣CPU就將執行我們定義的代碼段中的指令;
對於棧段,將它的段地址放在SS中,將棧頂單元的偏移地置放在 SP 中,這樣CPU在需要進行棧操作的時候,比如執行 push、pop 指令等,就將我們定義的棧段當作棧空間來用。
可見,不管我們如何安排 ,CPU 將內存中的某段內存當作代碼 ,是因為CS:IP指向瞭那裡;CPU將某段內存當作棧 ,是因為 SS:IP 指向瞭那裡
比如我們將10000H~1001FH安排為代碼段,並在裡面存儲如下代碼:
設置CS=1000H,IP=0,這段代碼將得到執行。
可以看到,在這段代碼中,我們又將10000H~1001FH 安排為棧段和數據段。
10000H~1001FH這段內存,既是代碼段,又是棧段和數據段。
一段內存,可以既是代碼的存儲空間,又是數據的存儲空間,還可以是棧空間,也可以什麼也不是。
關鍵在於CPU中寄存器的設置,即:CS、IP、SS、SP、DS的指向。
段前綴
在訪問內存單元的指令中,用於顯式地指明內存單元的段地址的“ds:”、“cs:”、“ss:”或“es:”
應用
場景1
問題:將內存ffff:0~ffff:b段元中的數據拷貝到 0:200~0:20b單元中。
分析
(1) 0:200~0:20b單元等同於0020:0~0020:b單元,它們描述的是同一段內存空間
(2)拷貝的過程應用循環實現,簡要描述如下:
- 初始化:X=0 循
- 環12次
將ffff:X單元中的數據送入0020:X(需要用一個寄存器中轉)
X=X+1
(3)在循環中,源單元ffff:X和目標單元的0020:X的偏移地址X是變量。我們用bx來存放
(4)我們用將0:200~0:20b用0020:0~0020:b描述,就是為瞭使目標單元的偏移地址和源始單元的偏移地址從同一數值0開始。
問題
因源單元ffff:X和目標單元0020:X 相距大於64KB,在不同的64KB段裡,程序中,每次循環要設置兩次ds。
這樣做是正確的,但是效率不高。
解決
我們可以使用兩個段寄存器分別存放源單元ffff:X和目標單元0020:X的段地址,這樣就可以省略循環中需要重復做12次的設置ds的程序段。
改進的程序中,使用 es 存放目標空間0020:0~0020:b的段地址,用ds存放源空間ffff:0~ffff:b的段地址。
在訪問內存單元的指令“mov es:[bx],al”中 ,顯式地用段前綴 “es:” 給出單元的段地址,這樣就不必在循環中重復設置ds
場景2
Debug和匯編編譯器Masm對指令的不同處理
Debug中
- mov ax,[0]
- 表示將ds:0處的數據送入al中。
在匯編源程序中,指令“mov ax,[0]”被編譯器當作指令“mov ax,0”處理。
解決:利用段前綴顯性的指出段地址
以上就是匯編基礎教程段的定義應用詳解的詳細內容,更多關於匯編基礎教程段的資料請關註WalkonNet其它相關文章!