55、深入探索MS - DOS编程:段操作、程序结构与中断处理

深入探索MS - DOS编程:段操作、程序结构与中断处理

1. 段覆盖(Segment Overrides)

段覆盖是一个单字节前缀,它能让当前指令在计算有效地址时,使用与 ASSUME 指令指定不同的段寄存器。例如,可用于访问当前与 CS DS 关联段之外的变量:

mov al,cs:varl ; 访问CS指向的段中的变量
mov al,es:var2 ; 访问ES指向的段中的变量

需要注意的是,在实地址模式下,可以将变量放在代码段,但在保护模式下则不行。

若要获取当前未被 DS ES 假定的段中变量的偏移量,可使用如下指令:

mov bx,OFFSET AltSeg:var2

对于变量的多次引用,可通过插入 ASSUME 指令临时更改默认段引用,从而更方便地处理:

ASSUME ds:AltSeg ; 暂时使用AltSeg段
mov ax,AltSeg
mov ds,ax
mov al,varl
ASSUME ds:data ; 恢复使用默认数据段
mov ax,data
mov ds,ax
2. 段组合(Combining Segments)

大型程序应划分为独立模块,以简化编辑和调试。不同模块中的源代码也可组合到同一分段,只需在每个模块中使用相同的段名,并指定 PUBLIC 组合类型。

段对齐类型有以下几种:
- BYTE :每个段紧跟前一个段。
- WORD :段会在下一个偶数字边界处跟随另一个段。
- PARA (默认):每个段会在下一个段落边界处跟随。

以下是一个包含一个代码段( CSEG )、一个数据段( DSEG )和一个堆栈段( SSEG )的双模块程序示例:

主模块(Seg2.asm)

TITLE Segment Example (main module, Seg2.asm)
EXTRN var2:WORD, subroutine_1:PROC
cseg SEGMENT BYTE PUBLIC 'CODE'
ASSUME cs:cseg,ds:dseg, ss:sseg
main PROC
mov ax,dseg ; 初始化DS
mov ds,ax
mov ax,varl ; 本地变量
mov bx,var2 ; 外部变量
call subroutine_i ; 外部过程
mov ax,4C00h ; 退出到操作系统
int 21h
main ENDP
cseg ENDS
dseg SEGMENT WORD PUBLIC 'DATA' ; 本地数据段
var WORD 1000h
dseg ends
sseg SEGMENT STACK 'STACK' ; 堆栈段
BYTE 100h dup('S')
sseg ENDS
END main

子模块(Seg2a.ASM)

TITLE Segment Example (submodule, Seg2a.ASM)
PUBLIC subroutine_i, var2
cseg SEGMENT BYTE PUBLIC 'CODE'
ASSUME cs:cseg, ds:dseg
subroutine_i PROC ; 由主程序调用
mov ah,9
mov dx,OFFSET msg
int 21h
ret
subroutine_i ENDP
cseg ENDS
dseg SEGMENT WORD PUBLIC 'DATA'
var2 WORD 2000h ; 被主程序访问
msg BYTE 'Now in Subroutine_i', 0Dh,0Ah,'$'
dseg ENDS
END

链接器生成的MAP文件显示了一个代码段、一个数据段和一个堆栈段:
| Start | Stop | Length | Name | Class |
| ---- | ---- | ---- | ---- | ---- |
| 00000H | 0001BH | 0001CH | CSEG | CODE |
| 0001CH | 00035H | 0001AH | DSEG | DATA |
| 00040H | 013FH | 0100H | SSEG | STACK |

程序入口点位于0000:0000。

3. 运行时程序结构(Runtime Program Structure)

当在MS - DOS提示符下输入命令时,会按以下顺序执行:
1. MS - DOS检查命令是否为内部命令(如 DIR REN DEL ),若是,则由驻留在内存中的MS - DOS例程立即执行。
2. 查找扩展名为 COM 的匹配文件,若在当前目录中找到,则执行该文件。
3. 查找扩展名为 EXE 的匹配文件,若在当前目录中找到,则执行该文件。
4. 查找扩展名为 BAT 的匹配文件,若在当前目录中找到,则执行该文件。 BAT 文件是包含MS - DOS命令的文本文件,执行时就像在控制台输入这些命令一样。
5. 若在当前目录中未找到匹配的 COM EXE BAT 文件,MS - DOS会搜索当前路径中的第一个目录,若未找到匹配项,则继续搜索路径中的下一个目录,直到找到匹配文件或搜索完整个路径。

