一、 引言:
如果大家用過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