本應(yīng)用筆記演示了一種在數(shù)據(jù)存儲器中實現(xiàn)軟堆棧的簡單方法,用于基于匯編的應(yīng)用。該方法使用MAXQ2000和其他基于MAXQ20的微控制器。示例代碼使用MAX-IDE的宏預(yù)處理功能編寫,MAX-IDE是Maxim基于項目的MAXQ?系列應(yīng)用開發(fā)和調(diào)試環(huán)境。
概述
MAXQ2000微控制器與MaximRISC微控制器系列中的其他MAXQ器件一樣,基于MAXQ20內(nèi)核。基于MAXQ20的微控制器通常實現(xiàn)16位寬的硬件堆棧,其電平數(shù)固定(MAXQ2000為16級),存儲在獨立于數(shù)據(jù)和代碼空間的專用內(nèi)部存儲器中。此硬件堆棧用于在子例程調(diào)用和中斷操作中保存和恢復(fù)微控制器的操作狀態(tài)。
雖然完全適合小型、緊密集中的應(yīng)用程序,但在較大的裝配應(yīng)用程序中使用深度嵌套的子例程(或在堆棧上保存和恢復(fù)多個工作寄存器的子例程)時,硬件堆棧很快就會耗盡空間。用C編程語言編寫的應(yīng)用程序(使用IAR的嵌入式工作臺?等編譯器)或Rowley Associates的MAXQ交叉工作通過利用數(shù)據(jù)存儲器中包含的“軟堆?!眮肀苊膺@個問題。此軟堆棧存儲子例程的調(diào)用/返回地址和本地工作變量。但是,MAXQ20內(nèi)核上沒有內(nèi)置機制來定位數(shù)據(jù)存儲器中的堆棧,可用于僅組裝應(yīng)用。
本應(yīng)用筆記演示了一種在數(shù)據(jù)存儲器中實現(xiàn)軟堆棧的簡單方法,用于基于匯編的應(yīng)用。本應(yīng)用筆記中的代碼可用于MAXQ2000和其他基于MAXQ20的微控制器。示例代碼使用MAX-IDE的宏預(yù)處理功能編寫,MAX-IDE是Maxim基于項目的MAXQ系列應(yīng)用開發(fā)和調(diào)試環(huán)境。
MAX-IDE環(huán)境的最新安裝包和文檔可免費下載:
最大 IDE 安裝 (ZIP)
MAXQ磁芯組裝指南 (PDF)
開發(fā)工具指南 (PDF)
MAXQ20內(nèi)核中的硬件堆棧操作
MAXQ20內(nèi)核使用兩種不同類型的堆棧操作:
PUSH 操作(包括操作碼 PUSH、LCALL 和 SCALL)用于在堆棧上存儲數(shù)據(jù)。這些操作將堆棧指針 SP 預(yù)遞增 1,然后將數(shù)據(jù)存儲在 SP 指針 (@SP) 指向的堆棧位置。
POP 操作(包括操作碼 POP、POPI、RE 和 RETI)用于從堆棧中檢索數(shù)據(jù)。這些操作從 SP 指向的堆棧位置檢索數(shù)據(jù),然后將堆棧指針后減 1。
由于硬件堆棧的主要功能之一是在調(diào)用子例程時保存和恢復(fù)地址,因此堆棧由 16 位(字)位置組成。此寬度允許在單次推送或彈出操作中保存或恢復(fù) 16 位指令指針 (IP) 寄存器。即使使用 PUSH 或 POP 將 8 位寄存器(如 AP)保存到堆?;驈亩褩;謴?fù),每個堆棧操作也始終使用整個 16 位字。
除了利用堆棧的各種操作碼外,微控制器還自動使用硬件堆棧的另外兩種情況:
當(dāng)中斷被處理時,當(dāng)前程序執(zhí)行點在中斷服務(wù)例程(由中斷向量寄存器 IV 指向)開始執(zhí)行之前被推送到堆棧上。
當(dāng)調(diào)用某些調(diào)試命令(如讀寄存器和寫入數(shù)據(jù)存儲器)需要執(zhí)行實用程序ROM中的代碼才能完成時,在將控制權(quán)傳輸?shù)綄嵱贸绦騌OM之前,當(dāng)前執(zhí)行點被推送到堆棧上。實用程序 ROM 中的調(diào)試例程完成后,執(zhí)行點將從堆棧中彈出,并恢復(fù)處理器的先前狀態(tài)。
向量到中斷服務(wù)例程或執(zhí)行調(diào)試器命令將始終需要使用硬件堆棧。由于此行為嵌入在硬件中,因此無法解決它。但是,對于更常見的 PUSH/POP 和 CALL/RET 指令對,可以實現(xiàn)軟堆棧。
注意,當(dāng)出現(xiàn)以下任一堆棧錯誤情況時,MAXQ2000(與其它基于MAXQ20內(nèi)核的器件一樣)不提供任何錯誤檢測或警告:
堆棧溢出:當(dāng)值被推送到已經(jīng)滿的堆棧上時發(fā)生。此錯誤會導(dǎo)致堆棧中最舊的值被覆蓋。
堆棧下溢:當(dāng)堆棧為空時,從堆棧中彈出值時發(fā)生。此錯誤會導(dǎo)致返回?zé)o效的數(shù)據(jù)值。例如,如果在堆棧為空時執(zhí)行 RET,則執(zhí)行將轉(zhuǎn)移到不正確(可能是隨機)的地址。
在數(shù)據(jù)存儲器中創(chuàng)建軟堆棧
在數(shù)據(jù)存儲器中創(chuàng)建軟堆棧的第一步是定義將使用數(shù)據(jù)存儲器的哪一部分。然后,必須定義用于跟蹤堆棧頂部當(dāng)前位置的數(shù)據(jù)存儲器指針(DP[0]、DP[1]或BP[Offs])。注意:請注意,應(yīng)用軟件不會將專用于堆棧的數(shù)據(jù)存儲器用于其他目的(例如,變量或緩沖區(qū))。
定義和初始化這種軟堆棧的一種簡單方法涉及BP[Offs]寄存器對和一個等值。
SS_BASE equ 0100h ss_init: move DPC, #1Ch ; Set all pointers to word mode move BP, #SS_BASE ; Set base pointer to stack base location move Offs, #0 ; Set stack to start ret
如果基本指針 (BP) 寄存器設(shè)置為SS_BASE位置,則可以使用 Offs 寄存器指向堆棧的當(dāng)前頂部。由于 Offs 寄存器只有 8 位寬,因此硬件會自動將堆棧限制在范圍 (BP .(BP+255))在數(shù)據(jù)存儲器中。如果在 Offs 等于 255(溢出)時發(fā)生推送,或者在 Offs 等于 0(下溢)時發(fā)生彈出,則 Offs 寄存器將簡單地繞行到堆棧的另一端。此操作模擬硬件堆棧的運行方式,并允許簡單的軟堆棧實現(xiàn);這不會檢測下溢和溢出情況。
在使用軟堆棧之前,主應(yīng)用程序必須調(diào)用ss_init例程。它將 BP[Offs] 指針設(shè)置為字模式,必須這樣做,因為軟堆棧是作為 16 位寬堆棧實現(xiàn)的。它還將 BP[Offs] 寄存器對指向堆棧的開頭。
軟堆棧操作
使用堆棧的內(nèi)置操作碼(PUSH、POP、CALL、RET等)不能重新定義為使用軟堆棧,因為它們的含義被硬連接到MAXQ匯編器中。此外,可能仍需要在應(yīng)用程序的某些部分(如中斷服務(wù)例程)中使用標準硬件堆棧。由于這些原因,軟堆棧將由復(fù)制標準操作碼的宏訪問。
mpush MACRO Reg move @BP[++Offs], Reg ; Push value to soft stack endm mpop MACRO Reg move Reg, @BP[Offs--] ; Pop value from soft stack endm mcall MACRO Addr LOCAL return move @BP[++Offs], #return ; Push return destination to soft stack jump Addr return: endm mret MACRO jump @BP[Offs--] ; Jump to popped destination from soft stack endm
我們現(xiàn)在將討論這些宏是如何工作的。
mpush
此宏的使用方式與 PUSH 操作代碼相同。它允許將 8 位或 16 位寄存器或即時值推送到堆棧。
mpush A[0] ; Save the value of the A[0] register mpush A[1] ; Save A[1] mpush A[2] ; Save A[2] ... ; code which destroys A[0]-A[2] mpop A[2] ; Restore the value of A[2] (pop in reverse order) mpop A[1] ; Restore A[1] mpop A[0] ; Restore A[0]
mpop
此宏的使用方式與 POP 操作代碼相同。它允許從堆棧加載 8 位或 16 位寄存器,如上所示。請注意,如果推送 16 位值并將該值彈出到 8 位寄存器中,則寄存器中將僅存儲低字節(jié)。高字節(jié)將丟失。這與內(nèi)置硬件堆棧的行為相同。
subroutine: mpush A[0] ; Save the current value of A[0] ... ; Code which destroys A[0] mpop A[1] ; Restore A[0] mret
mcall 宏的使用方式與 CALL op 代碼用于執(zhí)行子例程的方式相同。此子例程必須使用 mret 宏(而不是標準 RET 操作代碼)在完成執(zhí)行后返回。
mcall mySub ... mySub: mpush A[0] ; Save A[0] mpush A[1] ; Save A[1] ... ; Perform calculations, etc. mpop A[1] mpop A[0] mret
擴展軟堆棧的大小
某些應(yīng)用需要大于 256 個級別的軟堆棧。通過使用其他數(shù)據(jù)指針(DP[0] 或 DP[1])之一,可以實現(xiàn)任何大小的堆棧(最多可達可用數(shù)據(jù)存儲器的限制)。
SS_BASE equ 0000h SS_TOP equ 01FFh ss_init: move DPC, #1Ch ; Set all data pointers to word mode move DP[0], #SS_BASE ; Set pointer to stack base location ret
上面顯示的代碼保留數(shù)據(jù)存儲器中的位置 0000h 到 01FFh,從而創(chuàng)建一個最多可容納 511 個級別的堆棧。(堆棧內(nèi)存空間中的一個位置未使用;這允許更短、更高效地實現(xiàn) mpush/mpop/mcall/mret 宏)。
只需更改數(shù)據(jù)指針并將代碼中的其他所有內(nèi)容保留相同即可擴展堆棧。盡管如此,由于DP[0]不限于數(shù)據(jù)存儲器的某個范圍,因此沒有什么可以阻止推送或彈出操作使DP[0]增加或減少到軟堆棧的指定邊界之外。為了避免這種情況,可以添加一些簡單的下溢/溢出檢查。
添加下溢和溢出檢查
為了使宏(每次使用宏時都會擴展為代碼)盡可能短,下溢和溢出檢查在子例程中執(zhí)行,這些子例程由宏使用硬件堆棧調(diào)用。
mpush MACRO Reg call ss_check_over ; Check for possible overflow move @++DP[0], Reg ; Push value to soft stack endm mpop MACRO Reg call ss_check_under ; Check for possible underflow move Reg, @DP[0]-- ; Pop value from soft stack endm mcall MACRO Addr LOCAL return call ss_check_over ; Check for possible overflow move @++DP[0], #return ; Push return destination to soft stack jump Addr return: endm mret MACRO call ss_check_under ; Check for possible underflow jump @DP[0]-- ; Jump to popped destination from soft stack endm ss_check_under: push A[0] push AP push APC push PSF move APC, #80h ; Set Acc to A[0], standard mode, no auto inc/dec move Acc, DP[0] ; Get current value of stack pointer cmp #SS_BASE jump NE, ss_check_under_ok nop ; < Error handler should be implemented here > ss_check_under_ok: pop PSF pop APC pop AP pop A[0] ret ss_check_over: push A[0] push AP push APC push PSF move APC, #80h ; Set Acc to A[0], standard mode, no auto inc/dec move Acc, DP[0] ; Get current value of stack pointer cmp #SS_TOP jump NE, ss_check_over_ok nop ; < Error handler should be implemented here > ss_check_over_ok: pop PSF pop APC pop AP pop A[0] ret
上面的代碼導(dǎo)致在推送或彈出(或調(diào)用或重新)操作發(fā)生之前檢查當(dāng)前堆棧位置。如果檢測到溢出或下溢錯誤,響應(yīng)將因應(yīng)用程序而異。通常,這種錯誤只應(yīng)在應(yīng)用程序開發(fā)期間發(fā)生;如果代碼編寫正確,則不應(yīng)發(fā)生這種情況。如果確實發(fā)生錯誤,通常應(yīng)將其視為致命錯誤,就像在使用硬件堆棧時發(fā)生下溢/溢出一樣。此錯誤的可能響應(yīng)包括停止和傳輸錯誤消息或閃爍 LED。開發(fā)過程中一個有用的技巧是在這兩個例程中的每一個例程中(即,在“錯誤處理程序應(yīng)在此處實現(xiàn)”行)中設(shè)置一個斷點,以便在發(fā)生下溢或溢出時立即反饋。
但是,有時應(yīng)用程序可以從堆棧下溢或溢出中恢復(fù)(例如,通過從頭開始重新加載和重新啟動應(yīng)用程序子任務(wù))。在這種情況下,可能需要簡單地設(shè)置一個標志,指示發(fā)生了堆棧錯誤。此類標志的可能候選者包括位于 PSF 寄存器中的兩個通用標志(GPF0 和 GPF1)。由于有兩個位標志可用,其中一個可用于指示溢出,另一個用于指示下溢。
結(jié)論
MAX-IDE提供強大的宏預(yù)處理功能,允許在MAXQ2000和其他基于MAXQ20的微控制器上直接實現(xiàn)數(shù)據(jù)存儲器中的替換軟堆棧。這種軟堆棧通過允許子例程更加模塊化和可重用來幫助開發(fā)更大的基于程序集的應(yīng)用程序。堆棧還允許檢測基于堆棧的錯誤。
審核編輯:郭婷
-
微控制器
+關(guān)注
關(guān)注
48文章
7555瀏覽量
151429 -
存儲器
+關(guān)注
關(guān)注
38文章
7492瀏覽量
163848 -
編譯器
+關(guān)注
關(guān)注
1文章
1634瀏覽量
49134
發(fā)布評論請先 登錄
相關(guān)推薦
評論