Ch10 程式庫


在這一章堙A我想介紹程式庫的用法。在前面幾章的經驗堙A各位應該可以看得出來,有許多副程式是非常有用的,如果我們把這些常用的程式寫好之後,將他們組譯好,變成一個『程式庫』,以後要使用時只要在原始程式中呼叫,就能節省許多時間。

事實上,有許多公司都有賣程式庫,特別是 C/C++ 語言,程式庫是很重要的。在這一章堙A我們自己也可以自製一個程式庫。步驟如下:

  1. 第一步當然是先寫好原始程式,要注意的是,副程式必須宣告為『public』這個 public 的意義是指公用的,就是能被許多程式所呼叫,所以稱為公用的。

  2. 此外為了與主程式區段正確連結,還得在 segment 之後加上
    byte    public  'code'
    這裡的『public』和上述的『public』(公用的) 並不相同,詳細情形下一章說明,您先把它當成是一個樣板吧!

  3. 第二步照正常方法,分別組譯這些原始程式變成目的檔『*.OBJ』。

  4. 用 MASM 5.0 內附的 LIB.EXE 把這些目的檔變成一個『程式庫』。

  5. 當其他程式要使用時,先宣告要呼叫的副程式(當然,這個副程式已經含在程式庫中了)為『extrn』,然後照正常方式組譯,只有到連結時,LINK.EXE 會要您輸入程式庫名,這時就輸入自己的程式庫檔名,LINK.EXE 就會到該程式庫去找被呼叫的副程式,再將它嵌入可執行檔內。

讓小木偶先自製一個名叫『MYASMLIB.LIB』的程式庫吧,這個程式庫包含兩個副程式,分別是印出BL、BX 內容的十六進位數,它們都曾在前一章內提到,副程式名稱分別是 print_bl_hex 和 print_bx_hex。我將 print_bl_hex 副程式從 MEMDUMP.ASM 原始檔中獨立出來,另成一個新檔。

以下是 print_bl_hex 副程式的原始檔,將它寫好後存檔為PNT_BL.ASM:

;***************************************
code    segment byte    public  'code'
        assume  cs:code,ds:code
        public  print_bl_hex
;---------------------------------------
;印出 BL 的十六進位數
;輸入:BL
;輸出:顯示BL的十六進位數
;備註:AX、CL、DX之值會改變
print_bl_hex    proc    near
        mov     cl,4
        rol     bl,cl
        call    print_4_bits
        rol     bl,cl
print_4_bits:
        mov     dx,bx
        and     dl,0fh
        add     dl,30h
        cmp     dl,3ah
        jb      print
        add     dl,7
print:  mov     ah,2
        int     21h
        ret
print_bl_hex    endp
;---------------------------------------
code    ends	
;***************************************
        end     print_bl_hex

以下是將 print_bx_hex 副程式,它的功用是把 BX 的內容以十六進位的方式顯示在螢幕上,底下是原始程式列表,將它存成 PNT_BX.ASM:

;***************************************
code    segment byte    public  'code'
        assume  cs:code,ds:code
        public  print_bx_hex
;---------------------------------------
;目的:在銀幕上印出 BX 之十六進位值
;輸入:BX
;輸出:印出 BX 的十六進位數
;備註:
print_bx_hex    proc    near
        mov     cl,4
        rol     bx,cl
        call    print_4_bits
        rol     bx,cl
        call    print_4_bits
        rol     bx,cl
        call    print_4_bits
        rol     bx,cl
print_4_bits:
        mov     dx,bx
        and     dl,0fh
        add     dl,30h
        cmp     dl,3ah
        jb      print
        add     dl,7
print:  mov     ah,2
        int     21h
        ret
print_bx_hex    endp
;---------------------------------------
code    ends	
;***************************************
        end     print_bx_hex

