組合語言命令參數程式的編寫
一、 引言:
如果大家用過TurboC2.0/3.0 or BorlandC3.X等編譯器編寫DOS應用程式的話,編寫一個命令列參數形式的應用程式對大家來說是一件非常容易的事情,只要在主函main()中加幾個參數就OK(int main(int argc,char *argv[],char *env[]){})。相對組合語言來說編寫一個命令列參數的程式就比較艱難,它要用到DOS的程式段首碼PSP(Program Segment Prefix)知識以及其他相關DOS知識。(本文只對參數介紹,環境塊不作討論)
二、相關知識:
在DOS的提示符下鍵入一個命令(內/外部命令)或程式的名字,DOS SHELL(COMMAND.COM)首先根據名字判別其是內部命令還是外部命令或使用者程式,若是內部命令則調用COMMAND.COM暫駐在記憶體中的部分的DOS內部命令代碼,若是外部命令或使用者程式,DOS SHELL則在目前的目錄和搜尋路徑中搜尋匹配的檔案名稱,找到了就載入程式,載入出錯顯示錯誤資訊,找不到則顯示Bad command or file name。
使用者載DOS提示符下輸入一串字串,DOS SHELL把以斷行符號(0Dh)為結束的這以字串作為一個命令和參數進行解釋,第一個空格以前的字串為命令名(必須符合DOS命名規則),第一個空格(包括空格)到斷行符號之間的字元作為命令或程式的參數。
程式段首碼--PSP是DOS載入一個外部命令或使用者程式(副檔名為COM or EXE)時,在程式段前設定一個以節為邊界,固定長度為10H(即256位元組,一節為16位元組)的儲存塊,PSP和程式段共有一個記憶體控制塊(MCB),PSP位於每個程式的開始部分,無論是COM還是EXE,PSP的資料結構是相同的。PSP是程式與DOS的介面,DOS利用PSP管理進程,DOS使用者進程指的是一個已被裝入記憶體的可執行程式或已被調入記憶體但未執行的程式,COMMAND.COM是一個最早被裝入記憶體的程式,因而可被看作祖先進程,外部命令或使用者程式作為子進程,被DOS通過INT 21H的4BH號子功能來載入。使用者程式也可以通過INT 21H的4BH號子功能調用來載入自己的子進程,控制子進程的執行,並通過4DH號子功能調用擷取子進程的健全狀態。
PSP中存有許多關於程式啟動、執行、結束以及進程調度、進程環境地址和進程標誌等重要訊息。程式利用PSP還可以控制父子進程間的通訊。至於PSP的資料結構的詳細內容請參考有關書籍,本文不詳細給出。
DOS載入一個COM或EXE程式時,段寄存器DS,ES都指向PSP段址(PSP段址是進程的唯一標誌符),而不是指向程式的資料區段和附加段。COM檔案的CS,SS也指向PSP的段址。EXE檔案的CS,SS,IP和SP需要進行重定位。
DOS載入一個外部命令或使用者程式時,把檔案名稱之後到斷行符號符之間的字串,最多可達127個字元作為參數,並把這些字串送到PSP位移81H開始的地區,位移80H的一個位元組存放參數字串長度(斷行符號符不算在內)。大家可用DEBUG.EXE載入一個帶參數的程式,然後用D DS:80子命令查看載入程式的參數。命令列參數一般以空格(20H)為開始,斷行符號符(0DH)為結束,但命令列中的重新導向,管道符以及有關資訊不作為參數傳遞給PSP。
三、樣本程式:
本常式序PARATEST.ASM在沒有參數(參數為一連串空格也視為無參數)的情況下顯示提示資訊,程式以字元'/'作為參數的標誌,'/'後的字元是參數,根據不同的參數顯示不同的字串,並忽略'/'前的空格。程式還把非法參數顯示出來,由於程式中儲存參數的單元只設了兩BYTES,如果字元'/'後的第一格字元是合法參數,程式不管字元'/'後有多少個字元都認為是合法的。
順便介紹一個彙編編程的技巧,文後附帶的樣本來源程式(PARATEST.ASM)中的DEBUG子程式是利用了INT 21H的07H號子功能等待使用者的鍵盤輸入,相當於TURBOC中的getch()函數,作為程式的斷點。我們還可以利用其顯示斷點的調試資訊(包括各寄存器的值)。但注意儲存現場,並進行現場恢複。
四、結束語:
一個月以前我還只是會看別人的組譯工具,自從自己動手寫程式後,自己的彙編編程水平有了很大的進步。寫程式的過程中遇到問題,然後自己看書自己解決問題,這樣學習彙編編程比光看書更有效。我的彙編編程的水平還很菜鳥,我會不斷提高自己的水平,同時也希望與廣大編程愛好者交流。
附:來源程式(PARATEST.ASM)
; ************************************************
; * Program:Use asm language to creat a command *
; * line and parameter program. *
; *==============================================*
; * Designer:Howard Original Place:Wuhan *
; * Creat Date:09/30/1999 *
; * Modification Date:10/05/1999 *
; * Now Version:1.0 *
; * Pass:Tasm 5.0,Tlink 3.1 *
; *==============================================*
; * Modification History *
; *----------------------------------------------*
; * Version 1.0 1.Command line and parameter *
; * 09/30/1999 test program. *
; *----------------------------------------------*
; * Version 1.1 2.Add the spaces parameters jud- *
; * 10/05/19999 gement. *
; ************************************************
;
.model small
.386
.code
org 100h
start:
main proc far
push cs
pop ds ;ds=psp seg address
cld ;cf=0
mov si,81h ;psp+81h is the first parameter char
lea bx,parameter ;parameter address(offset) saved to bx
;unit parameter is used to save the parameter
lodsb ;load a byte from [si] to al,and si=si+1
cmp al,0dh ; Enter?
jz scanexit ;if yes then scan parameter end
cmp al,' '
jz judgespace
lodsb
continue:
push si
cmp al,'/'
jz parascanloop
jmp error ;wrong parameter
judgespace:
lodsb
; call debug ;set break point
cmp al,0dh
je scanexit
cmp al,' '
je judgespace
jne continue
parascanloop: ;saved the parameter to unit parameter
mov [bx],al ;save al to [bx],just save the parameters
lodsb
cmp al,0dh ;Enter?
jz choise ;if yes then jump choise
inc bx
jnb parascanloop ;the next char
scanexit:
lea dx,noparametermsg
call disp
call rettodos
choise:
lea si,parameter
mov al,[si+1] ;load the parameter to al
cmp al,'?' ;judge the parameter and choose ;the different process
jz help
cmp al,'p'
jz print
cmp al,'P'
jz print
jmp error ;wrong parameter
print:
lea dx,message
call disp
call rettodos
help: ;print the help message
lea dx,helpmsg
call disp
call rettodos
error: ;print the error parameter message
lea dx,wrongparamsg
call disp
mov ax,0200h
pop si
dec si
prnwrongparameter:
lodsb
cmp al,0dh
jz retdos
mov dl,al
int 21h
loop prnwrongparameter
retdos:
mov dl,'"'
int 21h
mov dl,'!'
int 21h
call rettodos
main endp
disp proc near
mov ah,09h
int 21h
ret
disp endp
rettodos proc near
mov ah,4ch
int 21h
rettodos endp
;
;set a break point
;debug proc near
; push ax dx
; mov ax,0900h
; mov dx,offset debugmsg
; int 21h
; mov ax,0700h
; int 21h
; pop dx
; pop ax
;debug endp
;debugmsg db 0dh,0ah,'Program stop here,press any key to continue...','$'
noparametermsg db 0ah,0dh,'There is no parameter,enter paratest /? for help.','$'
message db 0dh,0ah,'This is a parameter test program.','$'
wrongparamsg db 0dh,0ah,'The wrong parameter:"','$'
helpmsg db 0dh,0ah,'1.Paratest /?'
db 0dh,0ah,' Print the help message.'
db 0dh,0ah,'2.Paratest /p'
db 0dh,0ah,' Print the test message.','$'
parameter db 2 dup(?)
end start