扩展名为 COM EXE 的应用程序称为临时程序,一般在执行时被加载到内存,执行完毕后释放所占用的内存。若需要,临时程序在退出时可将部分代码留在内存中,这类程序称为内存驻留程序(TSRs)。

3.1 程序段前缀(Program Segment Prefix)

MS - DOS在程序加载到内存时,会在程序开头创建一个特殊的256字节块,称为程序段前缀(PSP),其结构如下表所示:
| Offset | Comments |
| ---- | ---- |
| 00 - 15 | MS - DOS指针和向量地址 |
| 16 - 28 | 由MS - DOS保留 |
| 2C - 2D | 当前环境字符串的段地址 |
| 2E - 58 | 由MS - DOS保留 |
| 5C - 7F | 文件控制块1和2,主要用于MS - DOS 2.0之前的程序 |
| 80 - FF | 默认磁盘传输区域和当前MS - DOS命令尾部的副本 |

3.2 COM程序

COM程序是机器语言程序的未修改二进制映像,由MS - DOS加载到最低可用段地址,在偏移量0处创建PSP。代码、数据和堆栈存储在同一物理(和逻辑)段中,程序最大可达64K,减去PSP的大小和堆栈末尾的两个保留字节。所有段寄存器都设置为PSP的基地址,代码区从偏移量100h开始,数据区紧跟代码区,堆栈区在段末尾,因为MS - DOS将 SP 初始化为FFFEh。

以下是一个简单的COM格式程序示例:

TITLE Hello Program in COM format (HelloCom.asm)
.model tiny
code
org 100h ; 必须在main之前
main PROC
mov ah,9
mov dx,OFFSET hello_message
int 21h
mov ax,4C00h
int 21h
main ENDP
hello_message BYTE 'Hello, world!',0Dh,0Ah,'$'
END main

由于没有单独的数据段,变量通常位于主过程之后。若将数据放在程序顶部,CPU会尝试执行数据。另一种方法是在开头放置一条 JMP 指令,跳过数据到第一条实际指令:

TITLE Hello Program in COM format (HelloCom.asm)
.model tiny
code
org 100h ; 必须在入口点之前
main proc
jmp start ; 跳过数据
hello_message BYTE 'Hello, world!',0Dh,0Ah,'$'
start:
mov ah,9
mov dx,OFFSET hello_message
int 21h
mov ax,4C00h
int 21h
main ENDP
END main

使用Microsoft链接器创建COM文件时,需要使用 ,T 参数。COM程序通常比其EXE对应程序小,但在内存中会占用整个64K内存段,且不适合在多任务环境中运行。

3.3 EXE程序

EXE程序存储在磁盘上时,包含一个EXE头和一个包含程序本身的加载模块。程序头实际上不会加载到内存,而是包含MS - DOS用于加载和执行程序的信息。

MS - DOS加载EXE程序时,会在第一个可用地址创建程序段前缀(PSP),程序则放在其上方。解析程序头时,MS - DOS将 DS ES 设置为程序的加载地址(即PSP), CS IP 设置为程序代码的入口点, SS 设置为堆栈段的开头, SP 设置为堆栈大小。

EXE程序可包含多达65,535个段,但通常不会有这么多。若程序有多个数据段,程序员通常需要手动将 DS ES 设置为每个新段。

EXE程序使用的内存量由其程序头指定,特别是代码区之后处理变量和堆栈所需的最小和最大段落数(每个段落16字节)。默认情况下,链接器将最大值设置为65,535个段落,但MS - DOS会自动分配可用的内存。

可在链接程序时使用 /CP 选项设置最大分配量,例如:

link16 /cp:1024 prog1;

使用Microsoft汇编器提供的 exehdr 程序,可在EXE程序编译后修改其头值。例如,将名为 prog1.exe 的程序的最大分配量设置为400h段落(16,384字节)的命令为:

exehdr prog1 /max 400

