64、深入探索MS - DOS编程:段定义、运行时结构与中断处理

深入解析MS-DOS编程核心机制

深入探索MS - DOS编程:段定义、运行时结构与中断处理

1. 段定义

在汇编语言编程中,段定义是一个重要的概念。它允许我们将程序划分为不同的逻辑部分,如代码段、数据段和堆栈段。

1.1 获取段地址

可以使用 SEG 运算符来获取变量的段地址。例如:

mov ax, SEG val2 ; point ES to data2 segment
mov es, ax

汇编器生成的列表文件可能显示两个变量 val1 val2 具有相同的值(起始偏移),但段属性不同:
| Name | Type | Value | Attr |
| ---- | ---- | ---- | ---- |
| val1 | Word | 0000 | data1 |
| val2 | Word | 0000 | data2 |

1.2 段覆盖

段覆盖是一个单字节前缀,它使当前指令在计算有效地址时使用与 ASSUME 指令指定不同的段寄存器。例如:

mov al, cs:var1 ; segment pointed to by CS
mov al, es:var2 ; segment pointed to by ES

在实地址模式下,可以将变量放在代码段,但在保护模式下不行。若要获取当前 DS ES 未假定的段中变量的偏移,可以使用以下指令:

mov bx, OFFSET AltSeg:var2

对于变量的多次引用,可以通过插入 ASSUME 指令临时更改默认段引用,示例如下:

ASSUME ds:AltSeg ; use AltSeg for a while
mov ax, AltSeg 
mov ds, ax
mov al, var1
...
ASSUME ds:data ; use the default data segment
mov ax, data 
mov ds, ax
1.3 段组合

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

段对齐类型有 BYTE WORD PARA 。使用 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        ; initialize DS
mov ds, ax
mov ax, var1        ; local variable
mov bx, var2        ; external variable
call subroutine_1   ; external procedure
mov ax, 4C00h       ; exit to OS
int 21h
main ENDP
cseg ENDS
dseg SEGMENT WORD PUBLIC 'DATA'
; local data segment
var1 WORD 1000h
dseg ends
sseg SEGMENT STACK 'STACK'
; stack segment
BYTE 100h dup('S')
sseg ENDS
END main

子模块(Seg2a.ASM)

TITLE Segment Example           (submodule, Seg2a.ASM)
PUBLIC subroutine_1, var2
cseg SEGMENT BYTE PUBLIC 'CODE'
ASSUME cs:cseg, ds:dseg
subroutine_1 PROC
; called from MAIN
mov ah, 9
mov dx, OFFSET msg
int 21h
ret
subroutine_1 ENDP
cseg ENDS
dseg SEGMENT WORD PUBLIC 'DATA'
var2 WORD 2000h
; accessed by MAIN
msg  BYTE 'Now in Subroutine_1'
     BYTE 0Dh, 0Ah, '$'
dseg ENDS
END

链接器可以创建一个 MAP 文件,列出程序中的所有段。此程序的 MAP 文件显示如下:
| Start | Stop | Length | Name | Class |
| ---- | ---- | ---- | ---- | ---- |
| 00000H | 0001BH | 0001CH | CSEG | CODE |
| 0001CH | 00035H | 0001AH | DSEG | DATA |
| 00040H | 0013FH | 00100H | SSEG | STACK |
程序入口点在 0000:0000

下面是段定义相关操作的流程图:

graph TD;
    A[开始] --> B[定义段];
    B --> C{选择对齐类型};
    C -->|BYTE| D[段紧跟前一个段];
    C -->|WORD| E[段在下一个偶数字边界后跟随];
    C -->|PARA| F[段在下一个段落边界后跟随];
    D --> G[选择组合类型];
    E --> G;
    F --> G;
    G -->|PUBLIC| H[可组合不同模块同段名代码];
    G -->|其他| I[按规则处理];
    H --> J[完成段定义];
    I --> J;
    J --> K[结束];
2. 运行时程序结构

有效的汇编语言程序员需要深入了解MS - DOS。下面介绍命令处理器、程序段前缀以及 COM EXE 程序的结构。

2.1 命令处理流程

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

以下是命令处理流程的mermaid流程图:

