首先,你要明白一個概念,指針,是做什么的?答案是,指針,是指向地址的。程序指針,指向的空間,在物理上是Flash,在邏輯上,就是代碼空間。比如說51單片機的PC指針,指向的就是Flash,即程序下一步要執行的指令的地址。
數據指針,指向的空間,在物理上有Flash和RAM,在邏輯上是Flash里的常數空間和數據空間,注意,是對于單片機來說,對于我們的電腦,常數空間不是在Flash上。
比如說51單片機的DPTR,如果用MOVC A,@A+DPTR,此時,就是指向常數空間,如果用MOVX A,@A+DPTR就是指向的數據空間。
堆棧指針,指向的空間,在物理上是RAM,在邏輯上,就是數據空間,是特定的數據空間,堆棧是數據空間中單獨劃分出來,專門用于寄存中間結果的內存空間。
數據指針和堆棧指針主要有兩個區別:
一是數據指針可以指向Flash,即可以指向常數,比如說我們定義一個數組 unsigned char code Table[99],此時,就是DPTR可以指向常數空間。堆棧指針是不可以的,只能是指向RAM。
第二個區別,堆棧指針指向的是特定的數據空間,這個特定的數據空間,是從整個數據空間里劃分出來,專門用于作堆棧用的,堆棧區間一旦劃分出來,堆棧指針在規則上,就只能在這個范圍內活動,如果出了這個范圍,可能導致整個程序的崩潰。而數據指針在規則上,可以指向整個數據空間,但是,可以讀堆棧空間,不應該去修改,否則也可能導致程序的崩潰。
在51單片機中,SP棧指針是一個專用的8位寄存器,
系統復位后,SP初始化為07H,使得堆棧指針實際上是由08H單元開始。 在響應中斷或子程序調用時,發生入棧操作,入棧的是16位PC值; 51中有PUSH壓入和POP彈出棧操作指令,
如有必要,在中斷或調用子程序時可用POSU指令把PSW或其它需要保護的寄存器的內容
壓入堆棧加以保護;返回前再使用POP指令把它們恢復。
51的內部RAM只有從00H到7FH共計128字節的空間,而且00H~1FH是工作寄存器區, 所以SP的設定一般設定是從20H到70H這個范圍。 51堆棧的容量最大也不會超過128字節。
1、在計算機領域,堆棧是一個不容忽視的概念,但是很多人甚至是計算機專業的人也沒有明確堆棧其實是兩種數據結構。堆棧都是一種數據項按序排列的數據結構,只能在一端(稱為棧頂(top))對數據項進行插入和刪除。要點:堆,順序隨意。棧,后進先出(Last-In/First-Out)。區分隊列 先進先出
2、堆棧是一塊保存數據的連續內存。 一個名為堆棧指針(SP)的寄存器指向堆棧的頂部。 堆棧的底部在一個固定的地址。 堆棧的大小在運行時由內核動態地調整。 CPU實現指令 PUSH和POP, 向堆棧中添加元素和從中移去元素。 堆棧由邏輯堆棧幀組成。
當調用函數時邏輯堆棧幀被壓入棧中, 當函數返回時邏輯 堆棧幀被從棧中彈出。 堆棧幀包括函數的參數, 函數地局部變量, 以及恢復前一個堆棧 幀所需要的數據, 其中包括在函數調用時指令指針(IP)的值。 堆棧既可以向下增長(向內存低地址)也可以向上增長, 這依賴于具體的實現。 在我 們的例子中, 堆棧是向下增長的。 這是很多計算機的實現方式, 包括Intel, Motorola, SPARC和MIPS處理器。
堆棧指針(SP)也是依賴于具體實現的。 它可以指向堆棧的最后地址, 或者指向堆棧之后的下一個空閑可用地址。 在我們的討論當中, SP指向堆棧的最后地址。 除了堆棧指針(SP指向堆棧頂部的的低地址)之外, 為了使用方便還有指向幀內固定 地址的指針叫做幀指針(FP)。 有些文章把它叫做局部基指針(LB-local base pointer)。 從理論上來說, 局部變量可以用SP加偏移量來引用。 然而, 當有字被壓棧和出棧后, 這 些偏移量就變了。
盡管在某些情況下編譯器能夠跟蹤棧中的字操作, 由此可以修正偏移 量, 但是在某些情況下不能。 而且在所有情況下, 要引入可觀的管理開銷。 而且在有些 機器上, 比如Intel處理器, 由SP加偏移量訪問一個變量需要多條指
令才能實現。 因此, 許多編譯器使用第二個寄存器, FP, 對于局部變量和函數參數都可以引用, 因為它們到FP的距離不會受到PUSH和POP操作的影響。 在Intel CPU中, BP(EBP)用于這 個目的。 在Motorola CPU中, 除了A7(堆棧指針SP)之外的任何地址寄存器都可以做FP。 考慮到我們堆棧的增長方向, 從FP的位置開始計算, 函數參數的偏移量是正值, 而局部 變量的偏移量是負值。 當一個例程被調用時所必須做的第一件事是保存前一個FP(這樣當例程退出時就可以 恢復)。 然后它把SP復制到FP, 創建新的FP, 把SP向前移動為局部變量保留空間。 這稱為 例程的序幕(prolog)工作。 當例程退出時, 堆棧必須被清除干凈, 這稱為例程的收尾 (epilog)工作。 Intel的ENTER和LEAVE指令, Motorola的LINK和UNLINK指令, 都可以用于 有效地序幕和收尾工作。
3、普通的8051MCU堆棧指針只有8位,所以堆棧不可能超過256字節13086.
SP:堆棧指針(SP,Stack Pointer),專門用于指出堆棧頂部數據的地址。
堆棧介紹:日常生活中,我們都注意到過這樣的現象,家里洗的碗,一只一只摞起來,最晚放上去的放在最上面,而最早放上去的則放在最下面,在取的時候正好相反,先從最上面取,這種現象我們用一句話來概括:“先進后出,后進先出”。請大家想想,還有什么地方有這種現象?其實比比皆是,建筑工地上堆放的磚頭、材料,倉庫里放的貨物,都是“先進后出,后進先出”,這實際是一種存取物品的規則,我們稱之為“堆棧”。
在單片機中,我們也能在RAM中構造這樣一個區域,用來存放數據,這個區域存放數據的規則就是“先進后出,后進先出”,我們稱之為“堆棧”。為什么需要這樣來存放數據呢?存儲器本身不是能按地址來存放數據嗎?對,知道了地址的確就能知道里面的內容,但如果我們需要存放的是一批數據,每一個數據都需要知道地址那不是麻煩嗎?如果我們讓數據一個接一個地放置,那么我們只要知道第一個數據所在地址單元就能了(看圖2)如果第一個數據在27H,那么第二、三個就在28H、29H了。所以利用堆棧這種辦法來放數據能簡化操作
那么51中堆棧什么地方呢?單片機中能存放數據的區域有限,我們不能夠專門分配一塊地方做堆棧,所以就在內存(RAM)中開辟一塊地方,用于堆棧,但是用內存的哪一塊呢?還是不好定,因為51是一種通用的單片機,各人的實際需求各不相同,有人需要多一些堆棧,而有人則不需要那么多,所以怎么分配都不合適,怎樣來解決這個問題?分不好干脆就不分了,把分的權利給用戶(編程者),根據自已的需要去定吧,所以51單片機中堆棧的位置是能變化的。而這種變化就體現在SP中值的變化,看圖2,SP中的值等于27H不就相當于是一個指針指向27H單元嗎?當然在真正的51機中,開始指針所指的位置并非就是數據存放的位置,而是數據存放的前一個位置,比如一開始指針是指向27H單元的,那么第一個數據的位置是28H單元,而不是27H單元,為什么會這樣?
什么是堆棧?MCS-51單片機的堆棧怎樣設置的?
答:程序設計時,往往需要一個后進先出的RAM區,以保存CPU的現場。這種后進先出的緩沖區,就稱為堆棧。
MCS-51單片的堆棧原則上設在內部RAM的任意區域內 。但是,一般設在31H~7FH的范圍之間,棧頂的位置由棧指針SP指出。
51單片機堆棧操作指令舉例說明
這4類指令的作用是把直接尋址單元的內容傳送到堆棧指針SP所指的單元中,以及把SP
所指單元的內容送到直接尋址單元中。這類指令只有兩條,下述的第一條常稱為入棧操作指令,第二條稱為出棧操作指令。需要指出的是,單片機開機復位后,(SP)默認為07H,但一般都需要重新賦值,設置新的SP首址。入棧的第一個數據必須存放于SP+1所指存儲單元,故實際的堆棧底為SP+1所指的存儲單元。
堆棧操作指令有兩條: PUSH direct POP direct 第一條指令稱之為推入,就是將direct中的內容送入堆棧中,第二條指令稱之為彈出,就是將堆棧中的內容送回到direct中。推入指令的執行過程是,首先將SP中的值加1,然后把SP中的值當作地址,將direct中的值送進以 堆棧操作指令有兩條: PUSH direct POP direct
第一條指令稱之為推入,就是將direct中的內容送入堆棧中,第二條指令稱之為彈出,就是將堆棧中的內容送回到direct中。推入指令的執行過程是,首先將SP中的值加1,然后把SP中的值當作地址,將direct中的值送進以SP中的值為地址的RAM單元中。例: MOV SP,
#5FH MOV A,
#100 MOV B,
#20 PUSH ACC
PUSH B
則執行第一條PUSH ACC指令是這樣的:將SP中的值加1,即變為60H,然后將A中的值送到60H單元中,因此執行完本條指令后, 內存60H單元的值就是100,同樣,執行PUSH B時,是將SP+1,即變為61H,然后將B中的值送入到61H單元中,即執行完本條指令后,61H單元中的值變為20。
POP指令的在單片機中執行是這樣的,首先將SP中的值作為地址,并將此地址中的數送到POP指令后面的那個direct中,然后SP減1。 接上例: POP B POP ACC 則執行過程是:將SP中的值(現在是61H)作為地址,取61H單元中的數值(現在是20),送到B中,所以執行完本條指令后B中的值是20,然后將SP減1,因此本條指令執行完后,SP的值變為60H,然后執行POP ACC,將SP中的值(60H)作為地址,從該地址中取數(現在是100),并送到ACC中,所以執行完本條指令后,ACC中的值是100。
這有什么意義呢?ACC中的值本來就是100,B中的值本來就是20,是的,在本例中,的確沒有意義,但在實際工作中,則在PUSH B后一般要執行其他指令,而且這些指令會把A中的值,B中的值改掉,所以在程序的結束,如果我們要把A和B中的值恢復原值,那么這些指令就有意義了。 還有一個問題,如果我不用堆棧,比如說在PUSH ACC指令處用MOV 60H,A,在PUSH B處用指令MOV 61H,B,然后用MOV A,60H,MOV B,61H來替代兩條POP指令,不是也一樣嗎?是的,從結果上看是一樣的,但是從過程看是不一樣的,PUSH和POP指令
都是單字節,單周期指令,而MOV指令則是雙字節,雙周期指令。更何況,堆棧的作用不止于此,所以一般的計算機上都設有堆棧,單片機也是一樣,而我們在編寫子程序,需要保存數據時,常常也不采用后面的辦法,而是用堆棧的辦法來實現。 例:寫出以下單片機程序的運行結果 MOV 30H,
#12 MOV 31H,
#23 PUSH 30H
PUSH 31H POP 30H POP 31H
結果是30H中的值變為23,而31H中的值則變為12。也就兩者進行了數據交換。從這個例程能看出:使用堆棧時,入棧的書寫次序和出棧的書寫次序必須相反,才能保證數據被送回原位,不然就要出錯了。 另外特別注意事項:
進行堆棧操作時,我們不能: PUSH R0 PUSH R1 而只能: PUSH 00H PUSH 01H POP也是一樣。
評論
查看更多