exehdr 还可显示程序的重要统计信息,以下是一个示例输出:
| PROG1 | (Hex) | (Dec) |
| ---- | ---- | ---- |
| EXE size (bytes) | 876 | 2166 |
| Minimum load size (bytes) | 786 | 1926 |
| Overlay number | 0 | 0 |
| Initial CS:IP | 0000:0010 | 16 |
| Initial SS:SP | 0068:0100 | 256 |
| Minimum allocation (para) | 11 | 17 |
| Maximum allocation (para) | 400 | 1024 |
| Reader size (para) | 20 | 32 |
| Relocation table offset | 1E | 30 |
| Relocation entries | 1 | 1 |

EXE程序的头区包含以下信息:
- 重定位表,包含程序加载时需要计算的地址。
- EXE程序的文件大小,以512字节为单位。
- 最小分配量:程序代码区之后要保留的最小内存段落数,部分存储可用于保存动态数据的运行时堆。
- 最大分配量:程序上方所需的最大段落数。
- IP SP 寄存器的初始值。
- 堆栈和代码段相对于加载模块开头的位移(以16字节段落为单位)。
- 文件中所有字的校验和,用于在将程序加载到内存时捕获数据错误。

4. 中断处理(Interrupt Handling)

BIOS和MS - DOS包含用于简化输入/输出和基本系统任务的中断处理程序,但操作系统中同样重要的是响应硬件中断的一组中断处理程序。MS - DOS允许用自定义的服务例程替换其中任何一个。

不过,本章介绍的中断处理程序仅在计算机启动到MS - DOS模式时有效,可通过Windows 95和98实现,但Windows NT、2000和XP不支持,因为这些操作系统会对应用程序屏蔽系统硬件,以提高系统稳定性和安全性。

编写中断处理程序可能有多种原因,例如:
- 希望程序在按下热键时激活,即使在用户运行其他应用程序时也能如此。
- 替换MS - DOS的默认中断处理程序,以提供更完整的服务。
- 替换MS - DOS的关键错误处理程序或Ctrl - Break处理程序,使程序能从错误中恢复并让用户继续运行当前应用程序。
- 自定义中断服务例程能比MS - DOS更有效地处理硬件中断。

4.1 中断向量表(Interrupt Vector Table)

MS - DOS的灵活性关键在于位于RAM前1024字节(地址0:0到0:03FF)的中断向量表。表中的每个条目(中断向量)是一个32位段偏移地址,指向现有的服务例程之一。以下是向量表条目的简短示例:
| Interrupt Number | Offset | Interrupt Vectors |
| ---- | ---- | ---- |
| 00 - 03 | 0000 | 02C1:5186 0070:0c67 ODAD:2C1B 0070:0C67 |
| 04 - 07 | 0010 | 0070:0C67 F000:FFS4 F000:8378 F000:837B |
| 08 - 0B | 0020 | 0D70:022C ODAO:2BAD 0070:0325 0070:039F |
| 0C - 0F | 0030 | 0070:0419 0070:0493 0070:050D 0070:0C67 |
| 10 - 13 | 0040 | C000:OCD7 F000:F840 F000:F841 0070:237D |

不同计算机上的向量值会因BIOS和MS - DOS的不同版本而有所差异。每个中断向量对应一个中断号,中断向量的偏移量可通过将其中断号乘以4得到,例如,INT 9h的向量偏移量为9 * 4,即十六进制的0024。

4.2 执行中断处理程序

中断处理程序可通过以下两种方式执行:
1. 包含 INT 指令的应用程序可调用该例程,这称为软件中断。
2. 硬件设备(异步端口、键盘、定时器等)向可编程中断控制器芯片发送信号时,会发生硬件中断。

4.3 硬件中断(Hardware Interrupts)

硬件中断由Intel 8259可编程中断控制器(PIC)生成,它会向CPU发出信号,暂停当前程序的执行,并执行中断服务例程。

在对段寄存器和堆栈执行敏感操作时,程序有时需要禁用硬件中断。 CLI (清除中断标志)指令可禁用中断, STI (设置中断标志)指令可启用中断。

PC上的多个不同设备可触发中断,每个设备根据其中断请求级别(IRQ)有一个优先级,级别0优先级最高,级别15优先级最低。低级别中断不能中断正在进行的高级别中断,多个同时发生的中断请求将根据其优先级级别进行处理,中断调度由8259 PIC处理。

