目录
第六章:包含多个段程序
📌 主要内容
本章介绍了如何在汇编程序中使用多个独立的段来管理代码、数据和栈,提升程序的结构化和可维护性。
重点包括:
- 在代码段中使用数据;
- 在代码段中使用栈;
- 将数据、代码、栈放入不同的段;
- 实验5:编写、调试具有多个段的程序。
🧠 笔记重点
6.1 在代码段中使用数据
-
单段写法
将数据直接定义在代码段中:
assume cs:code code segment data dw 1234h ;数据 start: ;代码 code ends edd start- 优点:实现简单;
- 缺点:
- 可读性差:代码与数据混杂,不易维护;
- 段寄存器统一:只能用
CS访问,缺乏灵活性; - 扩展性低:无法在运行时调整数据段大小。
-
汇编器如何解析
data相对于当前段的偏移OFFSET data(汇编时已计算);- 真实物理地址 =
CS × 16 + OFFSET data; - 访问仍遵循段基址 + 偏移的方式。
💡 小结:虽然可以在代码段中定义数据,但不符合“职责分离”原则,建议将数据放入单独的数据段。
6.2 在代码段中使用栈
-
代码段中定义栈空间的写法
在程序中,如果暂时不使用
push或pop指令,栈空间也可以直接放在代码段中定义,例如:assume cs:code code segment mov ax, 0 dw 0, 0, 0, 0 ; 模拟栈空间 mov bx, 0 code ends end start- 这段程序没有定义独立的栈段;
- 其中的
dw 0, 0, 0, 0实际上是 4 个字(共 8 字节)的数据,用于暂时代替栈空间; - 如果程序中不使用
push和pop指令,CPU 并不会涉及SS和SP寄存器; - 因此也就不需要设置专门的栈段。
-
缺点与局限性
- 不清晰:数据和代码混合在同一个段内,结构混乱;
- 无法使用硬件栈机制:若后续加入
push或pop,将必须重构; - 存在安全隐患:极易因栈空间不足或地址错误造成运行时问题;
- 不利于扩展与维护:大型程序无法接受代码与栈混合布局。
⚠️ 建议:若程序使用了 push / pop,应当始终采用独立的栈段,并初始化 SS 和 SP。这是规范化汇编程序结构的基本要求。
-
推荐的标准写法
使用独立栈段定义,并设置 SS 和 SP:
assume cs:code, ss:stack stack segment dw 100 dup(0) ; 分配 200 字节栈空间 stack ends code segment start: mov ax, stack mov ss, ax mov sp, 200h ; 后续代码 code ends end start- 该写法使用
stack segment为程序提供专用栈区; - 设置
SP = 200h,表示栈顶从栈段末尾开始(栈向下增长); - 只要后续代码中用到
push或pop指令,就必须使用此结构; - 这也是实际开发中推荐的做法。
- 该写法使用
6.3 将数据、代码、栈放入不同的段
-
多段结构写法
为了结构清晰、职责明确,汇编程序应将代码、数据、栈分别定义在不同段中。这种方式符合结构化编程思想,示例如下:
assume cs:code, ds:data, ss:stack data segment dw 1111h, 2222h, 3333h ; 三个字数据 data ends stack segment dw 100 dup(0) ; 分配 200 字节栈空间 stack ends code segment start: mov ax, data mov ds, ax ; 初始化 DS 寄存器 mov ax, stack mov ss, ax ; 初始化 SS 寄存器 mov sp, 200h ; 设置栈顶 ; 以下为数据访问与栈操作指令 mov ax, [0] push ax mov ax, [2] push ax pop bx pop cx mov ax, 4C00h int 21h ; 程序结束 code ends end start -
说明:
assume指令用于告诉汇编器:哪个段寄存器对应哪个逻辑段;mov ax, data/mov ds, ax:将data segment的段地址送入DS,用于数据访问;mov ax, stack/mov ss, ax/mov sp, 200h:配置栈段与栈顶;- 栈的操作通过
SS:SP访问,数据访问通过DS:[offset]完成; - 段寄存器
CS是由程序加载器在程序开始运行前设置的,通常无需显式配置。
-
优点:
- 职责清晰:代码、数据、栈互不干扰,逻辑明确;
- 结构规范:便于调试、阅读和维护;
- 支持扩展:多段结构为模块化和子程序调用打下基础;
- 利于调试:用
Debug工具可轻松追踪每个段的起始地址与内容。
-
访问原理复习:
- 实际访问的物理地址 = 段寄存器 × 16 + 偏移地址;
- 所有内存访问本质上都是从某个段开始加上一个偏移;
- 示例:
mov ax, [2]实际是mov ax, ds:[0002h]。
💡 汇编程序中段的划分是模块化设计的前提,掌握分段和段寄存器的配合,是理解函数调用、子程序管理、中断和系统接口的基础。
🧪 实验5:编写、调试具有多个段的程序
-
实验目的:
- 掌握
ASSUME指令的作用; - 掌握多段程序中段寄存器(
DS、SS、SP)的初始化方法; - 能够正确组织代码段、数据段和栈段;
- 了解
Debug工具下多段程序的执行流程与调试技巧。
- 掌握
-
实验程序结构:
assume cs:code, ds:data, ss:stack data segment dw 1234h, 5678h data ends stack segment dw 100 dup(0) stack ends code segment start: mov ax, data mov ds, ax ; 初始化数据段 mov ax, stack mov ss, ax ; 初始化栈段 mov sp, 200h ; 设置栈顶 mov ax, [0] ; 从 data 段偏移 0 读取第一个字 add ax, [2] ; 加上第二个字 push ax ; 压入栈 pop bx ; 弹出到 BX mov ax, 4C00h int 21h ; 结束程序 code ends end start -
说明:
data segment中定义两个数据字,地址分别为偏移0和2;stack segment分配了 100 个 word(即 200 字节)作为栈空间;- 通过
mov ax, data等语句将段地址送入相应的段寄存器; - 程序将两个数据相加,并使用
push/pop实现简单的入栈出栈操作。
-
调试提示:
- 使用
Debug工具加载.exe文件:debug 程序名.exe - 进入单步执行模式,观察:
DS、SS是否被正确赋值;SP初始值是否为200h;- 内存栈区是否正确入栈/出栈;
AX与BX值是否符合逻辑;
- 使用
d ds:0查看数据段内容; - 使用
d ss:sp查看栈内容变化。
- 使用
-
关键点总结:
ASSUME并不会生成机器码,它只是告诉汇编器“某个段寄存器在程序中将对应哪个逻辑段名”;- 若使用
push或pop,必须先正确初始化SS和SP; - 多段程序更接近真实操作系统下程序结构,是今后学习模块调用、中断和子程序的基础。
✅ 实验5是对本章理论的完整实践,从段定义到寄存器初始化,再到栈操作与调试,全面锻炼了结构化汇编程序的组织能力。
✅ 检测点 6.1
-
mov cs:[bx],ax -
cs 32 pop cs:[bx]
🔍 拓展理解
- 段覆盖前缀:可在指令前加
CS:、DS:、ES:、SS:强制指定段,默认只有MOV SP/MOV BP以SS为默认段; - 段寄存器刷新限制:执行
PUSH SS或POP SS时,有额外时序约束; - 未来应用:子程序调用(
CALL/RET)、中断向量表、动态链接库等均依赖段机制; - 现代模式对比:保护模式取消段选择机制,改用分页与描述符,但段的“分离思想”仍影响 OS 设计。

1444

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



