組合語言使用SDCC進行組譯
先前提過SDCC全稱Small Device C Compiler,為一套微處理器或微控制器的編譯工具,支援多種架構,例如MCS-51 (8051)、PIC16、STM8、Z80等。
https://danchouzhou.blogspot.com/2018/06/sdccn76e003-1t-8051.html
https://github.com/danchouzhou/N76E003-SDCC
https://github.com/danchouzhou/MS51BSP_SDCC
基本上如果我們用C語言撰寫8051的程式,從C語言(source code, .c檔)一直到燒錄進晶片的執行檔(binary)通常要經過以下幾個步驟:
1. 編譯(Compile): 透過編譯器將C語言編譯成帶有標籤組合語言
2. 組譯(Assemble): 透過組譯器將帶有旗標符號組合語言組譯成帶有標籤機器碼(物件檔)
3. 鏈結(Link): 透過鏈結器將一份或多份機器碼合成為一份執行檔
由於一份有點規模且管理良好的微控制器韌體專案,也許會把各功能或者函式庫拆成獨立一份source code,編譯時會把每一份source code透過編譯器編成組合語言,再透過組譯器組譯成機器碼,並且會在各函式的機器碼加上一些標籤或符號。最後在鏈結時,鏈結器會把主程式、函式根據目標微控制器(target)合理的安排在程式記憶體裡面,當主程式呼叫函式時就會讓CPU去執行函式的機器碼,所以組譯完的機器碼要加上標籤或符號的目的就是讓鏈結器知道函式在哪裡。
綜合來說,如果從C語言到機器碼,編譯、組譯、鏈結這三個步驟其實是密不可分的,編譯器必須編譯出組譯器認得的標籤與符號的組合語言,組譯器要組譯出鏈結器認得的標籤與符號的機器碼,鏈結器要能認得這些標籤與符號根據target合成一份執行檔。因此如今的compiler已經會是"一套工具",包含編譯器、組譯器、鏈結器。
如果要徹底了解微控制器運作的方式,最好的方式還是試試一些簡單的組合語言程式。這幾天也因為教學目的開始研究看看怎麼使用SDCC帶有的組譯器。根據SDCC的手冊章節2.9 Components of SDCC,sdas8051與sdld分別是assembler與linker
http://sdcc.sourceforge.net/doc/sdccman.pdf
我們這次實驗使用的平台為新唐科技MS51FB9AE,基本上與N76E003AT20功能大同小異,多數的情況下可以pin to pin、code to code相容。具體可以參考新唐官網介紹。
N76E003 系列到 MS51 系列的移植指南
https://www.nuvoton.com/support/technical-support/technical-articles/TSNuvotonTechBlog-000094/
MS51FB9AE開發板
https://www.ruten.com.tw/item/show?22024679387134
首先我們可以開一個文字檔取名為test.asm,用VS code、記事本、Notepad++都可以,在裡面寫這段組語程式,這段程式只是要點亮開發板上連接在MS51FB9AE P12的LED。
.org是要告訴組譯器接下來的程式是要放在記憶體哪一個位址。
上述這些我們稱作假指令,都是給組譯器看的,並非MCS-51有的硬體指令集。
接著的MOV、CLR、SJMP都是真的指令,指令集可以參考這幾份文件,整理得還不錯。
http://ww1.microchip.com/downloads/en/DeviceDoc/doc0509.pdf
https://www.keil.com/dd/docs/datashts/intel/ism51.pdf
首先一開始我們先去設定P12為輸出功能,類似Arduino的pinMode()。用MOV指令把0x00搬移進P1M1這個特殊功能暫存器、把0x04搬移進P1M2這個特殊功能暫存器。
這邊可以參考technical reference 6.4.2.2:
https://www.nuvoton.com/export/resource-files/TRM_MS51_16KBFlash_Series_EN_Rev1.01.pdf
讓P12當輸出接腳後,把P12 "清除",就會輸出LOW (CLR P12)。最後SJMP是跳躍指令,後面加"."表示原地跳躍,功能相當於C語言中的while(1);,目的是避免微控制器執行到其他沒用到的記憶體空間發生hard fault。
了解這段程式在做些甚麼以後我們可以開始進行組譯。可以在儲存文字檔的目錄之下按著鍵盤shift,點滑鼠右鍵開啟PowerShell。使用VS code的人也可以直接開terminal。
在terminal裡面輸入sdas8051 -o test.asm 進行組譯,可以在目錄裡面看到多了test.rel。
輸入sdld -i test.rel 進行鏈結,要燒錄進晶片的執行檔test.ihx就會生出來了。
最後我們用NuMicro ICP Programming Tool燒錄開發板,將test.ihx匯入可以看到燒進去的機器碼具體內容。
可以對照指令集表格:
http://ww1.microchip.com/downloads/en/DeviceDoc/doc0509.pdf
為了方便大家解讀,畫了一張表格來翻譯。
原地跳躍SJMP . 的部分,為甚麼是跳到0xFE? 可以去看看指令的解釋,運算元放的是相對位址,並且以二補數表示法表示。因此0xFE二補數表示法代表-2,SJMP應擺在程式記憶體0x0008,0xFE擺在0x0009,執行後PC會指在0x000A,指令執行完畢後0x000A - 0x0002 = 0x0008,跳回SJMP反覆執行。
https://danchouzhou.blogspot.com/2018/06/sdccn76e003-1t-8051.html
https://github.com/danchouzhou/N76E003-SDCC
https://github.com/danchouzhou/MS51BSP_SDCC
基本上如果我們用C語言撰寫8051的程式,從C語言(source code, .c檔)一直到燒錄進晶片的執行檔(binary)通常要經過以下幾個步驟:
1. 編譯(Compile): 透過編譯器將C語言編譯成帶有標籤組合語言
2. 組譯(Assemble): 透過組譯器將帶有旗標符號組合語言組譯成帶有標籤機器碼(物件檔)
3. 鏈結(Link): 透過鏈結器將一份或多份機器碼合成為一份執行檔
由於一份有點規模且管理良好的微控制器韌體專案,也許會把各功能或者函式庫拆成獨立一份source code,編譯時會把每一份source code透過編譯器編成組合語言,再透過組譯器組譯成機器碼,並且會在各函式的機器碼加上一些標籤或符號。最後在鏈結時,鏈結器會把主程式、函式根據目標微控制器(target)合理的安排在程式記憶體裡面,當主程式呼叫函式時就會讓CPU去執行函式的機器碼,所以組譯完的機器碼要加上標籤或符號的目的就是讓鏈結器知道函式在哪裡。
綜合來說,如果從C語言到機器碼,編譯、組譯、鏈結這三個步驟其實是密不可分的,編譯器必須編譯出組譯器認得的標籤與符號的組合語言,組譯器要組譯出鏈結器認得的標籤與符號的機器碼,鏈結器要能認得這些標籤與符號根據target合成一份執行檔。因此如今的compiler已經會是"一套工具",包含編譯器、組譯器、鏈結器。
如果要徹底了解微控制器運作的方式,最好的方式還是試試一些簡單的組合語言程式。這幾天也因為教學目的開始研究看看怎麼使用SDCC帶有的組譯器。根據SDCC的手冊章節2.9 Components of SDCC,sdas8051與sdld分別是assembler與linker
http://sdcc.sourceforge.net/doc/sdccman.pdf
我們這次實驗使用的平台為新唐科技MS51FB9AE,基本上與N76E003AT20功能大同小異,多數的情況下可以pin to pin、code to code相容。具體可以參考新唐官網介紹。
N76E003 系列到 MS51 系列的移植指南
https://www.nuvoton.com/support/technical-support/technical-articles/TSNuvotonTechBlog-000094/
MS51FB9AE開發板
https://www.ruten.com.tw/item/show?22024679387134
首先我們可以開一個文字檔取名為test.asm,用VS code、記事本、Notepad++都可以,在裡面寫這段組語程式,這段程式只是要點亮開發板上連接在MS51FB9AE P12的LED。
P1M1=0x00B3 P1M2=0x00B4 .area HOME (ABS,CODE) .org 0x0000 MOV P1M1,#0x00 MOV P1M2,#0x04 CLR P1.2 SJMP ..area是要告訴組譯器接下來的程式是要放在甚麼記憶體中。
.org是要告訴組譯器接下來的程式是要放在記憶體哪一個位址。
上述這些我們稱作假指令,都是給組譯器看的,並非MCS-51有的硬體指令集。
接著的MOV、CLR、SJMP都是真的指令,指令集可以參考這幾份文件,整理得還不錯。
http://ww1.microchip.com/downloads/en/DeviceDoc/doc0509.pdf
https://www.keil.com/dd/docs/datashts/intel/ism51.pdf
首先一開始我們先去設定P12為輸出功能,類似Arduino的pinMode()。用MOV指令把0x00搬移進P1M1這個特殊功能暫存器、把0x04搬移進P1M2這個特殊功能暫存器。
這邊可以參考technical reference 6.4.2.2:
https://www.nuvoton.com/export/resource-files/TRM_MS51_16KBFlash_Series_EN_Rev1.01.pdf
讓P12當輸出接腳後,把P12 "清除",就會輸出LOW (CLR P12)。最後SJMP是跳躍指令,後面加"."表示原地跳躍,功能相當於C語言中的while(1);,目的是避免微控制器執行到其他沒用到的記憶體空間發生hard fault。
了解這段程式在做些甚麼以後我們可以開始進行組譯。可以在儲存文字檔的目錄之下按著鍵盤shift,點滑鼠右鍵開啟PowerShell。使用VS code的人也可以直接開terminal。
在terminal裡面輸入sdas8051 -o test.asm 進行組譯,可以在目錄裡面看到多了test.rel。
輸入sdld -i test.rel 進行鏈結,要燒錄進晶片的執行檔test.ihx就會生出來了。
最後我們用NuMicro ICP Programming Tool燒錄開發板,將test.ihx匯入可以看到燒進去的機器碼具體內容。
可以對照指令集表格:
http://ww1.microchip.com/downloads/en/DeviceDoc/doc0509.pdf
為了方便大家解讀,畫了一張表格來翻譯。
原地跳躍SJMP . 的部分,為甚麼是跳到0xFE? 可以去看看指令的解釋,運算元放的是相對位址,並且以二補數表示法表示。因此0xFE二補數表示法代表-2,SJMP應擺在程式記憶體0x0008,0xFE擺在0x0009,執行後PC會指在0x000A,指令執行完畢後0x000A - 0x0002 = 0x0008,跳回SJMP反覆執行。
留言
張貼留言