轉載:http://blog.csdn.net/u010093140/article/details/50021897
使用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指明以下的代碼是用於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