以键盘为例,当按下一个键时,8259 PIC会向CPU发送INTR信号,并传递中断号。若当前未禁用外部中断,CPU将按以下顺序执行操作:
1. 将标志寄存器压入堆栈。
2. 清除中断标志,防止其他硬件中断。
3. 将当前的 CS IP 压入堆栈。
4. 定位INT 9的中断向量表条目,并将该地址放入 CS IP

综上所述,MS - DOS编程涉及段操作、程序结构和中断处理等多个方面,理解这些概念和技术对于编写高效、稳定的程序至关重要。无论是段覆盖和组合的灵活运用,还是COM和EXE程序的不同特点,以及中断处理的多种应用场景,都需要开发者深入掌握并根据实际需求进行合理选择和使用。

5. 深入理解中断处理机制
5.1 硬件中断的优先级管理

硬件中断的优先级管理是确保系统稳定运行的关键。不同设备的中断请求级别(IRQ)决定了它们在系统中的优先级顺序。以下是一个常见设备的IRQ级别列表:
| 设备 | IRQ 级别 |
| ---- | ---- |
| 系统定时器 | 0 |
| 键盘控制器 | 1 |
| 级联(用于第二个 8259 PIC) | 2 |
| 串口 2 | 3 |
| 串口 1 | 4 |
| 并口 2 | 5 |
| 软盘控制器 | 6 |
| 并口 1 | 7 |
| 实时时钟 | 8 |
| 通用可用 | 9 |
| 通用可用 | 10 |
| 通用可用 | 11 |
| 鼠标控制器 | 12 |
| 数学协处理器 | 13 |
| 主硬盘控制器 | 14 |
| 从硬盘控制器 | 15 |

从这个列表中可以看出,系统定时器具有最高优先级,这是因为它对于系统的计时和任务调度至关重要。而一些通用可用的IRQ级别则可以根据需要分配给其他设备。

为了更好地理解硬件中断的优先级管理流程,我们可以使用 mermaid 绘制一个流程图:

graph TD;
    A[设备产生中断请求] --> B{判断 IRQ 级别};
    B -- 高优先级 --> C[立即响应中断];
    B -- 低优先级 --> D{是否有高优先级中断正在处理};
    D -- 是 --> E[等待高优先级中断处理完成];
    D -- 否 --> C;
    C --> F[执行中断服务程序];
    F --> G[恢复原程序执行];

这个流程图展示了硬件中断请求的处理过程。当设备产生中断请求时,系统首先判断其IRQ级别。如果是高优先级中断,将立即响应;如果是低优先级中断,则需要检查是否有高优先级中断正在处理。只有在没有高优先级中断处理时,低优先级中断才能被响应。

5.2 中断服务程序的编写要点

编写中断服务程序时,需要注意以下几个关键要点:
1. 保存和恢复寄存器状态 :在中断服务程序开始时,需要保存所有可能被修改的寄存器的值,以确保原程序的执行不受影响。在中断服务程序结束时,再恢复这些寄存器的值。例如:

push ax
push bx
; 中断服务程序的主要代码
pop bx
pop ax
  1. 快速响应和处理 :中断服务程序应该尽可能快速地完成任务,以减少对原程序执行的影响。如果中断服务程序需要处理大量数据或执行复杂操作,可以考虑将部分任务放到后台处理。
  2. 中断标志的管理 :在中断服务程序中,需要根据需要管理中断标志。如果在处理中断时需要禁止其他中断,可以使用 CLI 指令;在处理完成后,使用 STI 指令恢复中断。
6. 段操作与程序结构的优化策略
6.1 段覆盖的优化使用

段覆盖虽然提供了访问不同段中变量的灵活性,但过多使用段覆盖会增加指令的长度和执行时间。因此,在实际编程中,应该尽量减少段覆盖的使用。可以通过合理的段定义和 ASSUME 指令来减少对段覆盖的依赖。

例如,在一个程序中,如果需要频繁访问多个段中的变量,可以使用 ASSUME 指令临时更改默认段引用,而不是每次都使用段覆盖:

ASSUME ds:AltSeg
; 访问 AltSeg 段中的变量
mov ax, var1
ASSUME ds:data
; 访问 data 段中的变量
mov bx, var2
6.2 COM 和 EXE 程序的选择与优化

