摘要:本應用筆記介紹了匯編應用中在數(shù)據(jù)存儲器內(nèi)實現(xiàn)軟堆棧的簡單方法。該方法使用了MAXQ2000和其他基于MAXQ20的微控制器。采用了MAX-IDE的宏預處理特性,在Maxim的MAXQ?系列工程應用開發(fā)和調試環(huán)境中編寫了實例代碼。
雖然非常適合緊湊型應用,但是在規(guī)模較大的匯編應用中,使用深度嵌套的子程序(或者使用了在堆棧中保存和恢復多個工作寄存器的子程序),硬堆棧會很快耗盡空間。利用數(shù)據(jù)存儲器中的“軟堆棧”,以C編程語言(使用IAR的Embedded Workbench?等編譯器)或者Rowley Associates的Crossworks for MAXQ編寫應用程序可避免這一問題。(關于這些開發(fā)工具的詳細信息,請參考:開發(fā)工具)。這一軟堆棧存儲子程序的調用/返回地址以及本地工作變量。然而,MAXQ20內(nèi)核沒有內(nèi)置機制來定位數(shù)據(jù)存儲器中的堆棧,這些數(shù)據(jù)存儲器只用在匯編應用中。
本應用筆記介紹了匯編應用中在數(shù)據(jù)存儲器中實現(xiàn)軟堆棧的簡單方法。應用筆記中的代碼可以用于MAXQ2000和其他基于MAXQ20的微控制器。采用了MAX-IDE的宏預處理特性,在Maxim的MAXQ系列工程應用開發(fā)和調試環(huán)境中編寫了實例代碼。
可以免費下載MAX-IDE環(huán)境最新的安裝包和文檔:
除了利用堆棧的各種操作代碼之外,還有兩種其他的情況,在這些情況下,微控制器自動使用硬堆棧:
注意,當以下任一堆棧錯誤狀態(tài)出現(xiàn)時,MAXQ2000 (和其他的基于MAXQ20內(nèi)核的器件一樣)并不支持任何錯誤探測或者報警:
定義并初始化這類軟堆棧的一種簡單方法涉及到BP[Offs]寄存器對和一個公式。
在使用軟堆棧前,主程序必須調用ss_init例程。它將BP[Offs]指針設置為字模式,由于軟堆棧以16位寬堆棧實現(xiàn),因此,必須完成這一步。它還將BP[Offs]寄存器對指向堆棧開始。
mpush
該宏的使用方式和PUSH操作代碼相同。它支持將8位或者16位寄存器,或者將立即值推入堆棧。 mpop
該宏的使用方式和POP操作代碼相同。它支持從堆棧中裝入8位或者16位寄存器,如上所示。注意,如果推入了一個16位值,該值彈入到一個8位寄存器中,那么,只有低位字節(jié)被存儲在寄存器中。高位字節(jié)丟失。這與內(nèi)置硬堆棧的方式一致。 mcall
mcall宏的使用方式和CALL操作代碼執(zhí)行子程序相同。一旦執(zhí)行完成,子程序必須使用mret宏(而不是標準RET操作代碼)才能返回。
只改變數(shù)據(jù)指針,而代碼中的其他部分保持不變可以擴展堆棧。雖然如此,由于DP[0]并沒有限制在數(shù)據(jù)存儲器的一定范圍內(nèi),因此,沒有措施來防止推入或者彈出操作使DP[0]增加或者減小導致超出所指定的軟堆棧邊界。為避免這一點,可以加入簡單的下溢/上溢檢查。
但是,應用程序有時候也可以從堆棧下溢或者上溢中恢復(例如,重新裝入和從頭重新啟動應用程序子任務)。在這種情況下,需要簡單地設置一個標志,指示出現(xiàn)了堆棧錯誤??蛇x的這類標志包括位于PSF寄存器中的兩個通用標志(GPF0和GPF1)。由于有兩個位標志,因此,其中一個可以用于指示上溢,另一個用于指示下溢。
引言
MAXQ2000微控制器與Maxim的RISC微控制器系列MAXQ器件一樣,都是基于MAXQ20內(nèi)核?;贛AXQ20的微控制器通??蓪崿F(xiàn)一個16位寬硬堆棧,其深度固定不變(MAXQ2000為16),存儲在與數(shù)據(jù)和程序空間分開的專用內(nèi)部存儲器中。通過子程序調用和中斷,這一硬堆??捎糜诒4婧突謴臀⒖刂破鞯牟僮鳡顟B(tài)。雖然非常適合緊湊型應用,但是在規(guī)模較大的匯編應用中,使用深度嵌套的子程序(或者使用了在堆棧中保存和恢復多個工作寄存器的子程序),硬堆棧會很快耗盡空間。利用數(shù)據(jù)存儲器中的“軟堆棧”,以C編程語言(使用IAR的Embedded Workbench?等編譯器)或者Rowley Associates的Crossworks for MAXQ編寫應用程序可避免這一問題。(關于這些開發(fā)工具的詳細信息,請參考:開發(fā)工具)。這一軟堆棧存儲子程序的調用/返回地址以及本地工作變量。然而,MAXQ20內(nèi)核沒有內(nèi)置機制來定位數(shù)據(jù)存儲器中的堆棧,這些數(shù)據(jù)存儲器只用在匯編應用中。
本應用筆記介紹了匯編應用中在數(shù)據(jù)存儲器中實現(xiàn)軟堆棧的簡單方法。應用筆記中的代碼可以用于MAXQ2000和其他基于MAXQ20的微控制器。采用了MAX-IDE的宏預處理特性,在Maxim的MAXQ系列工程應用開發(fā)和調試環(huán)境中編寫了實例代碼。
可以免費下載MAX-IDE環(huán)境最新的安裝包和文檔:
- MAX-IDE安裝(ZIP)
- MAXQ內(nèi)核匯編指南(PDF)
- 開發(fā)工具指南(PDF)
MAXQ20內(nèi)核中的硬堆棧工作
MAXQ20內(nèi)核使用了兩種不同類型的堆棧工作模式:- PUSH工作(它包括操作代碼PUSH、LCALL和SCALL)被用于在堆棧中存儲數(shù)據(jù)。這些操作將堆棧指針SP預增1,然后,把數(shù)據(jù)存儲在SP指針(@SP)指向的堆棧位置。
- POP工作(它包括操作代碼POP、POPI、RET和RETI)被用于從堆棧中恢復數(shù)據(jù)。這些操作從SP指向的堆棧位置恢復數(shù)據(jù),然后,將堆棧指針減1。
除了利用堆棧的各種操作代碼之外,還有兩種其他的情況,在這些情況下,微控制器自動使用硬堆棧:
- 當處理中斷時,開始執(zhí)行中斷服務程序(中斷矢量寄存器IV所指)之前,當前程序執(zhí)行點被推入堆棧。
- 當調用某些調試命令(例如,讀寄存器和寫數(shù)據(jù)存儲器)時,需要執(zhí)行完程序ROM中的代碼,在控制被傳送給程序ROM之前,當前的執(zhí)行點被推入堆棧。一旦執(zhí)行完程序ROM中的調試程序,執(zhí)行點從堆棧中彈出,恢復處理器以前的狀態(tài)。
注意,當以下任一堆棧錯誤狀態(tài)出現(xiàn)時,MAXQ2000 (和其他的基于MAXQ20內(nèi)核的器件一樣)并不支持任何錯誤探測或者報警:
- 堆棧上溢:將數(shù)值推入已經(jīng)滿的堆棧時發(fā)生。這一錯誤導致堆棧中最早的數(shù)值被覆寫。
- 堆棧下溢:從空堆棧中彈出數(shù)值時發(fā)生。這一錯誤導致返回無效數(shù)據(jù)值。例如,如果堆??諘r執(zhí)行RET,執(zhí)行后將返回一個不正確的(有可能是隨機的)地址。
在數(shù)據(jù)存儲器中建立一個軟堆棧
在數(shù)據(jù)存儲器中建立一個軟堆棧的第一步是定義要使用數(shù)據(jù)存儲器的哪一部分。然后,必須定義跟蹤堆棧頂當前位置的數(shù)據(jù)存儲器指針(DP[0]、DP[1]或者BP[Offs])。注意:應用軟件不會出于其他目的使用堆棧專用數(shù)據(jù)存儲器(例如,變量或者緩沖)。定義并初始化這類軟堆棧的一種簡單方法涉及到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)寄存器被設置在SS_BASE位置,那么,可以采用Offs寄存器指向堆棧的當前頂部。由于Offs寄存器只有8位寬,硬件會自動把堆棧限制在數(shù)據(jù)存儲器的范圍內(nèi)(BP .. (BP+255))。如果當Offs等于255 (上溢)時出現(xiàn)推入,或者Offs等于0 (下溢)時出現(xiàn)彈出,那么,Offs寄存器只會限制在堆棧的另一端。這一行為模仿硬堆棧工作方式,支持簡單的軟堆棧實現(xiàn);它不探測下溢和上溢狀態(tài)。
在使用軟堆棧前,主程序必須調用ss_init例程。它將BP[Offs]指針設置為字模式,由于軟堆棧以16位寬堆棧實現(xiàn),因此,必須完成這一步。它還將BP[Offs]寄存器對指向堆棧開始。
軟堆棧工作
不能重新定義使用堆棧的內(nèi)置操作代碼(PUSH、POP、CALL、RET等)以使用軟堆棧,這是因為這些代碼意味著MAXQ匯編器的硬線連接。而且,還有可能需要在應用程序中的某些部分使用標準硬堆棧,例如,中斷服務例程等。出于這些原因,由復制標準操作代碼的宏來訪問軟堆棧。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下面將討論這些宏是怎樣工作的。
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
mret
mcall宏的使用方式和CALL操作代碼執(zhí)行子程序相同。一旦執(zhí)行完成,子程序必須使用mret宏(而不是標準RET操作代碼)才能返回。 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
擴展軟堆棧的大小
某些應用程序需要大于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,因此,產(chǎn)生的堆棧可以保持511級。(沒有使用堆棧存儲器空間中的某一位置;這樣可以更有效,以更短的代碼實現(xiàn)mpush/mpop/mcall/mret宏。)
只改變數(shù)據(jù)指針,而代碼中的其他部分保持不變可以擴展堆棧。雖然如此,由于DP[0]并沒有限制在數(shù)據(jù)存儲器的一定范圍內(nèi),因此,沒有措施來防止推入或者彈出操作使DP[0]增加或者減小導致超出所指定的軟堆棧邊界。為避免這一點,可以加入簡單的下溢/上溢檢查。
加入下溢和上溢檢查
為保持宏(每次使用時擴展為代碼)盡量短,在子程序中完成下溢和上溢檢查,使用硬堆棧,由宏調用子程序。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上面的代碼導致在推入或者彈出(或者call,或者ret)操作之前檢查當前的堆棧位置。如果探測到下溢或者上溢錯誤,不同的應用程序有不同的響應。一般情況下,這類錯誤只會出現(xiàn)在應用程序開發(fā)階段;如果代碼編寫正確,不會出現(xiàn)這類錯誤。如果的確出現(xiàn)了錯誤,一般將其考慮為重大錯誤,就像使用硬堆棧時出現(xiàn)下溢/上溢錯誤。對這類錯誤可能的響應包括暫停,發(fā)送錯誤消息,或者閃爍LED。在開發(fā)階段,一個好的方法是兩個程序中的每一個都在MAX-IDE中設置斷點(例如,在“Error handler should be implemented here”行中),如果出現(xiàn)下溢或者上溢,立即發(fā)出反饋。
但是,應用程序有時候也可以從堆棧下溢或者上溢中恢復(例如,重新裝入和從頭重新啟動應用程序子任務)。在這種情況下,需要簡單地設置一個標志,指示出現(xiàn)了堆棧錯誤??蛇x的這類標志包括位于PSF寄存器中的兩個通用標志(GPF0和GPF1)。由于有兩個位標志,因此,其中一個可以用于指示上溢,另一個用于指示下溢。
評論
查看更多