Mpasm Directive(假指令) 簡述

資料來源: Microchip文件DS33014G
(MPASM User's Guide with MPLINK and MPLIB)第一部份MPASM第五章Directive Language.

註一: 假指令是不分大小寫的. 也就是說
cblock 無論是寫成CBLOCK, cblock, Cblock都會被Mpasm視為相同的假指令.


註二: 因為我還沒設計過多檔連結的案子. 所以歸類為 Object File類別的指令,
只是我從Help檔翻譯而得,我不敢確定一定沒有誤譯.
(除了UDATA, IDATA, CODE指令外,其它都已經組譯確認過, 翻譯後的意思應該沒有跑掉.)

    被歸類為CONTROL的指令:

  1. CONSTANT 用來宣告常數, 

    語法  

    constant < label > [= < expr >, ... ,< label > [= < expr >]  ]


    這個假指令可以用來提高程式的易讀性,也有助於程式的維護和修改.
    比如說, 你用MCU抓到一個脈波的寬度,並與設定的寬度上限比較,
    超出則點亮警示燈. 這個寬度上限,就可以用constant來定義.如
    constant MaxPeriod=250
    日後若寬度上限需要更動時,只需找出定義區的constant MaxPeriod=250,
    把數字250改成需要的值就可以了.
    這樣絕對會比直接把250寫在程式碼來的好.


  2. #DEFINE 定義字串的替代, 

    語法  

    #define < name > [ [(< arg >, ... ,< arg >)] < value >]


    除了constant指令之外,其實define也可以用來定義常數. 例如 #define MaxPeriod 250 但是用#define來定義常數,其實並不是很恰當. (容易混淆,以至於降低程式的可讀性.) #define 可以用來 定義一些symbol, 達成 條件式編譯的效果. 舉例來說 #define Testing ifdef Testing < instructions for Testing > endif


  3. END 結束程式, 

    語法  

    End (放在end指令後面的東西,都不會被編譯.)


  4. EQU 與constant類似,可用來定義常數(從Help直接翻譯而來), 

    語法  

    < label > equ < expr >


    這個指令實際上,可以用來宣告變數所在的位址. (CBLOCK, ENDC也可以宣告變數位址) 例如 counter1 equ 0x20 W_Temp equ 0x21 也可以定義常數, 例如 b7 equ .7


  5. #INCLUDE 引入常數(,暫存器...)的定義檔, 

    語法

    include << include_file >>

    include "< include_file >"


    這個指令非常非常重要. Microchip已經把每一個MCU的特殊暫存器都定義好了. 連MCU不存在的RAM的位置都幫你定義完畢.(用__BADRAM指令), 請參考各MCU的inc檔. 例如p12c671.inc定義了 __MAXRAM H'FF' __BADRAM H'06'-H'09', H'0D'-H'1D' __BADRAM H'86'-H'89', H'8D', H'90'-H'9E', H'C0'-H'EF' 所以你不需要自行定義S.F.R.的名稱.類似下列的定義列 都不必自己寫了. (S.F.R.是Special File Register的縮寫.) STATUS equ H'0003' FSR equ H'0004' 這可以讓你的程式開頭的定義區減少一大堆瑣碎的定義. (筆者註: 別人寫好的工具檔或原始碼,尤其是Microchip寫的, 我們最好充分利用, 這樣才可以像 牛頓說的 踏在巨人的肩膀上. 讓自己可以看得更遠.)


  6. ORG 設定程式碼的位址, 

    語法

     < label > org < expr >


    不只是程式開頭需要這個指令, 在跨頁,比較大容量的MCU裡面,這個指令更是必備的. (參考MPASM的Directive的Example1)


  7. PROCESSOR 設定處理器的種類 (此指令很少用,因為list p=處理器名稱,就可以取代這個指令了), 

    語法

     processor < processsor_type >


  8. RADIX 指定預設的數字基底, 

    語法 radix < default_radix >


    數字基底共有Hex, Dec, Oct三種. 請依照你的習慣來設定數字基底.


  9. SET 定義變數, 

    語法  

    < label > set < expr >


    假指令SET和EQU的差別是, 被SET所定義的label可以用SET重新設定, 但EQU設定過的label,卻不可以重新設定.


  10. #UNDEFINE 取消字串的取代, 

    語法

     #undefine < label >


    注意:要取消字串取代的label,必須在取消前已經用#define定義過.


  11. VARIABLE 將某Symbol宣告為變數. 

    語法  

    variable < label > [= < expr >, ... ,< label > [= < expr >] ]


    假指令constant和variable所定義的symbol,只是用來幫助組譯器, 並不會被存在MCU的暫存器裡面. (所以用constant和variable所定義的symbol, 在watch window內是看不到數值的.)


    被歸類為CONDITIONAL ASSEMBLY的指令:


  1. IF 如果條件成立,之後的程式碼將被組譯, ELSE 當IF所列的條件不成立,則ELSE之後的程式馬會被組譯 ENDIF 結束條件式組譯.
    以上三指令的語法大致如下 If 條件 條件成立時,所要組譯的程式碼 else 條件不成立時,所要組譯的程式碼 endif 注意: else和之後的條件不成立所要組譯的程式碼可以省略. 這三個指令非常有用. 比如說,你要控制外部的LED,可以是 正邏輯推動(輸出High則LED亮, 輸出Low則LED熄), 也可以是 負邏輯(輸出low則LED亮..), 根據不同的外部的電路,搭配不同的推動邏輯. 在這個情況下, 你可以使用if這種條件式組譯的指令,讓程式更具有彈性.


  2. WHILE (條件) 條件成立時,組譯此處的指令 ENDW 結束While迴圈
    這一組指令,我知道意思,但卻不曉得該拿來做什麼. 懂的人不妨舉個例子,解釋一下. 謝謝! (很好奇,Microchip當初設計這組假指令的目的是什麼呀!?)


  3. IFDEF 若Symbol已經定義了,則組譯之後的程式碼, 

    語法

     ifdef < label > 

    當< label >已定義,將被組譯的程式碼 endif這一組指令,在開發階段使用16F87X, 16F62X之類的Flash MCU, 但是成品將換用比較便宜的16C7X, 16CR5X之類的OTP或ROM. 這種情況下,非常好用. ***使用flash type來開發最終IC是OTP或ROM的時候, 可以參考Application Note #TB033)***


  4. IFNDEF 若Symbol不曾定義,則組譯之後的程式碼, 

    語法 Ifndef < label > 

    當< label >未定義,將被組譯的程式碼 endif


    被歸類為DATA的指令:


  1. __MAXRAM 定義RAM位址的最大值(兩條底線相連), 

    語法

     __maxram < expr >


  2. __BADRAM 定義不存在的RAM位址(兩條底線相連), 

    語法

     __badram < expr >


    指令__BADRAM必須在__MAXRAM宣告後才可以使用. 這兩個指令可以避免我們用到不存在的RAM,但我們可以 引入Mplab內建的p12c508.inc, p12c508a.inc, …等inc檔,來達到同樣的效果. 這是因為那些inc檔都已經把不存在的RAM用__MAXRAM和__BADRAM 定義完畢了, 所以我們只須用include指令,把需要的inc檔引入. 就可以保證我們不會用到不存在的RAM.


  3. __MAXROM 定義ROM的最大位址(兩條底線相連), 

    語法

     __maxrom < expr >


  4. __BADROM 定義不存在的ROM位址(兩條底線相連), 

    語法

     __badrom < expr >[-< expr >] [, < expr >[-< expr >]]


    指令__BADROM必須在__MAXROM宣告後才可以使用. 程式開頭的LIST P=???? 可以定義我們的程式,使用了???? 這個型號的MCU,如果程式寫的太大了,組譯器在組譯時 就會警告我們. 所以這兩個指令似乎是派不上用場.


  5. CBLOCK 定義一個常數區塊(Define a block of constants) ENDC 結束常數區塊, 

    語法  

    cblock [< expr >] < label >[:< increment >][,< label >[:< increment >]] endc


    這一組假指令,可以用來定義存於RAM的變數的位址. 尤其是變數的個數很多的時候. 如果固定使用equ來宣告變數的位址,很容易出現keyin錯誤, 造成有的RAM還空著,有的卻已經被重複定義了 (兩個以上的變數使用同一個位址), 改用cblock和endc假指令, 就可以有效地消除 equ宣告變數位址可能造成的問題. 請看例子 cblock 0x20 Temp1, Temp2, TempW1:2, Counter1, Counter2 CounterW1:2, Counter3 endc 就會組譯出下列結果: Temp1位址=0x20, Temp2位址=0x21, TempW1位址=0x22, Counter1位址=0x24, Counter2位址=0x25, CounterW1位址=0x26 Counter3位址=0x28


  6. __CONFIG 設定燒錄的設定值, 

    語法  

    __config < expr > 或 __config < addr >, < expr >


    這個指令非常好用,可以免去燒錄時手動設定Osc, MCLR…等設定值. ***筆者註: 建議使用inc檔內所規定的常數來設定燒錄的設定值. 這樣可以提高程式的易讀性. (可參考mplab\template樣板程式碼,和各個inc檔)***


  7. __IDLOCS 設定辨識碼(ID), 

    語法  

    __idlocs < expr >


    ID可以用來辨識程式的名稱和版次. 以方便IC程式的種類和版次的追蹤和確認.


  8. DA 在Program Memory內儲存資料(14-bit數字代表兩個7-bit ASCII字元), 

    語法  

    [< label >] da < expr > [, < expr2 >, ..., < exprn >]


    例 DA "abcdef"將在Program Memory內儲存30E2 31E4 32E6. (a=110-0001, b=110-0010,結合成14bit,就變成30E2,依此類推, cd變成31E4, ef變成32E6. 注意.Microchip範例說明是錯的,多了3380. 例 DA "12345678" ,0將在Program Memory內儲存18B2 19B4 1AB6 1BB8 0000. (1=011-0001, 2=011-0010,結合成14bit,就變成18B2,依此類推, 34變成19B4, 56變成1AB6, 78變成1BB8. 注意Microchip範例說明是錯的,少了1BB8. 例 DA 0xFFFF將在Program Memory內儲存3FFF. (超過14-bit的部分都被刪除)


  9. DATA 宣告數字,文字資料(若配合IDATA假指令,DATA可用來宣告資料的初始值), 

    語法  

    [< label >] data < expr >[,< expr >, ... ,< expr >]


    DA是把資料截斷成2個7bit,塞入14bit內; DATA是直接把多餘的位元直接刪除, 比如說用在14bit長度的MCU,只保留14bit. (類似DW,但用在PIC18CXX這種16bit長的MCU, 則DW和DATA的組譯結果上下位元組是相反的,.請看例子 data "test A,B,C",12,4,0x5678 組譯後得到 3465 3374 2041 2C42 2C43 0012 0004 1678 (編譯時出現Message[303] Program word too large. Truncated to core size. (7465) (7374) (5678) 就是"te","st",0x5678太長了,被刪成3465,3374,1678.)


  10. DB 宣告1 Byte的資料(若配合IDATA假指令,DB可用來宣告資料的初始值), 

    語法  

    [< label >] db < expr >[,< expr >, ... ,< expr >]


    DA是把資料截斷成2個7bit,塞入14bit內; DB卻是直接保存8bit(=1Byte)的資料,但因為14bit的前段只有6bit, 所以存在前面的資料,只保留6bit.請看例子 Db 0x0f,0x89, 0x0f, '1', 0x0f, '3', 0xff, 'A', 0x0f, 't', '\n' 組譯後得到 0F89 0F31 0F33 3F41 0F74 0A00 (編譯時出現Message[303] Program word too large. Truncated to core size. (FF41)就是0xff太長了,被刪成3F.('A'=41), 最後一個0A00是'\n'(=0A)構成的.不成對的資料末端補0.)


  11. DW 宣告1 Word的資料(若配合IDATA假指令,DW可用來宣告資料的初始值), 

    語法  

    [< label >] dw < expr >[,< expr >, ... ,< expr >]


    DW和DB用法相似,只是資料長度不同; 在PIC18CXX等16-bit長度的MCU, DW可以保留16bit長度的資料, 但用於14bit的MCU,則資料只能保留14bit.請看例子 DW 0x6f, 0x89ee, 0x0fdd, '1', 0xff07, 't', '\n' 組譯後得到 006F 09EE 0FDD 0031 3F07 0074 000A (編譯時出現Message[303] Program word too large. Truncated to core size. (89EE)和(FF07),如組譯結果, 被刪成09EE和3F07.只保留14bit(=core size).)


  12. DE 宣告EEPROM的資料內容(8-bit長,和da指令的長度14bit不同), 

    語法  

    [< label >] de < expr >[,< expr >, ... ,< expr >]


    請注意, PIC18CXXX的EEPROM起始位址是0xF00000, 其它的PICmicro則是0x2100. (請參考MPASM Assembler Help) 這個指令可以在燒程式時一併把資料燒到EEPROM裡面.


  13. DT 定義表格(將組譯產生一串RETLW指令, RETLW回傳的資料就定義在dt後面的表示式裡面), 

    語法  

    [< label >] dt < expr >[,< expr >, ... ,< expr >]


    這個指令可以提高程式的易讀性,比如說,你想在LCD顯示 I am a student. 如果你用RETLW一個一個寫, 勢必增加閱讀的困擾,也使程式的維護修改更困難.


  14. FILL 將記憶體填滿特定數值或指令, 

    語法  

    [< label >] fill < expr >, < count >


    在Microchip的seminar講義中, 建議記憶體不要留空. 如果能把剩餘的記憶體統統填滿nop或goto reset指令, 就可以在程式誤迷失到剩餘的記憶體時, 因為goto reset而強制程式reset,免去當機的危險. ***此指令只是預防萬一. 要避免當機, 應該從良好的軟體和硬體下手, 千萬不要以為有了fill指令和watchdog, 就不必注意程式的運作流程是否合理.***


  15. RES 保留記憶體空間, 

    語法  

    [< label >] res < mem_units >


    若用在可重置程式碼位址(relocatable code)的場合, 此指令將保留資料儲存空間; 若用在不可重置程式碼位址(non-relocatable code)的場合, < label >將被指定為記憶體位址. 例如 buffer res 64 ;保留64個儲存空間.


    歸類為LISTING的指令:


  1. ERROR 在Build Results和Absolute Listing視窗內 都會產生一個使用者自訂的錯誤訊息, 

    語法  

    error "< text_string >"


    搭配if指令,我們可以設訂產生錯誤訊息的條件, 使組譯器在這些自訂的條件成立時,產生錯誤訊息 來提醒我們,某些我們不希望發生的情況已經發生了. 請看例子: cfl_jge macro file,con,jump_to if file < 0x20 ;if file register falls in S.F.R range error "The first parameter of macro cfl_jge must be >= 0x20" endif movlw con&0xff subwf file,w btfsc STATUS,C goto jump_to endm 如果使用cfl_jge巨集時的參數值file小於0x20,則錯誤訊息 The first parameter of macro cfl_jge must be >= 0x20就會 出現在Build Results和Absolute Listing視窗內


  2. MESSG 類似ERROR,但只是出現MESSAGE訊息, 

    語法  

    messg "< message_text >"


    這個指令和if搭配,可以用來偵測巨集的參數是否引用失當…


  3. ERRORLEVEL 設定訊息的等級, 

    語法  

    errorlevel 0|1|2|< +- >< msgnum > 

    0使messages, warnings, errors都會被列出來. 1使warnings, errors都會被列出來. (messages不列) 2使errors都會被列出來. (warnings, messages不列) +< msgnum >使編號msgnum的message被列出來, -< msgnum >使編號msgnum的message不被列孕X來. 關於message的編號,請參考DS33014G的Appendix C.


  4. LIST 根據選項的on-off,使Listing檔和組譯程序出現對應的變化, 

    語法  

    list [< option >[, ... ,< option >]]


    選項有很多個.簡述如下: b=nnn 設定Tab的間隔大小.(預設值為8.也就是一個Tab間隔8個空格.) c=nnn 設定欄位的寬度.(預設值132.) f= 設定組譯的hex檔的格式,有INHX32, INHX8M, INHX8S三種. (預設值是INHX8M) free 使用自由格式,提供倒退(backward)的能力.(預設值FIXED) fixed 使用固定格式.(預設值FIXED) mm={ON|OFF} 在list檔案內顯示記憶體的使用狀況圖. (預設值ON) n=nnn 設定每頁的行數. (預設值60,大約是A4紙的長度) p=< type > 設定處理器的種類,比如說PIC12C671. (無預設值) r=< radix > 設定數字基底HEX,DEC,或OCT. (預設值HEX) st={ON|OFF} 顯示symbol. (預設值ON) 這個很方便喔, 你可以從這裡察知變數或常數到底被定義成什麼了. 除非硬碟空間不足,否則最好還是保留ON狀態. t={ON|OFF} 把過長的行刪截掉(Truncate)或是不刪(即wrap換行再印). (預設值Off) w={0|1|2} 設定訊息的顯示層級.(請看ERRORLEVEL, 預設值是0) x={ON|OFF} 設定巨集是否展開. (預設值ON,也就是展開) 例子: list p=17c42, f=INHX32, r=DEC


  5. NOLIST 不產生listing檔案, 

    語法  

    Nolist


    為了除錯,通常我們都會設定"產出list檔",若因硬碟空間不足, 我們可以加入nolist指令,就不會產生list檔了.


  6. PAGE 使list檔換頁, 

    語法  

    Page


    這個換頁指令,可以使list檔段落更分明,使程式報表更容易閱讀.


  7. SPACE 在list檔插入空白行, 

    語法  

    space [< expr >]


    產生行的空白. 例如 space 3 ;插入三行空白.


  8. SUBTITLE 設定list檔每頁的開頭的副標題, 

    語法  

    subtitle "< sub_text >"


  9. TITLE 設定list檔每頁的開頭的標題, 

    語法  

    title "< title_text >"


    歸類為MACRO的指令:


  1. MACRO 宣告巨集的定義, 

    語法  

    < label > macro [< arg >, ... ,< arg >]


  2. ENDM 結束巨集的定義, 

    語法  

    Endm


    巨集的名稱必須明確易懂, 太複雜的首字縮寫名稱,會造成看不懂巨集的功能, 進而減低使用巨集的頻率. (請參考Microchip的巨集定義,領略巨集的威力.)


  3. EXITM 跳出巨集, 

    語法  

    exitm


  4. LOCAL 在巨集內宣告局部變數, 

    語法  

    local < label > [,< label >]


  5. EXPAND使巨集在list檔中,做展開顯示, 

    語法  

    expand


  6. NOEXPAND 使巨集在list檔中不做展開顯示, 

    語法  

    Noexpand


    如果想要詳細了解巨集被組譯為那些指令,就不該使用noexpand指令. 因為下達此指令,會使得巨集指令都不做展開顯示, 只會顯示單純的巨集指令.


    歸類為OBJECT FILE的指令:


  1. BANKISEL 根據label所在的RAM bank來切換bank. (設定間接RAM的指標(STATUS的IRP位元)), 

    語法  

    bankisel < label >


    注意,這個指令只在使用間接定址(FSR, INDF)時,才會用到. 使用本指令來切換間接RAM的頁次,可提高程式的易讀性.例如 VARIABLE Var1=0x192 movlw Var1 movwf FSR bankisel Var1 movwf INDF 如果MCU本身的RAM沒有bank2(3), 則BANKISEL Var1不會組譯出 任何指令碼,而且會出現訊息,提醒你該MCU是不必使用BANKISEL指令的. 如果Var1位於bank0(1), 則BANKISEL Var1 會組譯為bcf STATUS,IRP 如果Var1位於bank2(3), 則BANKISEL Var1 會組譯為bsf STATUS,IRP (此例的Var1=0x192在bank3,所以bankisel Var1將譯為bsf STATUS,IRP)


  2. BANKSEL 根據label所在的RAM bank來切換bank (設定RAM的指標(STATUS的RP1, RP0兩個位元)), 

    語法 banksel < label >


    這個指令可以減少自己切換RAM bank的麻煩. 使用本指令來換頁,可提高程式的易讀性.


  3. CODE 宣告目的檔(Object File)的程式節區的開始, 

    語法  

    [< label >] code [< ROM address >]


    適當的加上label, 可以提高易讀性. 例如 RESET code 0x1FF Goto START 這樣,很容易知道0x1FF存放了RESET的程式碼. (Object File不允許ORG指令)


  4. CODE_PACK 在可執行碼區塊填入位元組的資料, 

    語法  

    [< label >] code_pack [< ROM address >]


    例如 bytes CODE_PACK H'01FF' DB 1, 2, 3 ; at addresses 0x1FF, 0x200, 0x201 DB 4 ; at address 0x202 DB 5 ; at address 0x203


  5. EXTERN 宣告標記(變數或副程式)是在其它檔案內宣告的, 

    語法  

    extern < label > [ ,< label >]


    指令External所宣告的標記,必須在其它檔案內 宣告為global(公用標記). 例如, extern Function1, Function2 : call Function1 call Function2


  6. GLOBAL 宣告一個標記(變數或副程式名稱)給其它檔案使用, 

    語法  

    global < label > [, < label > …]


    指令GLOBAL宣告的標記,可以給其它檔案使用,但其他檔案 必須搭配EXTERN指出要引用的標記並不是在本身宣告定義的. (利用Edit project把會用到的檔案都列入,而且要設定用來link的 工具程式(通常是直接使用Microchip提供的mplink)) 例如 udata Var1 res 1 Var2 res 1 Global Var1, Var2 Code AddThree Global AddThree Addlw 3 Return


  7. IDATA 宣告有初值的RAM的資料區段的開始, 

    語法  

    [< label >] idata [< RAM address >]


    搭配RES指令(初值0)、DB指令(以Byte為單位)、 或DW指令(以Word為單位)可以規劃RAM空間, 並且設定初始值給變數使用(從設定的RAM address開始). 例如 idata LimitL dw 0 LimitH dw D'300' ;D'300'=012C,存為2C 01(低位元組先存) Gain dw D'5' Flags res 0 String db "Hi there!"


  8. PAGESEL 切換到label所在的頁, 

    語法  

    pagesel < label >


    這個指令可以減少自己設定頁次(跨頁)的麻煩. 若程式大到需要跨頁.最好使用本指令來換頁,以提高程式的易讀性.


  9. UDATA 宣告未初始化的RAM的資料區段的開始, 

    語法  

    [< label >] udata [< RAM address >]


    搭配RES指令後,可以規劃RAM空間給變數使用 (從設定的RAM address開始). 例如 udata Var1 res 1 Double res 2


  10. UDATA_ACS (限用於PIC18CXX)宣告 未初始化的RAM的資料區段的開始, 

    語法  

    [< label >] udata_acs [< RAM address >]


    這個指令可以規劃RAM空間讓變數使用(必須和RES指令搭配). 例如 udata_acs Var1 res 1 Double res 2


  11. UDATA_OVR 宣告可重複使用的RAM的資料區段的開始, 

    語法  

    [< label >] udata_ovr [< RAM address >]


    擁有相同名稱(udata_ovr指令前的label相同)的資料節區, 使用同一塊RAM. 例如 Temps udata_ovr Temp1 res 1 Temp2 res 1 Temp3 res 1 Temps udata_ovr LongTemp1 res 2 ;變數LongTemp1和變數Temp1、Temp2使用相同的RAM位址. LongTemp2 res 2 ;變數LongTemp2和變數Temp3使用相同的1Byte RAM位址, 但LongTemp2多用了一個Byte,則是單獨使用的.


  12. UDATA_SHR 宣告全部BANK都共享的RAM的資料區段的開始, 

    語法

    [< label >] udata_shr [< RAM address >]

    這個指令,只能用在有共享RAM的MCU,比如說,16F876/877的 70-7F(在bank0) =F0-FF(在bank1) =170-17F(在bank2) =1F0-1FF(在bank3), 這裡有16個RAM就可以讓UDATA_SHR指令拿去定義. 例如 Temps udata_shr Temp1 res 1 Temp2 res 1 Temp3 res 1

回DIY首頁