因為這兩個副程式將要加入程式庫,此時並不知道要被那個主程式呼叫,只有當 LINK.EXE 把副程式與主程式連結時,才能依實際情形接在主程式之後,所以不必在這兩個副程式加上『org 100h』,然後進行以下操作:

  1. 在 DOS 模式下,將原始程式 PNT_BL.ASM 組譯,得到目的檔,PNT_BL.OBJ:
    H:\HomePage\SOURCE>..\masm50\masm pnt_bl; [Enter]
    Microsoft (R) Macro Assembler Version 5.00
    Copyright (C) Microsoft Corp 1981-1985, 1987.  All rights reserved.
    
    
      51578 + 365350 Bytes symbol space free
    
          0 Warning Errors
          0 Severe  Errors
  2. 用 LIB.EXE 把目的檔組合起來,建立自己的程式庫:
    H:\HomePage\SOURCE>..\masm50\lib [Enter]
    
    Microsoft (R) Library Manager  Version 3.07
    Copyright (C) Microsoft Corp 1983-1987.  All rights reserved.
    
    Library name: myasmlib [Enter]
    Library does not exist.  Create?y [Enter]
    Operations: +pnt_bl [Enter]
    List file: myasmlib.lst [Enter]
    容我在此解釋:當 LIB.EXE 執行後,依次會問你要處理的程式庫,小木偶就輸入自己的程式庫檔名,MYASMLIB,副檔名可以不輸入,LIB.EXE 會自動加上 .LIB 作為副檔名。如果 LIB 找不著,就會問你是否要建立,當然小木偶回答 y (yes 之意)。接下來出現『Operations:』,意思是問你要對 MYASMLIB.LIB 做什麼事。小木偶輸入『+pnt_bl』意思是在這個程式庫加入副程式 pnt_bl,當然如果你想刪除副程式,就用『-』號﹔在這裡輸入的其實是目的檔的檔名,PNT_BL.OBJ,LIB 會將該 OBJ 檔的副程式自動地加入程式庫內。再接下來 LIB 會問你要產生列表檔的名字,LIB 所產生的列表檔案是一個純文字檔,可以讓使用者知道這個程式庫內有什麼副程式。

    現在已經有一個程式庫,雖然裡面只有一個副程式:
    H:\HomePage\SOURCE>type myasmlib.lst [Enter]
    PRINT_BL_HEX......pnt_bl
    
    pnt_bl            Offset: 00000010H  Code and data size: 1eH
      PRINT_BL_HEX
  3. 接下來也把 print_bx_hex 副程式加入程式庫吧。
    H:\HomePage\SOURCE>..\masm50\masm pnt_bx; [Enter]
    Microsoft (R) Macro Assembler Version 5.00
    Copyright (C) Microsoft Corp 1981-1985, 1987.  All rights reserved.
    
    
      51578 + 365350 Bytes symbol space free
    
          0 Warning Errors
          0 Severe  Errors
    
    H:\HomePage\SOURCE>..\masm50\lib [Enter]
    
    Microsoft (R) Library Manager  Version 3.07
    Copyright (C) Microsoft Corp 1983-1987.  All rights reserved.
    
    Library name: myasmlib [Enter]
    Operations: +pnt_bx [Enter]
    List file: myasmlib.lst [Enter]
    Output library: myasmlib [Enter]
    
    H:\HomePage\SOURCE>

    當 LIB 發現使用者輸入的程式庫已存在,LIB 會問要的程式庫名稱(白色部份),若您的回答和前面的程式庫名稱相同的話,那原程式庫就會被取代。底下看看列表:

    H:\HomePage\SOURCE>type myasmlib.lst [Enter]
    PRINT_BL_HEX......pnt_bl            PRINT_BX_HEX......pnt_bx
    
    
    pnt_bl            Offset: 00000010H  Code and data size: 1eH
      PRINT_BL_HEX
    
    pnt_bx            Offset: 000000a0H  Code and data size: 28H
      PRINT_BX_HEX
    
    H:\HomePage\SOURCE>
