
简介《IBM-PC汇编语言程序设计》习题答案文档面向正在学习沈美明、温冬婵版教材的初学者及备考者用于课后对照、验证解题思路与查漏补缺。内容覆盖前两章及后续章节的典型习题涉及十进制/二进制/十六进制数制转换、8位补码运算与OF/CF标志判断、位运算以及ASCII码与字符串处理等核心知识点并提供汇编程序设计的分段、寻址与存储器单元读取等基础练习可作为章节自测与复习的参考答案。包体为单个doc文档共1个文件压缩包约524KB轻量易下载适合配合教材按知识点翻阅。目前已有113人浏览学习。借助这份答案学习者可快速核对数制转换结果、补码计算过程及标志位变化也能通过ASCII码对照、物理地址计算等实例加深对汇编语言数据表示、存储结构和基本运算规则的理解便于系统巩固基础。1. 为什么「IBM-PC汇编语言程序设计答案」值得你自己动手敲一遍搜「IBM-PC汇编语言程序设计答案」的人一半是在找沈美明、温冬婵那本清华教材的课后习题参考另一半是刚接触汇编语言、想借习题验证自己到底有没有看懂段、寄存器、中断这些概念。这本书的配套答案网上一搜一大把但几乎没人告诉你汇编语言的答案和 C 语言答案不一样它不是抄进 IDE 就能编译通过的东西而是一段段依赖 16 位实模式环境、段寄存器状态和 DOS 中断的资源敏感型程序。抄错一个寄存器程序能跑结果完全不对抄对全部代码环境不对连编译都过不去。所以这篇文章不讲「去哪儿找答案」而是讲更值钱的路径——把题目答案变成一套你能复现、能验证、能改参数的汇编程序设计实践方案。我会从环境搭建、题型拆解、DEBUG 调试三个方向展开最后把常见翻车点一次性说完。适合三类人正在上课赶作业的学生、想系统入门汇编语言的初学者以及需要给初学者讲明白运行原理的教辅人员。先立一个结论答案的核心价值不在「对」而在「你能看着它说出为什么」——带着这个目标往下读。2. 先搭一套能跑的 IBM-PC 汇编环境DOSBox 与 MASM 的最小配置2.1 为什么选 DOSBox 而不是在 Windows 上硬跑现代 Windows 10/11 的 64 位系统已经不支持 16 位实模式程序连 debug.exe 都被移除了。书里绝大多数习题都假设你在 DOS 环境下运行直接双击编译出的 EXE 会提示「不是有效的 Win32 应用程序」。常见做法是装 DOSBox 这样的模拟器它把一个完整的 DOS 环境打包成一个小程序运行稳定且能挂载本地目录——这意味着你可以在 Windows 里用熟悉的编辑器写汇编源文件再进 DOSBox 里编译运行。另外一个被忽略的点是DOSBox 默认模拟的是 8086/80286 环境正贴合这本教材的教学范围。有些书上讲到的段最大 64KB、地址用段:偏移表示在现代 CPU 上已经不容易直观感受到但 DOSBox 里完全保留了这个行为算得上是一台不用花钱的「教学 IBM-PC 兼容机」。说到汇编语言入门教程的编写逻辑几乎都从 DEBUG 里的几条指令开始而 DOSBox 里正好自带 DEBUG.EXE这样你从第一条 MOV 指令到中断调用都处于同一个环境不用切换工具。2.2 挂载目录、编译与链接的完整流程先创建目录结构。在 Windows 下建一个D:\asm目录里面再建MASM子目录放编译工具WORK子目录放源代码。把 MASM 5.0 的四个核心文件放进去MASM.EXE、LINK.EXE、DEBUG.EXE、EDIT.COM。启动 DOSBox 后依次执行mount c d:\asm c: cd \work edit hello.asm这段命令把 Windows 的d:\asm目录挂载成 DOS 的 C 盘。edit打开全屏编辑器在里面写第一段代码。先写一个最简程序验证环境用 INT 21H 的 09H 功能输出字符串; hello.asm -- 最简 DOS 汇编验证环境 data segment msg db Hello, ASM!$ ; $ 是 INT 21H 09H 的结束标志 data ends code segment assume cs:code, ds:data start: mov ax, data mov ds, ax ; 设置数据段寄存器 lea dx, msg ; DX 字符串首地址 mov ah, 09h int 21h ; 调用 DOS 输出功能 mov ax, 4c00h int 21h ; 退出返回 DOS code ends end start保存退出后在work目录下依次执行编译和链接masm hello.asm; link hello.obj; hello.exe如果看到屏幕输出Hello, ASM!说明整套链路已经通了。这里的masm hello.asm;末尾分号表示一路接受默认选项——DOSBox 里直接敲回车确认也行。链接时如果 LINK 提示找不到目标文件检查一下第二步cd \work是否真的进入了存放源文件的目录。2.3 参数说明与常用调试开关MASM 编译时有两个可调参数值得记住。masm hello.asm,,,;四个逗号代表生成 .OBJ 文件的同时不生成 .LST 列表文件和 .CRF 交叉引用文件——需要看代码在内存里的分布时改成masm hello.asm,hello.lst,,;会多生成一个清单文件。这个文件里面标注了每条指令的偏移地址后面用 DEBUG 设断点时会频繁用到我一般建议编译时顺手生成masm hello.asm hello.lst hello.crf;LINK 链接时也有一个易踩的选项默认链接方式会询问是否生成 MAP 文件。如果只想快速出 EXE直接分号跳过但如果想验证段与段之间的布局生成 MAP 文件来查看 START 地址会方便很多。DOSBox 默认的内存大小是 16MB对本书习题完全够用不需要调 MEMSIZE 参数。如果你运行一些需要扩充内存的题比如涉及 EMM 的少数题目可以在 DOSBox 配置文件里把memsize调高但大多数教材习题用不到反而会掩盖段溢出的问题。3. 把书后题型拆成四类可复用的代码骨架3.1 查表与代码转换XLAT 指令的边界在哪教材里有一类经典题把十六进制数转成 ASCII 码或者把 BCD 码转成七段数码管显示码。这类题的核心段是查表而查表就是定义一个连续字节序列用 XLAT 指令按索引取数。; hex2ascii.asm -- 查表法将 0-F 转为 ASCII data segment table db 0123456789ABCDEF ; 16 字节查表 hex_val db 0Ah ; 待转换数 10 result db 1 dup(?), $ data ends code segment assume cs:code, ds:data start: mov ax, data mov ds, ax mov bx, offset table ; BX 表首地址 mov al, hex_val ; AL 待查索引 10 xlat ; 执行后 AL A mov result, al lea dx, result mov ah, 09h int 21h mov ax, 4c00h int 21h code ends end start逻辑说明XLAT 是一条单字节指令执行时用DS:BX作为表基址把AL当作偏移量从表中取出一个字节放回AL。最关键的前提是表地址必须放在BX索引必须提前放AL。许多答案代码乍看没问题但运行结果不对十有八九是这两处丢了其中一个——尤其是lea bx, table和mov bx, offset table混用但忘记先初始化DS的情况。参数注意查表长度不要超过 256 字节因为 AL 最大偏移是 255。如果想查的字表更大得改用BX加偏移的手动寻址而不是 XLAT。3.2 循环与字符串处理CX 是最容易翻车的寄存器数据块移动、字符串比较、数组求和这类题核心是循环控制。教材里反复强调 CX 存放循环次数而实际写答案时最常见的翻车点恰恰是 CX 被内部调用悄悄改掉。; strmove.asm -- 将 string1 复制到 string2 data segment src db IBMPC-ASM$ len equ $-src ; 汇编期常量$ 表示当前位置 dst db len dup(?) data ends code segment assume cs:code, ds:data, es:data start: mov ax, data mov ds, ax mov es, ax ; 字符串操作需 ES lea si, src lea di, dst mov cx, len ; CX 串长度 next: mov al, [si] mov [di], al inc si inc di loop next ; CX-- 且 CX≠0 时跳转 mov ax, 4c00h int 21h code ends end start循环体里的loop next会隐式修改 CX这是指令行为不是 bug。真正的坑是如果你在循环体内部为了做别的事把 CX 当临时寄存器用循环次数就会被破坏。书后有些参考答案为了节约寄存器会在循环内部复用 CX 存临时值然后在loop之前又push cx/pop cx恢复——初学者抄这种代码时很容易漏掉配对导致死循环或复制长度不对。一个更稳妥的做法是循环次数单独放内存变量或用 SI/DI 的差值做终止判断。比如复制串可以用cmp si, offset src_end判结束完全绕开 CX。前提是源串定义时留出可比较的结束地址。3.3 子程序与参数传递写在注释里比省几条 PUSH 更重要子程序设计题的评分点通常在三个位置入口参数怎么传、现场怎么保护、出口参数放哪。教材给的答案倾向于用寄存器传参——速度快、代码短但对阅读者不友好。; subr.asm -- 子程序计算 AX 的绝对值并返回 code segment assume cs:code abs_val proc ; 入口AX 待处理数 ; 出口AX 绝对值 cmp ax, 0 jge done ; AX0直接返回 neg ax ; AX0取补 done: ret abs_val endp start: mov ax, -5 call abs_val ; 调用后 AX 5 mov ax, 4c00h int 21h code ends end start参数说明这个例子用 AX 既传参又返回值简单但有个副作用——调用完AX 里原来的值被覆盖。如果调用方后续还要用原 AX必须在 call 之前 push ax。书里更复杂的子程序题比如十六进制数打印、多字节加法会涉及 DX、CX 同时传参这时要特别注意哪些寄存器是调用方保存、哪些是子程序保存。Intel 的 8086 没有硬件规定完全靠程序员约定——我一般在自己代码里统一采用「子程序内修改的寄存器必须先 push 再 pop」这样调用方永远不需要猜。3.4 输入输出与中断调用INT 21H 单字符输入有两副面孔大多数习题的 I/O 都通过 INT 21H 实现但初学者最容易在这里遇到玄学问题。01H 功能「读入一个字符并回显」和 07H 功能「读入一个字符不回显」均将结果放 AL。很多题要求输入密码或选择菜单答案贴的是 01H——如果题目明确说不显示输入字符要换 07H。更大的坑是输入的是 ASCII 码而不是数值本身。比如输入 0~9AL 得到的是 30H~39H要做减法sub al, 30h才能当数字用。; input.asm -- 录入两个一位十进制数相加 data segment prompt db Input two digits: $ result db Result: $ data ends code segment assume cs:code, ds:data start: mov ax, data mov ds, ax lea dx, prompt mov ah, 09h int 21h mov ah, 01h int 21h ; AL 第一个字符 sub al, 30h ; ASCII - 数值 mov bl, al mov ah, 01h int 21h ; AL 第二个字符 sub al, 30h add al, bl ; AL 和 add al, 30h ; 数值 - ASCII mov dl, al mov ah, 02h int 21h ; 单字符输出 mov ax, 4c00h int 21h code ends end start这个例子里出现两次add al, 30h的操作容易让人困惑。第一次sub al, 30h是输入的字符转成真值第二次add al, 30h是把计算结果转回可显示的 ASCII。两者方向相反漏掉任何一个的运行结果都是乱码或偏移数值。还有一点最终结果存 AL 后输出用了 02H 功能、把字符放 DL——AL 不与输出功能共用寄存器这个细节答案里经常少写运行时就表现为「计算出结果但没有输出」。4. 用 DEBUG 验证答案三条必会指令加一个断点习惯4.1 加载程序进入调试而不是直接运行拿到一份参考答案别急着exe跑完看输出。输出正确只能说明最终结果对了中间过程对错根本看不见。调试的正确姿势是把 EXE 加载进 DEBUGdebug hello.exeDEBUG 加载后用-r查看各寄存器初值。注意看 CS、DS、SS、SP 这几个关键值——它们由 EXE 头部的重定位信息自动设置。如果 DS 和 CS 相差较远说明程序有独立的堆栈段和数据段这是正常现象如果 DS 显示为一串 0 或乱值说明源文件里段定义有问题。-r的输出还包含 IP 起始地址。对照编译时生成的 .LST 清单文件能确认第一条指令是否落在start:标号对应的偏移位置。如果 IP 指向的字节不是预期指令最常见原因是源文件里忘记写end start导致入口地址指向了段定义的第一条伪指令。4.2 单步跟踪用 T 还是 G视情况而定DEBUG 里-t是单步跟踪每执行一条指令就停一次并显示寄存器状态。对于验证循环类题目-t有个致命缺陷遇到 INT 21H 这类中断时会一头扎进 DOS 内部的中断处理例程你会在屏幕上看到大量看不懂的内存操作指令一路-t按到手指酸。DOSBox 下这个问题尤其明显。正确做法是用-g配合断点地址跳过中断-u 100 10C ; 先反汇编查看当前地址附近指令 -g 115 ; 运行到偏移 115 处暂停断点地址来自程序加载后的 CS 段内偏移。-u输出的每一行左边是段地址:偏移右边是对应指令。想从 INT 21H 之后继续调试就在 INT 指令的后一条指令偏移处设断点。判断循环是否正确执行可以这样操作在 LOOP 指令所在偏移设断点运行后反复观察 CX 的变化。比如一段mov cx, 5的循环每次-g到断点时 CX 应比上次少 1。如果跑到第 3 次 CX 变成 0xFFFF说明循环体内有指令修改了 CX——回头看代码是不是把 CX 挪作他用了。4.3 内存区与结果的核对程序运行完结果常存在数据段变量中。用-d查看内存区内容比如调试本章查表的转换结果-d ds:0000 0010这条命令显示数据段偏移 0 到 0x10 的字节内容每行右侧还有 ASCII 字符对照区。假如结果应该是A0x41这一行里应该看到第二字节位置出现41。这里的检查方式特别适合考题中的「填写空白」类型先算出期望的存储结果再对照内存确认实际写入值。记录一次完整的-t运行过程能加深对指令的理解但对验证答案来说确认「某时刻某寄存器/内存值是否符合预期」比「从第一条到最后一个 LOOP 的单调操作」高效得多。5. 常见问题避坑与排查照着答案抄完还是跑不对怎么办5.1 链接时提示 0 个目标文件或找不到 OBJ现象执行masm hello.asm;后 MASM 报错退出或 LINK 提示找不到 HELLO.OBJ。原因MASM 5.0 对源文件语法要求较严最常见的错误是标号后缺冒号、伪指令拼写错误以及源文件路径含中文或空格。DOSBox 挂载路径后看不到中文目录而 MASM 在遇到第一个语法错误后会停止生成 OBJ。解决检查源文件是否与 MASM.EXE 在同一目录或当前工作目录看 MASM 给出的错误行号并对照修改养成先运行dir确认目录内容和当前盘符的习惯。一个更隐蔽的因素是 Windows 下用记事本保存文件时自动加了 BOM 头MASM 会把 BOM 当非法字符报错——用edit或 VS Code 的无 BOM UTF-8 编码保存即可。5.2 程序运行时死循环或按 CtrlC 都出不来现象运行 EXE 后光标一直在闪按什么键都没反应。原因循环次数寄存器 CX 初始化有问题或 LOOP 的跳转目标指向了循环体之前的位置也可能数据段里有$缺失导致 INT 21H 的字符串输出一直找到结尾标记。解决先用 DEBUG 的-g配合断点定位死循环位置。若-u显示程序卡在 LOOP 和一条跳转指令之间来回跳检查 CX 的初值——数一数循环要执行几次对比mov cx, n中的 n。另一种常见情况是循环体内INC SI该写INC DI导致指针始终不推进循环永远不结束。5.3 输出一片乱码或完全空白现象程序编译链接都成功输出却是「烫烫烫」或半个汉字样式的乱码有时什么都不显示。原因字符串定义时数据格式与输出中断不匹配。INT 21H 09H 功能要求字符串以$结尾如果msg db Hello, 0用 0 结尾程序会一直读到内存里的随机字符才停下。另一种情况是误把字符串定义成 DW 数据类型——每个字符占两个字DOSBox 环境下输出的字符串整体错位。解决检查所有用 09H 输出的字符串定义确认最后一个可见字符后是$。再查 ASCII 与数值混用问题直接输出一个数字必须加30H转成字符否则输出的是控制字符屏幕上看起来像空白。5.4 单步跟踪时 DS 值看起来不对现象DEBUG 里-r显示 DS 与源程序里的数据段名不一致访问变量地址时数据对不上。原因DEBUG 加载 EXE 后DS 指向程序段前缀 PSPProgram Segment Prefix这是 DOS 为每个程序自动建立的控制块偏移 0~255 处是命令行参数等信息。源程序里即使写了数据段也要执行mov ax, data和mov ds, ax两条指令后 DS 才真正指向数据段。解决不要直接用加载后的 DS 访问变量。要么在程序的 START 部分紧跟两条 MOV 指令设置 DS要么用-g执行到lea dx, msg这条指令之后再查看 DX 的值并-d对应内存。5.5 编译时报段超 64K 或程序过大现象MASM 编译显示类似「Segment exceeds 64K」的错误信息。原因8086 实模式下段寄存器是 16 位的地址范围天然被限制在 64KB。当题目的数据表、缓冲区定义过多累加超过了这个界限比如定义一个buf db 8000h dup (0)再加其他变量就会超。解决把大数据拆成两个段分别定义在程序里将对应段地址手动赋给 DS。还有一种情况是伪指令org 100h被错误用在多段程序中导致编译器的段内偏移计算溢出——这类程序应在 DOS 下用 .COM 格式编译使用 MASM 时要在源码顶部声明.model small和ASSUME正确段注册。实在拿不准就检查编译的源文件类型.ASM 默认生成 EXE多段只有.COM需要用org 100h并是单段格式两者混用是另一个常见的超限来源。6. 把答案变成自己的资产三条逆向拆解技巧要把这本书的课后题答案真正吃透光跑通是不够的。我自己做完一章题后会做三件事第一把答案编译时生成的 .LST 文件存好按章节命名第二用 DEBUG 在关键指令处设断点把每次断点时的寄存器状态截图或抄下来标在题号旁边第三故意对代码做「破坏性修改」——比如删掉一条 PUSH/POP、改错一个源操作数然后看 DEBUG 里哪个寄存器先变异常。这个操作连着做三章之后你对段寄存器、标志位和中断入口的直觉会明显好过只看书不调试的状态。对一份参考答案本身的甄别也有技巧。书后答案偶尔会有笔误不必全盘依赖。我常用的方法是把同一道题在纸上手写一遍参考答案然后不看原答案、用自己思路重新写一版最后对比两份版本的差异——如果原答案与你版本的最大区别只在于寄存器选用或指令替换那么两份基本等价如果对方的思路在逻辑上与你的完全不同比如用移位代替乘法那说明这道题考察的就是你对指令集理解深度这种差异值得单独整理成笔记。这些笔记随着章节推进会自然积累成自己的「错题索引」比到处找答案更可靠。最后说句实在话汇编语言程序设计的真功夫从来不在「能抄对答案」而在「看着一条不熟悉的指令能在脑子里推演它执行前后哪些寄存器、内存和标志位发生了变化」。我当年图省事把书后答案抄进 MASM 就跑结果到考试要求手写运行结果时完全懵了——寄存器变化靠背根本背不牢。后来我老老实实为每章题写一遍、调一遍、破坏一遍再修一遍后面遇到任何资料上的代码都能自然地用 DEBUG 拆开验证。希望帮到你少走这段弯路。本文还有配套的精品资源点击获取