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
文章標籤

Richard919 發表在 痞客邦 留言(0) 人氣(1,080)