好了,現在小木偶已經建立了含有兩個副程式的程式庫了,這個程式庫名叫 MYASMLIB.LIB,今後要寫印出暫存器的副程式就可以呼叫它了。底下我們舉一個實例吧!這個程式叫 PNTKYSC.COM(print key scan code 之意),執行後無任何訊息,但是當你按下一個鍵後,會在螢幕上顯示該鍵的掃描碼和 ASCII 數字。如果按下 Esc 鍵後,除顯示掃描碼和 ASCII 數字外,並跳回 DOS。
cr      equ     0dh             ;01 常數,表示歸位字元
lf      equ     0ah             ;02 常數,表示換行字元
;***************************************
code    segment public  'code'
        assume  cs:code,ds:code
        extrn   print_bl_hex:near   ;06 宣告外部副程式
        org     100h
;---------------------------------------
start:  jmp     short begin
msg1    db      cr,lf,cr,lf,'Scan code:$'
msg2    db      cr,lf,'ASCII character:$'
begin:  mov     ah,0
        int     16h
        mov     bx,ax           ;11 BH=掃描碼,BL=ASCII碼
        mov     dx,offset msg1
        mov     ah,9
        int     21h
        xchg    bh,bl           ;15 交換後,BL=掃描碼,BH=ASCII碼
        call    print_bl_hex    ;16 印出 BL

        mov     dx,offset msg2
        mov     ah,9
        int     21h
        mov     dl,bh
        mov     ah,2
        int     21h

        cmp     bl,1            ;23 檢查是否按下Esc鍵
        jne     start           ;24 若為1,表示按下Esc鍵,則結束

        mov     ax,4c00h
        int     21h

;---------------------------------------
code    ends
;***************************************
        end     start

接下來,在 DOS 模式組譯 PNTKYSC.ASM,並連結自製的程式庫:

H:\HomePage\SOURCE>..\masm50\masm pntkysc; [Enter]
Microsoft (R) Macro Assembler Version 5.00
Copyright (C) Microsoft Corp 1981-1985, 1987.  All rights reserved.


  51536 + 418704 Bytes symbol space free

      0 Warning Errors
      0 Severe  Errors

H:\HomePage\SOURCE>..\masm50\link pntkysc [Enter]

Microsoft (R) Personal Computer Linker  Version 2.40
Copyright (C) Microsoft Corp 1983, 1984, 1985.  All rights reserved.

Run File [PNTKYSC.EXE]:[Enter]
List File [NUL.MAP]:[Enter]
Libraries [.LIB]: myasmlib [Enter]
Warning: no stack segment

H:\HomePage\SOURCE>..\masm50\exe2bin pntkysc pntkysc.com [Enter]

H:\HomePage\SOURCE>
雖然在 PNTKYSC.ASM 中並沒有 print_bl_hex 副程式,但是因為一開始就已經將它宣告為『外部』近程副程式,故組譯時 (masm pntktsc;)並不會發生錯誤,而連結時,輸入含有 print_bl_hex 副程式的程式庫 (紅色部份),LINK 會將原始程式與程式庫連起來而變成可執行檔。我們執行 pntkysc.com 看看吧!
H:\HomePage\SOURCE>pntkysc [Enter]


Scan code:10             (按下 Q 鍵)
ASCII character:q

Scan code:10             (按下 Q 鍵及 Shift 鍵)
ASCII character:Q

Scan code:44             (按下 F10 鍵)
ASCII character:

Scan code:01             (按下 Esc 鍵結束)
ASCII character:.
H:\HomePage\SOURCE>


PUBLIC/EXTRN 假指令

PUBLIC 假指令是宣告副程式、標記或變數為『公用』的,MASM.EXE 看到這個假指令,便會把相關資料寫進目的檔,讓 LINK.EXE 能夠連結,使得其他程式能夠使用被宣告為公用的副程式或變數。所以 PUBLIC 應該用在被呼叫的程式中。