graph TD;
    A[输入命令] --> B{是否为内部命令};
    B -->|是| C[立即执行];
    B -->|否| D{是否有COM文件};
    D -->|是| E[执行COM文件];
    D -->|否| F{是否有EXE文件};
    F -->|是| G[执行EXE文件];
    F -->|否| H{是否有BAT文件};
    H -->|是| I[执行BAT文件];
    H -->|否| J{路径中是否还有目录};
    J -->|是| D;
    J -->|否| K[未找到可执行文件];
    C --> L[结束];
    E --> L;
    G --> L;
    I --> L;
    K --> L;
2.2 程序段前缀(PSP)

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

2.3 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       ; must be before 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

变量通常位于主程序之后,因为没有单独的数据段。也可以在开头放置 JMP 指令跳过数据到第一个实际指令。

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

2.4 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;

也可以使用 exehdr 程序在 EXE 程序编译后修改 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 |
| Header size (para) | 20 | 32 |
| Relocation table offset | 1E | 30 |
| Relocation entries | 1 | 1 |

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

3. 中断处理

在这部分,我们将探讨如何通过安装中断处理程序(中断服务例程)来自定义BIOS和MS - DOS。BIOS和MS - DOS包含简化输入/输出以及基本系统任务的中断处理程序,如INT 10h用于视频操作、INT 16h用于键盘操作、INT 21h用于MS - DOS服务等。同时,操作系统的一组中断处理程序会响应硬件中断,MS - DOS允许我们用自己的程序替换这些服务例程。

3.1 中断处理程序的用途

编写中断处理程序可能有多种原因:
- 热键激活 :希望程序在按下热键时激活,即使用户正在运行其他应用程序。例如,Borland的SideKick程序能够在按下特殊组合热键时弹出记事本或计算器。
- 提供更完善的服务 :可以替换MS - DOS的默认中断处理程序,以提供更完整的服务。比如,当CPU尝试除以零时,会触发除零中断,但程序没有标准的恢复方法,我们可以自定义处理程序。
- 错误处理 :可以用自己的程序替换MS - DOS的关键错误处理程序或Ctrl - Break处理程序。MS - DOS的默认关键错误处理程序会使程序中止并返回MS - DOS,而自定义处理程序可以从错误中恢复,让用户继续运行当前应用程序。
- 硬件中断处理 :用户编写的中断服务程序可以比MS - DOS更有效地处理硬件中断。例如,PC的异步通信处理程序(INT 14h)不执行输入/输出缓冲,输入字符如果在另一个字符到达之前未从端口复制就会丢失,而内存驻留程序可以等待传入字符生成硬件中断,从端口输入字符并存储在循环缓冲区中,使应用程序无需花费大量时间反复检查串行端口。

3.2 中断向量表

MS - DOS灵活性的关键在于位于RAM前1024字节(位置0:0到0:03FF)的中断向量表。表中的每个条目(称为中断向量)是一个32位的段 - 偏移地址,指向现有的服务例程之一。

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

以下是中断向量表条目的示例:
| Interrupt Number | Offset | Interrupt Vectors |
| ---- | ---- | ---- |
| 00 - 03 | 0000 | 02C1:5186 0070:0C67 0DAD:2C1B 0070:0C67 |
| 04 - 07 | 0010 | 0070:0C67 F000:FF54 F000:837B F000:837B |
| 08 - 0B | 0020 | 0D70:022C 0DAD:2BAD 0070:0325 0070:039F |
| 0C - 0F | 0030 | 0070:0419 0070:0493 0070:050D 0070:0C67 |
| 10 - 13 | 0040 | C000:0CD7 F000:F84D F000:F841 0070:237D |

3.3 执行中断处理程序

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

3.4 硬件中断

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

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

IRQ级别 :PC上的许多不同设备都可以触发中断,每个设备根据其中断请求级别(IRQ)具有优先级,级别0优先级最高,级别15优先级最低。低级别中断不能中断正在进行的高级别中断。例如,如果通信端口1(COM1)试图中断键盘中断处理程序,它必须等待键盘处理程序完成。同时,两个或多个同时发生的中断请求将根据其优先级级别进行处理,中断的调度由8259 PIC处理。

