匯編語言——寄存器(內存訪問 ss棧段寄存器)


一、棧的基本概念

棧有兩個基本的操作:入棧和出棧。
入棧:將一個新的元素放到棧頂;
出棧:從棧頂取出一個元素。
棧頂的元素總是最后入棧,需要出棧時,又最先被從棧中取出。
棧的操作規則:后進先出

8086CPU提供的棧機制

8086CPU提供入棧和出棧指令: (最基本的)
PUSH(入棧)
POP (出棧)
push ax:將寄存器ax中的數據送入棧中;
pop ax :從棧頂取出數據送入ax。

push和pop指令也可以在內存和寄存器傳輸數據(以棧的形式)
8086CPU的入棧和出棧操作都是以為單位進行的。

示例:

假設將10000H~1000FH這段內存當作棧來使用(其實CPU並不知道這段是代碼段,數據段還是棧段,都是人為設定的)
下面一段指令的執行過程:
 mov ax,0123H	 # AX=0123H
 push ax		 # 將AX的值推入棧中
 mov bx,2266H	 # BX=2266H
 push bx		 # 將BX的值推入棧中
 mov cx,1122H	 # CX=1122H
 push cx		 # 將CX的值推入棧中
 pop ax			 # 將棧頂的2個內存單元取出放到AX寄存器中,AX=1122H			
 pop bx			 # 將棧頂的2個內存單元取出放到BX寄存器中,BX=2266H
 pop cx			 # 將棧頂的2個內存單元取出放到CX寄存器中,CX=0123H

那么問題來了,在執行push和pop的時候,如何知道哪個單元是棧頂單元?

8086CPU中,有兩個寄存器:

  • 段寄存器SS  存放棧頂的段地址
  • 寄存器SP  存放棧頂的偏移地址

任意時刻,SS:SP指向棧頂元素

二、push和pop指令

push 指令的執行過程

在執行push ax指令時,主要做了一下2件事
(1)先將SP=SP–2
(2)將ax中的內容送入SS:SP指向的內存單元處,SS:SP此時指向新棧頂

pop 指令的執行過程

在執行pop ax指令時,主要做了一下2件事
(1)先將SS:SP指向的內存單元處的數據送入ax中;
(2)SP = SP+2,SS:SP指向當前棧頂下面的單元,以當前棧頂下面的單元為新的棧頂。

當棧為空的時候,SS:SP指向最高地址空間的下一位

任意時刻,SS:SP 指向棧頂元素,當棧為空的時候,棧中沒有元素,也就不存在棧頂元素,所以SS:SP 只能指向棧的最底部單元下面的單元,該單元的偏移地址為棧最底部的字單元的偏移地址+2,棧最底部字單元的地址為1000:000E,所以棧空時,SP=0010H。

棧頂超界的問題(簡單了解就好,我們又不做黑客)

原因:SS和SP只記錄了棧頂的地址,依靠SS和SP可以保證在入棧和出棧時找到棧頂

上面我說了,CPU並不知道這段是代碼段,數據段還是棧段,都是人為設定的所以加入這個棧空了,我們繼續pop,它就會把其它段的數據pop出。所以非常危險

解決辦法:一個很NC的辦法,就是我們在編程的時候要自己操心棧頂超界的問題 ,要根據可能用到的最大棧空間,來安排棧的大小,防止入棧的數據太多而導致的超界;執行出棧操作的時候也要注意,以防棧空的時候繼續出棧而導致的超界。

push和pop指令具體可以操作

# 可以為通用寄存器賦值
push ax
pop bx

# 可以為段寄存器賦值
push ds
pop es

# 可以為內存單元賦值
push [0]
pop [2]

示例:

1、將10000H~1000FH 這段空間當作棧,初始狀態是空的,將 AX、BX、DS中的數據入棧。

2、編程

(1)將10000H~1000FH 這段空間當作棧,初始狀態是空的;
(2)設置AX=001AH,BX=001BH;
(3)將AX、BX中的數據入棧;
(4)然后將AX、BX清零;
(5)從棧中恢復AX、BX原來的內容。

3、編程:

(1)將10000H~1000FH 這段空間當作棧,初始狀態是空的;
(2)設置AX=002AH,BX=002BH;
(3)利用棧 ,交換 AX 和 BX 中的數據。

 

4、補全下面的代碼,完成同樣的功能:在10000H處寫入字型數據2266H。

__________
__________
__________
mov ax,2266H
push ax

mov ax,1000H
mov ss,ax
mov sp,2    # 題目要求的是要將10000H位置寫入字形數據,10000H的偏移量是0,在執行push操作的時候回將sp-2,所以我們要把它指向10002H的位置
mov ax,2266H
push ax

棧段

上面說過我們可以定義一段內存單元為代碼段,數據段或棧段。

我們可以將一個段空間當做一個棧段,一個棧段最大64K,因為16位CPU的偏移地址最大為2^16B,如果超出FFFF它會重新變成0(FFFF+1=0000,1被拋棄了)。

CPU是如何判斷哪塊是什么段的?

我們可以將一段內存定義為一個段,用一個段地址指示段,用偏移地址訪問段內的單元。這完全是我們自己的安排。
我們可以用一個段存放數據,將它定義為“數據段”;
我們可以用一個段存放代碼,將它定義為“代碼段”;
我們可以用一個段當作棧,將它定義為“棧段”;
我們可以這樣安排,但若要讓CPU按照我們的安排來訪問這些段,就要:
對於數據段將它的段地址放在 DS中,用mov、add、sub等訪問內存單元的指令時,CPU就將我們定義的數據段中的內容當作數據段來訪問;

對於代碼段將它的段地址放在 CS中,將段中第一條指令的偏移地址放在IP中,這樣CPU就將執行我們定義的代碼段中的指令;

對於棧段,將它的段地址放在SS中,將棧頂單元的偏移地置放在 SP 中,這樣CPU在需要進行棧操作的時候,比如執行 push、pop 指令等,就將我們定義的棧段當作棧空間來用。

可見,不管我們如何安排 ,CPU 將內存中的某段內存當作代碼 ,是因為CS:IP指向了那里;CPU將某段內存當作棧 ,是因為 SS:IP 指向了那里。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM