16位MS-DOS编程全解析
1. 16位MS-DOS编程基础
在16位MS-DOS编程中,程序通常运行在实地址模式下,这是因为它们使用了INT指令。实地址模式程序具有以下特点:
- 只能寻址1MB的内存。
- 单会话中一次只能运行一个程序(单任务)。
- 没有内存边界保护,任何应用程序都可能覆盖操作系统使用的内存。
- 偏移量为16位。
早期的IBM - PC具有很强的吸引力,它价格实惠,并且能运行电子表格程序Lotus 1 - 2 - 3,这对PC在商业领域的普及起到了重要作用。计算机爱好者也喜欢它,因为它是学习计算机工作原理的理想工具。在PC - DOS之前,最流行的8位操作系统Digital Research CP/M只能寻址64K的RAM,相比之下,PC - DOS的640K内存显得非常可观。
由于早期英特尔微处理器存在明显的内存和速度限制,IBM - PC是单用户计算机,没有内置的保护机制来防止应用程序破坏内存。而当时的小型计算机系统可以处理多用户,并能防止应用程序相互覆盖数据。随着时间的推移,更强大的PC操作系统不断涌现,使PC成为小型计算机系统的可行替代方案,尤其是当PC联网时。
2. 内存组织
在实地址模式下,内存的组织如下:
- 最低的640K内存由操作系统和应用程序共同使用。
- 接着是视频内存和硬件控制器的保留内存。
- 最后,地址F0000到FFFFF保留给系统ROM(只读存储器)。
在操作系统的内存区域中,最低的1024字节(地址00000到003FF)包含一个名为中断向量表的32位地址表。这些地址(称为中断向量)在CPU处理硬件和软件中断时使用。
中断向量表上方是BIOS和MS - DOS数据区域,再往上是软件BIOS,它包含管理大多数I/O设备(如键盘、磁盘驱动器、视频显示器、串行端口和打印机端口)的程序。BIOS程序从MS - DOS系统(引导)磁盘上的隐藏系统文件加载。MS - DOS内核是一组程序(称为服务),也从系统磁盘上的文件加载。
与MS - DOS内核一起的是文件缓冲区和可安装设备驱动程序。内存中更高的位置,常驻命令处理器部分从名为command.com的可执行文件加载。命令处理器解释在MS - DOS提示符下输入的命令,并加载和执行存储在磁盘上的程序。命令处理器的另一部分占据地址A0000以下的高端内存。
应用程序可以从常驻命令处理器部分上方的第一个地址加载到内存中,并可以使用一直到地址9FFFF的内存。如果当前运行的程序覆盖了临时命令处理器区域,该区域将在程序退出时从引导磁盘重新加载。
视频内存区域(VRAM)在IBM - PC上从地址A0000开始,当视频适配器切换到图形模式时使用。当视频处于彩色文本模式时,内存位置B8000保存当前屏幕上显示的所有文本。屏幕是内存映射的,屏幕上的每一行和每一列对应内存中的一个16位字。当一个字符复制到视频内存中时,它会立即出现在屏幕上。
ROM BIOS位于内存位置F0000到FFFFF,是计算机操作系统的重要组成部分。它包含系统诊断和配置软件,以及应用程序使用的低级输入 - 输出程序。BIOS存储在系统板上的静态内存芯片中。大多数系统遵循基于IBM原始BIOS的标准化BIOS规范,并使用从00400到004FF的BIOS数据区域。
以下是MS - DOS内存映射的简单示意:
| 地址范围 | 用途 |
| ---- | ---- |
| 00000 - 003FF | 中断向量表 |
| 00400 - 004FF | BIOS和DOS数据区域 |
| 后续区域 | 软件BIOS、DOS内核、设备驱动程序等 |
| 常驻命令处理器区域 | 常驻命令处理器 |
| 临时程序区域(应用程序可用) | 应用程序加载区域 |
| A0000 | 视频内存(图形模式) |
| B8000 | 视频内存(彩色文本模式) |
| F0000 - FFFFF | ROM BIOS |
3. 输入 - 输出重定向
在MS - DOS中,标准输入设备和标准输出设备统称为控制台,输入使用键盘,输出使用视频显示器。在命令提示符下运行程序时,可以重定向标准输入,使其从文件或硬件端口而不是键盘读取;也可以将标准输出重定向到文件、打印机或其他I/O设备。
例如:
-
sort < myfile.txt
:对名为myfile.txt的文件进行排序并显示输出。
-
sort < myfile.txt > outfile.txt
:对myfile.txt文件进行排序,并将输出发送到outfile.txt。
-
dir | sort
:对当前磁盘目录进行排序并在屏幕上显示输出。
-
dir | sort > prn
:将排序程序的输出发送到默认(非网络)打印机。
常见的MS - DOS设备名称如下表所示:
| 设备名称 | 描述 |
| ---- | ---- |
| CON | 控制台(视频显示器或键盘) |
| LPT1或PRN | 第一台并行打印机 |
| LPT2, LPT3 | 并行端口2和3 |
| COM1, COM2 | 串行端口1和2 |
| NUL | 不存在或虚拟设备 |
4. 软件中断
软件中断是对操作系统程序的调用。大多数这些程序(称为中断处理程序)为应用程序提供输入 - 输出功能,用于以下任务:
- 显示字符和字符串。
- 从键盘读取字符和字符串。
- 以彩色显示文本。
- 打开和关闭文件。
- 从文件读取数据。
- 向文件写入数据。
- 设置和获取系统时间和日期。
5. INT指令
INT(调用中断程序)指令调用系统子程序,也称为中断处理程序。在INT指令执行之前,必须在寄存器中插入一个或多个参数。至少,必须将标识特定程序的数字移动到AH寄存器。根据功能的不同,可能还需要在寄存器中传递其他值给中断。语法如下:
INT number
其中number是0到FF十六进制范围内的整数。
6. 中断向量
CPU使用中断向量表处理INT指令,中断向量表位于内存最低的1024字节中。该表中的每个条目是一个32位的段 - 偏移地址,指向一个中断处理程序。表中的实际地址因机器而异。当程序调用INT指令时,CPU执行以下步骤:
graph LR
A[程序调用INT指令] --> B[将INT指令的操作数乘以4,定位匹配的中断向量表条目]
B --> C[CPU将标志和32位段/偏移返回地址压入堆栈,禁用硬件中断,并远调用中断向量表中存储的地址]
C --> D[中断处理程序执行,直到遇到IRET(中断返回)指令]
D --> E[IRET指令将标志和返回地址从堆栈中弹出,处理器在调用程序中INT指令之后继续执行]
7. 常见中断
常见的软件中断如下:
| 中断号 | 服务类型 | 说明 |
| ---- | ---- | ---- |
| INT 10h | 视频服务 | 控制光标位置、以彩色写入文本、滚动屏幕和显示视频图形等程序 |
| INT 16h | 键盘服务 | 读取键盘并检查其状态的程序 |
| INT 17h | 打印机服务 | 初始化、打印和返回打印机状态的程序 |
| INT 1Ah | 时间服务 | 获取机器开机以来的时钟滴答数或设置计数器为新值的程序 |
| INT 1Ch | 用户定时器中断 | 每秒执行18.2次的空程序 |
| INT 21h | MS - DOS服务 | 提供输入 - 输出、文件处理和内存管理的程序,也称为MS - DOS函数调用 |
8. 16位程序编码
为MS - DOS设计的程序必须是运行在实地址模式下的16位应用程序。实地址模式应用程序使用16位段,并遵循分段寻址方案。如果使用32位处理器,即使在实地址模式下也可以使用32位通用寄存器进行数据处理。以下是16位程序的编码特点总结:
- 使用
.MODEL
指令指定程序使用的内存模型,推荐使用Small模型,它将代码放在一个段中,堆栈和数据放在另一个段中:
.MODEL small
-
使用
.STACK指令为程序分配少量本地堆栈空间,通常256字节就足够了,以下分配了512字节:
.STACK 200h
-
可使用
.386指令启用32位寄存器的使用:
.386
- 如果程序引用变量,在main开始处需要两条指令来初始化DS寄存器,使其指向数据段的起始位置:
mov ax, @data
mov ds, ax
-
每个程序必须包含结束程序并返回操作系统的语句,一种方法是使用
.EXIT指令:
.EXIT
也可以调用INT 21h,功能号4Ch:
mov ah, 4ch ; 终止进程
int 21h ; MS - DOS中断
- 可以使用MOV指令为段寄存器赋值,但仅在分配程序段地址时使用。
在汇编16位程序时,使用示例程序文件夹中提供的make16.bat(批处理)文件,它链接到Irvine16.lib并执行较旧的Microsoft 16位链接器(版本5.6)。实地址模式程序只能在MS - DOS、Windows 95、98和Millenium下访问硬件端口、中断向量和系统内存,从Windows XP开始,这种访问不被允许。
当使用Small内存模型时,数据和代码标签的偏移量(地址)为16位。Irvine16库使用Small内存模型,其中所有代码适合一个16位段,程序的数据和堆栈适合一个16位段。在实地址模式下,堆栈条目默认是16位,但也可以将32位值放在堆栈上(使用两个堆栈条目)。
可以通过包含Irvine16.inc文件来简化16位程序的编码,它会插入以下语句:
.MODEL small, stdcall
.STACK 200h
.386
exit EQU <.EXIT>
9. MS - DOS函数调用(INT 21h)
MS - DOS提供了很多易于使用的函数来在控制台显示文本,这些函数都属于INT 21h MS - DOS函数调用组。该中断支持大约200种不同的函数,通过放在AH寄存器中的功能号来识别。
一些函数需要将输入参数的32位地址存储在DS:DX寄存器中。DS(数据段寄存器)通常设置为程序的数据区域。如果不是这种情况,可以使用SEG运算符将DS设置为包含传递给INT 21h的数据的段。示例代码如下:
.data
inBuffer BYTE 80 DUP(?)
.code
mov ax, SEG inBuffer
mov ds, ax
mov dx, OFFSET inBuffer
10. INT 21h功能4Ch:终止进程
INT 21h功能4Ch用于终止当前程序(称为进程)。在实地址模式程序中,可以使用Irvine16库中定义的宏exit,它是
.EXIT
的别名。在16位程序中,
.EXIT
生成的代码如下:
mov ah, 4Ch ; 终止进程
int 21h
如果为
.EXIT
宏提供可选的返回码参数,汇编器会生成额外的指令将返回码移动到AL:
.EXIT 0 ; 宏调用
; 生成的代码
mov ah, 4Ch ; 终止进程
mov al, 0 ; 返回码
int 21h
AL中的值(称为进程返回码)由调用进程(包括批处理文件)接收,以指示程序的返回状态。通常,返回码为零表示成功完成,1到255之间的其他返回码可用于表示程序的特定结果。
11. 部分输出函数
11.1 过滤控制字符
本节介绍的INT 21h函数会过滤或解释ASCII控制字符。例如,将退格字符写入标准输出时,光标会向左移动一列。常见的ASCII控制字符如下表所示:
| ASCII代码 | 描述 |
| ---- | ---- |
| 08h | 退格(向左移动一列) |
| 09h | 水平制表符(向前跳过n列) |
| 0Ah | 换行(移动到下一行输出) |
| 0Ch | 换页(移动到下一个打印机页面) |
| 0Dh | 回车(移动到输出列的最左侧) |
| 1Bh | 转义字符 |
11.2 INT 21h功能2
描述:将单个字符写入标准输出,并将光标向前移动一列。
接收参数:AH = 2,DL = 字符值。
返回值:无。
示例调用:
mov ah, 2
mov dl, 'A'
int 21h
11.3 INT 21h功能5
描述:将单个字符写入打印机。
接收参数:AH = 5,DL = 字符值。
返回值:无。
示例调用:
mov ah, 5 ; 选择打印机输出
mov dl, "Z" ; 要打印的字符
int 21h ; 调用MS - DOS
注意:MS - DOS会等待打印机准备好接受字符,可以通过按Ctrl - Break键终止等待。默认输出到LPT1打印机端口。
11.4 INT 21h功能6
描述:将字符写入标准输出。
接收参数:AH = 6,DL = 字符值。
返回值:如果ZF = 0,AL包含字符的ASCII代码。
示例调用:
mov ah, 6
mov dl, "A"
int 21h
注意:与其他INT 21h函数不同,此函数不过滤(解释)ASCII控制字符。
11.5 INT 21h功能9
描述:将以$结尾的字符串写入标准输出。
接收参数:AH = 9,DS:DX = 字符串的段/偏移地址。
返回值:无。
示例调用:
.data
string BYTE "This is a string$"
.code
mov ah, 9
mov dx, OFFSET string
int 21h
注意:字符串必须以美元符号字符($)结尾。
11.6 INT 21h功能40h
描述:将字节数组写入文件或设备。具体使用时,同样需要设置好相关的寄存器参数,如AH设置为对应的功能号,DS:DX指向要写入的数据等。这里暂不详细展开代码示例,其原理和前面的函数调用类似,根据具体需求进行参数设置和调用即可。
通过对这些MS - DOS函数的理解和运用,可以实现各种输入输出操作,为16位MS - DOS编程打下坚实的基础。在实际编程中,需要根据具体的需求选择合适的函数,并正确设置参数,以达到预期的效果。
16位MS-DOS编程全解析
12. 函数调用总结与对比
为了更清晰地了解这些INT 21h输出函数的特点和使用场景,我们对上述介绍的函数进行总结和对比,如下表所示:
| 功能号 | 描述 | 接收参数 | 返回值 | 注意事项 |
| ---- | ---- | ---- | ---- | ---- |
| INT 21h功能2 | 将单个字符写入标准输出,并将光标向前移动一列 | AH = 2,DL = 字符值 | 无 | 过滤控制字符 |
| INT 21h功能5 | 将单个字符写入打印机 | AH = 5,DL = 字符值 | 无 | 等待打印机准备好,可按Ctrl - Break终止,默认输出到LPT1 |
| INT 21h功能6 | 将字符写入标准输出 | AH = 6,DL = 字符值 | 如果ZF = 0,AL包含字符的ASCII代码 | 不过滤控制字符 |
| INT 21h功能9 | 将以$结尾的字符串写入标准输出 | AH = 9,DS:DX = 字符串的段/偏移地址 | 无 | 字符串必须以$结尾 |
| INT 21h功能40h | 将字节数组写入文件或设备 | 需设置相关寄存器参数,如AH为功能号,DS:DX指向数据 | | 具体参数依需求设置 |
从这个表格中可以看出,不同的功能号适用于不同的输出场景。如果只是简单地输出单个字符到标准输出,功能2是一个不错的选择;若要将字符输出到打印机,则使用功能5;当需要输出字符串时,功能9是专门为此设计的;而功能40h则用于更复杂的文件或设备写入操作。
13. 综合示例:简单的文本输出程序
下面我们通过一个综合示例,展示如何在一个程序中使用上述介绍的INT 21h函数进行文本输出。
.MODEL small
.STACK 200h
.386
.DATA
singleChar DB 'A'
printChar DB 'Z'
stringOutput DB 'This is a test string$'
byteArray DB 1, 2, 3, 4, 5
.CODE
MAIN PROC
MOV AX, @DATA
MOV DS, AX
; 使用INT 21h功能2输出单个字符
MOV AH, 2
MOV DL, singleChar
INT 21h
; 使用INT 21h功能5输出字符到打印机
MOV AH, 5
MOV DL, printChar
INT 21h
; 使用INT 21h功能6输出字符
MOV AH, 6
MOV DL, 'B'
INT 21h
; 使用INT 21h功能9输出字符串
MOV AH, 9
MOV DX, OFFSET stringOutput
INT 21h
; 这里简单示意INT 21h功能40h,实际使用需完善文件打开等操作
; MOV AH, 40h
; MOV DX, OFFSET byteArray
; INT 21h
; 终止程序
MOV AH, 4Ch
INT 21h
MAIN ENDP
END MAIN
这个示例程序依次展示了如何使用INT 21h的不同功能进行字符和字符串的输出。在实际运行时,需要根据具体的硬件环境和需求进行调整,例如对于功能5,要确保打印机正常连接;对于功能40h,需要先进行文件的打开和相关设置。
14. 实地址模式编程的局限性与应对策略
虽然实地址模式编程在早期的计算机系统中发挥了重要作用,但它也存在一些局限性,我们可以通过以下表格来了解:
| 局限性 | 描述 | 应对策略 |
| ---- | ---- | ---- |
| 内存寻址限制 | 只能寻址1MB的内存 | 对于大型程序,可以考虑使用内存管理技术,如内存分页等;或者将程序拆分成多个小模块,按需加载 |
| 单任务处理 | 单会话中一次只能运行一个程序 | 可以通过多线程或多进程的模拟技术,在程序内部实现任务的切换和调度;或者使用更高级的操作系统来支持多任务处理 |
| 缺乏内存保护 | 任何应用程序都可能覆盖操作系统使用的内存 | 编写程序时要严格控制内存的使用,避免越界访问;可以使用一些内存检测工具来辅助调试 |
| 偏移量为16位 | 限制了地址的表示范围 | 对于复杂的程序结构,可以采用分段管理的方式,合理分配不同的数据和代码段 |
15. 实地址模式编程的应用场景
尽管实地址模式编程存在一些局限性,但在某些特定的应用场景中,它仍然具有不可替代的优势,以下是一些常见的应用场景:
-
嵌入式系统开发
:在一些资源受限的嵌入式系统中,实地址模式编程可以充分利用有限的硬件资源,实现高效的程序运行。例如,一些小型的传感器节点、智能家居设备等,对内存和处理能力要求不高,实地址模式编程可以满足其基本的功能需求。
-
系统底层开发
:在操作系统的底层开发中,实地址模式编程可以直接访问硬件资源,如硬件端口、中断向量等。这对于实现设备驱动程序、系统初始化程序等非常重要。例如,BIOS程序通常就是在实地址模式下运行的,它负责计算机系统的基本初始化和硬件检测。
-
教学和学习
:对于计算机编程的初学者来说,实地址模式编程是一个很好的学习平台。它的原理相对简单,易于理解,可以帮助学习者更好地掌握计算机的底层原理和编程基础。通过学习实地址模式编程,学习者可以深入了解计算机的内存管理、中断处理、输入输出等基本概念。
16. 实地址模式编程的未来展望
随着计算机技术的不断发展,实地址模式编程的应用场景可能会逐渐减少,但它作为计算机编程的基础,仍然具有重要的历史意义和学习价值。在未来,虽然大多数应用程序会运行在更高级的操作系统和编程环境中,但在一些特定的领域,如嵌入式系统、底层开发等,实地址模式编程仍然会发挥一定的作用。
同时,对于计算机科学的研究和教育来说,实地址模式编程可以帮助人们更好地理解计算机的工作原理和发展历程。通过对实地址模式编程的学习和研究,人们可以更好地掌握计算机的底层机制,为开发更高效、更安全的计算机系统奠定基础。
综上所述,16位MS-DOS编程在实地址模式下具有独特的特点和应用价值。通过对其内存组织、输入输出重定向、软件中断、函数调用等方面的学习和掌握,我们可以开发出各种功能的程序。同时,我们也应该认识到实地址模式编程的局限性,并根据实际需求选择合适的编程方式和技术。在未来的计算机技术发展中,实地址模式编程虽然可能不会占据主导地位,但它仍然会在特定的领域和场景中发挥重要的作用。
超级会员免费看

1508

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