以下是IRQ分配表:
| IRQ | Interrupt Number | Description |
| ---- | ---- | ---- |
| 0 | 8 | 系统定时器(每秒18.2次) |
| 1 | 9 | 键盘 |
| 2 | 0Ah | 可编程中断控制器 |
| 3 | 0Bh | COM2(串行端口2) |
| 4 | 0Ch | COM1(串行端口1) |
| 5 | 0Dh | LPT2(并行端口2) |
| 6 | 0Eh | 软盘控制器 |
| 7 | 0Fh | LPT1(并行端口1) |
| 8 | 70h | CMOS实时时钟 |
| 9 | 71h | (重定向到INT 0Ah) |
| 10 | 72h | (可用)声卡 |
| 11 | 73h | (可用)SCSI卡 |
| 12 | 74h | PS/2鼠标 |
| 13 | 75h | 数学协处理器 |
| 14 | 76h | 硬盘控制器 |
| 15 | 77h | (可用) |

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

然后,INT 9的BIOS例程会按以下顺序执行:
1. 重新启用硬件中断,以免影响系统定时器。
2. 从键盘端口输入扫描码,尝试将其转换为ASCII字符,或分配一个等于零的ASCII码,然后将扫描码和ASCII码存储在BIOS数据区的32字节循环缓冲区中。

下面是硬件中断处理流程的mermaid流程图:

graph TD;
    A[硬件设备产生中断] --> B[8259 PIC发送INTR信号];
    B --> C{外部中断是否禁用};
    C -->|否| D[CPU将Flags寄存器压入堆栈];
    C -->|是| E[等待中断启用];
    D --> F[清除中断标志];
    F --> G[将当前CS和IP压入堆栈];
    G --> H[查找中断向量表条目并更新CS和IP];
    H --> I[执行中断服务例程];
    I --> J[重新启用硬件中断];
    J --> K[中断服务例程完成];
    E --> B;
    K --> L[恢复程序执行];
4. 总结

本文深入探讨了MS - DOS编程中的几个关键方面:
- 段定义 :通过 SEG 运算符获取段地址,利用段覆盖和段组合功能,将程序划分为不同的逻辑段,如代码段、数据段和堆栈段,有助于大型程序的模块化开发和管理。
- 运行时程序结构 :了解了MS - DOS的命令处理流程,以及 COM EXE 程序的特点和加载方式。 COM 程序结构简单,但内存使用不够灵活; EXE 程序可以包含多个段,内存分配更具弹性。
- 中断处理 :掌握了中断处理程序的用途和执行方式,以及硬件中断的工作原理和IRQ级别。通过自定义中断处理程序,可以实现热键激活、错误处理和更高效的硬件中断处理。

掌握这些知识对于深入理解MS - DOS编程,开发高效、稳定的汇编语言程序具有重要意义。在实际应用中,可以根据具体需求灵活运用这些技术,提高程序的性能和功能。

内容概要:本文档围绕直流微电网系统展开,重点介绍了包含本地松弛母线、光伏系统、锂电池储能和直流负载的Simulink仿真模型。其中,光伏系统采用标准光伏模型结合升压变换器实现最大功率点跟踪,电池系统则基于锂离子电池模型双有源桥变换器进行充放电控制。文档还涉及在dq坐标系中设计直流母线电压控制器以稳定系统电压,并实现功率协调控制。此外,系统考虑了不确定性因素,具备完整的微电网能量管理和保护机制,适用于研究含可再生能源的直流微电网动态响应稳定性分析。; 适合人群:电气工程、自动化、新能源等相关专业的研究生、科研人员及从事微电网系统仿真的工程技术人员;具备一定的MATLAB/Simulink使用【直流微电网保护】【本地松弛母线、光伏系统、电池和直流负载】【光伏系统使用标准的光伏模型+升压变换器】【电池使用标准的锂离子电池模型+双有源桥变换器】Simulink仿真实现基础和电力电子知识背景者更佳; 使用场景及目标:①构建含光伏储能的直流微电网仿真平台;②研究微电网中能量管理策略、电压稳定控制保护机制;③验证在不确定条件下系统的鲁棒性动态性能;④为实际微电网项目提供理论支持仿真依据; 阅读建议:建议结合文中提到的Simulink模型MATLAB代码进行实操演练,重点关注控制器设计、坐标变换系统集成部分,同可参考提供的网盘资源补充学习材料,深入理解建模思路参数整定方法。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值