使用STVD建立完匯編工程項目之后(具本建立方法可以看我的另一篇博文http://blog.csdn.net/u010093140/article/details/49983397),可以看到這個目錄結構(以STM8S105C6芯片為例)
其中.asm文件是匯編代碼的源文件,.inc文件是包含文件,類似于C語言當在的.c文件和.h文件。接下來讓我們來分析一下這三個文件。(分析匯編代碼最好也要對STM8單片機的啟動流程有所了解,可以看我的另一篇博文http://blog.csdn.net/u010093140/article/details/49982879)
首先是看mapping.inc文件:
;------------------------------------------------------
; SEGMENT MAPPING FILE AUTOMATICALLY GENERATED BY STVD
; SHOULD NOT BE MANUALLY MODIFIED.
; CHANGES WILL BE LOST WHEN FILE IS REGENERATED.
;------------------------------------------------------
#define RAM0 1
#define ram0_segment_start 0
#define ram0_segment_end FF
#define RAM1 1
#define ram1_segment_start 100
#define ram1_segment_end 5FF
#define stack_segment_start 600
#define stack_segment_end 7FF
這一段代碼應該不難看懂,就是定義了一些常量。需要注意的是,分號”;”是匯編代碼中用于寫注釋的符號。所以分號后面跟的是注釋。
接下來就是看一下mapping.asm文件
stm8/
;------------------------------------------------------
; SEGMENT MAPPING FILE AUTOMATICALLY GENERATED BY STVD
; SHOULD NOT BE MANUALLY MODIFIED.
; CHANGES WILL BE LOST WHEN FILE IS REGENERATED.
;------------------------------------------------------
#include "mapping.inc"
BYTES ; The following addresses are 8 bits long
segment byte at ram0_segment_start-ram0_segment_end 'ram0'
WORDS ; The following addresses are 16 bits long
segment byte at ram1_segment_start-ram1_segment_end 'ram1'
WORDS ; The following addresses are 16 bits long
segment byte at stack_segment_start-stack_segment_end 'stack'
WORDS ; The following addresses are 16 bits long
segment byte at 4000-43FF 'eeprom'
WORDS ; The following addresses are 16 bits long
segment byte at 8080-FFFF 'rom'
WORDS ; The following addresses are 16 bits long
segment byte at 8000-807F 'vectit'
END
上面的代碼第一行以stm8/開頭,很多人不知道為什么要這樣子。其實是因為我們所用的匯編連接器Assembler Linker不僅支持STM8匯編代碼而且還支持ST公司的另一款芯片ST7的匯編代碼,如果你用的是ST7芯片的話,就要以st7/開頭了。結論就是使用stm8/開頭是為了表明代碼的目標芯片是stm8芯片。
分號后面的注釋不算入代碼里面,剩下來的代碼就定義了芯片上的內存段,比如說segment byte at ram0_segment_start-ram0_segment_end ‘ram0’的意思就是,從ram0_segment_start到ram0_segment_end的這一段內存起個名字叫做“ram0”,segment byte at ram1_segment_start-ram1_segment_end ‘ram1’的意思就是,從ram1_segment_start到ram1_segment_end的這一段內存起個名字叫做“ram1”,其它的也是一樣的道理。那么,你也會注意到,每一句這樣的代碼之前都有一句”Bytes”或者”Words”,這是什么意思呢?按代碼注釋里的意思就是,Bytes代表內存段里內存的地址是8位的,而Words代表內存段里內存的地址是16位的。通過查Assembler Linker PDF,發現Bytes和Words用于指定跟在它下面的的標號的默認長度,什么意思?可以看以下的例子:
Bytes
label1
;下面這條語句是編譯通過的。因為A是8位的,label1也是8位的。
LD A,#label1
Words
label2
;下面這條語句是編譯不通過的。因為A是8位的,而label2是16位的,通過賦值給A。
LD A,#label2
Words
label3.b
;而下面這條語句是可以編譯通過的,因為我顯式地指定了label3為byte的長度(.b),是8位的。
我們再看回到mapping.asm那個文件,mapping文件里所有的指令都是偽指令,并不產生實際的可執行代碼,那么使用了bytes,words是什么作用呢?從上面bytes和words的作用來看,我個人認為它們在mapping.asm里不起作用,只起到說明的作用,相當于注釋。當然如有錯誤,歡迎大家指出^_^。所以mapping的作用就是給芯片的存儲空間劃分區域并命名。我們后面我們寫的代碼可以通過這個名字,指定存到該名字所代表的存儲區域下。比如說ram0區,ram1區,rom區等。
接下來再來看main.asm,這個代碼有一些長了,先貼出來吧。
stm8/
#include "mapping.inc"
segment 'rom'
main.l
; initialize SP
ldw X,#stack_end
ldw SP,X
#ifdef RAM0
; clear RAM0
ram0_start.b EQU $ram0_segment_start
ram0_end.b EQU $ram0_segment_end
ldw X,#ram0_start
clear_ram0.l
clr (X)
incw X
cpw X,#ram0_end
jrule clear_ram0
#endif
#ifdef RAM1
; clear RAM1
ram1_start.w EQU $ram1_segment_start
ram1_end.w EQU $ram1_segment_end
ldw X,#ram1_start
clear_ram1.l
clr (X)
incw X
cpw X,#ram1_end
jrule clear_ram1
#endif
; clear stack
stack_start.w EQU $stack_segment_start
stack_end.w EQU $stack_segment_end
ldw X,#stack_start
clear_stack.l
clr (X)
incw X
cpw X,#stack_end
jrule clear_stack
infinite_loop.l
jra infinite_loop
interrupt NonHandledInterrupt
NonHandledInterrupt.l
iret
segment 'vectit'
dc.l {$82000000+main} ; reset
dc.l {$82000000+NonHandledInterrupt} ; trap
dc.l {$82000000+NonHandledInterrupt} ; irq0
dc.l {$82000000+NonHandledInterrupt} ; irq1
dc.l {$82000000+NonHandledInterrupt} ; irq2
dc.l {$82000000+NonHandledInterrupt} ; irq3
dc.l {$82000000+NonHandledInterrupt} ; irq4
dc.l {$82000000+NonHandledInterrupt} ; irq5
dc.l {$82000000+NonHandledInterrupt} ; irq6
dc.l {$82000000+NonHandledInterrupt} ; irq7
dc.l {$82000000+NonHandledInterrupt} ; irq8
dc.l {$82000000+NonHandledInterrupt} ; irq9
dc.l {$82000000+NonHandledInterrupt} ; irq10
dc.l {$82000000+NonHandledInterrupt} ; irq11
dc.l {$82000000+NonHandledInterrupt} ; irq12
dc.l {$82000000+NonHandledInterrupt} ; irq13
dc.l {$82000000+NonHandledInterrupt} ; irq14
dc.l {$82000000+NonHandledInterrupt} ; irq15
dc.l {$82000000+NonHandledInterrupt} ; irq16
dc.l {$82000000+NonHandledInterrupt} ; irq17
dc.l {$82000000+NonHandledInterrupt} ; irq18
dc.l {$82000000+NonHandledInterrupt} ; irq19
dc.l {$82000000+NonHandledInterrupt} ; irq20
dc.l {$82000000+NonHandledInterrupt} ; irq21
dc.l {$82000000+NonHandledInterrupt} ; irq22
dc.l {$82000000+NonHandledInterrupt} ; irq23
dc.l {$82000000+NonHandledInterrupt} ; irq24
dc.l {$82000000+NonHandledInterrupt} ; irq25
dc.l {$82000000+NonHandledInterrupt} ; irq26
dc.l {$82000000+NonHandledInterrupt} ; irq27
dc.l {$82000000+NonHandledInterrupt} ; irq28
dc.l {$82000000+NonHandledInterrupt} ; irq29
end
上面的代碼看得明白嗎?哈哈。首先先給一個內存圖,有用到內存的地方可以回來看這個圖比較直觀。
下面采用一行一行注釋的方式給大家講解(發表完之后發現注釋跑到屏幕外面了,把代碼欄下面的橫條向右拉就可以看到了。)。
;就如之前所說的,stm8指明以下的代碼是用于stm8芯片的,而不是st7芯片。
stm8/
;以下代碼是把mapping.inc文件包含進來的意思,這樣就可以直接用mapping.inc里面定義的常量了。
#include "mapping.inc"
;以下代碼是指明往后的代碼都是放在rom存儲區域的意思,就如mapping.asm里所表明的,rom的地址范圍是8080-FFFF。
segment 'rom'
;main.l是一個標號,寫在最左邊的一行,標號不產生實際的指令。標號的作用時給一個地址進行命名,然后其它指令就可以使用這個名字來使用這個地址了。比如說下面的main.l的地址就跟下面的ldw X,#stack_end所在的地址相等的。而.l的意思是該地址是3個字節24位的。
main.l
; initialize SP
;下面這一句的意思是把stack_end的值加載到X寄存器,#是立即數的意思。ldw的w是word的意思,表明是16位是加載指令。也有8位的加載指令,為ld.
ldw X,#stack_end
;下面這一句的意思是把寄存器X的值賦給SP寄存器的意思,SP是棧指針,上下兩句的作用是讓SP指向棧頂。(STM8的棧結構是自頂向下的,棧頂的值就是stack_end,棧中地址值最大的那個數)。
ldw SP,X
;偽指令,如果定義了RAM0就編譯其后的代碼,顯然這個判斷是為真的,因為在mapping.inc中已經定義了RAM0和RAM1.
#ifdef RAM0
; clear RAM0
;偽指令,定義標號ram0_start.b的值為ram0_segment_start的值,$是16進制數的意思,ram0_end.b同理。這種直接賦值的方式跟前面的main.l標號有所不同,下面這種是賦絕對地址,而main.l是賦相對地址。
ram0_start.b EQU $ram0_segment_start
ram0_end.b EQU $ram0_segment_end
;加載ram0_start的值到X
ldw X,#ram0_start
;定義標號clear_ram0.l
clear_ram0.l
;clr是清除的意思,()是間接尋址的意思,clr(X)就是以X的值為地址,清除該地址上的值的意思。
clr (X)
;X加1,incw有個w是因為X是16位的。
incw X
;cpw是compare的意思,比較X和ram0_end的值,w的意思跟上面講的意思一樣。
cpw X,#ram0_end
;jrule(jump relative unsigned less than)這個意思明白了吧?就是如果小于就跳轉到clear_ram0標號地址的意思。
jrule clear_ram0
;跟前面的#ifdef RAM0相對應。
#endif
;這個面RAM1的操作跟以上對RAM0的操作一樣。整一段代碼的作用就是清零存儲區的作用。
#ifdef RAM1
; clear RAM1
ram1_start.w EQU $ram1_segment_start
ram1_end.w EQU $ram1_segment_end
ldw X,#ram1_start
clear_ram1.l
clr (X)
incw X
cpw X,#ram1_end
jrule clear_ram1
#endif
;下面初始化棧區的操作也是跟前面對RAM0的操作一樣的。
; clear stack
stack_start.w EQU $stack_segment_start
stack_end.w EQU $stack_segment_end
ldw X,#stack_start
clear_stack.l
clr (X)
incw X
cpw X,#stack_end
jrule clear_stack
;下面定義了infinite_loop.l標號。
infinite_loop.l
;jra是相對跳轉的意思,跳轉到上面那個標號。所以這是一個無限循環,代碼到這里就是不斷地執行jra infinite_loop這條語句,相當于C語言中的while(1);
jra infinite_loop
;interrupt是偽指令,把NoHandleInterrupt說明成是用于中斷的標號。
interrupt NonHandledInterrupt
;定義NonHandledInterrupt.l標號
NonHandledInterrupt.l
;iret是中斷返回的意思。而ret是函數返回的意思。
iret
;segment 'vectic'指令其下面的代碼是放在vectit存儲區的,即8000-807F所在的區域。
segment 'vectit'
;dc.l的意思是申請一段四個字節的空間,后面加的數字就是賦予這個空間的值。什么?前面的l的用法都是3個字節的,這里dc.l里的l就成4個字節了?沒錯,就是這樣子的,有點亂,這也是有點費解的地方,我也不明白為啥不改另一個說法。{}的用法是在編譯時運算里面的語句,而不是在代碼里演算。比如說{1+1}會在編譯后變成2.
;下面的所有dc.l其實就是定義了一個中斷向量表,分別對應于不同的中斷,比如第一個就是復位中斷,芯片復位后會在這里找到main標號,然后程序跳轉到main里去。當然如果你對main不爽,也可以改成其它的,比如說example.但是這個改了之后,最前面的main.l標號也要相應的改成example.l.就相當于這個程序里面“沒有”main函數了。是不是很神奇呢?呃。下面有注釋了trap,irq0,irq2等這些,其實就是對應了不同的中斷,比如說I2C的中斷就對應了其中的irq19,所以當你寫好I2C的中斷服務程序后,需要把它的標號填寫到irq19那一句中,可以參考dc.l{$82000000+main}這句,如果你把I2C中斷服務程序的標號定義I2C_Interrupt.l則irq19中那一句要改成dc.l{$82000000+I2C_Interrupt}.最后一個問題,中斷后單片機會跳到中斷標號里去執行這點沒問題了,那下面$82000000中的82是什么意思呢?(現在想找之前看到的資料已經找不到了。。。。不過我還記得那個意思)82是STM8指令集中的一個操作碼(匯編指令是由操作碼和操作數組成的),我想用在中斷這里的意思就是表面這個地址標號是中斷服務程序地址標號的意思吧,芯片可以識別82這個操作碼,從而區別對待。
dc.l{$82000000+main} ; reset
dc.l {$82000000+NonHandledInterrupt} ; trap
dc.l {$82000000+NonHandledInterrupt} ; irq0
dc.l {$82000000+NonHandledInterrupt} ; irq1
dc.l {$82000000+NonHandledInterrupt} ; irq2
dc.l {$82000000+NonHandledInterrupt} ; irq3
dc.l {$82000000+NonHandledInterrupt} ; irq4
dc.l {$82000000+NonHandledInterrupt} ; irq5
dc.l {$82000000+NonHandledInterrupt} ; irq6
dc.l {$82000000+NonHandledInterrupt} ; irq7
dc.l {$82000000+NonHandledInterrupt} ; irq8
dc.l {$82000000+NonHandledInterrupt} ; irq9
dc.l {$82000000+NonHandledInterrupt} ; irq10
dc.l {$82000000+NonHandledInterrupt} ; irq11
dc.l {$82000000+NonHandledInterrupt} ; irq12
dc.l {$82000000+NonHandledInterrupt} ; irq13
dc.l {$82000000+NonHandledInterrupt} ; irq14
dc.l {$82000000+NonHandledInterrupt} ; irq15
dc.l {$82000000+NonHandledInterrupt} ; irq16
dc.l {$82000000+NonHandledInterrupt} ; irq17
dc.l {$82000000+NonHandledInterrupt} ; irq18
dc.l {$82000000+NonHandledInterrupt} ; irq19
dc.l {$82000000+NonHandledInterrupt} ; irq20
dc.l {$82000000+NonHandledInterrupt} ; irq21
dc.l {$82000000+NonHandledInterrupt} ; irq22
dc.l {$82000000+NonHandledInterrupt} ; irq23
dc.l {$82000000+NonHandledInterrupt} ; irq24
dc.l {$82000000+NonHandledInterrupt} ; irq25
dc.l {$82000000+NonHandledInterrupt} ; irq26
dc.l {$82000000+NonHandledInterrupt} ; irq27
dc.l {$82000000+NonHandledInterrupt} ; irq28
dc.l {$82000000+NonHandledInterrupt} ; irq29
end
好了,本文就講完了,其實想盡量講得明白一些,但發現涉及到的方面太多了,展開來講的話,篇幅會很大,也會顯得很啰嗦了。比如說匯編指令我就沒有介紹過,匯編連接器所支持的偽指令有哪些我也沒有講到,想講,但可能花的篇幅比本文還長。。后續有時間,覺得有必要再進行補充吧。最近也比較忙。當然最重要的一點是,沒人看。沒人看。沒人看啊。
上一篇:學習STM8 關于數據類型的定義心得
下一篇:stm8單片機內部存儲EEPROM字節讀寫實例解析
推薦閱讀
史海拾趣