匯編語(yǔ)言的結構化設計及其在俄羅斯方塊中的應用
引言
匯編語(yǔ)言是一種用助記符表示的面向機器的程序設計語(yǔ)言。助記符使得原來(lái)的機器語(yǔ)言變得相對較為直觀(guān)、易懂、易用,并且匯編語(yǔ)言與機器語(yǔ)言具有一一對應的關(guān)系,因此它繼承了機器語(yǔ)言直接、快速、高效的特點(diǎn),是一種底層語(yǔ)言。但是匯編語(yǔ)言的劣勢也十分明顯,如對于編寫(xiě)較大的程序需要考慮諸多硬件存儲器的分配以及中斷程序的處理等非常細節的問(wèn)題,否則容易出現寄存器沖突,從而導致程序崩潰。為了簡(jiǎn)化匯編語(yǔ)言的編寫(xiě)過(guò)程,本文提出了一種結構化的匯編編程思路,并以基于A(yíng)T89C51芯片(以下對匯編語(yǔ)言的討論針對51單片機系統)的俄羅斯方塊游戲為例,來(lái)展現在51單片機中匯編語(yǔ)言結構化編寫(xiě)的優(yōu)勢。
1 匯編語(yǔ)言的結構化設計思想
1.1 變量定義
匯編語(yǔ)言中無(wú)需變量的聲明,因為匯編語(yǔ)言是直接對具體的內存單元操作,而每個(gè)單元有16進(jìn)制的地址碼,因此所有變量都可人為地由該地址碼表示。但是匯編語(yǔ)言提供了EQU偽指令,可以將特定的內存空間標記為特定的名稱(chēng),這就為變量定義提供了可能。而使用EQU偽指令的好處就是將抽象的物理內存分化為具體的變量名,避免了內存沖突,同時(shí)又增加了程序可讀性。
1.2 子函數設計
子函數對程序結構化的作用是其可簡(jiǎn)化主函數的編寫(xiě),使得程序主干的編寫(xiě)思路清晰化,而一些復雜的算法與功能則放在一層層的子函數中實(shí)現。但是,匯編語(yǔ)言在調用子函數的過(guò)程中如果處理不當,極其容易造成堆棧錯誤、內存沖突等問(wèn)題。本文提出了一種優(yōu)化的子函數設計方案。
圖1 工作寄存器區臨時(shí)變量存放層次結構
首先,把51單片機內存的4組工作寄存器區(00H~1FH),用作子函數的臨時(shí)變量存放區,如圖1所示;另一部分是用戶(hù)區(20H~7FH),用作主函數變量與堆棧區域。其次,4組工作寄存器區的每一組用作同一層次的子函數的臨時(shí)變量,低層次的子函數只能被高層次的子函數調用,同一層次的子函數不允許相互嵌套調用。所有的子函數在編寫(xiě)時(shí)需要聲明其使用的工作寄存器組編號,以防止沖突。在函數嵌套時(shí),用RS1、RS0兩個(gè)標志位的切換來(lái)實(shí)現工作寄存器組的切換,如此就可以方便可靠地實(shí)現子函數的調用和嵌套。
1.3 中斷函數設計
與順序設計的程序不同,51系列單片機還需考慮中斷函數的設計。51單片機的中斷有外部中斷、定時(shí)器中斷、串口中斷等。中斷程序在中斷源觸發(fā)后即起作用,換句話(huà)說(shuō),中斷程序可能隨時(shí)中止主程序的運行。如果在這個(gè)時(shí)候,中斷函數與主程序中的主函數或子函數享用相同的臨時(shí)變量,那么在中斷發(fā)生時(shí),這些臨時(shí)變量就會(huì )被改寫(xiě),從而導致內存沖突。因此,中斷函數的臨時(shí)變量體系應與主程序有別,以下是三種可選方案:
第一種方案是將工作寄存器區分為兩類(lèi),一類(lèi)用作主程序函數的臨時(shí)變量,另一類(lèi)用作中斷函數的臨時(shí)變量。這種方案中,單片機工作寄存器的組數對函數設計起限制作用。
第二種方案允許中斷函數與主程序的子函數共用工作寄存器區,但是代價(jià)是在調用中斷時(shí)必須保護和恢復現場(chǎng),即在中斷函數的開(kāi)始、結尾必須將中斷函數及其子函數使用的工作寄存器的數據壓入、彈出堆棧,從而保證中斷前后主程序函數臨時(shí)變量的一致。
第三種方法是通過(guò)設置標志變量,避免在中斷函數中插入子函數。在中斷程序中,根據狀態(tài)修改標志變量后即返回主函數。在主函數中,判斷相應的中斷標志執行相應的子程序。這種編程方法的優(yōu)點(diǎn)是中斷程序十分簡(jiǎn)單,能在很短的時(shí)間內完成,減少了中斷出錯的可能性;其缺點(diǎn)是中斷執行的反應速度會(huì )有所降低,因為主函數對中斷標志位一定是滯后于中斷發(fā)生的,且如果主函數的結構是大循環(huán)型的,那么一次循環(huán)中只能處理若干次中斷(大多數情況往往只為一次),這種編程方法對需要高頻中斷的功能是不合適的。
2 俄羅斯方塊的軟件實(shí)現方法
俄羅斯方塊是一款風(fēng)靡全世界的十分經(jīng)典的休閑游戲。本文在基于MCS51單片機和具有矩陣式按鍵、雙色LED點(diǎn)陣和數碼管等功能模塊的實(shí)驗系統上,采用以上所述的匯編語(yǔ)言結構化的編程思想,編寫(xiě)能夠運用按鍵操作游戲、將游戲圖像顯示于16×8的LED雙色點(diǎn)陣上,將玩家分數顯示在靜態(tài)數碼管上、并伴隨游戲產(chǎn)生音樂(lè )效果的俄羅斯方塊游戲。
2.1 功能分析
俄羅斯方塊游戲的規則很簡(jiǎn)單,屏幕上方隨機產(chǎn)生不同形狀的方塊并以一定速度落下,玩家可以控制方塊的左右位置以及旋轉方塊,巧妙地布置安排使方塊落下后充分利用屏幕空間。每當屏幕的一整行被方塊排滿(mǎn)時(shí),該行方塊從屏幕上消失,其上的方塊依次下降一行,玩家獲得一定的分數。當方塊堆積達到屏幕頂端的時(shí)候,游戲結束。本游戲的主要功能包括:
① 開(kāi)機進(jìn)入開(kāi)機歡迎界面。按任意鍵進(jìn)入游戲難度選擇界面,難度選擇后,按確定鍵進(jìn)入游戲界面。
② 每4個(gè)格點(diǎn)(雙色LED)組成一個(gè)圖形,游戲中共有7種方塊圖形。屏幕上端隨機產(chǎn)生一種方塊圖形,并按著(zhù)一定的時(shí)間周期向下移動(dòng)。當前一個(gè)方塊無(wú)法再次移動(dòng)時(shí),產(chǎn)生下一個(gè)方塊。
③ 當方塊向下移動(dòng)時(shí),玩家可以通過(guò)上、下、左、右4個(gè)按鍵分別調整方塊的角度、加速方塊的下移速度、向左移動(dòng)方塊1格、向右移動(dòng)方塊1格。
④ 游戲中,玩家可以按停止鍵,選擇停止游戲并返回到開(kāi)始界面;或者是按暫停鍵,暫停游戲;再次按暫停鍵時(shí),游戲繼續進(jìn)行。
⑤ 當一個(gè)方塊無(wú)法繼續向下移動(dòng)時(shí),判斷此時(shí)能否將屏幕的一行或多行完全填滿(mǎn)。若能則將這些行的方塊閃爍后消除,玩家獲得相應的分數(每消去一行,玩家得到10分),并顯示玩家總的分數;而未被消除的方塊則會(huì )一直積累。隨著(zhù)玩家分數的增加,游戲的難度會(huì )增加,方塊下落的速度會(huì )加快。
⑥ 如果未被消除的方塊堆放的高度很高,達到屏幕頂端以至無(wú)法產(chǎn)生新方塊,則游戲結束,返回到開(kāi)始歡迎界面。
⑦ 游戲開(kāi)始、結束、按鍵以及消行時(shí)會(huì )產(chǎn)生一定的音樂(lè )效果。
2.2 變量定義與子函數模塊
根據結構化的編程思想,程序中需要對變量進(jìn)行空間分配,并根據其功能進(jìn)行命名,以增加程序的可讀性,使得后期的調試工作更加方便。變量定義的具體內容包括單片機及功能模塊所需的引腳命名、功能模塊所需的常量命名、單片機用戶(hù)儲存空間的預分配和命名。
首先列出需要用到的所有引腳和變量,并將總程序空間分塊并合理分配每一塊的大小。本程序將RAM空間劃分為即時(shí)調用區、固定區和堆棧區。即時(shí)調用區為通用寄存器組,地址00H~1FH;固定區為用戶(hù)存儲區的20H~5FH;堆棧區為60H開(kāi)始的剩余空間。
對于函數的調用方法、數據的應用輸出、寄存器工作組的使用等關(guān)系到程序儲存空間的細節問(wèn)題,前文已經(jīng)作出論述。子函數與主函數的數據接口采用C51的4個(gè)工作寄存器組存放,在子函數調用時(shí)將臨時(shí)數據存入相應的工作寄存器進(jìn)行處理,執行完畢后將數據返回上一級函數。
2.3 中斷的設計
中斷的使用和中斷程序的設計是單片機應用的難點(diǎn)之一。
首先,要根據程序功能設計中斷的邏輯流程。80C51單片機中有兩個(gè)定時(shí)器/計數器T0、T1。程序要求同時(shí)實(shí)現定時(shí)掃描顯示以及播放音樂(lè )的功能(音樂(lè )功能通過(guò)一條口線(xiàn)和蜂鳴器實(shí)現),所以要同時(shí)使用T0和T1的中斷:T0控制顯示模塊,中斷間隔時(shí)間較長(cháng),優(yōu)先級較低;T1控制音樂(lè )模塊,中斷間隔較短,中斷中執行的代碼也較短,優(yōu)先級較高。
然后,根據中斷的特點(diǎn),合理設計中斷的使用規則。本程序中設計使用雙中斷,這使得程序的主體邏輯流程變得簡(jiǎn)單,但同時(shí)也使得中斷函數本身的設計,尤其是即時(shí)數據的空間分配和斷點(diǎn)的保護等,變得十分重要。為了使函數簡(jiǎn)單可靠,程序中允許中斷函數與主程序的其他函數共用工作寄存器區,但是在每次調用中斷函數時(shí)都需要全面保護和恢復現場(chǎng)。音樂(lè )中斷因為不涉及工作寄存器,所以只需要保護、恢復基本的數據就可以。
2.4 主函數流程和偽代碼描述
根據俄羅斯方塊游戲的功能以及結構化的匯編設計方法,主函數流程如圖2所示。
圖2 主函數流程
偽代碼如下:
歡迎界面;
難度選擇;
數據初始化;
主循環(huán) {
難度設置;
產(chǎn)生新方塊;
判斷新方塊是否已經(jīng)無(wú)法移動(dòng),如果無(wú)法移動(dòng)則游戲結束;
檢測按鍵,如果有按鍵則判斷方塊是否可執行相應的動(dòng)作 {
如果可以執行,則執行;
如果不可以執行,則保持不動(dòng);
}
判斷方塊是否已經(jīng)無(wú)法繼續向下移動(dòng) {
如果可以移動(dòng)則循環(huán)繼續檢測按鍵;
如果不可以移動(dòng)則判斷能否消行,如果能則消行、得分;
判斷分數是否需要加快游戲速度;
}
}
中斷1:定時(shí)方塊下落一格;
中斷2:產(chǎn)生相應的音樂(lè )效果;
2.5 實(shí)驗測試與結果
開(kāi)機測試與結果:開(kāi)機全速運行程序,LED雙色顯示器上顯示的“Hello”歡迎界面如圖3所示。按任意鍵可進(jìn)入難度選擇界面。
難度選擇界面測試:進(jìn)入難度選擇界面,雙色LED上顯示紅色的“123”字樣,并在‘3’下有一綠色小圓點(diǎn),如圖4所示。按數字鍵1、2、3鍵,用于選擇相應的等級,同時(shí)綠色小圓點(diǎn)會(huì )移到相應等級的下方;按“enter”鍵結束難度選擇,進(jìn)入游戲界面。
圖3 開(kāi)機界面 圖4 難度選擇界面
游戲流程測試:進(jìn)入游戲主界面。LED上方產(chǎn)生方塊,不操作時(shí)方塊可以自由下落,此時(shí)方塊為紅色。按動(dòng)‘7’鍵,方塊改變其角度,再次按動(dòng),角度可以繼續改變;按動(dòng)‘6’鍵,方塊下移一格,并且不影響其自由下落,表現為方塊下移速度加快;按動(dòng)‘2’鍵,方塊向左移動(dòng)一位,當移動(dòng)到左邊邊界時(shí),無(wú)法再移動(dòng);按動(dòng)‘A’鍵,方塊向右移動(dòng)一位,當移動(dòng)到右邊邊界時(shí),無(wú)法再移動(dòng)。方塊移動(dòng)到無(wú)法再向下時(shí),顏色變?yōu)榫G色,并且在LED上方產(chǎn)生新的方塊,如圖5所示。滿(mǎn)足消行條件時(shí),該行閃爍后即消失,同時(shí)在數碼管上顯示分數增加10分;多行消除時(shí),為每行依次消除,增加相應分數,如圖6所示。隨著(zhù)分數的增加,方塊的自由下落速度加快。按動(dòng)‘8’鍵,游戲暫停,此時(shí)按動(dòng)除‘8’鍵的其他鍵均無(wú)效。當再次按動(dòng)‘8’,游戲繼續進(jìn)行。游戲進(jìn)入、按鍵、消行時(shí),均有音樂(lè )產(chǎn)生,并且兩者同步。
游戲結束測試:按‘C’鍵或者游戲結束時(shí),屏幕所有LED燈從底端到上端逐行點(diǎn)亮,再從上端到底端逐行熄滅,此時(shí)伴隨著(zhù)音樂(lè )產(chǎn)生,然后進(jìn)入歡迎界面。
圖5 游戲流程
圖6 加分顯示
結語(yǔ)
本文以俄羅斯方塊游戲的程序編寫(xiě)為例子,提出、分析并具體說(shuō)明了在功能復雜的匯編程序設計過(guò)程中,采用的結構化編程思路。并從變量定義、子函數設計、中斷函數設計等方面探討了匯編語(yǔ)言結構化設計的具體方法,從而有效解決了匯編程序編寫(xiě)中易發(fā)生的寄存器內存沖突等問(wèn)題。這種匯編語(yǔ)言編程的結構化思維,對于運用編寫(xiě)匯編大程序具有重要的指導和借鑒作用。
評論