PUBLIC 常常和 EXTRN 配合使用。EXTRN 的意思是『外部』的,當 MASM.EXE 看到被宣告外部的變數、副程式或標記時,雖然在程式中找不到,也不會出現錯誤,而會把相關資訊寫進目的檔,等到連結時會尋找被連結的程式庫或目的檔,如果找到就不會產生錯誤。因為使用外部的『東東』是變數、標記還是副程式的處理方式不同,所以必須在 EXTRN 後面告訴 MASM.EXE 是那一種。

EXTRN 的語法是:

EXTRN   副程式名:呼叫方式
呼叫方式可以用 near 或 far。

EQU 假指令

EQU 是『等於』的意思。語法是

常數名  equ     數值

組譯器會將原始程式中所有的常數名更換成 EQU 後的數值。在這個程式的第一行將 cr 設定為十六進位的 0D (十進位的 12 ),而後組譯器看到原始程式中的 cr,都會換成 12,而且在程式執行時都無法更改,所以 cr 是一個常數。

那您可能會問為何不直接寫 12 就好了,還要設一個常數,不是多此一舉嗎?原來,如果程式使用一段時間後要修改成其他數 的話,用 equ 假指令,只需要修改一行,再重新組譯即可﹔假如不用 equ,那就得尋找原始程式一行一行看,您說那一種方便呢?


觀察副程式與主程式連結

底下我們來看看 LINK.EXE 是怎樣把副程式加到主程式之後呢?原來關鍵在於副程式區段定義的地方:

code    segment byte    public  'code'

當假指令 segment 之後接上 BYTE 則表示起始位址是以位元組為單位;另外還有一個較常用的是『PARA』,表示以一『節』為起始位址,所謂一節就是 16 個位元組,換句話說其起始位址的最低四個位元均為零。還有其他較少用的選項請參考第 11 章。底下我們用 DEBUG 載入 PNTKYSC.COM 觀察看看。

H:\HomePage\SOURCE>debug pntkysc.com [Enter]
-t [Enter]  →跳過資料

AX=0000  BX=0000  CX=006B  DX=0000  SP=FFFE  BP=0000  SI=0000  DI=0000
DS=1F91  ES=1F91  SS=1F91  CS=1F91  IP=0124   NV UP EI PL NZ NA PO NC
1F91:0124 B400          MOV     AH,00
-u 124 158 [Enter]
1F91:0124 B400          MOV     AH,00
1F91:0126 CD16          INT     16
1F91:0128 8BD8          MOV     BX,AX
1F91:012A BA0201        MOV     DX,0102
1F91:012D B409          MOV     AH,09
1F91:012F CD21          INT     21
1F91:0131 86FB          XCHG    BH,BL
1F91:0133 E81700        CALL    014D →呼叫 print_bl_hex 副程式
1F91:0136 BA1101        MOV     DX,0111
1F91:0139 B409          MOV     AH,09
1F91:013B CD21          INT     21
1F91:013D 8AD7          MOV     DL,BH
1F91:013F B402          MOV     AH,02
1F91:0141 CD21          INT     21
1F91:0143 80FB01        CMP     BL,01
1F91:0146 75B8          JNZ     0100
1F91:0148 B8004C        MOV     AX,4C00
1F91:014B CD21          INT     21    →主程式結束
1F91:014D B104          MOV     CL,04 →副程式開始
1F91:014F D2C3          ROL     BL,CL
1F91:0151 E80200        CALL    0156
1F91:0154 D2C3          ROL     BL,CL
1F91:0156 8BD3          MOV     DX,BX
1F91:0158 80E20F        AND     DL,0F

您可以試試,把 PNT_BL.ASM 中的『code segment byte』改成『code segment para』看看,會有什麼結果?


回到首頁到第九章到第十一章