ITEEDU

10.8.2 程序段前缀的应用

了解和掌握PSP中的信息分布就是为了利用其信息。对于EXE文件,可用中断21H之功能51H获取其段地址。如:

MOV AH, 51H

INT 21H ;BX=PSP的段地址 

MOV ES, BX

CMP byte ptr ES:[80H], 0 ;检查PSP的长度 

JE next

…

对于COM文件,因为其只有一个段,所以,可用更简单的方式来检查PSP的内容。

MOV BX, 80H

CMP byte ptr [BX], 0 ;检查PSP的长度 

JE next

…
例10.17 利用PSP中的信息来终止当前程序的运行。

解:

 
        DSEG  SEGMENT
MSG DB "Hello$" 
        DSEG  ENDS
CSEG SEGMENT 'CODE' 
ASSUME CS:CSEG, DS:DSEG 
MAIN PROC FAR 
      start:                        ;在装入内存时,DS存放的是PSP的段地址
              PUSH      DS
              XOR       AX, AX
              PUSH      AX          ;当前堆栈内容为0和PSP的段地址
              MOV       AX, DSEG
              MOV       DS, AX
              MOV       DX, OFFSET MSG
              MOV       AH, 9
              INT       21H
              RET
        MAIN  ENDP
        CSEG  ENDS
END start 

例10.17是早期利用PSP来终止程序的方法。子程序MAIN必须定义为FAR类型,其前三条语句是为用PSP的头二个字节来终止程序运行服务的,但现在通常用中断21H之功能4CH来终止程序的运行。

例10.18 编写程序,显示其命令行参数信息。

解:在使用命令行时,操作系统通常允许在被装入的程序名之后附加多达127个字符(包括最后的回车符)作为其命令的参数。

本例子的目的就是要了解命令行参数的存储方法,为以后使用命令行参数作准备。

 .MODEL SMALL 
.CODE 
 .STARTUP 
MOV AH, 51H 
INT 21H 
MOV  DS, BX ;DS=PSP的段地址 
MOV BX, 80H 
MOV  CL, [BX] 
XOR  CH, CH 
JCXZ  over 
INC  BX ;80~FFH字段中的跳过第二个字节 
.REPEAT 
INC
 BX 
MOV
 DL, [BX] 
MOV
 AH, 2 
INT
 21H 
.UNTILCXZ ;显示命令行参数中的字符 
over: 
 .EXIT 0 
END 
例10.19 编写一个类型于DOS内部命令TYPE的程序,该命令的使用方法如下:
 ……>type 文件1,文件2,… 

其功能是分别显示其参数文件1、文件2等内容,在显示文件内容前先显示文件名。显示完内容后,暂停,等待用户按任意键。若某文件不存在,则显示相应的提示信息。

解:为了较好地解答本题,我们可以对该问题按以下步骤来解决:

  1. 若命令行参数中的字符数为0,则结束本程序的运行;
  2. 从命令行参数中分离出一个文件名;
  3. 调用子程序,显示当前文件名、文件内容和等待用户按键;
  4. 重复步骤1~3,直到所有参数被处理完。
 .MODEL SMALL, C 
.DATA 
File
 DB 14 DUP(' ') 
Buff 
 DB 128 DUP(0) 
Msg1
 DB 0DH, 0AH, "$" 
Msg2
 DB "Not Found$" 
Msg3
 DB "Press any key...$" 
.CODE 
;显示字符串MSG后,再进行回车、换行 
DispStr PROC USES AX DX, MSG:PTR BYTE 
 MOV DX, MSG ;显示指定的信息  
MOV AH, 09H 
INT 21H 
LEA DX, Msg1 ;显示回车、换行 
MOV AH, 09H 
INT 21H 
RET 
DispStr ENDP 
;文件名的长度为传递参数Len。若文件不存在,则显示提示信息,否则,显示文件内容;
;完成上述操作后,等待用户按键 
Disp PROC USES AX BX CX DX, Len:BYTE 
XOR BX, BX 
MOV BL, Len 
MOV File[BX], '$' 
INVOKE DispStr, ADDR File ;显示文件名 
MOV File[BX],0 
LEA DX, File 
MOV AL, 0H 
MOV AH, 3DH 
INT 21H 
.IF CARRY? ;若创建失败,则显示失败信息 
INVOKE DispStr, ADDR Msg2
 
JMP
 over1 
.ENDIF 
MOV BX, AX ;参见例8.14中的有关说明 
read: 
 LEA DX, Buff 
MOV CX, 128 
MOV AH, 3FH 
INT 21H 
.IF CARRY? || AX==0 
JZ
 close 
.ENDIF 
PUSH BX 
LEA DX, Buff 
MOV CX, AX 
MOV BX, 1 
MOV AH, 40H 
INT 21H 
POP BX 
JMP read 
close: 
 MOV AH, 3EH ;关闭已打开的文件 
INT 21H 
over1:  
INVOKE DispStr, ADDR Msg3 ;显示等待用户按键提示信息 
MOV AH, 07H 
INT 21H ;等待用户按键 
RET 
Disp ENDP 
.STARTUP 
MOV AH, 51H 
INT 21H 
MOV ES, BX 
MOV BX, 80H 
MOV CL, ES:[BX] 
DEC CL 
INC BX 
.WHILE CL>0 ;CL=未被处理的参数字符数 
XOR
 AL, AL ;记录文件名的长度 
LEA
 DI, File 
.REPEAT
 
INC
 BX 
MOV
 AH, ES:[BX] 
DEC
 CL 
.BREAK .IF AH==','
 
MOV
 [DI], AH ;从参数中分离出字符,存入文件名中 
INC
 DI 
INC
 AL 
.UNTIL
 CL==0 
INVOKE
 Disp, AL ;处理当前分离出来的文件名 
.ENDW 
.EXIT 0 
END