深入探索MS-DOS文件输入输出服务
1. 标准MS-DOS文件输入输出服务基础
在MS-DOS环境下,文件的输入输出操作是非常重要的。下面我们先来看两个基础的程序:
ReadString
和
WriteString
。
ReadString
程序用于从键盘读取字符串,直到用户按下回车键为止,它返回输入字符串的大小。以下是其代码实现:
; Returns: AX = size of the input string
; Comments: Stops when the Enter key (0Dh) is pressed.
;--------------------------------------------------------
push cx
; save registers
push si
push cx
; save digit count again
mov si,dx
; point to input buffer
L1:
mov ah,1
; function: keyboard input
int 21h
; returns character in AL
cmp al,0Dh
; end of line?
je L2
; yes: exit
mov [si],al
; no: store the character
inc si
; increment buffer pointer
loop L1
; loop until CX=0
L2:
mov byte ptr [si],0
; end with a null byte
pop ax
; original digit count
sub ax,cx
; AX = size of input string
pop si
; restore registers
pop cx
ret
ReadString ENDP
这个程序的执行流程如下:
1. 保存相关寄存器的值。
2. 不断从键盘读取字符,直到用户按下回车键。
3. 将读取的字符存储到缓冲区中。
4. 最后以空字节结束字符串,并返回字符串的大小。
WriteString
程序则用于将以空字符结尾的字符串写入标准输出。它调用了一个名为
Str_length
的辅助程序来获取字符串的字节数。代码如下:
;--------------------------------------------------------
WriteString PROC
; Writes a null-terminated string to standard output
; Receives: DS:DX = address of string
; Returns: nothing
;--------------------------------------------------------
pusha
push ds
; set ES to DS
pop es
mov di,dx
; ES:DI = string ptr
call Str_length
; AX = string length
mov cx,ax
; CX = number of bytes
mov ah,40h
; write to file or device
mov bx,1
; standard output handle
int 21h
; call MS-DOS
popa
ret
WriteString ENDP
其执行步骤为:
1. 保存所有通用寄存器的值。
2. 获取字符串的长度。
3. 使用
INT 21h
的功能 40h 将字符串写入标准输出。
4. 恢复寄存器的值并返回。
2. 读取和复制文本文件示例
接下来,我们看一个读取和复制文本文件的示例程序
Readfile.asm
。它展示了几个重要的
INT 21h
功能:
- 功能 716Ch:创建新文件或打开现有文件。
- 功能 3Fh:从文件或设备读取数据。
- 功能 40h:向文件或设备写入数据。
- 功能 3Eh:关闭文件句柄。
以下是完整的
Readfile.asm
代码:
; Read a text file (Readfile.asm)
; Read, display, and copy a text file.
INCLUDE Irvine16.inc
.data
BufSize = 5000
infile BYTE "my_text_file.txt",0
outfile BYTE "my_output_file.txt",0
inHandle WORD ?
outHandle WORD ?
buffer BYTE BufSize DUP(?)
bytesRead WORD ?
.code
main PROC
mov
ax,@data
mov
ds,ax
; Open the input file
mov
ax,716Ch
; extended create or open
mov
bx,0
; mode = read-only
mov
cx,0
; normal attribute
mov
dx,1
; action: open
mov
si,OFFSET infile
int
21h
; call MS-DOS
jc
quit
; quit if error
mov
inHandle,ax
; Read the input file
mov
ah,3Fh
; read file or device
mov
bx,inHandle
; file handle
mov
cx,BufSize
; max bytes to read
mov
dx,OFFSET buffer
; buffer pointer
int
21h
jc
quit
; quit if error
mov
bytesRead,ax
; Display the buffer
mov
ah,40h
; write file or device
mov
bx,1
; console output handle
mov
cx,bytesRead
; number of bytes
mov
dx,OFFSET buffer
; buffer pointer
int
21h
jc
quit
; quit if error
; Close the file
mov
ah,3Eh
; function: close file
mov
bx,inHandle
; input file handle
int
21h
; call MS-DOS
jc
quit
; quit if error
; Create the output file
mov
ax,716Ch
; extended create or open
mov
bx,1
; mode = write-only
mov
cx,0
; normal attribute
mov
dx,12h
; action: create/truncate
mov
si,OFFSET outfile
int
21h
; call MS-DOS
jc
quit
; quit if error
mov
outHandle,ax
; save handle
; Write buffer to new file
mov
ah,40h
; write file or device
mov
bx,outHandle
; output file handle
mov
cx,bytesRead
; number of bytes
mov
dx,OFFSET buffer
; buffer pointer
int
21h
jc
quit
; quit if error
; Close the file
mov
ah,3Eh
; function: close file
mov
bx,outHandle
; output file handle
int
21h
; call MS-DOS
quit:
call
Crlf
exit
main ENDP
END main
这个程序的执行流程可以用以下 mermaid 流程图表示:
graph TD;
A[开始] --> B[打开输入文件];
B --> C{是否出错};
C -- 是 --> G[退出];
C -- 否 --> D[读取输入文件];
D --> E{是否出错};
E -- 是 --> G[退出];
E -- 否 --> F[显示缓冲区内容];
F --> H{是否出错};
H -- 是 --> G[退出];
H -- 否 --> I[关闭输入文件];
I --> J{是否出错};
J -- 是 --> G[退出];
J -- 否 --> K[创建输出文件];
K --> L{是否出错};
L -- 是 --> G[退出];
L -- 否 --> M[将缓冲区内容写入输出文件];
M --> N{是否出错};
N -- 是 --> G[退出];
N -- 否 --> O[关闭输出文件];
O --> G[退出];
3. 读取MS-DOS命令行尾信息
在MS-DOS中,当程序启动时,命令行上的额外文本会自动存储在位于内存偏移量 80h 处的 128 字节的 MS-DOS 命令行尾区域。这个区域位于由 ES 寄存器指定的段地址开始的位置,被称为程序段前缀(PSP)。
GetCommandTail
程序可以从 Irvine16 库中获取正在运行的程序的命令行尾的副本。以下是其代码:
;--------------------------------------------------
GetCommandTail PROC
;
; Gets a copy of the MS-DOS command tail at PSP:80h.
; Receives: DX contains the offset of the buffer
; that receives a copy of the command tail.
; Returns: CF=1 if the buffer is empty; otherwise,
; CF=0.
;--------------------------------------------------
SPACE = 20h
push es
pusha
; save general registers
mov
ah,62h
; get PSP segment address
int
21h
; returned in BX
mov
es,bx
; copied to ES
mov
si,dx
; point to buffer
mov
di,81h
; PSP offset of command tail
mov
cx,0
; byte count
mov
cl,es:[di-1]
; get length byte
cmp
cx,0
; is the tail empty?
je
L2
; yes: exit
cld
; scan in forward direction
mov
al,SPACE
; space character
repz
scasb
; scan for non space
jz
L2
; all spaces found
dec
di
; non space found
inc
cx
L1:
mov
al,es:[di]
; copy tail to buffer
mov
[si],al
; pointed to by DS:SI
inc
si
inc
di
loop
L1
clc
; CF=0 means tail found
jmp
L3
L2:
stc
; CF=1 means no command tail
L3:
mov
byte ptr [si],0
; store null byte
popa
; restore registers
pop
es
ret
GetCommandTail ENDP
这个程序的主要步骤如下:
1. 保存当前的 ES 寄存器值。
2. 使用
INT 21h
功能 62h 获取 PSP 段地址,并将其复制到 ES 寄存器。
3. 定位命令行尾的长度字节。
4. 跳过前导空格。
5. 将命令行尾的内容复制到指定的缓冲区。
6. 根据缓冲区是否为空设置进位标志。
4. 创建二进制文件示例
二进制文件是指文件中存储的数据是程序数据的二进制映像。例如,如果程序创建并填充了一个双字数组,将这个数组写入文本文件需要将每个整数转换为字符串并分别写入,而更高效的方法是直接将数组的二进制映像写入文件。
Binfile.asm
程序演示了如何创建一个包含双字数组的二进制文件,然后读取该文件并显示其中的值。以下是代码:
; Binary File Program (Binfile.asm)
; This program creates a binary file containing
; an array of doublewords. It then reads the file
; back in and displays the values.
INCLUDE Irvine16.inc
.data
myArray DWORD 50 DUP(?)
fileName BYTE "binary array file.bin",0
fileHandle WORD ?
commaStr BYTE ", ",0
; Set CreateFile to zero if you just want to
; read and display the existing binary file.
CreateFile = 1
.code
main PROC
mov
ax,@data
mov
ds,ax
.IF CreateFile EQ 1
call
FillTheArray
call
DisplayTheArray
call
CreateTheFile
call
WaitMsg
call
Crlf
.ENDIF
call
ReadTheFile
call
DisplayTheArray
quit:
call
Crlf
exit
main ENDP
;------------------------------------------------------
ReadTheFile PROC
;
; Open and read the binary file.
; Receives: nothing.
; Returns: nothing
;------------------------------------------------------
mov
ax,716Ch
; extended file open
mov
bx,0
; mode: read-only
mov
cx,0
; attribute: normal
mov
dx,1
; open existing file
mov
si,OFFSET fileName
; filename
int
21h
; call MS-DOS
jc
quit
; quit if error
mov
fileHandle,ax
; save handle
; Read the input file, then close the file.
mov
ah,3Fh
; read file or device
mov
bx,fileHandle
; file handle
mov
cx,SIZEOF myArray
; max bytes to read
mov
dx,OFFSET myArray
; buffer pointer
int
21h
jc
quit
; quit if error
mov
ah,3Eh
; function: close file
mov
bx,fileHandle
; output file handle
int
21h
; call MS-DOS
quit:
ret
ReadTheFile ENDP
;------------------------------------------------------
DisplayTheArray PROC
;
; Display the doubleword array.
; Receives: nothing.
; Returns: nothing
;------------------------------------------------------
mov
CX,LENGTHOF myArray
mov
si,0
L1:
mov
eax,myArray[si]
; get a number
call
WriteHex
; display the number
mov
edx,OFFSET commaStr
; display a comma
call
WriteString
add
si,TYPE myArray
; next array position
loop
L1
ret
DisplayTheArray ENDP
;------------------------------------------------------
FillTheArray PROC
;
; Fill the array with random integers.
; Receives: nothing.
; Returns: nothing
;------------------------------------------------------
mov
CX,LENGTHOF myArray
mov
si,0
L1:
mov
eax,1000
; generate random integers
call
RandomRange
; between 0 - 999 in EAX
mov
myArray[si],eax
; store in the array
add
si,TYPE myArray
; next array position
loop
L1
ret
FillTheArray ENDP
;------------------------------------------------------
CreateTheFile PROC
;
; Create a file containing binary data.
; Receives: nothing.
; Returns: nothing
;------------------------------------------------------
mov
ax,716Ch
; create file
mov
bx,1
; mode: write only
mov
cx,0
; normal file
mov
dx,12h
; action: create/truncate
mov
si,OFFSET fileName
; filename
int
21h
; call MS-DOS
jc
quit
; quit if error
mov
fileHandle,ax
; save handle
; Write the integer array to the file.
mov
ah,40h
; write file or device
mov
bx,fileHandle
; output file handle
mov
cx,SIZEOF myArray
; number of bytes
mov
dx,OFFSET myArray
; buffer pointer
int
21h
jc
quit
; quit if error
; Close the file.
mov
ah,3Eh
; function: close file
mov
bx,fileHandle
; output file handle
int
21h
; call MS-DOS
quit:
ret
CreateTheFile ENDP
END main
这个程序的执行流程可以用以下表格总结:
| 步骤 | 操作 | 功能 |
| ---- | ---- | ---- |
| 1 |
FillTheArray
| 用随机整数填充数组 |
| 2 |
DisplayTheArray
| 显示数组内容 |
| 3 |
CreateTheFile
| 创建包含二进制数据的文件 |
| 4 |
ReadTheFile
| 打开并读取二进制文件 |
| 5 |
DisplayTheArray
| 再次显示数组内容 |
通过以上内容,我们对MS-DOS环境下的文件输入输出操作有了更深入的了解,包括字符串的读取和写入、文件的读写和复制、命令行尾信息的获取以及二进制文件的创建和读取等。这些操作在MS-DOS编程中是非常基础且重要的。
5. 重要的MS-DOS函数总结
在MS-DOS编程中,有许多重要的
INT 21h
函数,下面为大家详细介绍:
| 函数编号 | 功能 | 描述 |
| ---- | ---- | ---- |
| 4Ch | 终止当前程序 | 结束程序并返回操作系统 |
| 2 和 6 | 写入单个字符到标准输出 | 将一个字符输出到标准输出设备 |
| 5 | 写入单个字符到打印机 | 把字符发送到打印机进行打印 |
| 9 | 写入字符串到标准输出 | 输出以空字符结尾的字符串到标准输出 |
| 40h | 写入字节数组到文件或设备 | 可将指定数量的字节写入文件或设备 |
| 1 | 从标准输入读取单个字符 | 从标准输入设备获取一个字符 |
| 6 | 从标准输入读取字符且不等待 | 不等待用户输入,直接尝试读取字符 |
| 0Ah | 从标准输入读取缓冲字符串 | 读取用户输入的字符串到缓冲区 |
| 0Bh | 获取标准输入缓冲区状态 | 检查标准输入缓冲区的状态 |
| 3Fh | 从文件或设备读取字节数组 | 从文件或设备中读取指定数量的字节 |
| 2Ah | 获取系统日期 | 返回当前系统的日期 |
| 2Bh | 设置系统日期 | 可以修改系统的日期 |
| 2Ch | 获取系统时间 | 得到当前系统的时间 |
| 2Dh | 设置系统时间 | 对系统时间进行设置 |
| 716Ch | 创建文件或打开现有文件 | 可创建新文件或打开已存在的文件 |
| 3Eh | 关闭文件句柄 | 关闭指定的文件句柄 |
| 42h | 移动文件位置指针 | 改变文件指针的位置 |
| 5706h | 获取文件创建日期和时间 | 得到文件的创建日期和时间 |
| 62h | 返回程序段前缀地址的段部分 | 获取程序段前缀的段地址 |
这些函数在不同的场景下发挥着重要作用,例如在文件操作、日期时间处理、输入输出等方面。
6. 编程练习
以下是一些编程练习,要求在实地址模式下完成,并且除特殊说明外,需使用
INT 21h
函数进行所有输入输出操作。
6.1 读取文本文件
编写一个程序,打开一个文件进行输入,读取文件内容并以十六进制形式显示在屏幕上。输入缓冲区设置为 256 字节左右,使用循环多次调用
INT 21h
功能 3Fh,直到整个文件处理完毕。
操作步骤如下:
1. 打开文件:使用
INT 21h
功能 716Ch 以只读模式打开文件。
2. 循环读取:在循环中,使用
INT 21h
功能 3Fh 每次读取 256 字节到缓冲区。
3. 显示内容:将缓冲区内容以十六进制形式输出到屏幕。
4. 检查文件结束:判断是否到达文件末尾,如果未结束则继续循环。
5. 关闭文件:使用
INT 21h
功能 3Eh 关闭文件。
graph TD;
A[开始] --> B[打开文件];
B --> C{是否出错};
C -- 是 --> G[退出];
C -- 否 --> D[读取数据到缓冲区];
D --> E{是否到达文件末尾};
E -- 是 --> F[关闭文件];
E -- 否 --> H[以十六进制显示缓冲区内容];
H --> D;
F --> G;
6.2 复制文本文件
修改
Readfile
程序,使其能够读取任意大小的文件。假设缓冲区小于输入文件,使用循环读取所有数据,缓冲区大小设为 256 字节。如果
INT 21h
函数调用后进位标志被设置,显示相应的错误信息。
操作步骤:
1. 打开输入文件和输出文件:分别使用
INT 21h
功能 716Ch 以只读和只写模式打开。
2. 循环读取和写入:在循环中,使用
INT 21h
功能 3Fh 读取 256 字节到缓冲区,再使用
INT 21h
功能 40h 将缓冲区内容写入输出文件。
3. 检查错误:每次调用
INT 21h
函数后检查进位标志,若设置则显示错误信息并退出。
4. 关闭文件:使用
INT 21h
功能 3Eh 关闭输入和输出文件。
6.3 设置日期
编写一个程序,显示当前日期并提示用户输入新日期。如果用户输入非空日期,则用其更新系统日期。
操作步骤:
1. 获取当前日期:使用
INT 21h
功能 2Ah 获取当前系统日期。
2. 显示日期:将获取的日期显示给用户。
3. 提示输入:提示用户输入新日期。
4. 检查输入:判断用户输入是否为空,若不为空则使用
INT 21h
功能 2Bh 设置新日期。
6.4 大写转换
编写程序,使用
INT 21h
从键盘输入小写字母并将其转换为大写字母,只显示大写字母。
操作步骤:
1. 读取字符:使用
INT 21h
功能 1 从键盘读取字符。
2. 判断大小写:检查字符是否为小写字母。
3. 转换并显示:如果是小写字母,将其转换为大写字母并显示。
6.5 文件创建日期
编写一个过程,显示文件的创建日期和文件名。在
DX
寄存器中传递文件名指针,编写测试程序,用不同的文件名(包括长文件名)测试该过程。如果文件未找到,显示相应的错误信息。
操作步骤:
1. 获取文件信息:使用
INT 21h
功能 5706h 获取文件的创建日期和时间。
2. 显示信息:将文件名和创建日期显示在屏幕上。
3. 错误处理:如果文件未找到,显示错误信息。
6.6 文本匹配程序
编写一个程序,打开一个最多包含 60K 字节的文本文件,进行不区分大小写的字符串搜索。字符串和文件名由用户输入,显示文件中包含该字符串的每一行,并在每行前加上行号。
操作步骤:
1. 获取用户输入:获取用户输入的字符串和文件名。
2. 打开文件:使用
INT 21h
功能 716Ch 以只读模式打开文件。
3. 逐行读取:在循环中逐行读取文件内容。
4. 字符串匹配:使用字符串匹配算法检查每行是否包含目标字符串。
5. 显示结果:如果匹配成功,显示行号和该行内容。
6. 关闭文件:使用
INT 21h
功能 3Eh 关闭文件。
6.7 文件加密使用异或
增强文件加密程序,具体步骤如下:
1. 提示用户输入:要求用户输入明文文件和密文文件的名称。
2. 打开文件:使用
INT 21h
功能 716Ch 分别以只读和只写模式打开明文文件和密文文件。
3. 获取加密代码:让用户输入一个 1 到 255 之间的整数作为加密代码。
4. 读取和加密:使用
INT 21h
功能 3Fh 读取明文文件到缓冲区,对每个字节进行异或操作。
5. 写入密文文件:使用
INT 21h
功能 40h 将加密后的缓冲区内容写入密文文件。
6. 关闭文件:使用
INT 21h
功能 3Eh 关闭两个文件。
6.8 单词计数程序
编写一个程序,统计文本文件中的单词数量。提示用户输入文件名,然后在屏幕上显示单词计数结果。
操作步骤:
1. 获取文件名:提示用户输入要统计的文件名。
2. 打开文件:使用
INT 21h
功能 716Ch 以只读模式打开文件。
3. 读取文件:逐字节读取文件内容。
4. 单词计数:根据空格和标点符号判断单词的开始和结束,进行计数。
5. 显示结果:使用
WriteDec
过程显示单词数量。
6. 关闭文件:使用
INT 21h
功能 3Eh 关闭文件。
通过这些编程练习,可以进一步巩固对MS-DOS文件输入输出服务的理解和掌握,提高在MS-DOS环境下的编程能力。在实际编程过程中,要注意对错误的处理,确保程序的健壮性。同时,合理运用各种
INT 21h
函数,能够高效地完成各种任务。
超级会员免费看
8

被折叠的 条评论
为什么被折叠?