COM 和 EXE 程序各有优缺点,在选择时需要根据实际需求进行考虑。
- COM 程序 :适用于小型、简单的程序,因为它的结构简单,加载速度快。但由于其内存限制和不适合多任务环境的特点,在编写大型程序时需要谨慎使用。为了优化 COM 程序的内存使用,可以尽量减少数据和代码的大小,避免不必要的变量和代码。
- EXE 程序 :适用于大型、复杂的程序,因为它可以包含多个段,并且可以灵活控制内存分配。在编写 EXE 程序时,可以通过合理设置程序头中的最小和最大分配量,以及使用 exehdr 程序进行优化,来提高内存使用效率。

7. 总结与展望

MS - DOS 编程涉及段操作、程序结构和中断处理等多个方面,这些知识对于深入理解计算机系统和编写高效程序具有重要意义。通过合理运用段覆盖和组合技术,可以实现更灵活的内存管理;了解 COM 和 EXE 程序的特点和优化策略,可以根据不同需求选择合适的程序类型;掌握中断处理机制,则可以提高系统的响应速度和稳定性。

随着计算机技术的不断发展,MS - DOS 已经逐渐退出历史舞台,但其中的许多编程思想和技术仍然具有借鉴价值。例如,中断处理机制在现代操作系统中仍然广泛应用,用于处理各种硬件设备的中断请求。未来,我们可以将这些传统的编程技术与现代编程语言和开发工具相结合,创造出更加高效、稳定的软件系统。

在实际开发中,开发者需要不断学习和实践,深入理解这些技术的原理和应用场景,才能编写出高质量的程序。同时,也需要关注计算机技术的发展趋势,及时掌握新的编程理念和方法,以适应不断变化的市场需求。

在数字化环境中,线上票务获取已成为参各类活动的主要途径。随着公众对热门演出需求的增长,票源往往在开放销售后迅速告罄,导致普通消费者难以顺利购得所需票券。为应对这一挑战,部分技术开发者借助编程构建了自动化购票辅助程序,旨在提升用户成功获取门票的概率。本文将以一个针对特定票务平台设计的自动化工具为例,系统阐述其设计理念、技术组成及具体实施流程。 秀动网作为国内知名的演出及体育赛事票务销售平台,因活动热度较高,常出现访问拥堵、瞬时抢购压力大等现象,使得常规购票过程面临困难。因此,开发一款能够协助用户更有效完成票务申购的辅助工具具有实际意义。 该工具主要具备以下几项关键功能:持续监控目标平台的票务信息更新;在票务释放时自动执行选座、添加至购物车及提交订单等系列操作;集成一定的异常处理机制,以应对网络延迟或服务器响应异常等情况。 在技术实现层面,选用Python作为开发语言,主要基于其语法简洁、标准库第三方资源丰富,适合快速构建功能原型。同时,Python在网络通信浏览器自动化方面拥有如requests、selenium等成熟支持库,为程序实现网页交互数据抓取提供了便利。 开发过程主要包括以下环节:首先解析目标网站的页面结构,明确可通过程序操控的网页元素路径;随后编写监控模块,实时检测新票务信息的上线并及时触发后续操作;接着模拟用户操作流程,包括自动填写个人信息、选择座位偏好、完成购物车添加等步骤,并通过行为模拟降低被平台反爬虫机制识别的可能;最终实现订单自动提交,并在成功购票后向用户发送通知。 此外,该工具提供了可配置的操作界面,允许用户根据个人需求设定抢票时间、目标活动类型及座位选择等参数,从而在提升使用体验的同时,减少对票务平台服务器资源的非必要占用。 需指出的是,尽管此类工具能提高购票效率,但其使用可能涉及违反平台服务协议或相关法规的风险。各票务销售方通常对自动化抢票行为设有明确约束,因此开发使用者均应遵守相应规定,确保技术应用的合法性。 综上所述,该基于Python的票务辅助工具是针对特定场景设计的自动化解决方案,通过技术手改善用户购票体验,但同时也强调必须在法律平台规则框架内合理使用此类技术。 资源来源于网络分享,仅用于学习交流使用,请勿用于商业,如有侵权请联系我删除!
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值